gdb使用及原理【转】

1、GDB对于基于GNU系统开发的程序员来说是最基本的东西,必须的。所以这篇学习总结中,不打算包括GDB的一般使用方法。因为这些东西必须是随手拈来的。所以也就不花时间来整理,我只把一些比较高级的应用在这里作一个整理。
 
2、在编译链接程序时需要使用"-ggdb"选项来生成可供GDB调试用的信息,否则GDB将失去作用,因此GDB和GCC联系的非常紧密。并且当-g和-O开关同时打开时,调试和优化可能会产生冲突,经常会发现所见和事实不合的情况,所以要选择性地开启优化开关。
 
3、GDB的一些使用技巧:
 1)设置断点的方法包括:函数,行号,if条件断点express,这些前面都可以跟上文件名。另外还可以设置地址断点:b *0x8048424.
 2)GDB用来分析core文件,启动格式:gdb debugme core.xyz
 3)开启core文件生成的方法是: ulimit -c unlimited
 4)在不同函数的调用栈上切换及查看当前信息:bt/frame XX/up/down/info frame/args/locals
 5)调试一正运行的进程:gdb debugme pid或者gdb debugme + attach pid + detach,类似的应用还有:strace/ltrace/truss
 6)如果某个线程/进程处于死锁状态,还可以通过gcore pid来手动生成core文件来分析当前线程/进程的状态,然后利用GDB来分析, gcore使用方法:gcore pid,注意被调试的进程会临时性停止去生成core文件
 7)查看函数的反汇编指令:disassemble fun_name
 8)汇编指令级别的单步执行:ni/si,显示当前执行的汇编指令: x/i $pc
 9)查看寄存器的内容:info registers/all-registers
 10)查看某地址开始的内容:x/num 0xYYYYYYY 查看从0xYYYYYYY开始的num个单元内容;p 输出数组内容
 11)在函数调试中途强制返回:return  <expression>;
 12)向被调试程序发送指定信号:在任意一点ctrl+C进入gdb调试命令行,然后:signal 1-15
 
4、用GDB来调试多线程程序:
 1)显示当前可调试的所有线程:info threads,GDB按照线程启动顺序重新安排了一个线程ID,这个ID是供GDB使用的
 2)在调试多线程的程序时,默认调试的是主线程,其他线程也同时处于暂停状态,如果想切换调试其他的线程,则只需要:thread id
 3)在对某一线程进行next/step执行的时候,其他线程也同时在执行,如果要限制其他线程执行,则可以使用:set scheduler-locking on
 4)对指定线程或者所有线程执行同样的操作,比如查看调用栈信息:thread apply ID1 ID2/all bt
 5)另外你也可以利用strace -p pid来显示某个线程当前的系统调用情况。或者利用gdb debugme pid来调试某个线程,但注意该方法会暂停整个进程的执行。对于多线程的程序gdb ./debugme相当于默认调试主线程,而gdb ./debugme pid则相当于默认调试pid线程。
 
5、用GDB来调试多进程程序:
 1)当fork子进程后,继续调试父进程或者调试刚产生的子进程:set follow-fork-mode parent/child,注意调试的时候其他的进程仍然在运行。
 2)如果父进程fork了多个子进程,上面的这种方法也只能跟踪调试到第一个子进程,并且不影响其他子进程的运行。
 3)如果想在调试一个进程的时候,其他进程处于暂停状态,则可以利用:set detach-on-fork off来做到
 4)利用attach来调试子进程。因为父进程fork子进程后,子进程会马上得到执行,如果恰好执行过了你要调试的地方,则来不及查询pid并且attach,所以为了支持直接attach调试,一般会在子进程的代码开始处加上一个sleep,以使得你有时间来查询pid,然后attach进入来调试。
   attach pid + stop + break XXX + continue + n + n ...+ s + s + ....
 5)利用gdb ./debugme pid都可以用来调试进程和线程,但不同的是GDB控制的范围不一样,前者不影响其他的并行单元(进程),而后则会使真个进程暂停。 
 
6、调试动态链接库函数:
 我们可能要调试动态库的函数,或者通过调试来学习动态库函数的实现。这个时候,则需要GDB包括该动态库的debug版本,否则在GDB下面只会打印:0xXXXXXX: ??
 比如包括:glibc debug version,如下是一些glibc的debug版本的下载地址:
 http://linux.maruhn.com/sec/glibc-debug.html
 
注:GDB的远端调试功能,暂时还没有接触过,现不做学习和总结.
 GDB对于多线程,多进程的调试支持并不强大,但可以利用其他专用调试器,比如TotalView:
 参考地址:http://www.totalviewtech.com/
         http://www.total-view.com.cn/
    
   
