GDB使用記錄

本文來自個人博客 sunyongfeng.com。博客的文章保持更新,此文可能不是最新狀態。

簡介

GDB,GNU Debugger,特性如下:

  • GDB具備各種調試功效,可對計算機程序的運行進行追蹤、警告。使用者可以監控及修改程序內部變量的值,甚至可在程序的正常運行之外調用函數。
  • GDB支持多數處理器架構
  • 持續開發中
  • 支持遠程調試
  • 支持內核調試,KGDB

從事嵌入式軟件開發兩年來,主要在以下幾方面使用GDB:

  1. 查看、修改運行時變量;
  2. 多線程調試,查看當前線程運行狀態(以確定當前線程是不是因爲等鎖等原因掛起);
  3. 查看coredump文件;
  4. 碰到難纏的內存非法改寫問題,用GDB的斷點、物理watch功能查看內存變化以定位改寫者;

引用公司一個技術牛人的話:在大型的項目中,使用GDB的單步調試、軟件watch是不現實的,因爲會運行得實在太慢。

命令小記:

linux提示符
1. GDB進入正在運行的進程
    gdb 可執行文件 core文件 
    gdb -p pid
    
GDB提示符
1. 查看調用棧信息
    bt / backtrace / bt full
    frame n
    info locals
    info args

2. 查看、設置變量
    p 變量
    p 變量 = 新值
    set 變量 = 新值
    
3. 查看內存
    x/<n/f/u> <addr>

4. 線程調試
    info thread
    thread n

啓動GDB

GCC選項###

想用GDB調試,則在GCC編譯的時候要加上-g選項。

啓動GDB

啓動GDB的方法主要有以下幾種:

  • gdb
  • gdb executable_file
  • gdb executable_file corefile:查看coredump文件信息,定位coredump產生原因、觸發源。
  • gdb attach pid:調度運行時的進程或線程,同gdb -p pid

善用help

在GDB提示符下輸入helphelp 命令,能夠查看命令的幫助說明。

(gdb) help
List of classes of commands:

aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

Type "help" followed by a class name for a list of commands in that class.
Type "help all" for the list of all commands.
Type "help" followed by command name for full documentation.
Type "apropos word" to search for commands related to "word".
Command name abbreviations are allowed if unambiguous.

查看調用棧

寫一個簡單的例子(僅爲樣例,並不嚴謹):

#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>
#include <sys/prctl.h>

typedef struct {
    int member_a;
    int member_b;
} test_t;

int g_int;
bool g_bool;
char *g_str[] = {
    "Hello, GDB!",
    "It's funny."
};

void stay_here(int arg, test_t *test)
{
    int local;

    local = 100;
    while (true) {
        local++;
        if (local % 200 == 0) {
            local = 0;
        }
        sleep(1);
    }

    return;
}

void *thread_process(void *arg)
{
    int in;
    char name[64];

    in = (int)arg;
    (void)snprintf(name, 64, "test-%d", in + 1);
    prctl(PR_SET_NAME, (unsigned long)name);  /* set thread name */

    while (true) {
        sleep(2);
    }

    return NULL;
}

void create_thread(void)
{
    int i, rv;
    pthread_t tid;

    for (i = 0; i < 5; i++) {
        rv = pthread_create(&tid, NULL, thread_process, (void *)i);
        assert(rv == 0);
    }
}

int main(int argc, char **argv)
{
    int local;
    test_t test;

    local = 999;
    test.member_a = 10;
    test.member_b = 11;

    create_thread();
    stay_here(local, &test);

    return 0;
}

編譯並運行起來,注意gcc的-g選項,這裏使用&讓程序運行到後臺,[1] 8043指剛剛這個程序運行時的進程號,也可用ps命令查看。

sunnogo@a3e420:~/test/gdb$ gcc -o prt_mod_var prt_mod_var.c -g -Wall -lpthread
sunnogo@a3e420:~/test/gdb$ 
sunnogo@a3e420:~/test/gdb$ ls
prt_mod_var  prt_mod_var.c
sunnogo@a3e420:~/test/gdb$ ./prt_mod_var &
[1] 8043
sunnogo@a3e420:~/test/gdb$ 
sunnogo@a3e420:~/test/gdb$ ps -e | grep prt_mod_var
 8043 pts/1    00:00:00 prt_mod_var

接下來使用gdb -p 8043連入正在運行的進程中。還不明白爲什麼我的計算機中要求使用root權限才能讓GDB attach到對應進程。

sunnogo@a3e420:~/test/gdb$ gdb -p 8043
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 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 "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 8043
Could not attach to process.  If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.
(gdb) quit

