小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

C語言調(diào)試接口

 quasiceo 2013-02-16

C語言調(diào)試接口

    在C語言程序設(shè)計(jì)中,常會(huì)出現(xiàn)各種各樣的bug:段錯(cuò)誤、參數(shù)異常等等。我們需要盡快定位錯(cuò)誤,輸出異常信息,出錯(cuò)位置及調(diào)用層次等,這對(duì)于解決bug問題是非常方便的,所以設(shè)計(jì)了如下調(diào)試接口。

調(diào)試級(jí)別:共有三級(jí),不同的級(jí)別對(duì)于錯(cuò)誤采取不同的處理方法,如異常退出還是函數(shù)返回還是僅僅輸出錯(cuò)誤信息,調(diào)試級(jí)別越高,給出的錯(cuò)誤信息越詳細(xì)。

    最高調(diào)試級(jí)別assert,當(dāng)斷言失效時(shí)打印最詳細(xì)的出錯(cuò)信息,包括斷言語句位置(文件,函數(shù),代碼行數(shù))、出錯(cuò)原因,并引發(fā)SIGTERM信號(hào),由該信號(hào)處理函數(shù)打印。程序的函數(shù)調(diào)用層次(從main開始)。

    如由assert(n> 0, "invalid n : %d", n); 引發(fā)異常如下:

     最上面幾行是樁輸出,樁可在程序中隨處插入,當(dāng)程序運(yùn)行到該行時(shí)就會(huì)打印出樁輸出信息。下面第二部分就是斷言錯(cuò)誤信息,包括斷言條件,補(bǔ)充的錯(cuò)誤信息等。下面第三部分就是打印函數(shù)調(diào)用層次,從main函數(shù)開始一直到斷言所在函數(shù)。

     第二級(jí)為return,當(dāng)條件失效時(shí)打印詳細(xì)出錯(cuò)信息并返回。

#ifndef MIN

#define MIN(a,b) (((a)>(b)) ? (b) : (a)) 
#endif 
#ifndef MAX 
#define MAX(a,b) (((a)>(b)) ? (a) : (b)) 
#endif 
 
#define return_if_fail(expr) if (!(expr)) {printf("%s:%i- assertion"#expr "failed\n",__FILE__,__LINE__); return;} 
#define return_val_if_fail(expr,ret) if (!(expr)) {printf("%s:%i- assertion" #expr "failed\n",__FILE__,__LINE__); return (ret);} 

     如由 return_val_if_fail(n >5, -1, "invalid n = %d", n); 引發(fā)異常如下:


     第三級(jí)為warn,當(dāng)條件失效時(shí)打印詳細(xì)出錯(cuò)信息,并繼續(xù)執(zhí)行下面的語句并不返回。


     除此之外,還對(duì)可能出現(xiàn)的段錯(cuò)誤進(jìn)行了處理,自動(dòng)調(diào)用assert級(jí)別,并由SIGSEGV信號(hào)處理函數(shù)打印程序的函數(shù)調(diào)用層次。

     如由*(int*)0x32 = 10; 引發(fā)段錯(cuò)誤如下:


     所有的調(diào)試語句都可用宏開關(guān)進(jìn)行控制,可隨時(shí)注銷

     接下來談?wù)剬?shí)現(xiàn),在調(diào)試接口內(nèi)部保存函數(shù)調(diào)用層次,在每個(gè)函數(shù)開頭都要插入ENTER__,并且函數(shù)返回都用RETURN,這樣就能記錄函數(shù)調(diào)用信息。

     為了最大程度降低對(duì)程序效率的影響,將所有的實(shí)現(xiàn)盡可能用宏完成,而將很少一部分工作利用接口函數(shù)完成。

/* 函數(shù)調(diào)用信息結(jié)構(gòu)體*/

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

typedef unsigned int dbgsize_t;
typedef unsigned int offset_t;

/*函數(shù)調(diào)用棧*/
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用于將調(diào)用信息壓棧,開頭幾行用于檢測(cè)對(duì)棧初始化和擴(kuò)充。

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
};

棧在接口內(nèi)部定義為靜態(tài)變量,棧的初始化函數(shù)如下

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

可見僅僅是指定信號(hào)處理函數(shù)。

當(dāng)函數(shù)返回時(shí),利用RETURN宏從棧中彈出一個(gè)調(diào)用記錄

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

宏D__為樁語句,打印函數(shù)運(yùn)行路徑

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

對(duì)于assert調(diào)試級(jí)別會(huì)引發(fā)SIGTERM信號(hào),對(duì)段錯(cuò)誤會(huì)引發(fā)SIGSEGV信號(hào)。

#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)

以下是兩種信號(hào)共有的處理函數(shù)

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

宏EXCEPTION_DUMP_STACK用于彈出調(diào)用棧

#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, "\n%s %s() at %s: %d", \
                            i== offset - 1 ? "Raised in" : "Called from", \
                            t->funcname, t->filename,  t->line); \
                   }\
                   DUMP_MSG(stdout,"\n"); \
         }while(0)

對(duì)于debug_ret級(jí)別,相關(guān)調(diào)試宏如下:

#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)

還有一些額外的宏可用于輔助調(diào)試

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

     對(duì)該調(diào)試接口的介紹該告一段落了,之所以會(huì)引發(fā)對(duì)這個(gè)問題的思考,主要是為了解決平時(shí)在程序設(shè)計(jì)中遇到的問題,當(dāng)程序比較大時(shí),bug的解決并不是件 容易的事。關(guān)于調(diào)試接口的設(shè)計(jì)還有許多問題要研究,如多線程,日志記錄,遠(yuǎn)程調(diào)試,有時(shí)需輸出一些更重要的運(yùn)行數(shù)據(jù),而非僅僅代碼級(jí)信息,這些如在實(shí)踐中 有需要,會(huì)進(jìn)一步完善的。歡迎大家討論...

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約