7、一些辅助的诊断及调试工具:
 1)strace:跟踪系统调用情况
 2)ltrace:跟踪动态库的调用情况
 3)mtrace,pmalloc:跟踪内存使用情况,需要嵌入代码,打印内存使用记录。
 4)Binuitls:Toolchain的工具,参考我的上一篇总结。
 5)Valgrind:非常好的内存泄露检测工具,限于i386
 6)oprofile, NPTL Trace Tool等
 7)ald:汇编语言调试器
 8)Dude:另一个运行linux上的调试器,未使用ptrace实现
 9)Linice(http://www.linice.com/)是SoftIce在Linux中的模拟软件,用于调试没有源代码的二进制文件的内核级调试器。
 10)其他
关于调试及诊断工具包括许多,估计可以写一系列的文章来说明。

其他参考资料:
0)GDB官方网站:http://www.gnu.org/software/gdb/gdb.html
1)快速参考GDB支持的所有调试命令:《GDB QUICK REFERENCE》
2)GDB的使用手册:《Debugging with gdb--The gnu Source-Level Debugger》
3)《Embedded linux prime》的第13/14/15章可以作为参考。
http://book.opensourceproject.org.cn/embedded/embeddedprime/index.html?page=opensource/0136130550/ch13lev1sec1.html
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/6_system/linux/Linuxjs/20091209/184488.html

 

***********************************************************************************************

1、GDB基本组成:
   GDB由三个部分组成:
 (1)用户接口user interface,除支持传统的CLI接口还支持mi接口(ddd等工具使用)
 (2)符号处理层symbol handling,当gdb ./debugme后GDB会读取文件的符号信息,之后的原代码,变量/函数/类型的显示都由该部分进行(everything you can do without live process)。
 (3)目标系统处理层target system handling。包括执行控制,断点设置,单步执行,堆栈分析等操作都有该部分来进行。
 
2、GDB各部分的实现:
 (1)用户接口层(CLI)的实现很显然要用到readline/history库,而图形界面mi则需要用到:GNU ncurses库。
   参考资料: http://www.gnu.org/software/ncurses/
          http://tiswww.case.edu/php/chet/readline/rltop.html
         
 (2)符号处理层则需要使用到:BFD/Opcodes库,分别用来读取分析ELF/Core文件,反汇编.
  参考资料: http://www.xfocus.net/articles/200109/265.html
     http://sourceware.org/binutils/docs/bfd/index.html#Top
     http://www.linuxselfhelp.com/gnu/bfd/html_chapter/bfd_toc.html
    
 (3)目标系统控制层:用ptrace系统调用来实现对其他进程的执行控制,检查和改变其核心映像以及寄存器等操作。
 
  
3、后端(目标系统控制层)实现:
 (1)内核在执行用户请求的系统调用之前回检查当前进程是否处于被“跟踪”状态,如果是的话内核暂停当前进程并将控制权交给调试进程,使跟踪调试进程可以查看甚至修改被调试进程的内存,寄存器等数据。而ptrace函数的作用就是告诉内核在执行子进程的系统调用之前做的动作。所有的动作都可以通过request进行传入。
 (2)设置断点原理:通过查找输入的断点和具体代码位置对应起来,并在该位置替换为一条断点指令,并且保存以前的指令,到目标程序运行到该断点处时,产生SIGTRAP信号,该信号被GDB捕获,GDB查找断点列表来确定是否命中断点。继续执行的时候则会把保存的指令重新放回并执行。n/s/ni/si/finish/uitil也会自动设置断点。
 (3)内核传递给被调试进程所有的信号,都会先传递给GDB再由gdb采取定义的动作来和被调试进程之间进行相互协调操作。gdb暂停目标程序运行的方法是向其发送SIGSTOP信号,GDB对于随机信号(非GDB产生的)的处理包括,可以通过handle signals命令来预定义
 
4、 ptrace函数简单介绍:long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);其中第一个参数代表告诉给kernel要做的动作。
 PTRACE_ME:设置自己的被跟踪标志,在被调试进程中使用。
 PTRACE_PEEKUSER:可以得到系统调用号及参数信息
 PTRACE_CONT:使被跟踪进程继续执行
 PRACE_GETREGS:一次性得到所有寄存器相关的值,提供输出参数
 PTRACE_POKEDATA:可用来改变子进程中变量的值
 PTRACE_SINGLESTEP:会使内核在子进程的每一条指令执行前先将其阻塞,然后将控制权交给父进程
 PTRACE_ATTACH:向运行着的子进程置上跟踪标志为。
 PTRACE_DETACH:和上面的行为相反。
很多工具strace/ltrace/stuss等工具都用到了ptrace,学习ptrace的最好的资料是这些工具的原代码和kernel相关代码。
 
其他参考资料:
1)Ptrace相关资料: http://linuxgazette.net/issue81/sandeep.html
       http://blog.chinaunix.net/u/19651/showart_362901.html //玩转ptrace系列
       http://blog.chinaunix.net/u/19651/showart_362921.html
      
2)GDB的实现说明: 《gdb Internals》

你可能感兴趣的:(gdb使用及原理【转】)