我相信每個程序員年輕的時候,在運行某程序,當屏幕彈出“Segmentation fault”時,你會變得非常焦慮,因爲你不知道代碼哪裏跑掛了,初級程序員常常使用printf函數在懷疑點一個個的加打印去定位問題出在哪裏了。
如果你熟悉gdb,你可能會使用gdb去運行這個程序,然後異常後執行backtrace(簡寫bt)命令打印異常調用棧。
但是gdb並不是萬能的,例如程序運行一段時間概率出現“Segmentation fault”的問題,gdb就派不上用場了,我們期望故障復現後,立馬能把異常調用棧打印出來。
我們可以利用linux消息機制,例如給0地址賦值會觸發段錯誤,系統會收到SIGSEGV消息,我們給消息註冊回調函數,如下部分代碼實現這一需求。
下面我們可以在回調函數backtrace_handler中實現異常調用棧的打印。
常規的實現方式是利用backtrace和backtrace_symbols函數就可以實現異常調用棧的打印了,如下部分代碼實現這一需求。
爲了看到驗證結果,我們編寫測試代碼example.c,初始化時候調用backtrace_reg註冊函數,如下部分代碼實現這一需求。
我們使用gcc example.c -g -o example編譯後運行,可以得到如下異常調用棧,
上面的地址看不懂怎麼辦?
可以藉助addr2line命令進行轉換,能夠轉換成功的前提是編譯的時候需要帶-g。這樣我們可以看到異常行號是63行,與實際代碼一致。
好了,這樣一個用戶態最基本的異常調用棧功能就實現了,但是這種方法也有缺陷,在你執行addr2line的時候,需要出錯的那個版本的可執行文件,有些情況下很可能出錯的版本和addr2line的版本不一致,翻譯不出正確的錯誤行號。
如何解決這個問題呢?把addr2line命令直接放到代碼中system執行?
這也太山寨了吧。
我們可以看看gdb源碼怎麼實現bt的,看看能不能對上面代碼做點優化?
預知詳情,請聽下回分解。
歡迎掃碼關注公衆號“嵌入式Linux技術分享”,一起學習Linux吧