调试符号与调试器
调试符号
调试符号由编译器生成,与相关的机器代码、全局数据对象等一同产生。链接器会收集并组织这些符号,将他们写入可执行文件的调试部分,或存储到一个单独文件中。
概览
-
全局函数和变量
-
源文件和行信息
为了优化程序性能,编译器可能会对源代码进行位移,情况可能变得复杂。由于宏和内联函数的存在,源代码的行信息可能与实际执行的指令地址并不连续或者交织在在其他源代码行中。 -
类型信息
-
静态函数和局部变量
-
架构和编译器以来信息
某些调试功能与特定的架构和编译器有关
调试的角度查看
使用下面命令生成汇编文件
g++ -g -S test.cpp
解码文件,查看调试符号
readelf --debug-dump test.o
调试器
一般是3个模块:用户界面、符号管理、目标管理
用户界面
gui或者命令行
符号管理
提供调试目标的调试符号,如果调试符号错误或者不完整,那么调试器将无法正常工作
目标管理模块
目标管理模块在系统和硬件层面处理被调试的进程。
在linux中,内核提供了一个系统调用ptrace函数,允许进程查询和控制另一个进程的执行(strace就是通过ptrace)。
跟踪gdb的调用
strace -o/home/self/ptrace.log -rptrace gdb app.out
cat /home/self/ptrace.log
注意事项
特殊的调试符号
- 已知某个变量的具体类型但无法打印该变量
无法理解变量主要是缺乏对应变量的调试符号
解决办法:
1、重新编译并生成包含我们所需的调试符号的新库文件;
2、使用Python扩展GDB
core文件
是进程内存映像在某一时刻的快照。
常见的生成core的原因:
- 段错误。程序正在试图访问未分配给进程的地址空间或者被保护免于特定的操作(读、写或者是运行)的内存。
- 总线错误。一般由访问未对齐的数据导致
- 非法地址。比如程序下一个运行指令不数据CPU指令集时,访问函数指针地址在堆段而不是文本段
- 未处理异常。c++程序抛出异常但没有代码捕获。
- 浮点数异常。除0,溢出
- 栈溢出。程序没有足够的栈空间来存储函数调用和本地变量。
core文件不完整
系统设置了仅允许部分转储;core文件超出了最大的核心转出文件大小的设置;磁盘空间不足等
参考:高效C/C++调试