首页 > 代码库 > 《软件调试的艺术》笔记--预备知识

《软件调试的艺术》笔记--预备知识

1.gcc的-g选项

如果要使用gdb进行调试,必须在编译时在gcc中加入-g选项,使用参数 -g 表示将源代码信息编译到可执行文件中。

如果不使用-g选项:

#include <stdio.h>

int main(void)
{
	int i = 1;
	i = i + 1;
	printf("i = %d\n",i);
	return 0;
}
gcc main.c

gdb a.out

(gdb) b main
Breakpoint 1 at 0x4004f8
(gdb) r
Starting program: /home/yanwenjie/ctest/a.out 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Breakpoint 1, 0x00000000004004f8 in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.
i = 2
0x00007ffff7a3b76d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) 


如果使用-g选项:

gcc -g main.c

yanwenjie@ywjpc:~/ctest$ gdb a.out 
(gdb) b main
Breakpoint 1 at 0x4004fc: file main.c, line 5.
(gdb) r
Starting program: /home/yanwenjie/ctest/a.out 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Breakpoint 1, main () at main.c:5
5 int i = 1;
(gdb) n
6 i = i + 1;
(gdb) 
7 printf("i = %d\n",i);
(gdb) 
i = 2
8 return 0;
(gdb) 

可以看到程序语句信息。


2.TUI(terminal User Interface)

从版本6.1开始,GDB已经以名为TUI的模式提供基于文本交互和图形交互之间的折中方法。命令为:

gdbtui a.out


如上图显示,上方为显示对应的代码语句,下方为通常gdb的命令模式。其他操作与命令行gdb一致。


3.上下移动调用栈

在函数调用期间,与调用关联的运行信息存储在栈帧的内存区域中。帧中包含函数的局部变量的值、其形参,以及调用该函数的
位置记录。每次发生函数调用时,都会创建一个新帧,并将其推到一个系统维护的栈上,栈最上方的帧表示正在执行的函数,当
函数推出时,这个帧被弹出栈,并且被释放。
在GDB中可以用如下命令查看以前的帧:
frame 1
当执行GDB的frame命令时,当前正在执行的函数的帧被编号为0,其父帧被编号为1,父帧的父帧被编号为2,以此类推。GDB
的up命令将你带到调用帧中的下一个父帧,down则引向相反方向。这样的操作非常有用,因为根据以前的一部分栈帧中的局部
变量的值。
GDB的backtrace命令会显示整个栈,即当前存在的所有帧的集合。
main.c:
#include <stdio.h>

void display(int i)
{
	printf("i = %d\n",i);
}

int main(void)
{
	int i = 1;
	i = i + 1;
	display(i);
	return 0;
}
对上面的程序进行gdb调试。
(gdb) b display 
Breakpoint 1 at 0x4004ff: file main.c, line 5.
(gdb) frame
No stack.
(gdb) r
Starting program: /home/yanwenjie/ctest/a.out 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Breakpoint 1, display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb) frame
#0  display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb) frame 0
#0  display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb) frame 1
#1  0x0000000000400535 in main () at main.c:12
12 display(i);
(gdb) frame 0
#0  display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb) up
#1  0x0000000000400535 in main () at main.c:12
12 display(i);
(gdb) down
#0  display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb) bt
#0  display (i=2) at main.c:5
#1  0x0000000000400535 in main () at main.c:12

4.gdbinit

使用gdb调试程序的时候,有时候需要设定多个断点,重复执行某些操作,而这些操作写起来比较麻烦,这时候可以使用gdbinit脚本。

在有如下脚本:

#filename: .gdbinit
b display
r

有两种方式来使用这个脚本:
1)启动gdb时候
gdb在启动的时候,会在当前目录下查找".gdbinit"这个文件,并把它的内容作为gdb命令进行解释,所以如果我把脚本命名为".gdbinit",

这样在启动的时候就会处理这些命令。

将脚本保存为.gdbinit,放在main.c同一目录下,执行gdb命令:

yan@ywjpc:~/ctest$ gdb a.out 
Breakpoint 1, display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb)


2)gdb运行期间
可以使用 source script-file 来解释gdb命令脚本script-file

将脚本保存为gdbscript,放在main.c同一目录下:

yan@ywjpc:~/ctest$ gdb a.out 
(gdb) source gdbscript 
Breakpoint 1 at 0x4004ff: file main.c, line 5.
Breakpoint 1, display (i=2) at main.c:5
5 printf("i = %d\n",i);
(gdb)