初步了解如何用GDB分析Core文件

之前初步了解過Windows 下強大的調試工具WinDbg,也簡單的整理了一個初級的文章《使用WinDbg、Map文件、Dump文件定位Access Violation的代碼行》,在Linux 下面也有對應的功能強大的調試工具:GDB,它可以用來斷點調試C/C++ 的程序,也可以用于分析Linux 下的C/C++ 程序運行崩潰產生的Core 文件……

另外對于GDB 工具,在《Linux gdb調試器用法全面解析》這篇文章中詳細的介紹了怎么使用GDB 去調試C/C++ 代碼

本文通過一個簡單的例子展示怎么使用GDB 分析Core 文件,但就像我一直強調的,完全停留在一個很膚淺的入門級的水平,只是先讓自己能有一個對GDB 的感性的認知,其實GDB 很強大,它能做的事情遠不止于本文所提到的這些皮毛

本文的內容也是參考了網絡上很多的文章,然后結合自己的驗證整理出來的

Core文件和段錯誤

當一個程序奔潰時,在進程當前工作目錄的Core 文件中復制了該進程的存儲圖像。Core 文件僅僅是一個內存映像(同時加上調試信息),主要用來調試的

通常情況下,Core 文件會包含程序運行時的內存、寄存器狀態、堆棧指針、內存管理信息還有各種函數調用堆棧信息等。我們可以理解為是程序工作當前狀態存儲生成第一個文件,許多程序出錯時都會產生一個Core 文件,通過工具分析這個文件,我們可以定位到程序異常退出時對應的堆棧調用等信息,找出問題所在并進行及時解決

段錯誤,就是大名鼎鼎的Segmentation Fault,這通常是由指針錯誤引起的。簡而言之,產生段錯誤就是訪問了錯誤的內存段,一般是你沒有權限,或者根本就不存在對應的物理內存,尤其常見的是訪問0 地址。

一般而言,段錯誤就是指訪問的內存超出了系統所給這個程序的內存空間,通常這個值是由gdtr 來保存的,這是一個48位的寄存器,其中的32位是保存由它指向的gdt 表,后13位保存相應于gdt 的下標,最后3位包括了程序是否在內存中以及程序在CPU 中的運行級別。指向的gdt 是由以64位為一個單位的表,在這張表中就保存著程序運行的代碼段以及數據段的起始地址以及與此相應的段限、頁面交換、程序運行級別、內存粒度等的信息。一旦一個程序發生了越界訪問,CPU 就會產生相應的異常保護,于是Segmentation fault就出現了

在編程中有以下幾種做法容易導致段錯誤,基本都是錯誤地使用指針引起的:

  • 訪問系統數據區,尤其是往系統保護的內存地址寫數據,最常見的就是給指針以0地址
  • 內存越界(數據越界、變量類型不一致等)訪問到不屬于你的內存區域

程序在運行過程中如果出現段錯誤,那么就會收到SIGSEGV 信號,SIGSEGV 默認handler 的動作是打印“段錯誤”的出錯信息,并產生Core 文件

GDB 斷點調試以定位錯誤代碼行

testCrash.cpp

void testCrash()
{
    int* p = 1; //p指針指向常量1 所在的內存地址
    *p = 3;     //將p指針指向的地址的值改為3,
    //因為本來p指向一個常量,是不允許被修改的
    //強行訪問系統保護的內存地址就會出現段錯誤
}

main.cpp

#include <stdio.h>

void testCrash();

int main()
{
    testCrash();
    return 0
}

編譯執行,報段錯誤

注意g++ 編譯的時候,需要使用參數-g,否則GDB 無法找到symbol 信息,從而無法定位問題

image

斷點調試

image

很明顯,在GDB 斷點調試的過程中,已經將錯誤的代碼行輸出了:在testCrash.cpp 的第4行,在testCrash()方法里面,而且也將錯誤的代碼*p = 3;打印出來了

還發現進程是由于收到了SIGSEGV 信號而結束的。通過進一步的查閱文檔(man 7 signal),SIGSEGV 默認handler 的動作是打印”段錯誤”的出錯信息,并產生Core 文件

分析Core 文件

設置Core文件大小,運行程序生成Core文件

