工具程序gdb是GNU调试器。为了能调试程序,编译时必须指定GCC的-g选项将源码与可执行代码联系起来。
fibonacci.c
int current;
int next;
int nextnext;
void setstart(void) {
current = 0;
next = 1;
}
void calcnext(void) {
nextnext = current + next;
current = next;
next = nextnext;
}
int main(int argc, char *argv[]) {
int i;
setstart();
for (i = 0; i < 20; i++) {
printf("%2d: %d\n", i+1, current);
calcnext();
}
return(0);
}
$ gcc -g fibonacci.c -o fibonacci \\ -g选项使得编译后的程序包含调试信息
当进程执行时,会中断在断点对应的行上。可根据行号、程序名、内存地址设置断点。
(gdb) break 20 // 设置断点(在行号为20处)
Breakpoint 17 at 0x8048479: file fibonacci.c, line 20.
(gdb) break main // 设置断点(在main函数开始处)
Breakpoint 18 at 0x804846a: file fibonacci.c, line 18.
(gdb) info address main // 查看地址(在main函数开始处)
Symbol "main" is a function at address 0x8048461.
(gdb) break *0x8048461 // 设置断点(在0x8048461地址处)
Breakpoint 19 at 0x8048461: file fibonacci.c, line 16.
(gdb) info breakpoints // 查看断点
Num Type Disp Enb Address What
17 breakpoint keep y 0x08048479 in main at fibonacci.c:20
18 breakpoint keep y 0x0804846a in main at fibonacci.c:18
19 breakpoint keep y 0x08048461 in main at fibonacci.c:16
(gdb) ignore 5 3 // 忽略断点(忽略Num=5的断点3次触发)
Will ignore next 3 crossings of breakpoint 5.
(gdb) delete 5 // 删除断点(断点列表中Num=5)
(gdb) clear main // 清除断点(main函数开始处)
(gdb) clear 20 // 清除断点(在行号为20处)
(gdb) enable 5 // 激活断点(断点列表中Num=5)
(gdb) disable 5 // 禁止断点(断点列表中Num=5)
当进程执行时,某个变量或内存地址被读或写了,就中断进程。
(gdb) info address current // 查看地址(current变量)
Symbol "current" is static storage at address 0x804a02c.
(gdb) watch *0x804a02c // 设置观察点(当写0x804a02c地址时)
Hardware watchpoint 14: *0x804a02c
(gdb) rwatch current // 设置观察点(当读到current变量时)
Hardware read watchpoint 15: current
(gdb) awatch current // 设置观察点(当读或写到current变量时)
Hardware access (read/write) watchpoint 16: current
(gdb) info watchpoints // 查看观察点
Num Type Disp Enb Address What
14 hw watchpoint keep y *0x804a02c
15 read watchpoint keep y current
16 acc watchpoint keep y current
(gdb) delete 2 // 删除观察点(观察点列表中Num=2)
(gdb) enable 2 // 激活观察点(观察点列表中Num=2)
(gdb) disable 2 // 禁止观察点(观察点列表中Num=2)
当进程执行到某一时刻中断后,调用checkpoint命令创建(fork)此进程的子进程,此时子进程相当于是父进程此时刻的快照。
(gdb) checkpoint // 创建快照(调用fork创建子进程)
checkpoint: fork returned pid 8684.
(gdb) info checkpoints // 查看快照(checkpoint和PID一一对应)
1 process 8684 at 0x8048479, file fibonacci.c, line 20
*' 0 process 8680 (main process) at 0x8048479, file fibonacci.c, line 20
(gdb) restart 1 // 载入快照(将快照列表中id=1的进程载入调试器)
设置catchpoint用于捕捉到事件后中断进程。catch命令设置事件,tcatch命令设置临时事件(事件执行一次后对应catchpoint自动删除)。
可捕获的事件类型如下
catch/tcatch assert -- Catch failed Ada assertions
catch/tcatch catch -- Catch an exception
catch/tcatch exception -- Catch Ada exceptions
catch/tcatch exec -- Catch calls to exec
catch/tcatch fork -- Catch calls to fork
catch/tcatch load -- Catch loads of shared libraries
catch/tcatch rethrow -- Catch an exception
catch/tcatch signal -- Catch signals by their names and/or numbers
catch/tcatch syscall -- Catch system calls by their names and/or numbers
catch/tcatch throw -- Catch an exception
catch/tcatch unload -- Catch unloads of shared libraries
catch/tcatch vfork -- Catch calls to vfork
(gdb) catch load // 设置捕获载入库事件
(gdb) info breakpoints // 查看catchpoints
Num Type Disp Enb Address What
7 catchpoint keep y load of library
(gdb) run // 从开始位置运行程序
Starting program: /home/fibonacci
Catchpoint 7
Inferior loaded /lib/i386-linux-gnu/libc.so.6
__GI__dl_debug_state () at dl-debug.c:74
(gdb) info breakpoints // 查看catchpoints
Num Type Disp Enb Address What
7 catchpoint keep y load of library
catchpoint already hit 1 time
每次程序中断时,显示指定变量的值。
(gdb) display current // 设置display(全局)
(gdb) display i // 设置display(临时)
(gdb) info display // 查看display
Auto-display expressions now in effect:
Num Enb Expression
2: y i
1: y current
(gdb) next // 继续运行1行源码(不跟进函数内)
main (argc=1, argv=0xbffff694) at fibonacci.c:19
19 for (i = 0; i < 20; i++) {
2: i = 11
1: current = 144
(gdb) undisplay 1 // 取消display(display列表中Num=1)
(gdb) info display // 查看display
Auto-display expressions now in effect:
Num Enb Expression
2: y i
$ gdb
(gdb) file fibonacci // 载入fibonacci可执行文件
Reading symbols from fibonacci...done.
(gdb) break main // 设置断点(在main函数开始处)
Breakpoint 1 at 0x804846a: file fibonacci.c, line 18.
(gdb) run // 启动进程(从开始位置运行)
Starting program: /home/ssyang/00_test/old/fibonacci
Breakpoint 1, main (argc=1, argv=0xbffff694) at fibonacci.c:18
18 setstart();
(gdb) backtrace // 打印栈调用(包含当前位置信息)
#0 main (argc=1, argv=0xbffff694) at fibonacci.c:18
(gdb) list // 列出源码(当前行附近10行)
13 next = nextnext;
14 }
15
16 int main(int argc, char *argv[]) {
17 int i;
18 setstart();
19 for (i = 0; i < 20; i++) {
20 printf("%2d: %d\n", i+1, current);
21 calcnext();
22 }
(gdb) next // 继续运行1行源码(不跟进函数内)
19 for (i = 0; i < 20; i++) {
(gdb) next 2
1: 0 (current全局变量——[写次数]: [上次值])
21 calcnext();
(gdb) step // 继续运行1行源码(跟进函数内)
calcnext () at fibonacci.c:11
11 nextnext = current + next;
(gdb) backtrace
#0 calcnext () at fibonacci.c:11
#1 0x0804849e in main (argc=1, argv=0xbffff694) at fibonacci.c:21
(gdb) watch current // 设置观察点
Hardware watchpoint 2: current
(gdb) continue // 继续运行直到被中断
Continuing.
Hardware watchpoint 2: current
Old value = 0
New value = 1
calcnext () at fibonacci.c:13
13 next = nextnext;
(gdb) next 5
2: 1 (current全局变量——[写次数]: [上次值])
19 for (i = 0; i < 20; i++) {
(gdb) Quit(Ctrl+C) // 按下Ctrl+C中断当前进程
(gdb) finish // 继续运行直到当前函数返回
Run till exit from #0 calcnext () at fibonacci.c:13
main (argc=1, argv=0xbffff694) at fibonacci.c:19
19 for (i = 0; i < 20; i++) {
(gdb) print i // 显示i变量的值
$3 = 2
(gdb) ptype/whatis i // 显示i变量的类型
type = int
......
(gdb) step
calcnext () at fibonacci.c:11
11 nextnext = current + next;
(gdb) return // 强制从当前函数立即返回
Make calcnext return now? (y or n) y
#0 main (argc=1, argv=0xbffff694) at fibonacci.c:19
19 for (i = 0; i < 20; i++) {
(gdb) set current=100 // 设置current变量值为100
(gdb) p current // 显示current变量的值
$9 = 100
(gdb) quit // 退出GDB
$
在UNIX系统中,程序崩溃时的内容可以完全复制到文件core中。ulimit
命令用于shell启动进程所占用的资源。
ulimit -a : 查看当前shell所有限制
ulimit -c 0 : 阻止当前shell生成core文件
ulimit -c 1024 : 限制当前shell生成core文件的最大大小为1024KB
falldown.c
char **nowhere;
void setbad(void) {
nowhere = 0;
*nowhere = "This is a string\n";
}
int main(int argc, char *argv[]) {
setbad();
printf("%s\n", *nowhere);
}
$ gcc -g falldown.c -o falldown
$ ulimit -c 1024
$ ./falldown
Segmentation fault (core dumped)
$ ll
-rw------- 1 test test 212992 3月 2 15:11 core
-rwxrwxr-x 1 test test 8290 3月 2 14:49 falldown*
-rw-rw-r-- 1 test test 163 3月 2 14:49 falldown.c
$ gdb falldown core
Reading symbols from falldown...done.
[New LWP 14136]
Core was generated by `./falldown'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0804842f in setbad () at falldown.c:5
5 *nowhere = "This is a string\n";
(gdb) print nowhere
$2 = (char **) 0x0
looper.c
void goaround(int counter) {
int i = 0;
while (i < counter) {
if (i++ == 17)
i = 10;
}
}
int main(int argc, char *argv[]) {
printf("started\n");
goaround(20);
printf("done\n");
return 0;
}
$ gcc -g looper.c -o looper
$ ./looper &
[1] 14308
$ ps -a
PID TTY TIME CMD
14308 pts/6 00:00:09 looper
14309 pts/6 00:00:00 ps
$ sudo gdb looper 14308
Reading symbols from looper...done.
Attaching to program: /home/ssyang/00_test/looper, process 14308
Reading symbols from /lib/i386-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug//lib/i386-linux-gnu/libc-2.19.so...done.
done.
Loaded symbols for /lib/i386-linux-gnu/libc.so.6
Reading symbols from /usr/lib/debug//lib/i386-linux-gnu/ld-2.19.so...done.
done.
Loaded symbols for /lib/ld-linux.so.2
goaround (counter=20) at looper.c:3
3 while (i < counter) {
(gdb) display i
1: i = 15
(gdb) step
step stepi stepping
(gdb) step
4 if (i++ == 17)
1: i = 15
(gdb)
3 while (i < counter) {
1: i = 16
(gdb)
4 if (i++ == 17)
1: i = 16