gdb調試入門

gdb調試工具

0 gdb介紹

調試器GDB容許查看在執行一個程序時其內部時發生了什麼,或者是程序奔潰(crashed)時它正在作什麼。
gdb經過如下四種事情來捕獲某個行爲的異常錯誤(bug):linux

  • 運行程序,指定可能影響其動做的內容。
  • 讓程序在指定的狀況下中止。
  • 檢查當程序中止時發生了什麼。
  • 改變程序中的內容,以便於更正一個錯誤,而後繼續尋找下一個錯誤。
    gdb可用於調試C,C++,Fortran,Modula-2。
    gdb經過在終端執行gdb命令激活。一旦啓動,他從命令行讀取命令,直到使用quit命令讓它退出。也能夠經過使用help command命令獲取在線幫助。
    gdb可使用無參或者帶參無選項執行,不過通常都使用帶參的命令,例如:指定一個程序或者指定core文件:
gdb program #指定一個可執行程序
gdb program core    #指定一個可執行程序以及core文件
gdb program pid #指定一個正在運行的程序
gdb -p pid  #同上

1 gdb經常使用命令

1.1 list

list命令用於查看代碼,可簡寫爲l。git

list    #查看上一次list中心附近的10行代碼,-5~+5
list n  #查看第n行附近10行代碼,n-5~n+5
list b,e    #查看b,e行範圍的代碼
list function   #查看函數function附近的代碼
list file:line  #查看文件file第line行附件的代碼
list file:function  #查看文件file的函數function附近的代碼
list *address   #地址爲address的行附近的代碼,使用info add name獲取地址

1.2 break

break命令用於設置斷點,可用b簡化;delete用於刪除斷點,可用d簡化。github

break n #在第n行設置斷點
break function  #在函數function設置斷點,能夠是庫函數
break file:line #在文件file第line行設置斷點
break file:function #在文件file的function函數設置斷點
break n if condition    #根據條件在第n行設置斷點,例如b 16 if i==10
break *address  #在地址爲address的行設置斷點

1.3 delete和clear

每次使用break設置斷點都會分配一個斷點號,例如:shell

(gdb) b 16
Breakpoint 1 at 0x400512: file test.cc, line 16.
(gdb) b 17
Breakpoint 2 at 0x40051b: file test.cc, line 17.

要刪除斷點使用可使用delete命令:express

delete [breakpoints num] [range...]
delete n    #刪除n號斷點
delete m-n  #刪除m-n號斷點

也可使用clear命令,clear是基於行的,不是刪除全部斷點:ubuntu

clear n #刪除n行的全部斷點
clear function  #刪除函數function的斷點
clear file:line #刪除文件file第n行的全部斷點
clear file:function #刪除文件:函數的全部斷點

1.4 查看變量

  1. print命令
    print命令用來在調試程序時查看變量值,可簡化爲p。
print var   #打印var的值
print *array@len    #以{a, b, ...}格式打印動態數組
print array #以{a, b, ...}格式打印靜態數組
print file::var or print function::var  #打印全局變量
#指定輸出格式:
print/u[d|o|x...] ...   #做爲無符號數輸出
print/t ... #二進制輸出
print/d ... #十進制輸出
print/o ... #八進制輸出
print/x ... #十六進制輸出
print/a ... #十六進制輸出
print/c ... #字符輸出
print/f ... #浮點數輸出
  1. display命令
display var #每次執行到斷點都打印var的值

1.5 程序執行時命令

  1. run命令
    run命令用來開始執行程序,直到第一個斷點中止程序。可簡寫爲r。例如:
(gdb) break 20
Breakpoint 1 at 0x40056d: file test.cc, line 20.
(gdb) run
Starting program: /home/chenjunhan/Learning/gdb/test Breakpoint 1, main () at test.cc:20
  1. continue命令
    continue命令用來從上次斷點中止以後開始繼續執行程序,直到下一個斷點。可簡寫爲c。例如:
(gdb) b 15
Breakpoint 1 at 0x40050b: file test.cc, line 15.
(gdb) b 16
Breakpoint 2 at 0x400512: file test.cc, line 16.
(gdb) r
Starting program: /home/chenjunhan/Learning/gdb/test
Breakpoint 1, main () at test.cc:15
(gdb) continue
Continuing.
Breakpoint 2, main () at test.cc:16
  1. next命令
    next命令用於執行一行源程序代碼,此行代碼中的函數調用也一併執行;即「step over」,可簡寫爲n。
  2. step命令
    step命令用於執行一行源程序代碼,若是此行代碼中有函數調用,則進入該函數;即「step into」,可簡寫爲s。

