嵌入式C開發中段錯誤的3種調試方法

2023-07-11 10:27:04 來源:嵌入式大雜燴

相信大家在嵌入式C開發中,或多或少都會遇到段錯誤(segmentation fault )。昨天分享了一個總線錯誤的例子:嵌入式軟件中,關于總線錯誤,我幫你們踩了這些坑!相比總線錯誤,段錯誤是一種更為常見的錯誤。

那么,段錯誤是怎么產生的呢?簡單來說,段錯誤是因為訪問不可訪問的內存產生的。

下面是一些典型的段錯誤產生的原因:


(資料圖片僅供參考)

訪問不存在的內存地址

訪問只讀的內存地址

棧溢出

內存越界

……

段錯誤實例

1、實例1:訪問不存在的內存地址

#includeintmain(intargc,char**argv){printf("==================segmentation fault test==================");int*p=NULL;*p=1234;return0;}

2、實例2:訪問只讀的內存地址

#includeintmain(intargc,char**argv){printf("==================segmentation fault test1==================");char*str="hello";str[0]="H";return0;}

3、實例3:棧溢出

#includestaticvoidtest(void){charbuf[1024*1024]={0};staticinti=0;i++;printf("i=%d",i);test();}intmain(intargc,char**argv){printf("==================segmentation fault test2==================");test();return0;}

4、實例4:內存越界

#includeintmain(intargc,char**argv){printf("==================segmentation fault test3==================");staticchararr[5]={0,1,2,3,4};printf("arr[10000]=%d",arr[10000]);return0;}

段錯誤調試方法

從上面的幾個例子中,我們應該對段錯誤有了一定的認識,但實際項目中,實際中,段錯誤可能沒有上面的例子那么明顯看出。如果之前沒有這方面的經驗,可能一時半會也定位不到問題。

接下來,分享一下段錯誤的3種調試方法,供大家參考。

我們依舊使用例子來說明,例子:

#includestaticvoidfunc0(void){printf("This is func0");int*p=NULL;*p=1234;}staticvoidfunc1(void){printf("This is func1");func0();}intmain(intargc,char**argv){printf("==================segmentation fault test4==================");func1();return0;}

1、gdb一步步運行

使用gdb調試,打一些斷點、按流程運行下去,運行到段錯誤的地方會直接提示報錯。

或者使用命令行直接gdb調試:

這里我們是在x86上運行,如果是定位arm嵌入式Linux程序,我們怎么做的?

同樣也是可以使用gdb的,可以參考我們之前分享的文章:VSCode+gdb+gdbserver遠程調試ARM程序

2、通過core文件

Linux下,一個程序崩潰時,它一般會在指定目錄下生成一個core文件。core文件僅僅是一個內存映象(同時加上調試信息),主要是用來調試的。

core文件可打開與關閉。相關命令:

ulimit-c#查看core文件是否打開ulimit-c0#禁止產生core文件ulimit-c unlimited#設置core文件大小為不限制大小ulimit-c1024#限制產生的core文件的大小不能超過1024KB

0代表關閉。下面我們打開它:

運行程序時,程序崩潰時,在程序目錄下會生成core文件,比如:

調試core文件:

gdbtestcore

3、利用backtrace進行分析

#include#include#include#includevoidfunc0(void){printf("This is func0");int*p=NULL;*p=1234;}voidfunc1(void){printf("This is func1");func0();}voidfunc2(void){printf("This is func2");func1();}voiddump(intsigno){void*array[100];size_tsize;char**strings;size=backtrace(array,100);strings=backtrace_symbols(array,size);printf("Obtained%zd stacks.",size);for(inti=0;i

當程序發生段錯誤時,內核會向程序發送SIGSEGV信號。dump為SIGSEGV信號處理函數,其實現用到了execinfo.h里的兩個函數:

intbacktrace(void**buffer,intsize);char**backtrace_symbols(void*const*buffer,intsize);

backtrace函數用于獲取當前線程的調用堆棧,獲取的信息將會被存放在buffer中,它是一個指針列表。參數 size 用來指定buffer中可以保存多少個void* 元素。函數返回值是實際獲取的指針個數,最大不超過size大小 在buffer中的指針實際是從堆棧中獲取的返回地址,每一個堆棧框架有一個返回地址。

backtrace_symbols將從backtrace函數獲取的信息轉化為一個字符串數組。參數buffer應該是從backtrace函數獲取的指針數組,size是該數組中的元素個數(backtrace的返回值)。函數返回值是一個指向字符串數組的指針,它的大小同buffer相同。

每個字符串包含了一個相對于buffer中對應元素的可打印信息。它包括函數名,函數的偏移地址,和實際的返回地址。

注意:該函數的返回值是通過malloc函數申請的空間,因此調用者必須使用free函數來釋放指針。如果不能為字符串獲取足夠的空間函數的返回值將會為NULL。

以上就是本次介紹的3種定位段錯誤問題的方法,可以定位不同程度的問題。

審核編輯:湯梓紅

標簽:

上一篇:一種Co/Pt結構的非易失性磁存儲器
下一篇:最后一頁