重新sudo gdb -p pid進入進程。

  • 使用bt查看當前調用棧信息(call stack,即函數調用層次信息),當前進程的是由main() -> sleep() -> nanosleep() -> __kernel_vsyscall()一層一層調入。注意“#數字”,在GDB中這叫stack frames,或直接稱爲frame,運行棧由一個或多個連續的frame組成,數字越小代表調用層次越深。
  • 使用bt full查看詳細調用棧信息,會把各個frame的入參和局部變量信息顯示出來。這裏bt是backtrace的縮寫,GDB的全命令經常有其簡短的寫法。

注意:GDB中,按回車默認是執行上一次命令。
先MARK下面的“No symbol table info available.

sunnogo@a3e420:~/test/gdb$ sudo gdb -p 8043
[sudo] password for sunnogo: 
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 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 "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 8043
Reading symbols from /home/sunnogo/test/gdb/prt_mod_var...done.
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0xb7751424 in __kernel_vsyscall ()
(gdb) bt 
#0  0xb7751424 in __kernel_vsyscall ()
#1  0xb7640ce0 in nanosleep () from /lib/i386-linux-gnu/libc.so.6
#2  0xb7640aff in sleep () from /lib/i386-linux-gnu/libc.so.6
#3  0x0804845b in stay_here (arg=999, test=0xbf8e5118) at prt_mod_var.c:26
#4  0x08048492 in main (argc=1, argv=0xbf8e51c4) at prt_mod_var.c:41
(gdb) bt full
#0  0xb7751424 in __kernel_vsyscall ()
No symbol table info available.
#1  0xb7640ce0 in nanosleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#2  0xb7640aff in sleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#3  0x0804845b in stay_here (arg=999, test=0xbf8e5118) at prt_mod_var.c:26
        local = 113
#4  0x08048492 in main (argc=1, argv=0xbf8e51c4) at prt_mod_var.c:41
        local = 999
        test = {member_a = 10, member_b = 11}
  • 使用frame n進入“#n”的frame。默認顯示當前函數名、函數入參、當前運行處所在源文件的代碼行位置,並顯示當前行代碼。
  • 使用info命令查看frame詳細信息,info命令不是全命令,後面還有子命令。info有很多子命令,除本frame外,還可以查看本進程信息、系統信息,這裏僅僅是冰山一角。
    • info frame 顯示當前frame信息
    • info args 顯示入參信息
    • info local 顯示局部變量信息
(gdb) frame 3
#3  0x0804845b in stay_here (arg=999, test=0xbf8e5118) at prt_mod_var.c:26
26              sleep(1);
(gdb) info frame
Stack level 3, frame at 0xbf8e5100:
 eip = 0x804845b in stay_here (prt_mod_var.c:26); saved eip 0x8048492
 called by frame at 0xbf8e5130, caller of frame at 0xbf8e50d0
 source language c.
 Arglist at 0xbf8e50f8, args: arg=999, test=0xbf8e5118
 Locals at 0xbf8e50f8, Previous frame's sp is 0xbf8e5100
 Saved registers:
  ebx at 0xbf8e50f4, ebp at 0xbf8e50f8, eip at 0xbf8e50fc
(gdb) info args
arg = 999
test = 0xbf8e5118
(gdb) info local
local = 113
(gdb) 

查看、修改變量

p var查看變量信息,p是print的縮寫。

  • p var
  • p *(指針類型)地址
  • p *結構體指針
  • p 數組名
# 打印變量 
(gdb) p g_int
$3 = 0
(gdb) p g_bool
$4 = false

# 打印特定類型指針
(gdb) info local
local = 113
(gdb) p &local
$11 = (int *) 0xbf8e50ec
(gdb) p *(int *) 0xbf8e50ec
$12 = 113
(gdb) 

# 打印結構體指針
(gdb) p test
$1 = (test_t *) 0xbf8e5118
(gdb) p *test
$2 = {member_a = 10, member_b = 11}

# 打印數組名
(gdb) p g_str
$5 = {0x8048538 "Hello, GDB!", 0x8048544 "It's funny."}
(gdb) p g_str[0]
$6 = 0x8048538 "Hello, GDB!"

print不僅可以用來查看變量,還可用於設置變量。print var=value
設置變量值的命令還有setset var=value

# set int
(gdb) print local
$1 = 109
(gdb) print local=20
$2 = 20
(gdb) print local
$3 = 20
(gdb) set local=30
(gdb) print local
$4 = 30

# set bool
(gdb) print g_bool
$5 = false
(gdb) set g_bool=true
No symbol "true" in current context.
(gdb) set g_bool=1
(gdb) print g_bool
$6 = true

# set pointer
(gdb) print g_str
$7 = {0x8048538 "Hello, GDB!", 0x8048544 "It's funny."}
(gdb) set g_str[0]="SETTING VAR"
(gdb) print g_str
$8 = {0x8e05008 "SETTING VAR", 0x8048544 "It's funny."}
(gdb) 

