11.调试优化的代码
几乎所有的编译器都支持优化。禁用掉优化后,编译器生成的汇编代码通过简单的方式直接对应到你的源代码。
由于编译器提供了更强大的优化能力,生成的汇编代码与源代码变得不尽相对应了。
由编译器生成的调试信息的帮助下,gdb可以从正在运行的程序映射出源代码的结构。
gdb在禁用掉优化后更准确。如果你可以使用不优化来重新编译,很容易在调试过程中跟踪你的程序的过程。但是,有许多情况下,您可能需要调试优化后的版本。
当你调试用'-g -O'编译的程序,请记住,优化已经重新安排你的代码,调试器会显示那里存在什么。不要对执行路径没有准确符合源文件感到奇怪。
一个极端的例子:如果你定义一个变量,但从来没有使用它,GDB从来没有看到这个变量,因为编译器优化它的存在。
某些情况下只用'-g'选项,没有使用'-g -O'来编译,特别是与指令调度相关的机器。如果有疑问,'-g'单独编译,如果解决了这个问题,请报告给我们一个错误
(包括一个测试案例!)。查看变量,从中获取调试优化代码的更多信息。
12.C宏预处理
In addition to source files, macros can be defined on the compilation command line using the -Dname=value syntax. For macros defined in such a way, gdb displays the location of their definition as line zero of the source file submitted to the compiler.
(gdb) info macro __STDC__
Defined at /home/jimb/gdb/macros/play/sample.c:0
-D__STDC__=1
(gdb)
__STDC__是预定义宏。当它被定义后,编译器将按照ansic标准来编译你的c程序。
ANSIC标准定义了以下6种可供C语言使用的预定义宏:
__LINE__ 在源代码中插入当前源代码行号
__FILE__ 在源代码中插入当前源代码文件名
__DATE__ 在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕
__TIME__ 在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕
__STDC__ 当要求程序严格遵循ANSIC标准时该标识符被赋值为1。
__cplusplus
标识符__LINE__和__FILE__通常用来调试程序;
标识符__DATE__和__TIME__通常用来在编译后的程序中加入一个时间标志,以区分程序的不同版本;
当要求程序严格遵循ANSIC标准时,标识符__STDC__就会被赋值为1;
当用C++编译程序编译时,标识符__cplusplus就会被定义。
13.跟踪点
Using gdb's trace and collect commands, you can specify locations in the program, called tracepoints, and arbitrary expressions to evaluate when those
tracepoints are reached. Later, using the tfind command, you can examine the values those expressions had when the program hit the tracepoints. The
expressions may also denote objects in memory—structures or arrays, for example—whose values gdb should record; while visiting a particular tracepoint,
you may inspect those objects as if they were in memory at that moment. However, because gdb records these values without interacting with you, it can do
so quickly and unobtrusively, hopefully not disturbing the program's behavior.
使用GDB的trace或者collect命令,可以指定程序里的位置,称为跟踪点,和在跟踪点执行到的时候要计算机的任意表达式。稍后在跟踪点执行到的时候,可以用tfind命令来
查看表达式的值
Using the Collected Data
In addition to letting you scan through the trace buffer manually, these commands make it easy to construct gdb scripts that scan through the trace buffer and print out whatever collected data you are interested in. Thus, if we want to examine the PC, FP, and SP registers from each trace frame in the buffer, we can say this:
(gdb) tfind start
(gdb) while ($trace_frame != -1)
> printf "Frame %d, PC = %08X, SP = %08X, FP = %08X\n", \
$trace_frame, $pc, $sp, $fp
> tfind
> end
Frame 0, PC = 0020DC64, SP = 0030BF3C, FP = 0030BF44
Frame 1, PC = 0020DC6C, SP = 0030BF38, FP = 0030BF44
Frame 2, PC = 0020DC70, SP = 0030BF34, FP = 0030BF44
Frame 3, PC = 0020DC74, SP = 0030BF30, FP = 0030BF44
Frame 4, PC = 0020DC78, SP = 0030BF2C, FP = 0030BF44
Frame 5, PC = 0020DC7C, SP = 0030BF28, FP = 0030BF44
Frame 6, PC = 0020DC80, SP = 0030BF24, FP = 0030BF44
Frame 7, PC = 0020DC84, SP = 0030BF20, FP = 0030BF44
Frame 8, PC = 0020DC88, SP = 0030BF1C, FP = 0030BF44
Frame 9, PC = 0020DC8E, SP = 0030BF18, FP = 0030BF44
Frame 10, PC = 00203F6C, SP = 0030BE3C, FP = 0030BF14
Or, if we want to examine the variable X at each source line in the buffer:
(gdb) tfind start
(gdb) while ($trace_frame != -1)
> printf "Frame %d, X == %d\n", $trace_frame, X
> tfind line
> end
Frame 0, X = 1
Frame 7, X = 2
Frame 13, X = 255