2 gdb調試函數

  • 列出可執行函數全部函數名稱
info|i functions
  • 執行函數內容
next|n  #不進入函數,直接執行函數並返回
step|s  #進入函數,執行函數體
call|print function #任意位置直接執行函數
  • 退出函數
finish  #退出正在執行的函數,自動執行剩下的代碼
return [expression] #退出正在執行的函數,後面的代碼不執行,能夠經過expression修改函數返回值
  • 函數堆棧幀
info|i frame    #顯示當前函數堆棧幀信息,包括指令寄存器的值,局部變量地址及值等信息。
backtrace|bt    #顯示堆棧幀層次結構
frame n|address #切換函數堆棧幀層次
up|down n   #向上/下選擇n層函數堆棧幀
up-silently|down-silently n #同上,不過不打印信息

3 gdb設置watchpoint

gdb可使用watch命令設置觀察點,也就是當一個變量值發生變化時,程序會停下來。例如:數組

int a = 0;
void *pthread_func(void *args)
{
    while (1)
    {
        ++a;
        sleep(1);
    }
}
int main()
{
    pthread_t tid;
    pthread_create(&tid, NULL, pthread_func, NULL);
    sleep(1000);
    return 0;
}

使用catch|wa觀察全局變量a:多線程

(gdb) file catch
Reading symbols from catch...done.
(gdb) break pthread_func
Breakpoint 1 at 0x400659: file catch.c, line 11.
(gdb) watch a
Hardware watchpoint 2: a
(gdb) r
Starting program: /home/chenjunhan/Learning/gdb/catch 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff77f6700 (LWP 6578)]
[Switching to Thread 0x7ffff77f6700 (LWP 6578)]

Breakpoint 1, pthread_func (args=0x0) at catch.c:11
11          ++a;
(gdb) continue
Continuing.
Hardware watchpoint 2: a

Old value = 0
New value = 1
pthread_func (args=0x0) at catch.c:12
12          sleep(1);
(gdb) continue
Continuing.

Breakpoint 1, pthread_func (args=0x0) at catch.c:11
11          ++a;
  • 設置觀察點
catch var   #爲var設置觀察點,在run以前
catch *(type*)(address) #爲地址address指向的變量設置觀察點
  • 列出全部觀察點
info watchpoints
  • 斷點控制命令
disable|enable|delete n
  • 爲watch指定生效線程
info threads    #列出全部threads編號等信息,thread_create以後
watch var thread t_num  #只有編號爲t_num的thread修改了var值纔會停下來
  • 只讀/讀寫觀察點,只對硬件觀察點才生效
rwatch|rw var   #設置只讀觀察點每次訪問var都會停下來
wwatch|aw var   #設置讀寫觀察點,當發生讀取或改變變量值的行爲時,程序就會暫停住

4 gdb設置catchpoint

使用gdb調試程序時,能夠用tcatch命令設置catchpoint只觸發一次。例如:app

int main()
{
    pid_t pid;
    int i = 0;

    for (i = 0;i < 4; ++i)
    {
        if ((pid = fork()) < 0) //fork error
            exit(1);
        else if (pid == 0)  //child
            exit(0);
    }

    printf("Hello World\n");//parent
    return 0;
}

使用tcatch fork爲fork設置catchpoint

(gdb) tcatch fork
Catchpoint 1 (fork)
(gdb) r
Starting program: /home/chenjunhan/Learning/gdb/catch 

Temporary catchpoint 1 (forked process 6838), 0x00007ffff7ad5ee4 in __libc_fork
    () at ../nptl/sysdeps/unix/sysv/linux/x86_64/../fork.c:130
130 ../nptl/sysdeps/unix/sysv/linux/x86_64/../fork.c: 沒有那個文件或目錄.
(gdb) c
Continuing.
Hello World
[Inferior 1 (process 6834) exited normally]
  • 爲fork/vfork/exec設置catchpoint
catch fork
catch vfork
catch exec
  • 爲系統調用設置catchpoint
catch syscall [syscall_name|syscall_num]    #例如catch syscall mmap
catch syscall   #爲全部系統調用設置catchpoint

5 gdb高級打印技巧

5.1 打印局部變量

  • 使用backtrace full命令
(gdb) bt full
#0  fun_a () at a.c:6
        a = 0
#1  0x000109b0 in fun_b () at a.c:12
        b = 1
#2  0x000109e4 in fun_c () at a.c:19
        c = 2
#3  0x00010a18 in fun_d () at a.c:26
        d = 3
#4  0x00010a4c in main () at a.c:33
        var = -1
  • 使用info locals命令,打印當前函數局部變量
