分类: 系统运维
2012-05-09 13:01:21
类UNIX下C/C++开发,代码调试比较麻烦,最原始的加跟踪调试很土,也很费时,特别是一个庞大的项目,代码行数非常大的时候调试起来非常费劲,当core dump时定位也不容易,这里介绍几个常用工具: gdb、dbx调试工具,valgrind内存检查工具(Linux) 。
gdb(GNU DeBugger)是GNU的调试器,一般和gcc配搭使用。要使用GDB进行调试,编译程序时要指定-g或-ggdb的编译选项。如: gcc –g main.c
gdb指令:f(file):指定可执行文件,l(list)列出源文件,r(run)运行可执行文件,可带执行参数,b(break)设置断点,c(continue)继续被中断程序执行,直到下一个断点或者程序结束,p(print)输出变量的值,如p aa;n(next)单步执行,s(step)程序执行到断点时中断执行,可以用s指令进行单步执行进入某一函数。q(quit)退出。
当在gdb运行时想运行shell命令,不必退出,执行shell切换到shell模式,执行shell命令。
GDB有能力在你调试程序的时候处理任何一种信号,你可以告诉GDB需要处理哪一种信号。你可以要求GDB收到你所指定的信号时,马上停住正在运行的程序,以供你进行调试。你可以用GDB的handle命令来完成这一功能。handle
在GDB中定义一个信号处理。信号
查看core文件:运行gdb 执行文件 core文件 来加载core文件,使用where来查看coredump位置。如果系统未产生core文件,可使用ulimit -c 2048命令,后运行执行文件产生。
多进程调试:在我们的测试程序在父进程fork后,子进程调用sleep睡了60秒。这就是关键,这个sleep本来是不该存在于子进程代码中的,而是为了使用GDB调试后加入的,为什么要让子进程刚刚运行就开始sleep呢?因为我们要在子进程睡眠期间,利用shell命令获取其process id,然后再利用gdb调试外部进程的方法attach到该process id上,调试该进程。
动态库调试,运行执行程序加载动态库来调试。
/***********************************************************************/
dbx(SUN studio)调试工具:在利用 dbx 对程序进行源代码级调试前,必须使用 -g 选项
启动:dbx program_name加载要调试的程序,调试JAVA程序dbx program_name.class|.jar,可以指定进程ID来连接正在运行的进程进行调试。
也可以在dbx运行时加载调试程序:(dbx)debug program_name
运行调试程序:run [运行参数]
查看core文件:可以dbx program_name core,或者dbx - core,再使用where命令显示栈跟踪,找到崩溃位置
设置断点:stop in 函数;stop at file:line ; 也可以先通过file file-name设置当前文件,list function查看源文件,确定要设置断点的行数。在C++中可以将关键字(inmember、inclass、infunction 或 inobject)在类,成员函数,对象中设置断点。stop change variable可设置当变量改变时停止。条件停止断点:stop cond condition。另外在动态库中可以在动态共享库的函数中以在程序启动时在链接的库中设置断点的同样方式来设置断点。
列出所有断点:status
删除断点:delete 断点号
单步执行:step、next,step 命令步入函数,而 next 命令步过函数。step up将一直执行,直至当前函数将控制返回调用它的函数为止。
继续执行:cont
查看调用栈:where 调用栈代表那些已被调用但尚未返回各自调用程序的所有当前活动例程。在栈中,函数及其参数按调用的顺序进行存放。栈跟踪显示程序流执行的停止位置以及执行如何到达此点。它提供程序状态的简明描述。
检查变量:print 变量名
退出:quit
连接已运行的进程:attach pid
分离已连接的进程:detach pid
查找符号:whereis symbol 打印指定符号所有具体值的列表
要打印输出标识符的声明whatis
对子进程使用运行时检查,可通过attach 子进程ID连接到子进程进行调试。
要显示当前进程的所有线程信息:执行thread,切换到指定线程:thread thr_id
信号处理:
cont -sig signal将信号转发到进程;catch显示当前正在捕获的信号列表;ignore显示程序忽略信号列表
shell方式切换:sh
------------------------------dbx调试跟踪的常用子命令
dbx是UNIX下基于命令行界面的程序调试器。 dbx是通过交互执行dbx子命令来达到调试的目的的。在调试程序前,必须先将-g选项包含在编译信息中,编译生成带调试信息的文件,即:cc -o filename -g file.c。 进入dbx通常只需输入"dbx filename"即可,filename为待调试的可执行程序名。dbx加载后就显示提示符:(dbx),此时用户就可以输入dbx子命令进行调试了。
语法:
dbx [ -a ProcessID ] [ -c CommandFile ] [ -d NestingDepth ] [ -I Directory ] [ -E DebugEnvironment ] [ -p oldpath=newpath:...| pathfile ] [ -k ] [ -u ] [ -F ] [ -r ] [ -x ] [ ObjectFile [ CoreFile ] ]
-a ProcessID
将调试程序和正在运行的进程连接起来。要连接调试程序,您需要拥有对该进程使用 kill 命令的权限。使用 ps 命令来决定进程的 ID。如果您获得许可,dbx 程序中断该进程,决定对象文件的完整名称,读入符号信息和提示输入命令。
-c CommandFile
读入标准输入之前,在文件中运行 dbx 子命令。$HOME 目录中指定的文件将首先被处理;然后处理当前目录中的文件。当前目录中的命令文件将会覆盖 $HOME 目录中的命令文件。如果指定的文件既不存在于 $HOME 目录也不存在于当前目录,将会显示警告消息。 子命令可以在 dbx 程序运行后使用。
-d NestingDepth
设置程序块的嵌套限制。缺省的嵌套深度限制为 25。
-E DebugEnvironment
指定调试程序的环境变量。
-p oldpath=newpath:...| pathfile
以 oldpath=newpath 的格式在检查核心文件时指定替换的库的路径。oldpath 指定了将被替换的值(存储在核心文件中)而 newpath 指定了将要替换的新的值。这些可能是全部或者部分的,绝对路径或者相对路径。可以指定若干个替换,它们之间用冒号隔开。相反 -p 标志可以指定文件名,映射以前读入的描述格式。每行中只允许有一个映射从文件中读出。
-F 可以用来关闭缓慢读入模式并使 dbx 命令在启动时就读入全部符号。缺省情况下,缓慢读入模式是打开的:它在 dbx 会话初始化时读入需要的符号表信息。。在该模式下,dbx 将不会读入那些符号信息尚未被读入的变量和类型。因此,诸如 whereis i 等命令并不列出在所有函数中的变量 i 的全部实例。
-I Directory
(大写 i)将 Directory 变量指定的目录包含到搜索源文件目录列表中。搜索源文件的缺省目录为:
-k 映射内存地址;这对于内核调试是非常有用的。
-r 立即运行对象文件。如果它成功结束,dbx 调试程序将会退出。否则,将会进入调试程序并报告中断的原因。
注意:除非指定了 -r,dbx 命令将会提示用户并等待命令输入。
-u 让 dbx 命令为文件名加上 @ (at 符号) 前缀。该标志减少符号名混淆的可能性。
-x 防止 dbx 命令跳过来自于 FORTRAN 源代码的 _(下划线)字符。该符号允许 dbx 在符号之间区别哪些除了下划线以外都是相同的,例如 xxx 和xxx_。
下面是一些dbx的常用子命令:
1.基本操作命令
run arg1 arg2 ... :以arg1,arg2,...为参数开始运行现有进程。
r:用上次使用的参数再次运行现有进程。
source filename:从文件名为filename的文件中读入dbx子命令并执行。
return:执行完目前的进程后返回。
sh command:不退出dbx,执行一条操作系统shell命令。
sh:暂时进入shell状态。
quit:退出dbx,若程序未执行完则终止其执行。
2.置断点与跟踪点命令
stop var at n:置断点命令,当第n行的变量var发生变化时将程序挂起。
stop var in proc:置断点命令,当过程proc的变量var发生变化时将程序挂起。
stop at n:置断点命令,当执行到第n行时将程序挂起。
stop in proc:置断点命令,当执行到过程proc时将程序挂起。
trace var at n:置跟踪点命令,当第n行的变量var改变时显示跟踪信息。
trace var in proc:置跟踪点命令,当过程proc的变量var改变时显示跟踪信息。
trace n:置跟踪点命令,当执行到第n行时显示被跟踪信息。
trace proc:置跟踪点命令,当执行到过程proc时显示被跟踪信息。
trace expr at n:置跟踪点命令,当执行到第n行时显示var的值。
delete n|all:删除第n行的/所有的断点与跟踪点。
3.调试命令
cont at n:运行直至第n行。
print var:打印变量var的值。
printf "string",expr,...:以C语言的格式打印。
where:打印当前调试状态,包括当前进程的信息。
func:查看当前运行的进程名。
func proc:移至到调用proc进程的母进程处。
whatis name:显示对变量名或过程名name的类型。
step [n]:单步执行一行或n行,遇到线程调用时进入线程调用。
next [n]:单步执行一行或n行,遇到线程调用时拒绝进入线程调用。
skip [n]:跳过一个或n个断点,继续往下执行
dump [proc] [>;file]:显示当前或proc过程的所有变量名及其值
assign var=expr:给变量var赋以表达式expr的值
4.读取被调试程序命令
list:列出从当前行开始的若干行源程序。
list n,m:列出从第n行到第m行的源程序。
/string:朝文件尾方向查找字符串string
?string:朝文件头方向查找字符串string
file filename:将查看的文件切换到文件filename处。
(dbx)run——运行可执行二进制文件
(dbx)list——列出代码
(dbx)next——跳转到下一步执行
(dbx)print
(dbx)stop at
(dbx)stop at
(dbx)cont——继续执行
(dbx)cont at
(dbx)call——调用func()
(dbx)delete
(dbx)quit——退出执行
Example1:
$cc -g -o test test.c
$dbx test
(dbx)stop at main
Example2:
$ cc -g looper.c -o looper
$ ps -u UserIDPID TTY TIME COMMAND更多命令参考:
Linux gdb调试
GDB是如今最广为人知的著名的自由和开放源码软件之一。它被大量GNU软件项目以及众多与GNU没有关联但却希望能有一个高质量调试器的第三方软件所使用。事实上,许多第三方工具合并gdb并将其作为它们的调试功能的基础,即便它们在gdb之上建立了各级图形化抽象。你很可能已遇到过GDB——但可能根本没有意识到这一点。
GDB建立在任何调试器都有两个组成部分这一基本概念之上。首先,GDB的底层处理单独进程或线程的启动和关闭、跟踪代码执行以及在运行代码中插入和删除断点。GDB支持大量不同的平台和机制以在各种架构上实现这些(看似简单的)操作。其具体的功能可能会受底层硬件功能的影响而偶尔有所变动。
$ gcc -o hello -g hello.c
$ gdb ./hello
(gdb) help
(gdb) break main (或者 b main)
(gdb) run
(gdb) next (或者 next i,不进入函数内部)
(gdb) step (或者 step i,进入函数内部)
(gdb) print argv[1]
(gdb) bt (或者backtrace, 查看程序运行到当前位置之前所有的堆栈帧情况)
/********************************************************************************/
valgrind是Linux(x86)环境的内存调试工具,可以在此工具中运行程序来检查内存使用,可以自动检测到内存泄漏及内存管理的BUG,使你的程序更加健壮。
1。valgrind安装:下载地址:,安装很简单,执行configure,make,make install
2。valgrind工具集:
memcheck:检测程序中的内存管理BUG,所有的写/读操作都会被、malloc/free都会被截获
cachegrind:cache剖析器,它模拟 CPU中的一级缓存I1,D1和L2二级缓存,能够精确地指出程序中 cache的丢失和命中。
Callgrind:同cachegrind一样,但能跟踪函数的调用返回关系,及有选择的模拟cache。
Massif:堆栈分析器,它能检测程序在堆栈中使用了多少内存,以及堆块,堆管理块和栈的大小。帮助我们减少程序对内存的使用。
Helgrind:检测使用POSIX pthread多线程同步问题。
3。valgrind命令格式:
valgrind [valgrind-options] your-prog [your-prog-options]
valgrind-options选项:
--tool=
-h --help 帮助
--version 显示版本
-q --quiet 只显示错误
-v --verbose 更多显示
-d 显示valgrind自身调试信息
--trace-children=
--child-silent-after-fork=
--track-fds=
--time-stamp=
--log-fd=
--log-file=
--log-socket=
与错误相关的选项:
--xml=
--num-callers=
--error-limit=
--error-exitcode=
--show-below-main=
--db-attach=
--db-command=
--leak-check=
--show-reachable=
其它选项不很常用。有些也不很理解,这里就省略了。
在检测前,确认使用-g选项编译你的程序,这样以便能报告错误的行数
4。valgrind错误报告例子:
memcheck
非法的读写访问,数组越界
Invalid read of size 4 at 0x40F6BBCC:
(within /usr/lib/libpng.so.2.1.0.9) by 0x40F6B804:
(within /usr/lib/libpng.so.2.1.0.9) by 0x40B07FF4: read_png_image(QImageIO *)
(kernel/qpngio. by 0x40AC751B: QImageIO::read()
(kernel/qimage.cpp:3621) Address 0xBFFFF0E0 is not stack’d, malloc’d or free’d
使用未初始化指针
Conditional jump or move depends on uninitialised value(s) at 0x402DFA94:
_IO_vfprintf (_itoa.h:49) by 0x402E8476:
_IO_printf (printf.c:36) by 0x8048472: main (tests/manuel1.c:8)
非法释放
Invalid free() at 0x4004FFDF: free (vg_clientmalloc.c:577) by 0x80484C7:
main (tests/doublefree.c:10) Address 0x3807F7B4 is 0 bytes
inside a block of size 177 free’d at 0x4004FFDF: free (vg_clientmalloc.c:577) by 0x80484C7:
main (tests/doublefree.c:10)
不适当的释放
Mismatched free() / delete / delete [] at 0x40043249: free (vg_clientfuncs.c:171) by 0x4102BB4E:
QGArray::~QGArray(void) (tools/qgarray.cpp:149) by 0x4C261C41:
PptDoc::~PptDoc(void) (include/qmemarray.h:60) by 0x4C261F0E:
PptXml::~PptXml(void) (pptxml.cc:44) Address 0x4BB292A8 is 0 bytes
inside a block of size 64 alloc’d at 0x4004318C:
operator new[](unsigned int) (vg_clientfuncs.c:152) by 0x4C21BC15: KLaola::
readSBStream(int) const (klaola.cc:314) by 0x4C21C155: KLaola::
stream(KLaola::OLENode const *) (klaola.cc:416) by 0x4C21788F:
OLEFilter::convert(QCString const &) (olefilter.cc:272)
源与目的块重叠
==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
==27492== at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
==27492== by 0x804865A: main (overlap.c:40)
缺省输出报告文件名:工具名.out.pid
Helgrind线程BUG检测工具:能够报告一些线程使用的常见问题。比如:
释放一个无效mutex、释放未加锁的mutex、释放一个被其它线程持有的mutex、销毁一个无效的或者加锁的mutex、递归加锁一个非递归锁、释放内存包含加锁的mutex、必须处理使用pthread函数失败返回错误码、当线程退出时仍持有着锁,调用pthread_cond_wait时使用未申请的mutex或一个被其它线程加锁的mutex
错误报告如下:
Thread #1 unlocked a not-locked lock at 0x7FEFFFA90 at 0x4C2408D: pthread_mutex_unlock (hg_intercepts.c:492) by 0x40073A: nearly_main (tc09_bad_unlock.c:27) by 0x40079B: main (tc09_bad_unlock.c:50) Lock at 0x7FEFFFA90 was first observed at 0x4C25D01: pthread_mutex_init (hg_intercepts.c:326) by 0x40071F: nearly_main (tc09_bad_unlock.c:23) by 0x40079B: main (tc09_bad_unlock.c:50)
比较常用的是memcheck、Helgrind工具。经常使用valgrind能使你的程序更加完美。
参考链接:http://www.cnblogs.com/wdpp/archive/2011/05/13/2386787.html