- 论坛徽章:
- 0
|
调试简介
调试是一种动态的以跟踪和监视程序执行的方式发现程序bug或者窥探程序内部结构的行为。
对于查找bug来说,调试是一种后期补救的行为。所以我们从开始就应该养成良好的编码风格和编码习惯,以尽量减少程序中bug的数量。
还有一些号习惯包括,在使用gcc时使用这样的参数:
-Wall
-pedantic -ansi.
是程序都有出错的可能,所以我们还应该编写有效的出错处理代码。使用宏定义的方式可以让我们的程序在开发时有大量的调式信息输出,以帮助诊断可能发生的错误。调式信息可能会是程序增大数倍,这样会影响到程序的执行效率,所以我们应该在发布版程序中剔除调式信息,strip工具可以帮助我们删除可执行文件中的调试信息。但是保留一个可以开关的程序运行信息打印或者完整的日志记录系统对于程序的后期维护是十分必要的,而此也不会对程序的执行效率产生多大影响(别人讲的).
下面几个出错处理代码中常用的宏:
--LINE--
表示当前行号的十进制数
--FILE--
表示当前文件名的字串
--DATE--表示当前日期
--TIME--
表示当前时间
调试原理
在生成需要调试的目标程序时,编译器会添加一些调式信息(据说是一些源码相关符号等信息)在可执行二进制文件里。这样在调试器在调试程序是就可以读取这些信息以便执行程序和其源代码联系起来。
调试需要操作系统和cpu的支持,这个条件我们一般都是满足的。
Linux
可调试的程序需要在编译时添加-g选项,如下
#gcc -g test.c
Gdb简介
gdb是linux下的一款开发源码且功能无比强大的调式工具。
在gdb的man手册里这样写:
gdb
- The GNU Debugger
使用gdb可以做如下四种类型的工作以帮助我们发现程序中bugs.
·
Start your program, specifying anything that might affect its
behavior.
·
Make your program stop on specified conditions.
·
Examine what has happened, when your program has stopped.
·
Change things in your program, so you can experiment with
correcting the effects of one bug and go on to learn about another.
gdb命令介绍
-g
gcc编译参数,开启调试开关,在Makefile文件中一般定义在CFLAGS变量中。
gdb
使用方式(prog_name为目标程序的文件名)
#gdb prog_name
*list
:简记l
,作用就是列出程序的源代码,后面可以加行号和函数名等参数。
注意:如果运行list
命令得到类似如下的打印,那是因为在编译程序时没有加入
-g
选项:
(gdb) list
1
../sysdeps/i386/elf/start.S: No such file or directory.
in
../sysdeps/i386/elf/start.S
*
run:简记为
r
,作用是运行程序,后面可以直接加程序的运行参数。
*
set args:设置运行程序时的命令行参数,如:set
args 33 55
*
show args:显示命令行参数
*
set variable xx = value :设置变量的值。
*
continue:简讯为
c
ont(c),其作用是继续运行被断点中断的程序。
*
break:简记b,作用是为程序设置断点,后面可以是行号,函数名等。也可以添加条件断点。例如:break
200 if count == 100(当变量count为100时,200行出中断)。
*
disable/enable break Num:关闭/开启断点“Num”,其中“Num”为
info
breakpoints 中显示的对应值
*
del :删除所有断点。
*
step:简记为
s
,单步跟踪程序,当遇到函数调用时,则进入此函数体。
*
next:简记为
n,单步跟踪程序,当遇到函数调用时,也不进入此函数体。
*
until:
执行到某处,后面可以接行号等信息。
*
finish:运行程序,直到当前调用栈完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
*
return [表达式]:强制函数返回,表达式可选,将作为返回值。
*
call 函数
:强制调用函数。
*
signal n,发送信号量给被调试程序,UNIX的系统信号量通常从1到15.
*
stepi或nexti:以机器指令的方式单步跟踪。
*
print 表达式:简记为
p
,其中“表达式”可以是任何当前正在被测试程序的有效表达式,比如当前正在调试C语言的程序,那么“表达式”可以是任何C语言的有效表达式,包括数字,变量甚至是函数调用。
*
print a:将显示整数
a
的值,可以设置打印格式,如:p/x(以十六进制格式打印)。
*
print ++a:将把
a
中的值加1,并显示出来
*
print name:将显示字符串
name
的值
*
print gdb_test(22):将以整数22作为参数调用
gdb_test()
函数
*
print gdb_test(a):将以变量
a
作为参数调用
gdb_test()
函数
*
backtrace:简记为
bt,显示当前程序的函数调用堆栈。
*
display 表达式:简记为
d,监视指定变量的值,可以设置打印格式,如:p/d(以十进制格式打印)。
*
watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。
*
kill:将强行终止当前正在调试的程序
*
help 命令:
gdb内置的帮助命令,无比的详细,强烈推荐阅读。
*
call 函数(参数):调用“函数”,并传递“参数”,如:call
gdb_test(55)
*
layout:用于分割窗口,可以一边查看代码,一边测试:
layout
src:显示源代码窗口
layout
asm:显示反汇编窗口
layout
regs:显示源代码/反汇编和CPU寄存器窗口
layout
split:显示源代码和反汇编窗口
*
线程调试支持
info
thread : 查看当前线程信息。
thread
NO. : 切换当前线程到指定号线程(NO.为序号,而非线程实际标号).
*
调试已运行程序
1
使用file命令载入符号表(我理解为包含调试信息的调试目标程序,如调试firefox,那么就使用file
/home/user/work/firefox/firefox,要求是此firefox在编译时打开了-g选项)。
2
使用ps
aux查看目标程序的pid,在gdb
提示符下使用attach
pid命令是调试器与调试目标程序建立联系。
3
现在就可以使用诸如单步跳等命令开始调试程序了。
4
使用decath命令关闭对指定程序(使用attach命令指定)的调试。
*
quit:简记为
q
,退出gdb
file,如果在启动gdb时没有指定程序名,也可以用file命令指定调试目标程序。
info
file,查看当前调试的目标程序的相关信息。
info
break/b,查看断点信息。
info
source ,查看源码信息。
info
program,查看当前被调试程序状态信息。
x/fmt
内存地址,
显示内存数据信息,fmt为数据显示格式。
shell command, 执行shell命令。
make
,编译程序,相当与shell
make的作用。
pwd,显示当前工作目录。
cd,改变当前工作目录。
frame
或
f
,会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。
info
frame(f)
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。
forward-search(search)
string, 向前面搜索源代码。
Reverse-search
string , 全部搜索。
*
在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中。
"@
", 是一个和数组有关的操作符,在后面会有更详细的说明。
大约是下面这个样子的:(gdb)
p *array@len
@的左边是数组的首地址(也可以是内存地址),也就是变量array所指向的内容,右边len则是需要显示的数据的长度。
"::"
, 指定一个在文件或是一个函数中的变量。
file::variable
如:printf
main.c::count(输出main.c文件中的count变量)
function::variable
如:main::count(输出main函数中的count变量)
"::"与c++中的"::"同名,但gdb会很好的区别它们(据说)。
"{}",
表示一个指向内存地址的类型为type的一个对象。
*
输出格式(在使用printf,x时有效)
一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:
x
按十六进制格式显示变量。
d
按十进制格式显示变量。
u
按十六进制格式显示无符号整型。
o
按八进制格式显示变量。
t
按二进制格式显示变量。
a
按十六进制格式显示变量。
c
按字符格式显示变量。
f
按浮点数格式显示变量。
强烈推荐,上面的命令只是仅仅提到了作用,详细使用方式使用help
command获取帮助信息。
补充
1
条件断点的cpu开销比较大,尤其是在大型循环内的条件断点,所以此情况下推荐手动添加判断代码加无条件中断的方式代替条件中断。
2
gdb shell下,终端下的快键仍然有效,tan键不全支持gdb内置命令甚至包括一些源码内符号(函数名,变量名等)。
3
真正的调试高手,对程序了如指掌。这包括两层含义:一对可执行程序的结构和运行机制了如指掌,二对自己程序要做什么,能做什么和做了什么了如指掌。所以想要成为调试高手就扎实的学习基础,真正的高手其实不用调试器就可以调试程序。
Gdb包装工具ddd
ddd,一个gdb的图形实现,提供以鼠标的方式控制调试步骤和窗口的形式显示程序信息的gdb包装工具。但它仍然保留了gdb命令行下的所有特性,表现还不错。
Gdb调试示例
参考资料
1
gdb man手册页
2
linux程序设计(第3版)第十章
3
http://www.cublog.cn/u/11826/showart.php?id=175385
4
http://fanqiang.chinaunix.net/program/other/2005-03-23/2993.shtml
用gdb调试程序(超级详细的一个教程)
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/104336/showart_2069731.html |
|