天天看點

GDB調試段錯誤

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結和本聲明。 本文連結: https://blog.csdn.net/hushujian/article/details/39854185

一、GDB的調試指令。

C語言是:cc -g tst.c -o tst;C++是g++  -g -o (生成的檔案) file.cpp

C++調試程式指令:gdb  file 啟動,羅列代碼行數ist 1,break (行數),info break,run(r)調試運作,step(s)單步調試,檢視變量 print(p) 變量名,檢視堆棧式bt,繼續調試continue(c),退出程式q

二、Core檔案的産生

當linux程式在運作過程中挂掉的時候,可能就出現core檔案。通過調試core檔案就可以看出來程式在代碼那個位置挂掉了。會在指定的目錄下生成core檔案,core檔案時記憶體的映像并加入了調試資訊,主要用來調試

用以下指令來阻止系統生成core檔案:

ulimit -c 0

下面的指令可以檢查生成core檔案的選項是否打開:

ulimit -a

該指令将顯示所有的使用者定制,其中選項-a代表“all”。

不産生core檔案原因,1、沒有足夠記憶體空間,2、禁用了core檔案的建立,3、設定一個程序目前目錄沒有寫檔案的權限

調試core檔案

ulimit  -c  unlimited 表示要生成core檔案 不限制core檔案的大小

三、利用gdb來調試段錯誤

産生段錯誤就是通路了錯誤的記憶體段,一般是沒有權限,或者根本就不存在對應的實體記憶體,尤其常見的是通路0位址. 在程式設計中以下幾類做法容易導緻段錯誤,基本是是錯誤地使用指針引起的

1)通路系統資料區,尤其是往 系統保護的記憶體位址寫資料

最常見就是給一個指針以0位址

2)記憶體越界(數組越界,變量類型不一緻等) 通路到不屬于你的記憶體區域

解決方法

我們在用C/C++語言寫程式的時侯,記憶體管理的絕大部分工作都是需要我們來做的。實際上,記憶體管理是一個比較繁瑣的工作,無論你多高明,經驗多豐富,難免會在此處犯些小錯誤,而通常這些錯誤又是那麼的淺顯而易于消除。但是手工“除蟲”(debug),往往是效率低下且讓人厭煩的,本文将就"段錯 誤"這個記憶體通路越界的錯誤談談如何快速定位這些"段錯誤"的語句。

下面将就以下的一個存在段錯誤的程式介紹幾種調試方法:

以下是程式代碼

 dummy_function(void)

  2 {

  3   unsigned char*ptr=0x00;

  4   *ptr =0x00;

  5

  6 }

  7 int main(void)

  8 {

  9

10    dummy_function();

11    return 0;

12 }

作為一個熟練的C/C++程式員,以上代碼的bug應該是很清楚的,因為它嘗試操作位址為0的記憶體區域,而這個記憶體區域通常是不可通路的禁區,當然就會出錯了。我們嘗試編譯運作它:

GDB調試段錯誤

1.利用gdb逐漸查找段錯誤

這種方法也是被大衆所熟知并廣泛采用的方法,首先我們需要一個帶有調試資訊的可執行程式,是以我們加上“-g -rdynamic"的參數進行編譯,然後用gdb調試運作這個新編譯的程式,具體步驟如下:

GDB調試段錯誤

哦?!好像不用一步步調試我們就找到了出錯位置d.c檔案的第4行,其實就是如此的簡單。

從這裡我們還發現程序是由于收到了SIGSEGV信号而結束的。通過進一步的查閱文檔(man 7 signal),我們知道SIGSEGV預設handler的動作是列印”段錯誤"的出錯資訊,并産生Core檔案,由此我們又産生了方法2

2.通過對core檔案進行調試

GDB調試段錯誤

哇,好曆害,還是一步就定位到了錯誤所在地,佩服一下Linux/Unix系統的此類設計。

接着考慮下去,以前用windows系統下的ie的時侯,有時打開某些網頁,會出現“運作時錯誤”,這個時侯如果恰好你的機器上又裝有windows的編譯器的話,他會彈出來一個對話框,問你是否進行調試,如果你選擇是,編譯器将被打開,并進入調試狀态,開始調試。

Linux下如何做到這些呢?我的大腦飛速地旋轉着,有了,讓它在SIGSEGV的handler中調用gdb,于是第三個方法又誕生了

3.段錯誤時啟用調試程式

 1 #include <stdio.h>

  2 #include <stdlib.h>

  3 #include <signal.h>

  4 #include <string.h>

  5 void dump(int signo)

  6 {

  7    char buf[1024];

  8    char cmd[1024];

  9    FILE*fh;

10   snprintf(buf,sizeof(buf),"/proc/%d/cmdline",getpid());

11    if(!(fh=fopen(buf,"r")))

12    exit(0);

13    if(!fgets(buf,sizeof(buf),fh))

14    exit(0);

15    fclose(fh);

16    if(buf[strlen(buf)-1]='\n')

17    buf[strlen(buf)-1]='\0';

18    sprintf(cmd,sizeof(cmd),"gdb %s%d",buf,getpid());

19    system(cmd);

20    exit(0);

21 }

22

23 void dummy_function(void)

24 {

25   unsigned char*ptr=0x00;

26   *ptr=0x00;

27 }

28

29 int main(void)

30 {

31    signal(SIGSEGV,&dump);

32    dummy_function();

33    return 0;

34 }

GDB調試段錯誤

怎麼樣?是不是依舊很酷?

以 上方法都是在系統上有gdb的前提下進行的,如果沒有呢?其實glibc為我們提供了此類能夠dump棧内容的函數簇,詳見 /usr/include/execinfo.h(這些函數都沒有提供man page,難怪我們找不到),另外你也可以通過gnu的手冊進行學習

4.利用backtrace和objdump進行分析

 1#include <execinfo.h>

  2 #include <stdio.h>

  3 #include <stdlib.h>

  4 #include <signal.h>

  5 void dummy_function(void)

  6 {

  7

  8     unsigned char*ptr=0x00;

  9     *ptr=0x00;

10

11 }

12 void dump(int signo)

13 {

14

15   void*array[10];

16   size_t size;

17   char**strings;

18   size_t i;

19

20   size=backtrace(array,10);

21   strings =backtrace_symbols(array,size);

22

23   printf("Obtained %zd stack frames.\n",size);

24

25   for(i=0;i<size;i++)

26   {

27     printf("%s\n",strings[i]);

28

29

30   }

31

32   free(strings);

33

34   exit(0);

35

36 }

37

38 int main(void)

39 {

40    signal(SIGSEGV,&dump);

41    dummy_function();

42    return 0;

43 } 

這次你可能有些失望,似乎沒能給出足夠的資訊來标示錯誤,不急,先看看能分析出來什麼吧,用objdump反彙程式設計式,找到位址0x804876f對應的代碼位置:

繼續閱讀