zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    (一)IOC 容器:【2】@ComponentScan 自动扫描组件&指定扫描规则
    (一)IOC 容器:【11】Aware 注入Spring底层组件
    (一)IOC 容器:【10】@Autowired 自动装配
    (一)IOC 容器:【3】@Scope 设置组件作用域
    (一)IOC 容器:【8】Bean组件的生命周期
    每日日报7月13日
    每日日报7月12日
    每日日报7月11日
    Prism中的WeakEventHandlerManager
    博客开通标记
  • 原文地址:https://www.cnblogs.com/flash610/p/3307849.html
Copyright © 2011-2022 走看看