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

标签 c debugging gdb local-variables callstack

我有一段 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 被列出。

为什么会有这样的行为?

在函数内部声明的任何非静态变量,都应该在执行期间放在调用堆栈上,并且应该在完整的回溯中列出,不是吗?但是,回溯中缺少一些局部变量。有人可以解释一下吗?

最佳答案

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

  • 函数本地对象只能在处理器寄存器内创建和使用。当有足够的处理器寄存器来保存函数的本地对象或其中一些时,将它们写入内存是没有意义的,因此优化的代码不会这样做。
  • 优化可能会完全消除局部对象或将其折叠为其他值。例如,给定 void foo(int x) { int t = 10;条(x+2*t); ... },编译器可能只是生成将立即数 20 添加到 x 的代码,结果是 10 或 t 的任何其他实例都不会出现出现在堆栈中、寄存器中,甚至指令的立即操作数中。它根本不存在于生成的代码中,因为不需要它。
  • 函数本地对象可能会在函数代码期间的某一时刻出现在堆栈上,但在其他时刻不会出现。而且它出现的地方可能会因代码中的不同地方而有所不同。例如,使用 { int t = x*x; … 酒吧(t); … t = x/3; … 酒吧(t); ... },编译器可能决定将 t 的第一个值存储在堆栈上的一个位置。但是分配给 t 的第二个值实际上是一个单独的生命周期,编译器可能会将其存储在堆栈上的另一个位置(或者根据上述情况根本不存储)。在良好的实现中,调试器可能会意识到这些不同的位置,并在程序计数器位于匹配的代码部分时显示存储的 t 值。而且,虽然程序计数器不在匹配的代码部分中,但 t 实际上可能不存在,并且调试器可以报告它在此时已被优化。

关于c - 为什么使用GDB检查时,某些局部变量没有列在相应的堆栈帧中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52190506/

相关文章:

c++ - 是否可以在 cdb/windbg 中进行语法高亮显示?

c - AVR 8 位定时器 - 当比较值不适合寄存器时该怎么办?

c# - Visual Studio 无法附加到 Unity,为什么?

python - lldb 中用于 STL 的 pretty-print

windows - 查找非官方命令行开关

iphone - 如何在 Xcode 中使用带有嵌套静态库的调试器?

gdb - SPARC 和 HP-UX 中的叶函数

保存宏定义的C字符串

c - 如何捕获 SIGSEGV 并写入它出现在哪一行

在 Cuda 内核中调用 Opencv 函数