2008 02/15 - dbg_log() support stdout and file out
2010 08/11 - thread safe 지원, dbg_log() support syslog
BSD License 지정
2010 09/05 - modify wrong print time
modify wrong unsetTrace() proto type
modify _AUGMENT_MSG format and order
add printing thread id in _AUGMENT_MSG
add weak symbols for multi definition global variable
add checking wrong directive case
support c89 standard without thread safe case
2011 01/02 - modify logout module to be run as atomic
(before printing, aggregate all text in a buffer)
add pid, tid, processname filter
add DEBUG_EXT_# Policies
*/
/*
Copyright (c) <2010>, <copyright initproc@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by the <initproc@gmail.com>.
4. Neither the name of the <initproc@gmail.com> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <initproc@gmail.com> ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <initproc@gmail.com> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _DEBUG_LOG_MODULE
#define _DEBUG_LOG_MODULE
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#ifdef _DEBUG_THREAD_SAFE
#include <pthread.h>
#define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */
#include <unistd.h>
#endif
#include <sys/syscall.h> /* For SYS_xxx definitions */
/* debug run policy */
/* #define _DEBUG */
/* Apply debug codes to be thread safe */
/* #define _DEBUG_THREAD_SAFE */
#if !defined(_DEBUG) && defined(_DEBUG_THREAD_SAFE)
#error "_DEBUG must be set if _DEBUG_THREAD_SAFE defined"
#endif
/* debug level policy */
#define DEBUG_ALL INT_MAX
#define DEBUG_NONE 0
#define DEBUG_INFO 1
#define DEBUG_EXCEPT 2
#define DEBUG_ERROR 4
#define DEBUG_FATAL 8
#define DEBUG_EXT_1 16
#define DEBUG_EXT_2 32
#define DEBUG_EXT_3 64
#define DEFAULT_DEBUG_LEVEL DEBUG_ALL
pid_t pid_filter __attribute__((weak)) = -1;
pid_t tid_filter __attribute__((weak)) = -1;
char *process_filter __attribute__((weak)) = NULL;
char *process_name __attribute__((weak)) = NULL;
/* debug level */
unsigned int dbg_lv __attribute__((weak)) = DEFAULT_DEBUG_LEVEL;
/* debug print mode */
#define PRINT_MODE_STDOUT 1
#define PRINT_MODE_FILE 2
#define PRINT_MODE_SYSLOG 3
unsigned char print_mode __attribute__((weak)) = PRINT_MODE_STDOUT;
/* for logging out to file */
FILE *dbg_fp __attribute__((weak)) = NULL;
#ifdef _DEBUG_THREAD_SAFE
pthread_rwlock_t dbg_lock __attribute__((weak)) = PTHREAD_RWLOCK_INITIALIZER;
#define dbg_write_lock(lock) pthread_rwlock_wrlock (lock);
#define dbg_read_lock(lock) pthread_rwlock_rdlock (lock);
#define dbg_read_unlock(lock) pthread_rwlock_unlock (lock);
#define dbg_write_unlock(lock) pthread_rwlock_unlock (lock);
#else
#define dbg_write_lock(lock)
#define dbg_read_lock(lock)
#define dbg_read_unlock(lock)
#define dbg_write_unlock(lock)
#endif
/* augment message (thread id, time, file, func, line) */
#define _AUGMENT_MSG
#ifndef _DEBUG_THREAD_SAFE
#define loc_time(now, tm_arg) memcpy(tm_arg, localtime(now), sizeof(struct tm))
#else
#define loc_time(now, tm_arg) localtime_r(now, tm_arg)
#endif
#define now_str(buf, max) \
do { \
time_t now; \
struct tm tm; \
now = time (NULL); \
loc_time(&now, &tm); \
strftime (buf, max, "%H:%M:%S", &tm); \
} while (0)
#ifdef _DEBUG_THREAD_SAFE
#define AUGMENT_LOG "tid(%ld):%s:%s():%d: "
#define AUGMENT_LOG_ARGS syscall (SYS_gettid), __FILE__, __func__, __LINE__
#else
#define AUGMENT_LOG "%s:%s():%d: "
#define AUGMENT_LOG_ARGS __FILE__, __func__, __LINE__
#endif
static inline void setProcessname()
{
FILE *fp = NULL;
char pathbuf[PATH_MAX] = { 0x00,} , cmdline_buf[256] = { 0x00, };
if (process_filter == NULL)
return ;
snprintf (pathbuf, sizeof(pathbuf), "/proc/%d/cmdline", getpid ());
fp = fopen (pathbuf, "ro");
if (fp == NULL)
return ;
fscanf (fp, "%s\n", cmdline_buf);
fclose(fp);
if (cmdline_buf[0])
{
if (process_name)
{
free (process_name);
process_name = NULL;
}
if (process_name == NULL)
process_name = strndup (cmdline_buf, 256);
}
}
static inline void setProcessfilter(const char *name)
{
dbg_write_lock(&dbg_lock);
if (name)
{
if (process_filter)
{
free (process_filter);
process_filter = NULL;
}
if (process_filter == NULL)
{
process_filter = strndup (name, 256);
setProcessname ();
}
}
dbg_write_unlock(&dbg_lock);
}
static inline int check_process_filter()
{
if (strstr (process_name, process_filter) != NULL)
return 1;
return 0;
}
#ifdef _DEBUG
#define setPidfilter(pid) \
do { \
dbg_write_lock(&dbg_lock); \
pid_filter = pid; \
dbg_write_unlock(&dbg_lock); \
} while (0)
#define setTidfilter(tid) \
do { \
dbg_write_lock(&dbg_lock); \
tid_filter = tid; \
dbg_write_unlock(&dbg_lock); \
} while (0)
#define setMode(mode) \
do { \
dbg_write_lock(&dbg_lock); \
if (print_mode != mode) { \
print_mode = mode; \
} \
dbg_write_unlock(&dbg_lock); \
} while (0)
#define setLevel(lv) \
do { \
dbg_write_lock(&dbg_lock); \
dbg_lv = (lv); \
dbg_write_unlock(&dbg_lock); \
} while (0)
#define setTrace(path, lv) \
do { \
dbg_write_lock(&dbg_lock); \
if (path == NULL) { \
if (dbg_fp) { \
fclose(dbg_fp); \
dbg_fp = NULL; \
} \
print_mode = PRINT_MODE_STDOUT; \
} else if (path) { \
if (dbg_fp) { \
fclose(dbg_fp); \
dbg_fp = NULL; \
}; \
dbg_fp = fopen(path, "a"); \
/* same as setlinebuf(xxx), use for compatible with c89 */ \
setvbuf(dbg_fp, (char *)NULL, _IOLBF, 0); \
print_mode = PRINT_MODE_FILE; \
}; \
dbg_lv = (lv); \
dbg_write_unlock(&dbg_lock) \
} while (0)
#define unsetTrace() \
do { \
fclose (dbg_fp); \
dbg_fp = NULL; \
dbg_lv = 0; \
} while (0)
#ifdef _AUGMENT_MSG
#define dbg_log(lv, fmt, args...) \
do { \
dbg_read_lock(&dbg_lock); \
if (lv & dbg_lv) { \
char buf[32] = { 0x00, }; \
char txt_buffer[4096] = { 0x00, }; \
if (pid_filter != -1 && getpid() != pid_filter) \
break; \
if (tid_filter != -1 && syscall (SYS_gettid) != tid_filter) \
break; \
if (process_filter != NULL && check_process_filter () == 0) \
break; \
if (dbg_fp && print_mode == PRINT_MODE_FILE) { \
now_str(buf, sizeof(buf)); \
snprintf (txt_buffer, sizeof(txt_buffer), \
"%s:"AUGMENT_LOG fmt, buf, AUGMENT_LOG_ARGS, ##args); \
fprintf(dbg_fp, "%s", txt_buffer); \
fflush (dbg_fp); \
} \
else if (print_mode == PRINT_MODE_STDOUT) { \
now_str(buf, sizeof(buf)); \
snprintf (txt_buffer, sizeof(txt_buffer), \
"%s:"AUGMENT_LOG fmt, buf, AUGMENT_LOG_ARGS, ##args); \
printf("%s", txt_buffer); \
} \
else if (print_mode == PRINT_MODE_SYSLOG) { \
now_str(buf, sizeof(buf)); \
snprintf (txt_buffer, sizeof(txt_buffer), \
"%s:"AUGMENT_LOG fmt, buf, AUGMENT_LOG_ARGS, ##args); \
syslog(LOG_DEBUG, "%s", txt_buffer); \
} \
} \
dbg_read_unlock(&dbg_lock); \
} while (0)
#else
#define dbg_log(lv, fmt, args...) \
do { \
dbg_read_lock(&dbg_lock); \
if (lv & dbg_lv) { \
if (dbg_fp && print_mode == PRINT_MODE_FILE) \
fprintf(dbg_fp, fmt, ##args); \
else if (print_mode == PRINT_MODE_STDOUT) \
printf(fmt, ##args); \
else if (print_mode == PRINT_MODE_SYSLOG)\
syslog(LOG_DEBUG, fmt, ##args); \
} \
dbg_read_unlock(&dbg_lock); \
} while (0)
#endif
#else
#define setPidfilter(pid) \
do { \
} while (0)
#define settidfilter(pid) \
do { \
} while (0)
#define setMode(mode) \
do { \
} while (0)
#define setTrace(path, lv) \
do { \
} while (0)
#define unsetTrace() \
do { \
} while (0)
#define setLevel(lv) \
do { \
} while (0)
#define dbg_log(lv, fmt, args...) \
do { \
} while (0)
#endif
#endif
---------------
/*
Sampels..
Compile 1 (not safe in thread)
gcc debug.c -lpthread -g -Wall -D_DEBUG
Compile 2 (thread safe)
gcc debug.c -lpthread -g -Wall -D_DEBUG -D_DEBUG_THREAD_SAFE
*/
/* example */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define __USE_GNU
#include <pthread.h>
#undef __USE_GNU
#include <sched.h>
#include "debug.h"
void *thread_log (void *arg)
{
int ret = 0, arg_val;
srand(time(NULL));
arg_val = (int)arg;
while (1)
{
ret = rand() % 3;
dbg_log (DEBUG_EXT_2,
"arg: %d,new thread log...\n",
arg_val);
pthread_yield();
sleep(ret);
}
return NULL;
}
int main(void)
{
#if 0
int bg = 0;
setTrace (NULL, DEBUG_ALL); /* NULL (stdout) */
dbg_log (DEBUG_INFO, "1 :[%s] [%d]\n", "kkk", bg);
dbg_log (DEBUG_INFO, "2 :[%s] [%d]\n", "kkk", bg);
setLevel (DEBUG_NONE); /* no logs anymore */
dbg_log (DEBUG_INFO, "3 :[%s] [%d]\n", "kkk", bg);
setTrace ("logfile", DEBUG_ALL); /* file path */
dbg_log (DEBUG_INFO, "file_1 :[%s] [%d]\n", "kkk", bg);
dbg_log (DEBUG_INFO, "file_2 :[%s] [%d]\n", "kkk", bg);
setMode(PRINT_MODE_SYSLOG);
dbg_log (DEBUG_INFO, "file_2 :[%s] [%d]\n", "kkk", bg);
dbg_log (DEBUG_INFO, "file_1 :[%s] [%d]\n", "kkk", bg);
setMode(PRINT_MODE_STDOUT);
dbg_log (DEBUG_INFO, "file_2 :[%s] [%d]\n", "kkk", bg);
dbg_log (DEBUG_INFO, "file_1 :[%s] [%d]\n", "kkk", bg);
setMode(PRINT_MODE_FILE);
dbg_log (DEBUG_INFO, "last change..\n");
unsetTrace();
#endif
system ("rm -rf ./thread_log");
setTrace ("thread_log", DEBUG_EXT_1 | DEBUG_EXT_2);
/*
setProcessfilter ("a.out");
setTidfilter (syscall(SYS_gettid));
*/
pthread_t id_1, id_2, id_3, id_4, id_5;
pthread_create (&id_1, NULL, thread_log, (void*)1);
pthread_create (&id_2, NULL, thread_log, (void*)2);
pthread_create (&id_3, NULL, thread_log, (void*)3);
pthread_create (&id_4, NULL, thread_log, (void*)4);
pthread_create (&id_5, NULL, thread_log, (void*)5);
while (1)
{
dbg_log (DEBUG_EXT_1, "main thread log\n");
pthread_yield();
sleep (1);
}
return 0;
}