本文來自個人博客 sunyongfeng.com。博客的文章保持更新,此文可能不是最新狀態(tài)毫目。
簡介
GDB,GNU Debugger诲侮,特性如下:
- GDB具備各種調(diào)試功效镀虐,可對計算機程序的運行進行追蹤、警告沟绪。使用者可以監(jiān)控及修改程序內(nèi)部變量的值刮便,甚至可在程序的正常運行之外調(diào)用函數(shù)。
- GDB支持多數(shù)處理器架構(gòu)
- 持續(xù)開發(fā)中
- 支持遠程調(diào)試
- 支持內(nèi)核調(diào)試绽慈,KGDB
從事嵌入式軟件開發(fā)兩年來恨旱,主要在以下幾方面使用GDB:
- 查看、修改運行時變量坝疼;
- 多線程調(diào)試搜贤,查看當前線程運行狀態(tài)(以確定當前線程是不是因為等鎖等原因掛起);
- 查看coredump文件钝凶;
- 碰到難纏的內(nèi)存非法改寫問題仪芒,用GDB的斷點、物理watch功能查看內(nèi)存變化以定位改寫者;
引用公司一個技術(shù)牛人的話:在大型的項目中桌硫,使用GDB的單步調(diào)試夭咬、軟件watch是不現(xiàn)實的,因為會運行得實在太慢铆隘。
命令小記:
linux提示符
1. GDB進入正在運行的進程
gdb 可執(zhí)行文件 core文件
gdb -p pid
GDB提示符
1. 查看調(diào)用棧信息
bt / backtrace / bt full
frame n
info locals
info args
2. 查看卓舵、設(shè)置變量
p 變量
p 變量 = 新值
set 變量 = 新值
3. 查看內(nèi)存
x/<n/f/u> <addr>
4. 線程調(diào)試
info thread
thread n
啟動GDB
GCC選項###
想用GDB調(diào)試,則在GCC編譯的時候要加上-g選項膀钠。
啟動GDB
啟動GDB的方法主要有以下幾種:
gdb
gdb executable_file
-
gdb executable_file corefile
:查看coredump文件信息掏湾,定位coredump產(chǎn)生原因、觸發(fā)源肿嘲。 -
gdb attach pid
:調(diào)度運行時的進程或線程融击,同gdb -p pid
。
善用help
在GDB提示符下輸入help
或help 命令
雳窟,能夠查看命令的幫助說明尊浪。
(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.
查看調(diào)用棧
寫一個簡單的例子(僅為樣例,并不嚴謹):
#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權(quán)限才能讓GDB attach到對應(yīng)進程惩坑。
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
查看當前調(diào)用棧信息(call stack,即函數(shù)調(diào)用層次信息)以舒,當前進程的是由main() -> sleep() -> nanosleep() -> __kernel_vsyscall()一層一層調(diào)入趾痘。注意“#數(shù)字”,在GDB中這叫stack frames蔓钟,或直接稱為frame永票,運行棧由一個或多個連續(xù)的frame組成,數(shù)字越小代表調(diào)用層次越深奋刽。 - 使用
bt full
查看詳細調(diào)用棧信息瓦侮,會把各個frame的入?yún)⒑途植孔兞啃畔@示出來艰赞。這里bt是backtrace的縮寫佣谐,GDB的全命令經(jīng)常有其簡短的寫法。
注意:GDB中方妖,按回車默認是執(zhí)行上一次命令狭魂。
先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。默認顯示當前函數(shù)名、函數(shù)入?yún)⒋瞥巍斍斑\行處所在源文件的代碼行位置斋泄,并顯示當前行代碼。 - 使用
info
命令查看frame詳細信息镐牺,info命令不是全命令炫掐,后面還有子命令。info有很多子命令睬涧,除本frame外募胃,還可以查看本進程信息、系統(tǒng)信息畦浓,這里僅僅是冰山一角痹束。-
info frame
顯示當前frame信息 -
info args
顯示入?yún)⑿畔?/li> -
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 *結(jié)構(gòu)體指針
p 數(shù)組名
# 打印變量
(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)
# 打印結(jié)構(gòu)體指針
(gdb) p test
$1 = (test_t *) 0xbf8e5118
(gdb) p *test
$2 = {member_a = 10, member_b = 11}
# 打印數(shù)組名
(gdb) p g_str
$5 = {0x8048538 "Hello, GDB!", 0x8048544 "It's funny."}
(gdb) p g_str[0]
$6 = 0x8048538 "Hello, GDB!"
print
不僅可以用來查看變量,還可用于設(shè)置變量夺溢。print var=value
论巍。
設(shè)置變量值的命令還有set
,set 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)
查看內(nèi)存
examine
查看內(nèi)存环壤,縮寫是x
。命令格式:
x/<n/f/u> <addr>
n钞诡、f擅威、u是可選參數(shù),說明如下:
(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
表示要打印的多少個單位的內(nèi)存承耿,默認是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字節(jié),h表示2字節(jié)兰粉,w表示4字節(jié)故痊,g表示8字節(jié)。
# 當前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的結(jié)果比較
# 以二進制形式打印
(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!"
查看線程信息
有兩種方法可以進入線程調(diào)試:
- 設(shè)置線程名愕秫,用ps查看母進程的線程信息,獲取tid焰络,再啟動GDB進入戴甩;
- 直接啟動GDB調(diào)試母進程,
info thread
查看所有線程信息闪彼,獲取到想要的線程的GDB內(nèi)部編號n甜孤,thread n
進入線程的調(diào)用棧。
直接獲取畏腕、調(diào)試線程
上面樣例中創(chuàng)建5條線程缴川,并使用prctl函數(shù)為每條線程命名為"test-n"。
這樣可以通過ps -eL | grep test(或者test進程的pid)
來查看剛創(chuàng)建的線程的tid描馅。然后gdb -p tid
進入線程調(diào)度把夸。這里進入編號為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)
間接獲取流昏、調(diào)試線程
注意和上一種方法的對比扎即,相比起來,第一種方法要方便得多况凉。也從側(cè)面看出為每個線程命名的重要性谚鄙。
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中調(diào)用調(diào)用函數(shù)
call func_name(param1, param2, ...)
,目前還沒有明白如果參數(shù)是結(jié)構(gòu)體要怎么整刁绒。注意闷营,只能在進程上下文中才能使用,coredump中無法使用知市。
gdb中申請內(nèi)存
p malloc(size)
傻盟,結(jié)果會返回一個指針,即可正常使用這個指針嫂丙。注意娘赴,只能在進程上下文中才能使用,coredump中無法使用跟啤。如下例:
(gdb) p malloc(4)
[New Thread 0x693ff460 (LWP 2033)]
[Switching to Thread 0xb6101000 (LWP 1456)]
$1 = (void *) 0xb58d01e0 <----使用這個返回的指針诽表。
查看寄存器信息
to-do
GDB反匯編
to-do
斷點設(shè)置
to-do
內(nèi)存監(jiān)控
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