Chinaunix
标题:
一个简单的 debug_printf / stack trace 库
[打印本页]
作者:
wolf0403
时间:
2006-09-16 02:19
标题:
一个简单的 debug_printf / stack trace 库
一个简单的 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 修改。
作者:
SammyLan
时间:
2006-09-16 19:42
标题:
回复 1楼 wolf0403 的帖子
特意路过
受教了(=_=)
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2