1
backtrace基本原理
backtrace英譯為回溯的意思,這聽起來有點專業了,其實大部分搞嵌入式的朋友都有聽說過函數調用棧callstack。而backtrace說白了就是我們呈現函數調用關系的一項功能。
所以backtrace調試功能的實現原理基于函數調用棧的概念。
那什么是函數調用棧呢?
函數調用棧是一個記錄程序中函數調用關系的數據結構,它在程序運行時動態生成和維護。當程序執行函數調用時,它將當前函數的返回地址和一些其他信息壓入堆棧中,并跳轉到被調用的函數執行。當被調用函數執行完畢后,它將返回地址彈出堆棧,并跳回到調用函數繼續執行。
backtrace調試功能的實現原理就是利用函數調用棧中的信息來追蹤程序執行的路徑和調用關系。當程序出現錯誤或崩潰時,backtrace可以通過分析函數調用棧信息來確定出錯的位置和原因。
在Linux系統中,backtrace通常是通過使用調試器比如我們常用的gdb來實現的。調試器會在程序執行時,動態地獲取函數調用棧信息,并將其保存在調試器的內部數據結構中。當程序出現錯誤或崩潰時,調試器就可以利用保存的函數調用棧信息來進行backtrace操作。
2
backtrace功能
而對于backtrace這個功能在不同的平臺和開發環境中的使用是不同的.
比如在我們平時的linux環境中:可以使用glibc提供的backtrace()函數實現backtrace功能。該函數通過解析函數調用棧信息獲取函數名、參數和返回地址等信息,并將其打印到標準輸出或指定的文件中。
此外,還可以使用gdb或libunwind庫來實現backtrace功能。gdb是一個強大的調試器,可以實時追蹤程序的執行,獲取程序的調用棧信息,并提供各種調試工具和命令。
而其中的libunwind則是一個開源的C/C++庫,也可以用于在運行時獲取當前程序的調用棧信息,并且在不同的平臺和架構上運行,并提供了簡單易用的API接口,同樣也是非常方便的。
3
glibc下的backtrace功能使用
glibc提供了backtrace函數,可以用來獲取當前程序的調用棧信息,使用方法如下:
包含頭文件:
#include
定義一個數組,用于存儲回溯信息:
#defineBT_BUF_SIZE100 void*bt_buffer[BT_BUF_SIZE];
該數組用于存儲backtrace信息,數組大小可以根據需要進行調整。
3. 調用backtrace函數:
intbt_size=backtrace(bt_buffer,BT_BUF_SIZE);
該函數會獲取當前程序的調用棧信息,并將其存儲在bt_buffer數組中。bt_size表示實際獲取到的調用棧信息的條數,該值不會超過BT_BUF_SIZE。
4. 使用backtrace_symbols函數將backtrace信息轉換成字符串:
char**bt_strings=backtrace_symbols(bt_buffer,bt_size);
該函數將backtrace信息轉換成字符串數組,每個字符串表示一個調用棧信息。bt_strings指向字符串數組的首地址,需要在使用完畢后手動釋放內存。
5. 打印回溯信息:
for(inti=0;i
該代碼會將回溯信息打印到標準輸出中,可以根據需要進行調整。完整的使用示例代碼如下:
#include#include #include #defineBT_BUF_SIZE100 voidprint_backtrace(){ void*bt_buffer[BT_BUF_SIZE]; intbt_size=backtrace(bt_buffer,BT_BUF_SIZE); char**bt_strings=backtrace_symbols(bt_buffer,bt_size); printf("backtrace: "); for(inti=0;i
該程序會輸出調用棧信息,格式如下:
backtrace: ./backtrace_demo(func_c+0x16)[0x40069a] ./backtrace_demo(func_b+0xd)[0x4006c5] ./backtrace_demo(func_a+0xd)[0x4006e0] ./backtrace_demo(main+0xe)[0x4006f6] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7f6a69e2b1c1] ./backtrace_demo(_start+0x2a)[0x400529]
其中每一行表示一個調用棧信息,格式為"函數名+偏移量+[地址]"。
4
gdb的backtrace功能
在Linux下進行嵌入式開發,backtrace通常是通過使用調試器來實現的,這樣的話,gdb都跟你封裝成了相應的命令,使用起來也簡單很多。
下面以gdb為例來介紹如何使用backtrace:
1、編譯程序時添加-g選項,以在可執行文件中包含調試信息。因為backtrace函數需要獲取調用棧信息,因此需要包含符號信息。如果使用了-g選項進行編譯,則可以保證符號信息的完整性,如果沒有使用-g選項編譯程序,則可能會出現獲取不到符號信息的情況,導致backtrace函數無法正常工作。
例如,使用gcc編譯時可以添加-g選項:
gcc-g-oprogramprogram.c2、使用gdb啟動程序并暫停程序的執行。例如,可以使用以下命令啟動程序:然后使用以下命令在程序執行時暫停程序的執行:這將在程序的main函數處設置斷點,并啟動程序的執行。gdbprogram(gdb)breakmain (gdb)run3、當程序崩潰或出現錯誤時,gdb會自動暫停程序的執行,并顯示當前程序的調用棧信息。可以使用以下命令查看調用棧信息:這將顯示當前程序的調用棧信息,包括每個函數的名稱、參數和返回值等信息,以及每個函數在調用棧中的位置。(gdb)backtrace4、最后可以使用其他gdb命令來查看每個函數的參數和局部變量等信息,以幫助定位代碼崩潰或錯誤的原因。
5
跟蹤的準確性
在實現backtrace功能時,還需要注意一些細節問題。例如,需要注意函數調用棧的深度和堆棧溢出等問題,以及需要保證backtrace操作的可靠性和準確性,下面簡單聊聊如下三個值得注意的方面:
優化選項:程序使用了-O選項進行優化時,可能會改變函數調用棧的結構,從而使backtrace函數獲取到的信息不完整或不準確。因此,在使用backtrace函數時,建議關閉優化選項,以保證其可靠性。
棧溢出:如果程序發生棧溢出,可能會破壞調用棧信息,導致backtrace函數獲取到的信息不完整或不準確。因此,在程序中應該避免出現棧溢出的情況,以保證backtrace函數的可靠性。
線程安全:如果程序使用多線程,每個線程都有自己的調用棧,因此需要在每個線程中分別調用backtrace函數來獲取相應的調用棧信息。此外,在多線程環境下,需要注意避免競爭條件的出現,以保證backtrace函數的可靠性。
總之,在使用glibc提供的backtrace函數時,需要注意編譯選項、優化選項、棧溢出和線程安全等因素,以保證其可靠性。此外,不同的硬件平臺和操作系統可能有不同的backtrace實現方式和接口,需要使用相應的工具和API來實現。
審核編輯:劉清
-
嵌入式
+關注
關注
5069文章
19021瀏覽量
303386 -
Linux系統
+關注
關注
4文章
591瀏覽量
27356 -
調試器
+關注
關注
1文章
300瀏覽量
23691 -
API接口
+關注
關注
1文章
82瀏覽量
10430 -
GDB調試
+關注
關注
0文章
24瀏覽量
1437
原文標題:嵌入式C代碼調試利器---backtrace
文章出處:【微信號:最后一個bug,微信公眾號:最后一個bug】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論