1. 程式人生 > >用gdb分析core檔案及常見gdb命令操作示例

用gdb分析core檔案及常見gdb命令操作示例

1.概述
在實際的軟體開發專案中,程式出現問題是在所難免的。遙想本人蔘加工作之後首次遇到程式的情景,至今還歷歷在目。之前的經驗告訴我,我們越是驚慌失措,問題就越是解決不了。我們要先讓自己平靜下來,然後再尋找解決程式問題的辦法。
在Linux下做開發的朋友,想必都與core檔案打過交道。當看到自己的程式執行之後出現core時,很多人都慌亂了,彷彿天快要塌下來一樣。其實,我們大可不必如此,只要我們掌握了用gdb除錯core檔案的辦法,依然可以很快定位程式問題,一舉將bug消滅掉。有關Linux core檔案的更多介紹,請閱讀此文:http://www.cnblogs.com/dongzhiquan/archive/2012/01/20/2328355.html


本文以一個實際的程式為例,介紹了用gdb分析core檔案的方法和步驟,同時演示了常見gdb命令的操作方法。如果大家想對相關gdb命令有更多的瞭解,請自行百度之。

2.示例程式

/**********************************************************************
* 版權所有 (C)2015, Zhou Zhaoxiong。
*
* 檔名稱:GdbDebug.c
* 檔案標識:無
* 內容摘要:Gdb命令演示程式
* 其它說明:無
* 當前版本:V1.0
* 作    者:Zhou Zhaoxiong
* 完成日期:20151008
*
**********************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> // 資料型別重定義 typedef unsigned char UINT8; typedef signed int INT32; typedef unsigned int UINT32; // 函式宣告 void Sleep(UINT32 iCountMs); void PrintInfo(void); INT32 main(); /*****
***************************************************************** * 功能描述:主函式 * 輸入引數:無 * 輸出引數:無 * 返 回 值:無 * 其它說明:無 * 修改日期 版本號 修改人 修改內容 * ------------------------------------------------------------------- * 20151008 V1.0 Zhou Zhaoxiong 建立 ***********************************************************************/ INT32 main() { PrintInfo(); // 在螢幕上輸出訊息 return 0; } /********************************************************************** * 功能描述: 在螢幕上輸出訊息 * 輸入引數: 無 * 輸出引數: 無 * 返 回 值: 無 * 其它說明: 無 * 修改日期 版本號 修改人 修改內容 * ---------------------------------------------------------------------- * 20151008 V1.0 Zhou Zhaoxiong 建立 ************************************************************************/ void PrintInfo(void) { UINT32 iLoopFlag = 0; UINT32 iSum = 0; UINT32 iLen = 0; UINT8 *pCtrStr = NULL; iLen = strlen(pCtrStr); for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++) // 列印訊息iLen次 { printf("PrintInfo: hello, world!\n"); iSum = iSum + iLoopFlag; Sleep(10 * 1000); // 每10s列印一次 } return; } /********************************************************************** * 功能描述: 程式休眠 * 輸入引數: iCountMs-休眠時間(單位:ms) * 輸出引數: 無 * 返 回 值: 無 * 其它說明: 無 * 修改日期 版本號 修改人 修改內容 * ------------------------------------------------------------------ * 20151008 V1.0 Zhou Zhaoxiong 建立 ********************************************************************/ void Sleep(UINT32 iCountMs) { struct timeval t_timeout = {0}; if (iCountMs < 1000) { t_timeout.tv_sec = 0; t_timeout.tv_usec = iCountMs * 1000; } else { t_timeout.tv_sec = iCountMs / 1000; t_timeout.tv_usec = (iCountMs % 1000) * 1000; } select(0, NULL, NULL, NULL, &t_timeout); // 呼叫select函式阻塞程式 }

3.用gdb分析core檔案
在Linux上用“gcc -g -o GdbDebug GdbDebug.c”命令對程式進行編譯之後,執行“GdbDebug”命令,發現在當前目錄下出現了core檔案。利用gdb命令對core檔案進行分析的過程如下所示:

~/zhouzhaoxiong/zzx/GdbDebug> gdb GdbDebug core     -- 啟動gdb對core檔案的分析
GNU gdb (GDB) SUSE (7.3-0.6.1)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug...done.
Core was generated by `GdbDebug'.
Program terminated with signal 11, Segmentation fault.
#0  0x00007f4a736f9812 in __strlen_sse2 () from /lib64/libc.so.6
(gdb) where          -- 檢視程式出問題的地方
#0  0x00007f4a736f9812 in __strlen_sse2 () from /lib64/libc.so.6
#1  0x000000000040061a in PrintInfo () at GdbDebug.c:64   -- 可以看到,在GdbDebug.c檔案的第64行出的問題
#2  0x00000000004005e5 in main () at GdbDebug.c:41
(gdb) b 41           -- 在GdbDebug.c檔案第41行設立斷點
Breakpoint 1 at 0x4005e0: file GdbDebug.c, line 41.
(gdb) b 64           -- 在GdbDebug.c檔案第64行設立斷點
Breakpoint 2 at 0x400611: file GdbDebug.c, line 64.
(gdb) info b         -- 顯示斷點資訊
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004005e0 in main at GdbDebug.c:41
2       breakpoint     keep y   0x0000000000400611 in PrintInfo at GdbDebug.c:64
(gdb) r              -- 執行GdbDebug
Starting program: /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug 

Breakpoint 1, main () at GdbDebug.c:41
41          PrintInfo();   // 在螢幕上輸出訊息
(gdb) n             -- 執行下一步

Breakpoint 2, PrintInfo () at GdbDebug.c:64
64              iLen = strlen(pCtrStr);
(gdb) p iLen        -- 列印(輸出)iLen的值
$1 = 0
(gdb) p iLoopFlag   -- 列印(輸出)iLoopFlag的值
$2 = 0
(gdb) c             -- 繼續執行     
Continuing.

Program received signal SIGSEGV, Segmentation fault.    -- 程式core掉了
0x00007ffff7ae9812 in __strlen_sse2 () from /lib64/libc.so.6
(gdb) q             -- 退出gdb
A debugging session is active.

        Inferior 1 [process 26640] will be killed.

Quit anyway? (y or n) y
~/zhouzhaoxiong/zzx/GdbDebug>

從以上分析可知,執行GdbDebug.c檔案的第64行時程式core掉了。此時仔細分析程式,發現pCtrStr指標為空。當對一個不存在的指標取長度時,由於找不到地址,程式便崩潰了。修改的辦法也非常的簡單,只需要讓pCtrStr指標指向具體的地址即可。

4.常見gdb命令操作示例
修改之後的程式碼如下:

/**********************************************************************
* 版權所有 (C)2015, Zhou Zhaoxiong。
*
* 檔名稱:GdbDebug.c
* 檔案標識:無
* 內容摘要:Gdb命令演示程式
* 其它說明:無
* 當前版本:V1.0
* 作    者:Zhou Zhaoxiong
* 完成日期:20151008
*
**********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 資料型別重定義
typedef unsigned char       UINT8;
typedef signed   int        INT32;
typedef unsigned int        UINT32;


// 函式宣告
void Sleep(UINT32 iCountMs);
void PrintInfo(void);
INT32 main();


/**********************************************************************
* 功能描述:主函式
* 輸入引數:無
* 輸出引數:無
* 返 回 值:無
* 其它說明:無
* 修改日期        版本號     修改人            修改內容
* -------------------------------------------------------------------
* 20151008       V1.0    Zhou Zhaoxiong       建立
***********************************************************************/
INT32 main()
{
    PrintInfo();   // 在螢幕上輸出訊息

    return 0;
}


/**********************************************************************
 * 功能描述: 在螢幕上輸出訊息
 * 輸入引數: 無
 * 輸出引數: 無
 * 返 回 值: 無
 * 其它說明: 無
 * 修改日期            版本號            修改人           修改內容
 * ----------------------------------------------------------------------
 * 20151008           V1.0         Zhou Zhaoxiong        建立
 ************************************************************************/
void PrintInfo(void)
{
    UINT32 iLoopFlag = 0;
    UINT32 iSum      = 0;
    UINT32 iLen      = 0;
    UINT8 *pCtrStr   = "hello, world!";  // 修改了這行程式碼

    iLen = strlen(pCtrStr);

    for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++)      // 列印訊息iLen次
    {
        printf("PrintInfo: hello, world!\n");

        iSum = iSum + iLoopFlag;

        Sleep(10 * 1000);   // 每10s列印一次
    }

    return;
}


/**********************************************************************
* 功能描述: 程式休眠
* 輸入引數: iCountMs-休眠時間(單位:ms)
* 輸出引數: 無
* 返 回 值: 無
* 其它說明: 無
* 修改日期          版本號       修改人              修改內容
* ------------------------------------------------------------------
* 20151008         V1.0     Zhou Zhaoxiong          建立
********************************************************************/
void Sleep(UINT32 iCountMs)
{
    struct timeval t_timeout = {0};

    if (iCountMs < 1000)
    {
        t_timeout.tv_sec = 0;
        t_timeout.tv_usec = iCountMs * 1000;
    }
    else
    {
        t_timeout.tv_sec = iCountMs / 1000;
        t_timeout.tv_usec = (iCountMs % 1000) * 1000;
    }
    select(0, NULL, NULL, NULL, &t_timeout);   // 呼叫select函式阻塞程式
}

編譯並執行之後,程式正常,說明問題已被我們解決掉。下面是常見的gdb命令的操作示例:

~/zhouzhaoxiong/zzx/GdbDebug> gdb GdbDebug    -- 啟動gdb除錯
GNU gdb (GDB) SUSE (7.3-0.6.1)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-suse-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug...done.
(gdb) b 64     -- 在GdbDebug.c檔案第64行設立斷點
Breakpoint 1 at 0x400611: file GdbDebug.c, line 64.
(gdb) b 72     -- 在GdbDebug.c檔案第72行設立斷點
Breakpoint 2 at 0x400637: file GdbDebug.c, line 72.
(gdb) info b   -- 顯示斷點資訊
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400611 in PrintInfo at GdbDebug.c:64
2       breakpoint     keep y   0x0000000000400637 in PrintInfo at GdbDebug.c:72
(gdb) r        -- 執行GdbDebug
Starting program: /home/zhou/zhouzhaoxiong/zzx/GdbDebug/GdbDebug 

Breakpoint 1, PrintInfo () at GdbDebug.c:64
64              iLen = strlen(pCtrStr);
(gdb) p iLen    -- 列印(輸出)iLen的值
$1 = 0
(gdb) n         -- 執行下一步
66              for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++)      // 列印訊息iLen次
(gdb) n         -- 執行下一步
68              printf("PrintInfo: hello, world!\n");
(gdb) p iLoopFlag   -- 列印(輸出)iLoopFlag的值
$2 = 0
(gdb) p iLen    -- 列印(輸出)iLen的值
$3 = 13
(gdb) n         -- 執行下一步
PrintInfo: hello, world!    -- 程式的輸出結果
70                      iSum = iSum + iLoopFlag;
(gdb) p iSum    -- 列印(輸出)iSum的值
$4 = 0
(gdb) n        -- 執行下一步

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72                      Sleep(10 * 1000);   // 每10s列印一次
(gdb) n      
66              for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++)      // 列印訊息iLen次
(gdb) p iLoopFlag
$5 = 0
(gdb) n
68              printf("PrintInfo: hello, world!\n");
(gdb) p iLoopFlag
$6 = 1
(gdb) n
PrintInfo: hello, world!
70                      iSum = iSum + iLoopFlag;
(gdb) p iSum
$7 = 0
(gdb) n

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72                      Sleep(10 * 1000);   // 每10s列印一次
(gdb) p iSum
$8 = 1
(gdb) finish        -- 一直執行到函式返回
Run till exit from #0  PrintInfo () at GdbDebug.c:72
PrintInfo: hello, world!

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72                      Sleep(10 * 1000);   // 每10s列印一次
(gdb) c           -- 繼續執行 
Continuing.
PrintInfo: hello, world!

Breakpoint 2, PrintInfo () at GdbDebug.c:72
72                      Sleep(10 * 1000);   // 每10s列印一次
(gdb) bt            -- 列印當前的函式呼叫棧的所有資訊
#0  PrintInfo () at GdbDebug.c:72
#1  0x00000000004005e5 in main () at GdbDebug.c:41
(gdb) q              -- 退出gdb
A debugging session is active.

        Inferior 1 [process 26685] will be killed.

Quit anyway? (y or n) y
~/zhouzhaoxiong/zzx/GdbDebug> 

作為Linux下除錯C/C++程式的工具,大家一定要熟練掌握gdb的用法。