用gdb分析core文件及常見gdb命令操作示例
1.概述
在實際的軟件開發(fā)項目中,程序出現(xiàn)問題是在所難免的。遙想本人參加工作之后***遇到程序的情景,至今還歷歷在目。之前的經(jīng)驗告訴我,我們越是驚慌失措,問題就越是解決不了。我們要先讓自己平靜下來,然后再尋找解決程序問題的辦法。
在Linux下做開發(fā)的朋友,想必都與core文件打過交道。當看到自己的程序運行之后出現(xiàn)core時,很多人都慌亂了,仿佛天快要塌下來一樣。其實,我們大可不必如此,只要我們掌握了用gdb調(diào)試core文件的辦法,依然可以很快定位程序問題,一舉將bug消滅掉。有關(guān)Linux core文件的更多介紹,請閱讀此文:http://www.cnblogs.com/dongzhiquan/archive/2012/01/20/2328355.html。
本文以一個實際的程序為例,介紹了用gdb分析core文件的方法和步驟,同時演示了常見gdb命令的操作方法。如果大家想對相關(guān)gdb命令有更多的了解,請自行百度之。
2.示例程序
- /**********************************************************************
- * 版權(quán)所有 (C)2015, Zhou Zhaoxiong。
- *
- * 文件名稱:GdbDebug.c
- * 文件標識:無
- * 內(nèi)容摘要:Gdb命令演示程序
- * 其它說明:無
- * 當前版本:V1.0
- * 作 者:Zhou Zhaoxiong
- * 完成日期:20151008
- *
- **********************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- // 數(shù)據(jù)類型重定義
- typedef unsigned char UINT8;
- typedef signed int INT32;
- typedef unsigned int UINT32;
- // 函數(shù)聲明
- void Sleep(UINT32 iCountMs);
- void PrintInfo(void);
- INT32 main();
- /**********************************************************************
- * 功能描述:主函數(shù)
- * 輸入?yún)?shù):無
- * 輸出參數(shù):無
- * 返 回 值:無
- * 其它說明:無
- * 修改日期 版本號 修改人 修改內(nèi)容
- * -------------------------------------------------------------------
- * 20151008 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ***********************************************************************/
- INT32 main()
- {
- PrintInfo(); // 在屏幕上輸出消息
- return 0;
- }
- /**********************************************************************
- * 功能描述: 在屏幕上輸出消息
- * 輸入?yún)?shù): 無
- * 輸出參數(shù): 無
- * 返 回 值: 無
- * 其它說明: 無
- * 修改日期 版本號 修改人 修改內(nèi)容
- * ----------------------------------------------------------------------
- * 20151008 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ************************************************************************/
- 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");
- iSumiSum = iSum + iLoopFlag;
- Sleep(10 * 1000); // 每10s打印一次
- }
- return;
- }
- /**********************************************************************
- * 功能描述: 程序休眠
- * 輸入?yún)?shù): iCountMs-休眠時間(單位:ms)
- * 輸出參數(shù): 無
- * 返 回 值: 無
- * 其它說明: 無
- * 修改日期 版本號 修改人 修改內(nèi)容
- * ------------------------------------------------------------------
- * 20151008 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ********************************************************************/
- 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); // 調(diào)用select函數(shù)阻塞程序
- }
3.用gdb分析core文件
在Linux上用“gcc -g -o GdbDebug GdbDebug.c”命令對程序進行編譯之后,運行“GdbDebug”命令,發(fā)現(xiàn)在當前目錄下出現(xiàn)了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 -- 執(zhí)行下一步
- 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 -- 繼續(xù)執(zhí)行
- 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>
從以上分析可知,執(zhí)行GdbDebug.c文件的第64行時程序core掉了。此時仔細分析程序,發(fā)現(xiàn)pCtrStr指針為空。當對一個不存在的指針取長度時,由于找不到地址,程序便崩潰了。修改的辦法也非常的簡單,只需要讓pCtrStr指針指向具體的地址即可。
4.常見gdb命令操作示例
修改之后的代碼如下:
- /**********************************************************************
- * 版權(quán)所有 (C)2015, Zhou Zhaoxiong。
- *
- * 文件名稱:GdbDebug.c
- * 文件標識:無
- * 內(nèi)容摘要:Gdb命令演示程序
- * 其它說明:無
- * 當前版本:V1.0
- * 作 者:Zhou Zhaoxiong
- * 完成日期:20151008
- *
- **********************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- // 數(shù)據(jù)類型重定義
- typedef unsigned char UINT8;
- typedef signed int INT32;
- typedef unsigned int UINT32;
- // 函數(shù)聲明
- void Sleep(UINT32 iCountMs);
- void PrintInfo(void);
- INT32 main();
- /**********************************************************************
- * 功能描述:主函數(shù)
- * 輸入?yún)?shù):無
- * 輸出參數(shù):無
- * 返 回 值:無
- * 其它說明:無
- * 修改日期 版本號 修改人 修改內(nèi)容
- * -------------------------------------------------------------------
- * 20151008 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ***********************************************************************/
- INT32 main()
- {
- PrintInfo(); // 在屏幕上輸出消息
- return 0;
- }
- /**********************************************************************
- * 功能描述: 在屏幕上輸出消息
- * 輸入?yún)?shù): 無
- * 輸出參數(shù): 無
- * 返 回 值: 無
- * 其它說明: 無
- * 修改日期 版本號 修改人 修改內(nèi)容
- * ----------------------------------------------------------------------
- * 20151008 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ************************************************************************/
- 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");
- iSumiSum = iSum + iLoopFlag;
- Sleep(10 * 1000); // 每10s打印一次
- }
- return;
- }
- /**********************************************************************
- * 功能描述: 程序休眠
- * 輸入?yún)?shù): iCountMs-休眠時間(單位:ms)
- * 輸出參數(shù): 無
- * 返 回 值: 無
- * 其它說明: 無
- * 修改日期 版本號 修改人 修改內(nèi)容
- * ------------------------------------------------------------------
- * 20151008 V1.0 Zhou Zhaoxiong 創(chuàng)建
- ********************************************************************/
- 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); // 調(diào)用select函數(shù)阻塞程序
- }
編譯并運行之后,程序正常,說明問題已被我們解決掉。下面是常見的gdb命令的操作示例:
- ~/zhouzhaoxiong/zzx/GdbDebug> gdb GdbDebug -- 啟動gdb調(diào)試
- 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 -- 執(zhí)行下一步
- 66 for (iLoopFlag = 0; iLoopFlag < iLen; iLoopFlag ++) // 打印消息iLen次
- (gdb) n -- 執(zhí)行下一步
- 68 printf("PrintInfo: hello, world!\n");
- (gdb) p iLoopFlag -- 打印(輸出)iLoopFlag的值
- $2 = 0
- (gdb) p iLen -- 打印(輸出)iLen的值
- $3 = 13
- (gdb) n -- 執(zhí)行下一步
- PrintInfo: hello, world! -- 程序的輸出結(jié)果
- 70 iSumiSum = iSum + iLoopFlag;
- (gdb) p iSum -- 打印(輸出)iSum的值
- $4 = 0
- (gdb) n -- 執(zhí)行下一步
- 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 iSumiSum = 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 -- 一直運行到函數(shù)返回
- 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 -- 繼續(xù)執(zhí)行
- Continuing.
- PrintInfo: hello, world!
- Breakpoint 2, PrintInfo () at GdbDebug.c:72
- 72 Sleep(10 * 1000); // 每10s打印一次
- (gdb) bt -- 打印當前的函數(shù)調(diào)用棧的所有信息
- #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下調(diào)試C/C++程序的工具,大家一定要熟練掌握gdb的用法。
【本文是51CTO專欄作者周兆熊的原創(chuàng)文章,作者微信公眾號:周氏邏輯(logiczhou)】