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)

  • 相关阅读:
    Knockout应用开发指南 第八章:简单应用举例(2)
    微软ASP.NET站点部署指南(7):生产环境部署
    Knockout应用开发指南 第七章:Mapping插件
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(6)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(5)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(3)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(9)
    《Microsoft Sql server 2008 Internals》读书笔记第九章Plan Caching and Recompilation(8)
    Microsoft Visual Studio .NET 2003 引导程序插件下载地址(非官方)
    Vs2010在没有安装SQL Server 2005/2008 Express时如何连接MDF数据文件?
  • 原文地址:https://www.cnblogs.com/flash610/p/3307849.html
Copyright © 2011-2022 走看看