在下面的示例代码中,我们总共有四个在不同条件下的中止调用,但是当我们使用优化标志(-O3)编译时,我们只能看到一个中止调用的调试信息。因此,在这四个中止调用中发生崩溃时,gdb总是给出具有调试信息的调用。
#include <stdio.h>
#include <stdlib.h>
void level_aa(int a)
{
if (a == 0)
abort();
if (a == 1)
abort();
if (a == 2)
abort();
abort();
}
int main(int argc,char *argv[])
{ int D;
D = atoi(argv[1]);
printf(" Value = %d", D);
level_aa(D);
return 0;
}
将上面的代码与优化标志(-O3)组合,并使用gdb运行
>gcc -g -O3 abort_crash.c -o abort
>gdb ./abort
(gdb)run 1
(gdb) bt
#0 0x00007ffff7ab2945 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00007ffff7ab3f21 in *__GI_abort () at abort.c:92
#2 0x0000000000400634 in level_aa (a=<optimized out>) at abort_crash.c:13
#3 main (argc=<optimized out>, argv=<optimized out>) at abort_crash.c:20
(gdb)
如果我们观察第2帧(#2),崩溃实际上发生在第9行,但gdb显示第13行。我可以理解这是因为源代码的优化。因为如果它被多次使用,那么gdb backtrace中显示的行号可能不正确。是否可以通过不查看源代码来知道中止调用是否被多次使用?如果我们知道调用不止一次被使用,并且也经过了优化,那么我们可以打印一条警告消息。我们是工具(内部使用gdb)提供者,除了用户的转储文件之外,我们看不到用户的源代码。
非常感谢你的帮助。。!!
最佳答案
不,这不可能。特别是在您给出的所有路径导致调用abort
的示例中,GCC很可能会放弃对a
的所有检查,而只生成对abort
的一个调用。
据我所知,DWARF中的行表信息(将地址映射到源文件和行号)没有任何工具可以使行号成为特定的地址映射,以某些DWARF表达式为条件。因此,据我所知,一个地址只能代表一个源行,因此GCC必须为每个地址选择一个行号。
在您的示例中,GCC选择了第13行,与前面的所有代码一样,这似乎是一个明智的选择,检查a
确实是多余的。
获得更具体的回溯跟踪的唯一方法是编译而不进行优化,或者使用比GDB更强大的工具。