gdb調試指南

gdb入門指令

gcc -g hello.c //加入調試信息
gdb ./a.out

l           // list,顯示函數
start		// 開始逮捕調試,顯示即將運行的行
n			// next,運行到下一行,到函數時不會進入函數
s			// step,運行到下一行,到函數時進入函數
p a         // 顯示函數內a的值
bt          // 查看函數堆棧,當前棧序號爲0,main棧序號爲1
f 1         // 切換到main的棧
p a			// 顯示main中a的值
p &a  		// 打印a的地址
q			// 退出調試

gdb調試運行中的程序

gdb中忽略信號處理 SIGPIPE

GDB調試網絡程序時,會遇到SIGPIPE信息,默認GDB會把程序停下來,用handle命令設置一下缺省的signal的處理行爲即可:(在gdb模式下運行下面的命令)

handle SIGPIPE nostop print

如果連信息提示都不想看見,可以這樣設置:

handle SIGPIPE nostop noprint

命令小記:

GDB進入正在運行的進程
1,基本操作
    gdb -p pid //  在線調試已經存在的進程
    gdb attach // 同上
    b fun // 設置斷點函數 
    c // 繼續執行程序,直到下個斷點

2.斷點管理
//設置斷點
break n,break 函數名(b n, b 函數名)
//查看斷點
info breakpoints(i breakpoints)
//刪除斷點,其中n爲斷點的序列號,可以用info breakpoints查看
delete breakpoints n
//使斷點失效
disable breakpoints n
//使斷點生效
enable breakpoints n

GDB提示符
1. 變量信息管理
    info 變量名(i 變量名)//查看一個變量的值
    info locals //查看所有局部變量的值
    info args

2. 查看、設置變量
    p 變量
    p 變量 = 新值
    set 變量 = 新值
    
3. 查看內存
    x/<n/f/u> <addr>
    x/nbx 變量名 //查看從變量名開始的n個字節,例x/7bx input 表示查看從變量input開始的7個內存單元的內容

4. 線程調試
    info thread
    thread n

gdb調試Core Dump

測試代碼:

#include <stdio.h>  
  
int main()  
{  
        int *p = NULL;  
        *p = 0;  
  
        printf("bad\n");  
        return 0;  
}  

編譯運行,會發生Segmentation fault (core dumped) 錯誤,使用gdb調試:

[taoge@localhost test]$ gcc -g main.c   
[taoge@localhost test]$ ./a.out   
Segmentation fault (core dumped)  
[taoge@localhost test]$ gdb a.out  
(gdb) r  
Starting program: /home/taoge/test/a.out   

Program received signal SIGSEGV, Segmentation fault.  
0x080483c9 in main () at main.c:6  
6               *p = 0;  

可以看到, 在gdb調試的時候, 用r命令讓程序再跑起來, 可以定位到問題出在第6行。 我們再來看看bt命令:

(gdb) bt  
#0  0x080483c9 in main () at main.c:6  
(gdb)    

可以看到,通過bt查看函數堆棧, 也能定位到第6行。

我們知道,在實際中, 有很多問題是概率發生的, 很難重現。 此時, 如果用gdb的r命令(實際相當於重新運行程序)則是不可能的。 所以,對於概率性問題, 如果程序出現堆棧錯誤時, 產生了core文件, 我們一定要視之爲寶貝, 記得保存, 否則很可能被沖掉。 拿到core文件後, 我們可以用gdb調試core。

1,什麼是Core Dump?

Core的意思是內存, Dump的意思是扔出來, 堆出來.
開發和使用Unix程序時, 有時程序莫名其妙的down了, 卻沒有任何的提示(有時候會提示core dumped). 這時候可以查看一下有沒有形如core.進程號的文件生成, 這個文件便是操作系統把程序down掉時的內存內容扔出來生成的, 它可以做爲調試程序的參考.
core dump又叫核心轉儲, 當程序運行過程中發生異常, 程序異常退出時, 由操作系統把程序當前的內存狀況存儲在一個core文件中, 叫core dump.

2,怎麼生成Core Dump?

在linux平臺下,設置core dump文件生成的方法:

  1. 在終端中輸入ulimit -c 如果結果爲0,說明當程序崩潰時,系統並不能生成core dump。

  2. 使用ulimit -c unlimited命令,開啓core dump功能,並且不限制生成core dump文件的大小。如果需要限制,加數字限制即可:ulimit - c 1024,如果生成的信息超過此大小,將會被裁剪,最終生成一個不完整的core文件。在調試此core文件的時候,gdb會提示錯誤。

