首页 > 其他 > 详细

Debug相关的边边角角

时间:2014-03-03 17:17:57      阅读:361      评论:0      收藏:0      [点我收藏+]

大学四年,自诩努力上进,看了不少专业书,一到实习工作,马上显示出菜鸟的本性,连基本的程序调试都不会。大学时程序调试不是用printf输出运行时变量内容,就是肉眼看代码,小的算法Demo和功能代码,这样子调试就当是卖萌了;工作时一个服务器程序近万个源代码文件,客户端崩溃只有dump文件和日志信息反馈到服务器上,卖萌式的debug已经无法满足需求了。这段时间抽空看了一些关于debug的文档资料,加上工作半年来的一些实践,在这里小小的总结一下,大部分内容都是自己在阅读文档和实践中的一些想法,抛砖引玉,一些错误和不足,欢迎大家指出。

l  程序的debug信息

对于编译器来说(比如gcc),如果直接对程序进行编译,不保留编译时的信息,则编译后的汇编代码(当然最后是符合操作系统支持的格式的binary可执行文件)根本完全不知道源代码的任何信息,不知道汇编代码与源代码之间的关系,比如源代码中的函数名称、变量的类型和名称等。比如在使用GDB进行程序调试的时候,step(单步调试)命令默认是不进入共享库函数(.lib,.so,.a等文件)的,因为共享库函数都是不带调试信息的二进制代码文件,当然共享库文件一般来说也都是已经调试好了的没有bug的文件。

在Unix/Linux环境下,使用gcc的-g选项会将调试信息编码输出到编译后的可执行目标代码文件中,这些调试信息一般来说包括:

  1. 自定义的变量类型信息(struct、class等)
  2. 函数的名称信息和入口地址(运行时虚拟内存地址)
  3. 全局变量的名称的地址
  4. 参数,局部变量的名字及其在堆栈中的偏移量

上面这些信息是肯定的,我认为,应该还包括源代码和编译后的汇编代码之间的对应关系,在现在的编译技术下,汇编代码和源代码之间的一一对应关系完全不可能从汇编代码倒推出来了。

用一个简单的31行的C语言Demo程序文件做试验,使用同样的编译优化级别,不使用-g选项编译出来的可执行目标代码文件的大小是6.85KB,使用-g选项后编译出的大小为8.79KB,而源代码文件的大小为404Byte,可见在使用-g选项后在可执行目标代码文件中调试信息所占文件大小的比重。当将源文件从当前目录下删除后,在调试目标代码文件时使用list指令,将出现No such file or directory的提示,可见在目标代码文件中保存的是源文件的路径信息。

在Windows环境中,调试信息并不被编码到最后的可执行目标文件中,而是通过一个和最后生成的.exe同名的.pdb文件来保存。在使用windbg或者VS来调试Windows下的程序的时候,需要设置和加载相应的.pdb文件,否则给出的调试信息很可能就是错误的。相关的信息可以查看:http://www.wintellect.com/blogs/jrobbins/pdb-files-what-every-developer-must-know(PDB Files: What Every Developer Must Know)。

 另外还有两点需要说明一下:

  1. 现在的编译器都可以设置代码优化等级,编译器会使用一些我这种菜鸟连做梦都想不到的高级的方式来优化代码,在优化过程中,编译器会剔除很多局部变量,合并部分代码,调整一些代码的执行顺序,甚至可能删除代码。在高优化等级下调试代码的时候,很多程序中很多局部变量的值可能会看不到,一些断点可能就完全跟不到。所以,如果需要调试程序,还是将代码的优化等级调至最低比较妥当,在gcc和VS中都可以进行相应的设置。当然,在最后发布程序时调至高等级的优化级别就可以了。
  2. 在关于pdb文件介绍的上述博文中,提到了调试信息除了上面提到的4点之外,还包括了 Frame Pointer Omission 数据(FPO)。FPO的直译就是”省略帧指引”,这个东西比较高端的样子,查了一下资料,大概意思是:在一般函数调用的时候,会在程序的当前栈中保存调用者函数的栈帧指针(寄存器ebp的值),然后将当前栈指针esp保存到ebp中,然后再将栈指针esp减去一定值,用以分配函数的局部变量的内存地址,最后在函数退出的时候,会围绕寄存在ebp中的值进行一系列的弹栈恢复到原调用者函数的执行环境。EPO的使用是去除了这一系列过程中的保存栈帧指针ebp和恢复的过程,直接通过栈指针esp的操作来进行局部变量的地址分配和函数参数的传递,这样就将寄存器ebp解放了出来,可以用来保存经常使用的变量,减少了其保存和恢复的过程,提高了程序的性能。一般来说,EPO只有x86处理器支持。

l  一些Tips

  1. 远程调试。VS和GDB都支持远程调试,GDB还没有试过,VS支持使用TCP/IP和PIPE两种方式来连接远程的Windows虚拟机服务器,连接并Attach上在虚拟机上运行的服务器进程,这时你在本地使用VS打开的服务器代码上进行打断点,就和调试本地代码一样简单和轻松。不得不说,VS真是一个伟大的开发工具。
  2. 在GDB中,有很多指令可以用来查看代码的汇编和堆栈信息。Info frame可以查看当前栈帧信息,dissassemle <function name>指令可以将内存中对应函数的汇编代码dump出来,一遍的程序调试用不着看汇编代码找bug这么高端的东西,但是可以使用这两个指令结合《深入理解计算机系统》(《Computer System A Programmer’s Perspective》)一书的第三章研究源代码和汇编之间的关系。
  3. 学习GDB推荐网上的一个近30页的文档《Linux下GDB教程》,具体和指令相关的东西自己多用用就熟悉了,最常用的就是break(b,打断点),Print(p,输出变量),Continue(c,继续执行),我自己也需要多用用。

 

整篇就是自己的读书笔记和一些理解,有什么错误,请大家指出。邮箱:wangjian_pg@sohu.com。

Debug相关的边边角角,布布扣,bubuko.com

Debug相关的边边角角

原文:http://www.cnblogs.com/wj-19910302/p/3577440.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!