免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1825 | 回复: 0
打印 上一主题 下一主题

一个简单的 console logger 和 stack tracer [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-09-19 04:37 |只看该作者 |倒序浏览
一个简单的 stack trace 例程,C/C++ 两套接口
debug.h,前半是 C 接口,后半是 C++ 接口
  1. #ifndef DEBUG_H
  2. #define DEBUG_H

  3. #include <unistd.h>
  4. #include <pthread.h>
  5. #include <stdlib.h>
  6. #include <stdio.h>
  7. #include <stdarg.h>
  8. #include <assert.h>

  9. #ifdef __cplusplus
  10. extern "C" {
  11. #endif

  12.   // Will enable seperate output for each thread, set default level to EMERG
  13.   int dbg_enable ();

  14.   // Logging output control level. EMERG can't be hided. GETVAL retrieves current value without changing.
  15.   typedef enum {GETVAL = -1, EMERG = 0, ERROR, WARNING, INFO, DEBUG, NUM_LEVELS} dbg_level_t;

  16.   dbg_level_t dbg_level ( dbg_level_t newlvl );

  17.   // Setting prefix / postfix for each message.
  18.   void dbg_set_decorate ( char *dec[NUM_LEVELS * 2] );

  19.   // Output function.
  20.   int dbg_printf ( dbg_level_t lvl, const char *fmt, ... );

  21.   // Print stack point info with current thread's output.
  22.   int dbg_dump_stacktrace ();

  23.   void __dbg_enter (const char *file, const char *func, int line);

  24.   // Setting / cancelling stack point. C++ should use DECLARE_TRACEPOINT() macro
  25.   #define dbg_enter() do { __dbg_enter(__FILE__,__FUNCTION__,__LINE__); } while(0)
  26.   void dbg_leave ();

  27. #ifdef __cplusplus
  28. }
  29. #endif

  30. #ifdef __cplusplus

  31. #include <string>
  32. #include <list>

  33. using namespace std;

  34. // Prefered C++ interface to tracepoint. "name" is an identifier that won't conflict
  35. // within the scope of calling.
  36. #define DECLARE_TRACEPOINT(name) tracepoint name (__FILE__, __FUNCTION__, __LINE__)

  37. struct tracepoint
  38. {
  39.   tracepoint ( const char *file, const char *function, int line);
  40.   tracepoint ( const tracepoint& other );

  41.   ~tracepoint ();

  42.   tracepoint & operator= ( const tracepoint & other );

  43.   private:
  44.   int *cnt;
  45. };

  46. #endif // __cplusplus

  47. #endif // DEBUG_H
复制代码


debug.cpp,实现部分,C++ 代码

  1. #include "debug.h"

  2. static pthread_key_t debug_key;
  3. static pthread_once_t key_init_once = PTHREAD_ONCE_INIT;

  4. const char *default_dec[NUM_LEVELS * 2] = {
  5.   "\033[31mEMERG\033[0m\t", "",
  6.   "\033[33mERROR\033[0m\t", "",
  7.   "\033[36mWARNING\033[0m\t", "",
  8.   "\033[34mINFO\033[0m\t", "",
  9.   "\033[35mDEBUG\033[0m\t", "",
  10. };

  11. struct tp_frame
  12. {
  13.   string file, function;
  14.   int line;

  15.   tp_frame (const char *f, const char *fn, int l)
  16.     : file(f), function(fn), line(l), cnt(0)
  17.   {}

  18.   ~tp_frame ()
  19.   { assert( cnt == 0 ); }

  20.   // Not yet used.
  21.   tp_frame *get ()
  22.   { ++cnt; return this; }

  23.   void put ()
  24.   { --cnt; }

  25. private:
  26.   int cnt;
  27. };

  28. struct debug_info
  29. {
  30.   debug_info ();
  31.   ~debug_info ();

  32.   int ready;

  33.   int fd;
  34.   FILE * fp;
  35.   dbg_level_t level;
  36.   const char *dec[NUM_LEVELS * 2];

  37.   // private use below this point

  38.   char * buf;
  39.   unsigned bufsz;

  40.   list < tp_frame *> tracestack;

  41. };

  42. debug_info::debug_info ()
  43.     : ready(false)
  44. {
  45.   fd = -1;
  46.   fp = NULL;

  47.   int fd = dup (STDERR_FILENO);
  48.   if ( fd < 0 )
  49.     goto out;
  50.   FILE *fp = fdopen (fd, "a");
  51.   if ( fp == NULL )
  52.     goto out1;

  53.   this->fd = fd;
  54.   this->fp = fp;

  55.   this->level = EMERG;

  56.   memcpy (dec, default_dec, sizeof(default_dec));

  57.   this->buf = NULL;
  58.   this->bufsz = 0;

  59.   this->ready = true;
  60.   return;

  61. out1:
  62.   close (fd);
  63. out:
  64.   (void)0;
  65. }

  66. debug_info::~debug_info ()
  67. {
  68.   if ( fp )
  69.     fclose (fp);
  70. }


  71. static void di_dtor (void *pdi)
  72. {
  73.   struct debug_info *p = (struct debug_info *)pdi;
  74.   delete p;
  75. }

  76. static void init_key ()
  77. {
  78.   pthread_key_create (&debug_key, di_dtor);
  79. }

  80. int dbg_enable ()
  81. {
  82.   int ret = 0;
  83.   pthread_once ( &key_init_once, &init_key );

  84.   debug_info *pdi = new debug_info();

  85.   ret = (pthread_setspecific (debug_key, pdi));
  86.   return ret;
  87. }

  88. dbg_level_t dbg_level ( dbg_level_t newlvl )
  89. {
  90.   struct debug_info *pdi = (struct debug_info *)pthread_getspecific(debug_key);
  91.   if ( !pdi || !pdi->ready )
  92.     return EMERG;
  93.   dbg_level_t rt = pdi->level;
  94.   if ( (rt != newlvl) && (newlvl != GETVAL) )
  95.     {
  96.       pdi->level = newlvl;
  97.       pthread_setspecific (debug_key, pdi);
  98.     }
  99.   return rt;
  100. }

  101. void dbg_set_decorate ( char *dec[NUM_LEVELS * 2] )
  102. {
  103.   struct debug_info *pdi = (struct debug_info *)pthread_getspecific(debug_key);
  104.   if ( !pdi || !pdi->ready )
  105.     return;
  106.   memcpy( pdi->dec, dec, sizeof(default_dec) );
  107. }

  108. int dbg_printf ( dbg_level_t lvl, const char *fmt, ... )
  109. {
  110.   struct debug_info *pdi = (struct debug_info *)pthread_getspecific(debug_key);
  111.   if ( !pdi || !pdi->ready )
  112.     return 0;
  113.   if ( lvl > pdi->level )
  114.     return 0;

  115.   int rt;
  116.   va_list ap;
  117.   va_start (ap, fmt);

  118.   unsigned sz = pdi->bufsz;
  119.   char *buf = pdi->buf;

  120.   if ( buf == NULL )
  121.     {
  122.       sz = 32;
  123.       buf = (char *)malloc(sz);
  124.       if ( buf == NULL )
  125.         return 0; // ENOMEM;
  126.     }

  127.   const char *pre = pdi->dec[(int)lvl * 2];
  128.   pre = pre == NULL ? default_dec[(int)lvl * 2] : pre;
  129.   const char *post = pdi->dec[(int)lvl * 2 + 1];
  130.   post = post == NULL ? default_dec[(int)lvl * 2 + 1] : post;

  131.   while ( 1 )
  132.     {
  133.       unsigned n;
  134.       if ( (n = snprintf(buf, sz, "%s%s%s", pre, fmt, post)) > sz )
  135.         {
  136.           free (buf);
  137.           sz = n + 1;
  138.           buf = (char *)malloc(sz);
  139.           if ( buf == NULL )
  140.             return 0; // ENOMEM
  141.         }
  142.       else
  143.         break;
  144.     }
  145.   pdi->buf = buf;
  146.   pdi->bufsz = sz;

  147.   rt = vfprintf (pdi->fp, pdi->buf, ap);
  148.   va_end (ap);

  149.   return rt;
  150. }

  151. tracepoint::tracepoint ( const char *file, const char *function, int line )
  152. {
  153.   tp_frame *f = new tp_frame(file, function, line);
  154.   cnt = new int(1);

  155.   debug_info *p = (debug_info *)pthread_getspecific(debug_key);
  156.   if ( p )
  157.     {
  158.       p->tracestack.push_back (f);
  159.     }
  160. }

  161. tracepoint::tracepoint ( const tracepoint & other )
  162. {
  163.   cnt = other.cnt;
  164.   ++(*cnt);
  165. }

  166. tracepoint & tracepoint::operator= ( const tracepoint & other )
  167. {
  168.   cnt = other.cnt;
  169.   ++(*cnt);
  170.   return *this;
  171. }

  172. tracepoint::~tracepoint ()
  173. {
  174.   --(*cnt);
  175.   if ( 0 == *cnt )
  176.     {
  177.       debug_info *p = (debug_info *)pthread_getspecific(debug_key);
  178.       delete p->tracestack.back();
  179.       p->tracestack.pop_back ();
  180.       delete cnt;
  181.     }
  182. }

  183. int dbg_dump_stacktrace ()
  184. {
  185.   debug_info *p = (debug_info *)pthread_getspecific(debug_key);
  186.   list < tp_frame *> &s = p->tracestack;
  187.   while ( s.size() > 0 )
  188.     {
  189.       tp_frame *pt = s.back();
  190.       dbg_printf (EMERG, "%s() at %s:%d\n", pt->function.c_str(), pt->file.c_str(), pt->line);
  191.       s.pop_back();
  192.     }
  193.   fflush(NULL);
  194.   return 1;
  195. }


  196. // Setting / cancelling stack point. C++ should use DECLARE_TRACEPOINT() macro
  197. void __dbg_enter (const char *file, const char *function, int line)
  198. {
  199.   tp_frame *f = new tp_frame(file, function, line);

  200.   debug_info *p = (debug_info *)pthread_getspecific(debug_key);
  201.   if ( p )
  202.     {
  203.       p->tracestack.push_back (f);
  204.     }

  205. }

  206. void dbg_leave ()
  207. {
  208.   debug_info *p = (debug_info *)pthread_getspecific(debug_key);
  209.   delete p->tracestack.back();
  210.   p->tracestack.pop_back ();
  211. }
复制代码


测试例程:C 版
  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include "debug.h"

  4. void onabrt (int sig) {
  5.   dbg_dump_stacktrace();
  6.   fflush(NULL);
  7.   _exit(127 + sig);
  8. }

  9. int main () {
  10.   // 确保出错的时候自动调用 dump_stacktrace
  11.   // C++ 程序可以通过注册 SIGABRT 捕获 uncaught_exception,获得类似 Java 的 st 输出
  12.   signal( SIGSEGV, onabrt );
  13.   dbg_enable(); // 启用 debug。每个线程都必需。
  14.   dbg_enter(); // 调用 C 接口设置 stack point。可以嵌套设置多个以缩小调试范围
  15.   printf ("%d\n", (int)sizeof(long double));
  16.   *(int *)NULL = 3; // 制造 SIGSEGV
  17.   dbg_leave(); // 与 dbg_enter() 对应
  18.   return 0;
  19. }
复制代码


本来设计时候是为了要一个顺手的 logger,没想到最后竟然弄了个 stack trace 的东西出来:)

dbg_printf 用作输出,会自动加上前后缀,并且根据 dbg_set_level 设置的等级确定是否输出。默认的前后缀是等级名称加上 ANSI color code,在 Emacs shell / GDB 中看着像是乱码。可以用 dbg_set_decorate 修改。
Part of OLFS v0.3
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP