http://www.cnblogs.com/moodlxs/archive/2010/03/30/2345401.html 类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 <signal> <keywords...> 在GDB中定义一个信号处理。信号<signal>可以以SIG开头或不以SIG开头,可以用定义一个要处理信号的范围(如:SIGIO-SIGKILL,表示处理从SIGIO信号到SIGKILL的信号,其中包括SIGIO,SIGIOT,SIGKILL三个信号),也可以使用关键字all来标明要处理所有的信号。一旦被调试的程序接收到信号,运行程序马上会被GDB停住,以供调试。其<keywords>可以是以下几种关键字的一个或多个。 nostop 不停止运行,只输出显示信号,stop:停住程序,print显示一条信息,noprint当被调试的程序收到信号时,GDB不会告诉你收到信号的信息,noignore当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程序会处理。nopass,ignore当被调试的程序收到信号时,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子命令进行调试了。 语法: -a ProcessID -c CommandFile -d NestingDepth -E DebugEnvironment -p oldpath=newpath:...| pathfile -F 可以用来关闭缓慢读入模式并使 dbx 命令在启动时就读入全部符号。缺省情况下,缓慢读入模式是打开的:它在 dbx 会话初始化时读入需要的符号表信息。。在该模式下,dbx 将不会读入那些符号信息尚未被读入的变量和类型。因此,诸如 whereis i 等命令并不列出在所有函数中的变量 i 的全部实例。 -I Directory 文件编译时该源文件所在的目录。只有编译器设置了对象中的源路径时才能搜索目录。 -r 立即运行对象文件。如果它成功结束,dbx 调试程序将会退出。否则,将会进入调试程序并报告中断的原因。 注意:除非指定了 -r,dbx 命令将会提示用户并等待命令输入。 -x 防止 dbx 命令跳过来自于 FORTRAN 源代码的 _(下划线)字符。该符号允许 dbx 在符号之间区别哪些除了下划线以外都是相同的,例如 xxx 和xxx_。 下面是一些dbx的常用子命令:
(dbx)run——运行可执行二进制文件 Example1: $cc -g -o test test.c 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安装:下载地址:www.,安装很简单,执行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=<name> [default=memcheck]选择工具 -h --help 帮助 --version 显示版本 -q --quiet 只显示错误 -v --verbose 更多显示 -d 显示valgrind自身调试信息 --trace-children=<yes|no> [default: no]跟踪子进程通过exec系统调用,缺省是no --child-silent-after-fork=<yes|no> [default: no] ?? --track-fds=<yes|no> [default: no]退出时显示所有打开的文件描述符列表 --time-stamp=<yes|no> [default: no] 运行加时间戳 --log-fd=<number> [default: 2, stderr]输出LOG到描述符文件 --log-file=<filename>指定日志文件 --log-socket=<ip-address:port-number>日志通过SOCKET输出到指定IP端口 与错误相关的选项: --xml=<yes|no> [default: no]将信息以xml格式输出,只有memcheck可用 --num-callers=<number> [default: 12]缺省valgrind显示12级函数调用帮助识别程序定位 --error-limit=<yes|no> [default: yes]如果错误总数超过1千万,或不同的错误超过1000,则停止显示新错误 --error-exitcode=<number> [default: 0]指定在运行时检查到错误时返回的错误码 --show-below-main=<yes|no> [default: no]缺省不显示栈跟踪报告在main下层的函数错误 --db-attach=<yes|no> [default: no]设置为yes时valgrind会暂停提示是否转入调试工具 --db-command=<command> [default: gdb -nw %f %p]指定调试工具缺省为gdb --leak-check=<no|summary|yes|full> [default: summary]内存检测时,给出详细的内存泄漏信息 --show-reachable=<yes|no> [default: no]内存检测时显示不可达指针 其它选项不很常用。有些也不很理解,这里就省略了。 在检测前,确认使用-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能使你的程序更加完美。 |
|