为什么在使用GDB检查时,某些局部变量未在相应的堆栈帧中列出?

问题描述 投票:-2回答:1

我在C中有一段代码,如下所示 -

在.c文件中 -

1    custom_data_type2 myFunction1(custom_data_type1 a, custom_data_type2 b)
2    {
3        int c=foo();
4        custom_data_type3 t;
5        check_for_ir_path();
6        ...
7        ...
8    }
9
10    custom_data_type4 myFunction2(custom_data_type3 c, const void* d)
11    {
12        custom_data_type4 e;
13        struct custom_data_type5 f;
14        check_for_ir_path();
15        ...
16        temp = myFunction1(...);
17        return temp;
18    }

在头文件中 -

1    void CRASH_DUMP(int *i)
2     __attribute__((noinline));
3    
4    #define INTRPT_FORCE_DUMMY_STACK    3
5    
6    #define check_for_ir_path() { \
7        if (checkfunc1() && !checkfunc2()) { \
8            int sv = INTRPT_FORCE_DUMMY_STACK; \
9            ...
10            CRASH_DUMP(&sv);\
11        }\
12    }\

在未知的情况下,发生了崩溃。在使用GDB处理核心转储后,我们得到如下的调用堆栈:

#0  0x00007ffa589d9619 in myFunction1 [...] 
(custom_data_type1=0x8080808080808080, custom_data_type2=0x7ff9d77f76b8) at ../xxx/yyy/zzz.c:5

        sv = 32761

        t = <optimized out>



#1  0x00007ffa589d8f91 in myFunction2 [...]

(custom_data_type3=<optimized out>, d=0x7ff9d77f7748) at ../xxx/yyy/zzz.c:16

        sv = 167937677

        f = {

          ...

        }

如果你看到函数,myFunction1有三个局部变量 - ctsv(定义为宏定义的一部分)。但是,在回溯中,在第0帧中,我们只看到两个局部变量 - tsv。我没有看到变量c被列出。

同样如此,在函数myFunction2中,有三个局部变量 - efsv(定义为宏定义的一部分)。但是,从回溯中,在第1帧中,我们只看到两个局部变量 - fsv。我没有看到变量e被列出。

为什么这样的行为?

在函数内声明的任何非静态变量应该在执行期间放在callstack上,并且应该已经在backtrace中列出,不是吗?但是,回溯中缺少一些局部变量。有人可以提供解释吗?

c debugging gdb local-variables callstack
1个回答
-1
投票

C函数本地的对象通常不会出现在堆栈中,因为编译期间的优化通常使得不必在堆栈上存储对象。通常,虽然C抽象机器的实现可以被视为将对象存储到堆栈上的函数的本地,但是在编译和优化之后在真实处理器上的实际实现可能是非常不同的。特别是:

  • 可以创建函数本地的对象并仅在处理器寄存器内使用。当有足够的处理器寄存器来保存函数的本地对象或其中一些时,将它们写入内存是没有意义的,因此优化的代码不会这样做。
  • 优化可以完全消除本地对象或将其折叠为其他值。例如,给定void foo(int x) { int t = 10; bar(x+2*t); … },编译器可能只生成向x添加立即值20的代码,结果是,任何10或t的任何其他实例都不会出现在堆栈,寄存器或甚至是直接操作数中。一个指令。它在生成的代码中根本不存在,因为不需要它。
  • 函数本地的对象可能在函数代码期间的某一点出现在堆栈中,但在其他函数处则不会出现。并且它出现的位置可能因代码中的不同位置而异。例如,使用{ int t = x*x; … bar(t); … t = x/3; … bar(t); … },编译器可能决定将t的第一个值存储在堆栈中的一个位置。但是,分配给t的第二个值实际上是一个单独的生命周期,编译器可能会将其存储在堆栈中的另一个位置(或根本不存在)。在一个好的实现中,调试器可能知道这些不同的位置并显示t的存储值,而程序计数器在代码的匹配部分中。并且,虽然程序计数器不在匹配的代码段中,但t可能实际上不存在,并且调试器可以报告它在那时被优化。
© www.soinside.com 2019 - 2024. All rights reserved.