執行ulimit -c unlimited表示不限制生成的Core 文件的大小,注意這個命令只在當前的bash 下生效!然后運行這個有bug 的程序,可以看到在當前目錄下生成了core文件

image

GDB 分析Core 文件

image

同樣也是一步到位的定位到錯誤所在的代碼行!

為了獲取更詳細的函數調用信息,在執行gdb 可執行文件 core文件啟動gdb后,調用gdb的where或bt命令可以查看當時的調用棧信息!確定是什么樣的函數調用棧導致的程序崩潰!

接著考慮下去,在Windows 系統下的運行程序時,可能會出現“運行時錯誤”,這個時侯如果恰好你的機器上又裝有Windows 的編譯器的話,它會彈出來一個對話框,問你是否進行調試,如果你選擇是,編譯器將被打開,并進入調試狀態,開始調試

Linux下可以做到嗎?可以讓它在SIGSEGV 的handler中調用gdb

段錯誤時啟動調試

testCrash.cpp

void testCrash()
{
    int* p = 1; //p指針指向常量1 所在的內存地址
    *p = 3;     //將p指針指向的地址的值改為3,
    //因為本來p指向一個常量,是不允許被修改的
    //強行訪問系統保護的內存地址就會出現段錯誤
}

main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>

void testCrash();

void dump(int signo)
{
    char buf[1024];
    char cmd[1024];
    FILE *fh;

    snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());
    if(!(fh = fopen(buf, "r")))
    {
        exit(0);
    }
    if(!fgets(buf, sizeof(buf), fh))
    {
        exit(0);
    }
    fclose(fh);
    if(buf[strlen(buf) - 1] == '\n')
    {
        buf[strlen(buf) - 1] = '\0';
    }
    snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());
    system(cmd);

    exit(0);
}

int main()
{
    signal(SIGSEGV, &dump);
    testCrash();
    return 0;
}

編譯程序

注意g++ 編譯的時候,需要使用參數-g,否則GDB 無法找到symbol 信息,從而無法定位問題

image

運行程序

首先必須要切換到root 用戶運行,否則因為權限問題導致無法調試,另外就是進入調試模式后執行bt 以顯示程序的調用棧信息!

image

路漫漫其修遠兮

以上展示的這些東西很簡單,在你對Linux 進程的虛擬內存、進程的堆棧結構等沒有任何了解的情況下,完全照葫蘆畫瓢也能簡單的使用GDB

但是上面的程序、上面的代碼、上面的場景都完全是一個極其理想化的場景,在這種場景下排查問題當然是很簡單的

而在實際的場景中,往往比這個要復雜的多

  • 程序遠不止上面的十幾二十幾行,可能是上萬、上百萬行!
  • 絕不是簡單的單線程程序,可能會有多進程、多線程,這種場景該怎么調試?
  • 假如程序崩潰了,但調用的是外部提供的.so文件,根本沒有對應源碼,此時就無法結合代碼分析了!
  • 假如編譯時沒有加-g 參數,那么GDB 無法找到symbol信息,那怎么辦?
  • 等等等等

針對上面的這些復雜的場景,上面展示的這些GDB 的簡單招式可能就沒有效果了,所以就需要更深層次的研究GDB 的使用,以及GDB 調試進程、分析Core 文件背后的操作系統、編譯原理層面的機制是什么

在Windows 下使用WinDbg 調試進程、分析dump 文件也是一樣的情況!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容

  • 程序調試的基本思想是“分析現象->假設錯誤原因->產生新的現象去驗證假設”這樣一個循環過程,根據現象如何假設錯誤原...
    Manfred_Zone閱讀 16,558評論 0 26
  • 一、溫故而知新 1. 內存不夠怎么辦 內存簡單分配策略的問題地址空間不隔離內存使用效率低程序運行的地址不確定 關于...
    SeanCST閱讀 7,856評論 0 27
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,120評論 1 32
  • 在Linux下程序不尋常退出時,內核會在當前工作目錄下生成一個core文件(是一個內存映像,同時加上調試信息)。使...
    隨風化作雨閱讀 46,346評論 2 15
  • 現在化妝的普及已經到03后這一代,可見化妝人群之多。在這個發展迅速的時代里,化妝已經是一件正常的不能再正常的事情,...
    guacai4093閱讀 356評論 0 0