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
如由 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{ typedef unsigned int dbgsize_t; /*函數(shù)調(diào)用棧*/ #define ENTER__ENTER_FUNCTION(__FILE__, __FUNCTION__, __LINE__); if(unlikely(global_debug_infop == NULL)) \ debug_initialize();\ struct debug_struct *p = global_debug_infop;\ } 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) 可見僅僅是指定信號(hào)處理函數(shù)。 當(dāng)函數(shù)返回時(shí),利用RETURN宏從棧中彈出一個(gè)調(diào)用記錄 #define RETURN(...)\ 宏D__為樁語句,打印函數(shù)運(yùn)行路徑 #define D__\ 對(duì)于assert調(diào)試級(jí)別會(huì)引發(fā)SIGTERM信號(hào),對(duì)段錯(cuò)誤會(huì)引發(fā)SIGSEGV信號(hào)。 #define assert(p, ...) \ 以下是兩種信號(hào)共有的處理函數(shù) static void exception_handler(int signo) 宏EXCEPTION_DUMP_STACK用于彈出調(diào)用棧 #define EXCEPTION_DUMP_STACK()\ struct debug_struct *p = global_debug_infop; \ 對(duì)于debug_ret級(jí)別,相關(guān)調(diào)試宏如下: #define debug_ret_series(p, ...) \ #define retv_if(p, ret,...) \ 還有一些額外的宏可用于輔助調(diào)試 #define Show_Value(x, u) \ 對(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)一步完善的。歡迎大家討論... |
|
|