永久設置, 修改/etc/security/limits.conf文件:

soft    core            0
修改成:
soft    core            unlimited
  1. 默認情況下,core dump生成的文件名爲core,而且就在程序當前目錄下。新的core會覆蓋已存在的core。通過修改/proc/sys/kernel/core_uses_pid文件,可以將進程的pid作爲作爲擴展名,生成的core文件格式爲core.xxx,其中xxx即爲pid。爲0則表示生成的core文件統一命名爲core。可通過以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid

注意 proc 是內存文件,使用vi 是不能 編譯的,所有隻能使用append 的方式

/proc 這個目錄是虛擬在內存中的,不在硬盤保存,
proc文件系統是一個僞文件系統,它只存在內存當中,而不佔用外存空間。它以文件系統的方式爲訪問系統內核數據的操作提供接口。用戶和應用程序可以通過proc得到系統的信息,並可以改變內核的某些參數。由於系統的信息,如進程,是動態改變的,所以用戶或應用程序讀取proc文件時,proc文件系統是動態從系統內核讀出所需信息並提交的。

  1. 通過修改/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件格式。例如:
# 將所有core文件生成到/corefile下,格式爲core-命令名-pid-時間戳.

echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern

# 以下是參數列表:

%%	單個%字符
%p	dump進程的進程ID
%u	dump進程的用戶ID
%g	dump進程的組ID
%s	導致core dump的信號
%t	core dump的時間
%h	主機名
%e	程序文件名

永久設置, 修改/etc/sysctl.conf配置文件,添加:

kernel.core_pattern = /corefile/core-%e-%p-%t
kernel.core_uses_pid = 1 #即使core_pattern中沒有設置%p,最後生成的core dump文件名仍會加上進程ID

/* 執行以下命令立即生效 */
# sysctl -p

3,如何使用core文件?

gdb [exec file] [core file]

在編譯程序的時候加入選項-g,下面我們可以在發生運行時信號引起的錯誤時發生core dump了。發生core dump之後, 用gdb進行查看core文件的內容, 以定位文件中引發core dump的行。比如:

gdb /usr/sbin/ovs-vswitchd vat/log/idump/core-3219-1512819153 
bt //查看其調用函數的信息
frame n (f n)//n爲棧的層次,然後可以用其他命令(info)查看此級別的變量信息

補充:

// 查看core生成詳細時間
date --date='@1512819153'

在進入gdb後, 用bt命令查看backtrace以檢查發生程序運行到哪裏, 來定位core dump的文件->行.
進去後輸入where回車, 也可以顯示程序在哪一行down掉的, 在哪個函數中.

如果不知道coredump對應的執行文件,可以使用以下方式查找:

gdb -c corefile  # 使用gdb調試core文件
info auxv        # 索引31對應的是core文件的應用程序exec_file
find / -name exec_file

core dump原因分析

造成程序core dump的原因很多,這裏根據以往的經驗總結一下:

1 內存訪問越界

a) 由於使用錯誤的下標,導致數組訪問越界
b) 搜索字符串時,依靠字符串結束符來判斷字符串是否結束,但是字符串沒有正常的使用結束符
c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函數,將目標字符串讀/寫爆。應該使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函數防止讀寫越界。

2 多線程程序使用了線程不安全的函數。

應該使用下面這些可重入的函數,尤其注意紅色標示出來的函數,它們很容易被用錯:
asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

3 多線程讀寫的數據未加鎖保護。

對於會被多個線程同時訪問的全局數據,應該注意加鎖保護,否則很容易造成core dump

4 非法指針

a) 使用空指針
b) 隨意使用指針轉換。一個指向一段內存的指針,除非確定這段內存原先就分配爲某種結構或類型,或者這種結構或類型的數組,否則不要將它轉換爲這種結構或類型的指針,而應該將這段內存拷貝到一個這種結構或類型中,再訪問這個結構或類型。這是因爲如果這段內存的開始地址不是按照這種結構或類型對齊的,那麼訪問它時就很容易因爲bus error而core dump.

5 堆棧溢出

不要使用大的局部變量(因爲局部變量都分配在棧上),這樣容易造成堆棧溢出,破壞系統的棧和堆結構,導致出現莫名其妙的錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章