(gdb) info locals
a = 0

5.2 打印打印進程內存信息

info proc mappings  #查看內存映像信息
info files  #更詳細地輸出進程的內存信息,包括引用的動態連接庫等
info target #功能同上

5.3 打印變量類型

typedef struct student_t{
    char *name;
    int age;
} student;

int main()
{
    student s;
    student *ps;

    return 0;
}

使用wathisptype命令調試,ptype能夠列出詳細的類型。

(gdb) whatis s
type = student
(gdb) whatis ps
type = student *
(gdb) ptype s
type = struct student_t {
    char *name;
    int age;
}

5.4 打印變量所在文件

info variables var

6 gdb應用於多進程/線程

6.1 多進程調試

a.調試正在運行的進程
#沒啓動gdb
ps -a | grep program    #查看進程id
gdb program pid #調試
gdb --pid pid   #同上

#啓動gdb
attach pid

#斷開
detach
b.調試子進程

gdb調試默認追蹤父進程,要追蹤子進程,則執行下列命令:

set follow-fork-mode child
c.同時調試父進程和子進程

gdb調試時,默認追蹤一個進程,使用下列命令能夠同時調試多個進程:

set detach-on-fork off  #默認運行父進程,掛起其餘進程
set schedule-multiple on    #父進程,子進程一塊兒運行

以後gdb默認追蹤父進程,其餘進程被掛起,使用下列命令能夠切換進程:

inferior infno  #切換到子進程

info inferior   #打印全部進程信息
inferior n  #選擇切換調試進程

6.2 多線程調試

a.顯示線程信息
  • thread|_thread 查看當前線程id
(gdb) thread
[Current thread is 1 (Thread 0x7ffff7fcc740 (LWP 9494))]
(gdb) printf "current thread:%d\n",$_thread
current thread:1
  • info threads 查看全部線程信息
(gdb) info threads
Id   Target Id         Frame
3   Thread 0x7ffff6ff5700 (LWP 9627) "thread" 0x00007ffff78b7dfd in nanosleep () at ../sysdeps/unix/syscall-template.S:81
2   Thread 0x7ffff77f6700 (LWP 9626) "thread" 0x00007ffff78b7dfd in nanosleep () at ../sysdeps/unix/syscall-template.S:81
1   Thread 0x7ffff7fcc740 (LWP 9622) "thread" main () at thread.cc:21
b.切換線程
thread id
c.只容許一個線程執行

gdb調試程序時,一旦程序斷住,全部線程都會中止。當調試其中一個線程時,全部線程都會開始執行。若是想調試一個線程同時其餘線程中止,可用如下命令:

set scheduler-locking on

6.3 調試多個程序

#添加一個新的調試程序。-copies指定執行多少份,默認1
add-inferior [-copies n] [-exec program]

#複製添加一個新的調試程序,infno表示調試進程編號,默認爲當前inferior
clone-inferior [-copies -n] [infno]

info inferior   #列出全部調試的進程
maint info program-spaces   #列出全部調試的進程名稱,inferior編號,進程id

inferior n  #切換進程

7 gdb分析core dump

7.1 core文件

程序因爲各類異常或者bug致使在運行過程當中異常退出或者停止,而且在知足必定條件下(這裏爲何說須要知足必定的條件呢?下面會分析)會產生一個叫作core的文件。
一般狀況下,core文件會包含了程序運行時的內存,寄存器狀態,堆棧指針,內存管理信息還有各類函數調用堆棧信息等,咱們能夠理解爲是程序工做當前狀態存儲生成第一個文件,許多的程序出錯的時候都會產生一個core文件,經過工具分析這個文件,咱們能夠定位到程序異常退出的時候對應的堆棧調用等信息,找出問題所在並進行及時解決。
ubuntu默認不生成core文件,可用一下命令解決:

ulimit -c unlimited

7.2 gdb顯式生成core文件

generate-core-file  #先run
gcore   #上面的簡寫

7.3 使用core文件進行調試

示例程序:

int main()
{
    int *p = NULL;
    *p = 0;

    return 0;
}

上面程序當執行*p = 0時程序會crashed,產生core dump file,下面使用gdb進行調試:

#shell環境加載core文件
gdb test corefile

#同上,gdb環境中
file test
core corefile

#gdb加載core file以後打印的信息
[New LWP 9672]
Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000000040054b in main () at test.c:8
8       *p = 0;

#也可使用where命令定位錯誤位置
(gdb) where
#0  0x000000000040054b in main () at test.c:8

參考文獻

相關文章
相關標籤/搜索