1
backtrace基本原理
backtrace英譯為回溯的意思,這聽起來有點(diǎn)專業(yè)了,其實(shí)大部分搞嵌入式的朋友都有聽說過函數(shù)調(diào)用棧callstack。而backtrace說白了就是我們呈現(xiàn)函數(shù)調(diào)用關(guān)系的一項(xiàng)功能。
所以backtrace調(diào)試功能的實(shí)現(xiàn)原理基于函數(shù)調(diào)用棧的概念。
那什么是函數(shù)調(diào)用棧呢?
函數(shù)調(diào)用棧是一個(gè)記錄程序中函數(shù)調(diào)用關(guān)系的數(shù)據(jù)結(jié)構(gòu),它在程序運(yùn)行時(shí)動(dòng)態(tài)生成和維護(hù)。當(dāng)程序執(zhí)行函數(shù)調(diào)用時(shí),它將當(dāng)前函數(shù)的返回地址和一些其他信息壓入堆棧中,并跳轉(zhuǎn)到被調(diào)用的函數(shù)執(zhí)行。當(dāng)被調(diào)用函數(shù)執(zhí)行完畢后,它將返回地址彈出堆棧,并跳回到調(diào)用函數(shù)繼續(xù)執(zhí)行。
backtrace調(diào)試功能的實(shí)現(xiàn)原理就是利用函數(shù)調(diào)用棧中的信息來追蹤程序執(zhí)行的路徑和調(diào)用關(guān)系。當(dāng)程序出現(xiàn)錯(cuò)誤或崩潰時(shí),backtrace可以通過分析函數(shù)調(diào)用棧信息來確定出錯(cuò)的位置和原因。
在Linux系統(tǒng)中,backtrace通常是通過使用調(diào)試器比如我們常用的gdb來實(shí)現(xiàn)的。調(diào)試器會(huì)在程序執(zhí)行時(shí),動(dòng)態(tài)地獲取函數(shù)調(diào)用棧信息,并將其保存在調(diào)試器的內(nèi)部數(shù)據(jù)結(jié)構(gòu)中。當(dāng)程序出現(xiàn)錯(cuò)誤或崩潰時(shí),調(diào)試器就可以利用保存的函數(shù)調(diào)用棧信息來進(jìn)行backtrace操作。
2
backtrace功能
而對(duì)于backtrace這個(gè)功能在不同的平臺(tái)和開發(fā)環(huán)境中的使用是不同的.
比如在我們平時(shí)的linux環(huán)境中:可以使用glibc提供的backtrace()函數(shù)實(shí)現(xiàn)backtrace功能。該函數(shù)通過解析函數(shù)調(diào)用棧信息獲取函數(shù)名、參數(shù)和返回地址等信息,并將其打印到標(biāo)準(zhǔn)輸出或指定的文件中。
此外,還可以使用gdb或libunwind庫來實(shí)現(xiàn)backtrace功能。gdb是一個(gè)強(qiáng)大的調(diào)試器,可以實(shí)時(shí)追蹤程序的執(zhí)行,獲取程序的調(diào)用棧信息,并提供各種調(diào)試工具和命令。
而其中的libunwind則是一個(gè)開源的C/C++庫,也可以用于在運(yùn)行時(shí)獲取當(dāng)前程序的調(diào)用棧信息,并且在不同的平臺(tái)和架構(gòu)上運(yùn)行,并提供了簡(jiǎn)單易用的API接口,同樣也是非常方便的。
3
glibc下的backtrace功能使用
glibc提供了backtrace函數(shù),可以用來獲取當(dāng)前程序的調(diào)用棧信息,使用方法如下:
包含頭文件:
#include
定義一個(gè)數(shù)組,用于存儲(chǔ)回溯信息:
#defineBT_BUF_SIZE100 void*bt_buffer[BT_BUF_SIZE];
該數(shù)組用于存儲(chǔ)backtrace信息,數(shù)組大小可以根據(jù)需要進(jìn)行調(diào)整。
3. 調(diào)用backtrace函數(shù):
intbt_size=backtrace(bt_buffer,BT_BUF_SIZE);
該函數(shù)會(huì)獲取當(dāng)前程序的調(diào)用棧信息,并將其存儲(chǔ)在bt_buffer數(shù)組中。bt_size表示實(shí)際獲取到的調(diào)用棧信息的條數(shù),該值不會(huì)超過BT_BUF_SIZE。
4. 使用backtrace_symbols函數(shù)將backtrace信息轉(zhuǎn)換成字符串:
char**bt_strings=backtrace_symbols(bt_buffer,bt_size);
該函數(shù)將backtrace信息轉(zhuǎn)換成字符串?dāng)?shù)組,每個(gè)字符串表示一個(gè)調(diào)用棧信息。bt_strings指向字符串?dāng)?shù)組的首地址,需要在使用完畢后手動(dòng)釋放內(nèi)存。
5. 打印回溯信息:
for(inti=0;i
該代碼會(huì)將回溯信息打印到標(biāo)準(zhǔn)輸出中,可以根據(jù)需要進(jìn)行調(diào)整。完整的使用示例代碼如下:
#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
該程序會(huì)輸出調(diào)用棧信息,格式如下:
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]
其中每一行表示一個(gè)調(diào)用棧信息,格式為"函數(shù)名+偏移量+[地址]"。
4
gdb的backtrace功能
在Linux下進(jìn)行嵌入式開發(fā),backtrace通常是通過使用調(diào)試器來實(shí)現(xiàn)的,這樣的話,gdb都跟你封裝成了相應(yīng)的命令,使用起來也簡(jiǎn)單很多。
下面以gdb為例來介紹如何使用backtrace:
1、編譯程序時(shí)添加-g選項(xiàng),以在可執(zhí)行文件中包含調(diào)試信息。因?yàn)閎acktrace函數(shù)需要獲取調(diào)用棧信息,因此需要包含符號(hào)信息。如果使用了-g選項(xiàng)進(jìn)行編譯,則可以保證符號(hào)信息的完整性,如果沒有使用-g選項(xiàng)編譯程序,則可能會(huì)出現(xiàn)獲取不到符號(hào)信息的情況,導(dǎo)致backtrace函數(shù)無法正常工作。
例如,使用gcc編譯時(shí)可以添加-g選項(xiàng):
gcc-g-oprogramprogram.c2、使用gdb啟動(dòng)程序并暫停程序的執(zhí)行。例如,可以使用以下命令啟動(dòng)程序:然后使用以下命令在程序執(zhí)行時(shí)暫停程序的執(zhí)行:這將在程序的main函數(shù)處設(shè)置斷點(diǎn),并啟動(dòng)程序的執(zhí)行。gdbprogram(gdb)breakmain (gdb)run3、當(dāng)程序崩潰或出現(xiàn)錯(cuò)誤時(shí),gdb會(huì)自動(dòng)暫停程序的執(zhí)行,并顯示當(dāng)前程序的調(diào)用棧信息??梢允褂靡韵旅畈榭凑{(diào)用棧信息:這將顯示當(dāng)前程序的調(diào)用棧信息,包括每個(gè)函數(shù)的名稱、參數(shù)和返回值等信息,以及每個(gè)函數(shù)在調(diào)用棧中的位置。(gdb)backtrace4、最后可以使用其他gdb命令來查看每個(gè)函數(shù)的參數(shù)和局部變量等信息,以幫助定位代碼崩潰或錯(cuò)誤的原因。
5
跟蹤的準(zhǔn)確性
在實(shí)現(xiàn)backtrace功能時(shí),還需要注意一些細(xì)節(jié)問題。例如,需要注意函數(shù)調(diào)用棧的深度和堆棧溢出等問題,以及需要保證backtrace操作的可靠性和準(zhǔn)確性,下面簡(jiǎn)單聊聊如下三個(gè)值得注意的方面:
優(yōu)化選項(xiàng):程序使用了-O選項(xiàng)進(jìn)行優(yōu)化時(shí),可能會(huì)改變函數(shù)調(diào)用棧的結(jié)構(gòu),從而使backtrace函數(shù)獲取到的信息不完整或不準(zhǔn)確。因此,在使用backtrace函數(shù)時(shí),建議關(guān)閉優(yōu)化選項(xiàng),以保證其可靠性。
棧溢出:如果程序發(fā)生棧溢出,可能會(huì)破壞調(diào)用棧信息,導(dǎo)致backtrace函數(shù)獲取到的信息不完整或不準(zhǔn)確。因此,在程序中應(yīng)該避免出現(xiàn)棧溢出的情況,以保證backtrace函數(shù)的可靠性。
線程安全:如果程序使用多線程,每個(gè)線程都有自己的調(diào)用棧,因此需要在每個(gè)線程中分別調(diào)用backtrace函數(shù)來獲取相應(yīng)的調(diào)用棧信息。此外,在多線程環(huán)境下,需要注意避免競(jìng)爭(zhēng)條件的出現(xiàn),以保證backtrace函數(shù)的可靠性。
總之,在使用glibc提供的backtrace函數(shù)時(shí),需要注意編譯選項(xiàng)、優(yōu)化選項(xiàng)、棧溢出和線程安全等因素,以保證其可靠性。此外,不同的硬件平臺(tái)和操作系統(tǒng)可能有不同的backtrace實(shí)現(xiàn)方式和接口,需要使用相應(yīng)的工具和API來實(shí)現(xiàn)。
審核編輯:劉清
-
嵌入式
+關(guān)注
關(guān)注
5060文章
18979瀏覽量
302235 -
Linux系統(tǒng)
+關(guān)注
關(guān)注
4文章
590瀏覽量
27320 -
調(diào)試器
+關(guān)注
關(guān)注
1文章
300瀏覽量
23669 -
API接口
+關(guān)注
關(guān)注
1文章
82瀏覽量
10420 -
GDB調(diào)試
+關(guān)注
關(guān)注
0文章
24瀏覽量
1432
原文標(biāo)題:嵌入式C代碼調(diào)試?yán)?--backtrace
文章出處:【微信號(hào):最后一個(gè)bug,微信公眾號(hào):最后一個(gè)bug】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論