查看內存

examine查看內存,縮寫是x。命令格式:

x/<n/f/u> <addr>

n、f、u是可選參數,說明如下:

(gdb) help x
Examine memory: x/FMT ADDRESS.
ADDRESS is an expression for the memory address to examine.
FMT is a repeat count followed by a format letter and a size letter.
Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
  t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).
The specified number of objects of the specified size are printed
according to the format.

Defaults for format and size letters are those previously used.
Default count is 1.  Default address is following last thing printed
with this command or "print".
  • n表示要打印的多少個單位的內存,默認是1,單位由u定義;
  • f表示打印的格式,格式有:
    • o,octal,八進制;
    • x,hex,十六進制;
    • d,decimal,十進制;
    • u,unsigned decimal,無符號十進制;
    • t,binary,二進制;
    • f,float;
    • a,address;
    • i,instruction,指令;
    • c,char,字符;
    • s,string,字符串。
  • u定義單位,b表示1字節,h表示2字節,w表示4字節,g表示8字節。
# 當前CPU是intel i3,小端

# 以十進制形式打印
(gdb) x/8db test
0xbf8e5118:     10      0       0       0       11      0       0       0
(gdb) x/4dh test
0xbf8e5118:     10      0       11      0
(gdb) x/2dw test
0xbf8e5118:     10      11
(gdb) x/2d test
0xbf8e5118:     10      11
(gdb) x/1dg test
0xbf8e5118:     47244640266  # 注意和x/1xg test的結果比較

# 以二進制形式打印
(gdb) x/1tg test
0xbf8e5118:     0000000000000000000000000000101100000000000000000000000000001010
(gdb) x/2tw test
0xbf8e5118:     00000000000000000000000000001010        00000000000000000000000000001011
(gdb) x/4th test
0xbf8e5118:     0000000000001010        0000000000000000        0000000000001011        0000000000000000
(gdb) x/8tb test
0xbf8e5118:     00001010        00000000        00000000        00000000        00001011        0000000000000000        00000000

# 以十六進制形式打印
(gdb) x/8xb test
0xbf8e5118:     0x0a    0x00    0x00    0x00    0x0b    0x00    0x00    0x00
(gdb) x/4xh test
0xbf8e5118:     0x000a  0x0000  0x000b  0x0000
(gdb) x/2xw test
0xbf8e5118:     0x0000000a      0x0000000b
(gdb) x/1xg test
0xbf8e5118:     0x0000000b0000000a

# 打印字符或字符串
(gdb) x/30cb g_str[0]
0x8048538:      72 'H'  101 'e' 108 'l' 108 'l' 111 'o' 44 ','  32 ' '  71 'G'
0x8048540:      68 'D'  66 'B'  33 '!'  0 '\000'        73 'I'  116 't' 39 '\'' 115 's'
0x8048548:      32 ' '  102 'f' 117 'u' 110 'n' 110 'n' 121 'y' 46 '.'  0 '\000'
0x8048550:      1 '\001'        27 '\033'       3 '\003'        59 ';'  56 '8'  0 '\000'
(gdb) x/s g_str[0]
0x8048538:      "Hello, GDB!"

查看線程信息

有兩種方法可以進入線程調試:

  • 設置線程名,用ps查看母進程的線程信息,獲取tid,再啓動GDB進入;
  • 直接啓動GDB調試母進程,info thread查看所有線程信息,獲取到想要的線程的GDB內部編號n,thread n進入線程的調用棧。

直接獲取、調試線程

上面樣例中創建5條線程,並使用prctl函數爲每條線程命名爲"test-n"。
這樣可以通過ps -eL | grep test(或者test進程的pid)來查看剛創建的線程的tid。然後gdb -p tid進入線程調度。這裏進入編號爲4的線程。

sunnogo@a3e420:~/test/gdb$ gcc -o test test.c -g -Wall -lpthread
sunnogo@a3e420:~/test/gdb$ ./test &
[2] 16427
sunnogo@a3e420:~/test/gdb$ ps -eL | grep test
16427 16427 pts/1    00:00:00 test
16427 16428 pts/1    00:00:00 test-1
16427 16429 pts/1    00:00:00 test-2
16427 16430 pts/1    00:00:00 test-3
16427 16431 pts/1    00:00:00 test-4
16427 16432 pts/1    00:00:00 test-5
sunnogo@a3e420:~/test/gdb$ sudo gdb -p 16431
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 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 "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 16431

