- 论坛徽章:
- 0
|
一个简单的 stack trace 例程,C/C++ 两套接口
debug.h,前半是 C 接口,后半是 C++ 接口
- #ifndef DEBUG_H
- #define DEBUG_H
- #include <unistd.h>
- #include <pthread.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <assert.h>
- #ifdef __cplusplus
- extern "C" {
- #endif
- // Will enable seperate output for each thread, set default level to EMERG
- int dbg_enable ();
- // Logging output control level. EMERG can't be hided. GETVAL retrieves current value without changing.
- typedef enum {GETVAL = -1, EMERG = 0, ERROR, WARNING, INFO, DEBUG, NUM_LEVELS} dbg_level_t;
- dbg_level_t dbg_level ( dbg_level_t newlvl );
- // Setting prefix / postfix for each message.
- void dbg_set_decorate ( char *dec[NUM_LEVELS * 2] );
- // Output function.
- int dbg_printf ( dbg_level_t lvl, const char *fmt, ... );
- // Print stack point info with current thread's output.
- int dbg_dump_stacktrace ();
- void __dbg_enter (const char *file, const char *func, int line);
- // Setting / cancelling stack point. C++ should use DECLARE_TRACEPOINT() macro
- #define dbg_enter() do { __dbg_enter(__FILE__,__FUNCTION__,__LINE__); } while(0)
- void dbg_leave ();
- #ifdef __cplusplus
- }
- #endif
- #ifdef __cplusplus
- #include <string>
- #include <list>
- using namespace std;
- // Prefered C++ interface to tracepoint. "name" is an identifier that won't conflict
- // within the scope of calling.
- #define DECLARE_TRACEPOINT(name) tracepoint name (__FILE__, __FUNCTION__, __LINE__)
- struct tracepoint
- {
- tracepoint ( const char *file, const char *function, int line);
- tracepoint ( const tracepoint& other );
- ~tracepoint ();
- tracepoint & operator= ( const tracepoint & other );
- private:
- int *cnt;
- };
- #endif // __cplusplus
- #endif // DEBUG_H
复制代码
debug.cpp,实现部分,C++ 代码
- #include "debug.h"
- static pthread_key_t debug_key;
- static pthread_once_t key_init_once = PTHREAD_ONCE_INIT;
- const char *default_dec[NUM_LEVELS * 2] = {
- "\033[31mEMERG\033[0m\t", "",
- "\033[33mERROR\033[0m\t", "",
- "\033[36mWARNING\033[0m\t", "",
- "\033[34mINFO\033[0m\t", "",
- "\033[35mDEBUG\033[0m\t", "",
- };
- struct tp_frame
- {
- string file, function;
- int line;
- tp_frame (const char *f, const char *fn, int l)
- : file(f), function(fn), line(l), cnt(0)
- {}
- ~tp_frame ()
- { assert( cnt == 0 ); }
- // Not yet used.
- tp_frame *get ()
- { ++cnt; return this; }
- void put ()
- { --cnt; }
- private:
- int cnt;
- };
- struct debug_info
- {
- debug_info ();
- ~debug_info ();
- int ready;
- int fd;
- FILE * fp;
- dbg_level_t level;
- const char *dec[NUM_LEVELS * 2];
- // private use below this point
- char * buf;
- unsigned bufsz;
- list < tp_frame *> tracestack;
- };
- debug_info::debug_info ()
- : ready(false)
- {
- fd = -1;
- fp = NULL;
- int fd = dup (STDERR_FILENO);
- if ( fd < 0 )
- goto out;
- FILE *fp = fdopen (fd, "a");
- if ( fp == NULL )
- goto out1;
- this->fd = fd;
- this->fp = fp;
- this->level = EMERG;
- memcpy (dec, default_dec, sizeof(default_dec));
- this->buf = NULL;
- this->bufsz = 0;
- this->ready = true;
- return;
- out1:
- close (fd);
- out:
- (void)0;
- }
- debug_info::~debug_info ()
- {
- if ( fp )
- fclose (fp);
- }
- static void di_dtor (void *pdi)
- {
- struct debug_info *p = (struct debug_info *)pdi;
- delete p;
- }
- static void init_key ()
- {
- pthread_key_create (&debug_key, di_dtor);
- }
- int dbg_enable ()
- {
- int ret = 0;
- pthread_once ( &key_init_once, &init_key );
- debug_info *pdi = new debug_info();
- ret = (pthread_setspecific (debug_key, pdi));
- return ret;
- }
- dbg_level_t dbg_level ( dbg_level_t newlvl )
- {
- struct debug_info *pdi = (struct debug_info *)pthread_getspecific(debug_key);
- if ( !pdi || !pdi->ready )
- return EMERG;
- dbg_level_t rt = pdi->level;
- if ( (rt != newlvl) && (newlvl != GETVAL) )
- {
- pdi->level = newlvl;
- pthread_setspecific (debug_key, pdi);
- }
- return rt;
- }
- void dbg_set_decorate ( char *dec[NUM_LEVELS * 2] )
- {
- struct debug_info *pdi = (struct debug_info *)pthread_getspecific(debug_key);
- if ( !pdi || !pdi->ready )
- return;
- memcpy( pdi->dec, dec, sizeof(default_dec) );
- }
- int dbg_printf ( dbg_level_t lvl, const char *fmt, ... )
- {
- struct debug_info *pdi = (struct debug_info *)pthread_getspecific(debug_key);
- if ( !pdi || !pdi->ready )
- return 0;
- if ( lvl > pdi->level )
- return 0;
- int rt;
- va_list ap;
- va_start (ap, fmt);
- unsigned sz = pdi->bufsz;
- char *buf = pdi->buf;
- if ( buf == NULL )
- {
- sz = 32;
- buf = (char *)malloc(sz);
- if ( buf == NULL )
- return 0; // ENOMEM;
- }
- const char *pre = pdi->dec[(int)lvl * 2];
- pre = pre == NULL ? default_dec[(int)lvl * 2] : pre;
- const char *post = pdi->dec[(int)lvl * 2 + 1];
- post = post == NULL ? default_dec[(int)lvl * 2 + 1] : post;
- while ( 1 )
- {
- unsigned n;
- if ( (n = snprintf(buf, sz, "%s%s%s", pre, fmt, post)) > sz )
- {
- free (buf);
- sz = n + 1;
- buf = (char *)malloc(sz);
- if ( buf == NULL )
- return 0; // ENOMEM
- }
- else
- break;
- }
- pdi->buf = buf;
- pdi->bufsz = sz;
- rt = vfprintf (pdi->fp, pdi->buf, ap);
- va_end (ap);
- return rt;
- }
- tracepoint::tracepoint ( const char *file, const char *function, int line )
- {
- tp_frame *f = new tp_frame(file, function, line);
- cnt = new int(1);
- debug_info *p = (debug_info *)pthread_getspecific(debug_key);
- if ( p )
- {
- p->tracestack.push_back (f);
- }
- }
- tracepoint::tracepoint ( const tracepoint & other )
- {
- cnt = other.cnt;
- ++(*cnt);
- }
- tracepoint & tracepoint::operator= ( const tracepoint & other )
- {
- cnt = other.cnt;
- ++(*cnt);
- return *this;
- }
- tracepoint::~tracepoint ()
- {
- --(*cnt);
- if ( 0 == *cnt )
- {
- debug_info *p = (debug_info *)pthread_getspecific(debug_key);
- delete p->tracestack.back();
- p->tracestack.pop_back ();
- delete cnt;
- }
- }
- int dbg_dump_stacktrace ()
- {
- debug_info *p = (debug_info *)pthread_getspecific(debug_key);
- list < tp_frame *> &s = p->tracestack;
- while ( s.size() > 0 )
- {
- tp_frame *pt = s.back();
- dbg_printf (EMERG, "%s() at %s:%d\n", pt->function.c_str(), pt->file.c_str(), pt->line);
- s.pop_back();
- }
- fflush(NULL);
- return 1;
- }
- // Setting / cancelling stack point. C++ should use DECLARE_TRACEPOINT() macro
- void __dbg_enter (const char *file, const char *function, int line)
- {
- tp_frame *f = new tp_frame(file, function, line);
- debug_info *p = (debug_info *)pthread_getspecific(debug_key);
- if ( p )
- {
- p->tracestack.push_back (f);
- }
- }
- void dbg_leave ()
- {
- debug_info *p = (debug_info *)pthread_getspecific(debug_key);
- delete p->tracestack.back();
- p->tracestack.pop_back ();
- }
复制代码
测试例程:C 版
- #include <stdio.h>
- #include <signal.h>
- #include "debug.h"
- void onabrt (int sig) {
- dbg_dump_stacktrace();
- fflush(NULL);
- _exit(127 + sig);
- }
- int main () {
- // 确保出错的时候自动调用 dump_stacktrace
- // C++ 程序可以通过注册 SIGABRT 捕获 uncaught_exception,获得类似 Java 的 st 输出
- signal( SIGSEGV, onabrt );
- dbg_enable(); // 启用 debug。每个线程都必需。
- dbg_enter(); // 调用 C 接口设置 stack point。可以嵌套设置多个以缩小调试范围
- printf ("%d\n", (int)sizeof(long double));
- *(int *)NULL = 3; // 制造 SIGSEGV
- dbg_leave(); // 与 dbg_enter() 对应
- return 0;
- }
复制代码
本来设计时候是为了要一个顺手的 logger,没想到最后竟然弄了个 stack trace 的东西出来:)
dbg_printf 用作输出,会自动加上前后缀,并且根据 dbg_set_level 设置的等级确定是否输出。默认的前后缀是等级名称加上 ANSI color code,在 Emacs shell / GDB 中看着像是乱码。可以用 dbg_set_decorate 修改。
Part of OLFS v0.3  |
|