不管在PC端還是在嵌入式設備中,代碼執行過程中運行出錯,如棧溢出,引用野指針,重複free同一塊內存等等,都是件非常可怕的事情。好在PC端作業系統會幫我們終止相關進程,並回收其資源,不至於導致整個作業系統崩潰。 而嵌入式設備就比較慘了,往往會導致系統宕機,直接重啟,很大程度影響了我們產品的可靠性。 同時隨著工程代碼越來越龐大, 出現宕機的原因也難以具體分析出到底是哪一行導致的系統崩潰。今天我們就來介紹一個神器,即如何使用addr2line命令來跟蹤代碼運行出錯時,具體是哪行代碼導致的,以幫助我們定位問題所在。
addr2line 工具(它是標準的 GNU Binutils 中的一部分)是一個可以將指令的地址和可執行映像轉換成文件名、函數名和原始碼行數的工具。通俗來講就是可以根據宕機時PC(程序計數器)的值來轉化為具體執行了哪個文件下的函數中的代碼(行號)。這裡說的是esp8266和esp32採用xtensa交叉編譯工具鏈,集成了Binutils中的工具集,如addr2line, ar,as,ld等等。它們分別在xtensa-lx106-elf\bin(esp8266),xtensa-esp32-elf\bin(esp32)目錄中。
既然知道了addr2line是GNU Binutils中的一個工具,那麼我們就先在linux中嘗嘗鮮,看看addr2line是如何使用的吧。 這裡我們簡單編寫一個簡單的例子:定義一個int *指針,並初始化為NULL, 然後解引用,即賦值為0,具體如下:
//測試源文件test.c39;ed (StoreProhibited). Exception was unhandled
其中StoreProhibited 粗略的告知我們是關於存儲錯誤導致的,並列印了系統崩潰時,一些寄存器的值,而我們最關心的就是當前PC(程序計數器)的值, 上圖中PC的值顯示: 0x400e4235, 以及下方
Backtrace: 0x400e4235:0x3ffb4cf0 (這裡需要說明的是esp8266和esp32是雙核cpu,因此有2個PC程序計數器), 這裡我們根據提示是核心core0崩潰的,對應的PC就是0x400e4235。OK,接下來我們就利用addr2line來定位出問題的代碼:
首先我們cd到編譯生成的buile文件夾中:
然後輸入命令:
xtensa-esp32-elf-addr2line -e project-DD7002B.elf 0x400e4235
Enter回車:
可以看到輸出顯示出問題的代碼所在的詳細路徑 和 行號,即在Test_Hub.c的第55行代碼導致的系統崩潰,之後大家就可以以此為突破口來分析問題了。
addr2line工具雖然能夠幫助我們定位問題代碼的路徑和行號,但是不是所有的環境都支持該功能,因此大家在實際開發過程應當有自我意識,自己所寫的每一行代碼做到心中有數,尤其是在內存,指針等操作。 這就離不開平時的不斷學習,提高自己的編程能力。