c语言调试接口

http://blog.chinaunix.net/uid-10106787-id-2985587.html

  在C语言程序设计中,常会出现各种各样的bug:段错误、参数异常等等。我们需要尽快定位错误,输出异常信息,出错位置及调用层次等,这对于解决bug问题是非常方便的,所以设计了如下调试接口。

调试级别:共有三级,不同的级别对于错误采取不同的处理方法,如异常退出还是函数返回还是仅仅输出错误信息,调试级别越高,给出的错误信息越详细。

    最高调试级别assert,当断言失效时打印最详细的出错信息,包括断言语句位置(文件,函数,代码行数)、出错原因,并引发SIGTERM信号,由该信号处理函数打印。程序的函数调用层次(从main开始)。

    如由assert(n> 0, "invalid n : %d", n); 引发异常如下:

 

     最上面几行是桩输出,桩可在程序中随处插入,当程序运行到该行时就会打印出桩输出信息。下面第二部分就是断言错误信息,包括断言条件,补充的错误信息等。下面第三部分就是打印函数调用层次,从main函数开始一直到断言所在函数。

     第二级为return,当条件失效时打印详细出错信息并返回。

     如由 return_val_if_fail(n >5, -1, "invalid n = %d", n); 引发异常如下:

     第三级为warn,当条件失效时打印详细出错信息,并继续执行下面的语句并不返回。

     除此之外,还对可能出现的段错误进行了处理,自动调用assert级别,并由SIGSEGV信号处理函数打印程序的函数调用层次。

     如由*(int*)0x32 = 10; 引发段错误如下:

     所有的调试语句都可用宏开关进行控制,可随时注销

     接下来谈谈实现,在调试接口内部保存函数调用层次,在每个函数开头都要插入ENTER__,并且函数返回都用RETURN,这样就能记录函数调用信息。

     为了最大程度降低对程序效率的影响,将所有的实现尽可能用宏完成,而将很少一部分工作利用接口函数完成。

/* 函数调用信息结构体*/

struct debug_function_info{
         constchar * filename;
         constchar * funcname;
         unsigned int   line;
};

typedef unsigned int dbgsize_t;
typedef unsigned int offset_t;

/*函数调用栈*/
struct debug_struct {
         dbgsize_t stack_size;
         offset_t stack_offset;
         dbgsize_t stack_unitsz;
         struct debug_function_info *stack_buff;
         struct debug_function_info   dbg_funcinf_array[INITIAL_STACK_SIZE];
};

#define ENTER__ENTER_FUNCTION(__FILE__, __FUNCTION__, __LINE__);
#define ENTER_FUNCTION(x, y, z)
        do {

                   if(unlikely(global_debug_infop == NULL))

                            debug_initialize();

                   struct debug_struct *p = global_debug_infop;
                   if (unlikely(p->stack_offset >= p->stack_size))
                            debug_stack_resize();
                   offset_t* t = &p->stack_offset;
                   p->dbg_funcinf_array[*t].filename = (x);
                   p->dbg_funcinf_array[*t].funcname = (y);
                   p->dbg_funcinf_array[*t].line = (z);
                   p->stack_offset ;  

         } while(0)

 

宏ENTER_FUNCTION用于将调用信息压栈,开头几行用于检测对栈初始化和扩充。

static struct debug_structglobal_debug_info = {

         .stack_size = INITIAL_STACK_SIZE,

         .stack_offset = 0,

         .stack_unitsz = sizeof(struct debug_function_info),

         .stack_buff = global_debug_info.dbg_funcinf_array
};

栈在接口内部定义为静态变量,栈的初始化函数如下

void debug_initialize(void)
{
         global_debug_infop = &global_debug_info;
         signal(SIGSEGV, exception_handler);
         signal(SIGTERM, exception_handler);
}

可见仅仅是指定信号处理函数。

当函数返回时,利用RETURN宏从栈中弹出一个调用记录

#define RETURN(...)
         do{
                  global_debug_infop->stack_offset--;
                   return __VA_ARGS__;  
         }while(0)

宏D__为桩语句,打印函数运行路径

#define D__
         do{
                   STACK_PUSH_LINE(__LINE__);  
                   DUMP_MSG(stdout, " Running over %s() at %s: %d",
                   __FUNCTION__, __FILE__,  __LINE__);  
         }while(0);

 

对于assert调试级别会引发SIGTERM信号,对段错误会引发SIGSEGV信号。

#define assert(p,  ...)
         do {
                   if (!!!(p)){
                            charerr_msg[DEBUG_ERRMSG_LEN] = {0};
                            MAKE_ERROR_MSG(err_msg, __VA_ARGS__);  
                            DUMP_DEBUG_MSG(p);  
                            DUMP_ERROR_MSG("Error Msg",err_msg);  
                            STACK_PUSH_LINE(__LINE__);
                            raise(SIGTERM);
                   }
         }while(0)

以下是两种信号共有的处理函数

static void exception_handler(int signo)
{
         fprintf(stdout, " Caught signal %s...", 
                   signo== SIGSEGV ? "SIGSEGV" : "SIGTERM"); 
         ((void)signo);
         EXCEPTION_DUMP_STACK();
         abort();
}

宏EXCEPTION_DUMP_STACK用于弹出调用栈

#define EXCEPTION_DUMP_STACK()
         do {
                  int i;
                   intoffset = global_debug_infop->stack_offset;                        

                   struct debug_struct *p = global_debug_infop;
                   for(i = offset - 1; i >= 0; i--) {
                            pdbg_nodet = p->stack_buff i;
                            DUMP_MSG(stdout, " %s %s() at %s: %d",
                            i== offset - 1 ? "Raised in" : "Called from",
                            t->funcname, t->filename,  t->line);
                   }
                   DUMP_MSG(stdout," ");
         }while(0)

对于debug_ret级别,相关调试宏如下:

#define debug_ret_series(p, ...)
         do {
                   charerr_msg[DEBUG_ERRMSG_LEN] = {0};
                   MAKE_ERROR_MSG(err_msg, __VA_ARGS__);
                   DUMP_DEBUG_MSG(p);
                   DUMP_ERROR_MSG("ErrorMsg", err_msg);
                   STACK_PUSH_LINE(__LINE__);
                   DUMP_LAST_ERROR_MSG();
         }while(0)
#define debug_retv(p, ret, ...)
         do {
                   debug_ret_series(p, __VA_ARGS__);
                   RETURN(ret);
         }while(0)

#define retv_if(p, ret,...)
         do {
                   if(!!(p)){
                            debug_retv(p, ret, __VA_ARGS__);
                   }
         }while(0)
#define retv_if0(p, ret, ...)
         do {
                   if(!!!(p)){
                            debug_retv(p, ret, __VA_ARGS__);
                   }
         }while(0)

还有一些额外的宏可用于辅助调试

#define Show_Value(x, u)
                   DUMP_MSG(stdout, " Called From %s() at %s : %d, "
                   "TheValue of "#x" is %"#u" ", __FUNCTION__, __FILE__, __LINE__, x)

原文地址:https://www.cnblogs.com/flash610/p/3307849.html