warning: process 16431 is a cloned process
Reading symbols from /home/sunnogo/test/gdb/test...done.
Reading symbols from /lib/i386-linux-gnu/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Loaded symbols for /lib/i386-linux-gnu/libpthread.so.0
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0xb774f424 in __kernel_vsyscall ()
(gdb) bt full
#0  0xb774f424 in __kernel_vsyscall ()
No symbol table info available.
#1  0xb7623d06 in nanosleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#2  0xb7623aff in sleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#3  0x080485ee in thread_process (arg=0x3) at test.c:46
        in = 3
        name = "test-4", '\000' <repeats 57 times>
#4  0xb771cd4c in start_thread () from /lib/i386-linux-gnu/libpthread.so.0
No symbol table info available.
#5  0xb765abae in clone () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
(gdb) 

間接獲取、調試線程

注意和上一種方法的對比,相比起來,第一種方法要方便得多。也從側面看出爲每個線程命名的重要性。

sunnogo@a3e420:~/test/gdb$ 
nnogo@a3e420:~/test/gdb$ sudo gdb attach 16427
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 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 "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
attach: No such file or directory.
Attaching to process 16427
Reading symbols from /home/sunnogo/test/gdb/test...done.
Reading symbols from /lib/i386-linux-gnu/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 16432]
[New LWP 16431]
[New LWP 16430]
[New LWP 16429]
[New LWP 16428]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Loaded symbols for /lib/i386-linux-gnu/libpthread.so.0
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0xb774f424 in __kernel_vsyscall ()
(gdb) info thread
  Id   Target Id         Frame 
  6    Thread 0xb7568b40 (LWP 16428) "test-1" 0xb774f424 in __kernel_vsyscall ()
  5    Thread 0xb6d67b40 (LWP 16429) "test-2" 0xb774f424 in __kernel_vsyscall ()
  4    Thread 0xb6566b40 (LWP 16430) "test-3" 0xb774f424 in __kernel_vsyscall ()
  3    Thread 0xb5d65b40 (LWP 16431) "test-4" 0xb774f424 in __kernel_vsyscall ()
  2    Thread 0xb5564b40 (LWP 16432) "test-5" 0xb774f424 in __kernel_vsyscall ()
* 1    Thread 0xb75696c0 (LWP 16427) "test" 0xb774f424 in __kernel_vsyscall ()
(gdb) thread 3
[Switching to thread 3 (Thread 0xb5d65b40 (LWP 16431))]
#0  0xb774f424 in __kernel_vsyscall ()
(gdb) bt full
#0  0xb774f424 in __kernel_vsyscall ()
No symbol table info available.
#1  0xb7623d06 in nanosleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#2  0xb7623aff in sleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#3  0x080485ee in thread_process (arg=0x3) at test.c:46
        in = 3
        name = "test-4", '\000' <repeats 57 times>
#4  0xb771cd4c in start_thread () from /lib/i386-linux-gnu/libpthread.so.0
No symbol table info available.
#5  0xb765abae in clone () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
(gdb) q
A debugging session is active.

        Inferior 1 [process 16427] will be detached.

Quit anyway? (y or n) y
Detaching from program: /home/sunnogo/test/gdb/test, process 16427
sunnogo@a3e420:~/test/gdb$ 
sunnogo@a3e420:~/test/gdb$ 

gdb中調用調用函數

call func_name(param1, param2, ...),目前還沒有明白如果參數是結構體要怎麼整。注意,只能在進程上下文中才能使用,coredump中無法使用。

gdb中申請內存

p malloc(size),結果會返回一個指針,即可正常使用這個指針。注意,只能在進程上下文中才能使用,coredump中無法使用。如下例:

(gdb) p malloc(4)
[New Thread 0x693ff460 (LWP 2033)]
[Switching to Thread 0xb6101000 (LWP 1456)]
$1 = (void *) 0xb58d01e0    <----使用這個返回的指針。

查看寄存器信息

to-do

GDB反彙編

to-do

斷點設置

to-do

內存監控

to-do

GCC選項對GDB的影響

GCC -g選項的影響

注意上面的,如果gcc編譯的時候不加-g選項,那麼frame 3也會顯示“No symbol table info available.”,無符號表信息可用,全局變量g_str也打不出來。

(gdb) bt full
#0  0xb77a3424 in __kernel_vsyscall ()
No symbol table info available.
#1  0xb7692ce0 in nanosleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#2  0xb7692aff in sleep () from /lib/i386-linux-gnu/libc.so.6
No symbol table info available.
#3  0x08048462 in main ()
No symbol table info available.
(gdb) p g_str
$1 = 134513928
(gdb) p g_str[0]
cannot subscript something of type `<data variable, no debug info>'
(gdb) p g_bool
$2 = 0
(gdb) 

GCC -fomit-frame-pointer選項的影響

to-do

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