'공부/C'에 해당되는 글 2건

  1. 2009.07.15 MIME
  2. 2008.11.01 Debug Macro

MIME

공부/C 2009. 7. 15. 16:45
Posted by initproc
,

Debug Macro

공부/C 2008. 11. 1. 00:47


내가 만든 로그 매크로 ...

/*
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;
}

Posted by initproc
,