【问题标题】:main() calls main() recursively - gdb backtrace does not show multiple main() frames - Why?main() 递归调用 main() - gdb 回溯不显示多个 main() 帧 - 为什么?
【发布时间】:2012-12-19 20:08:34
【问题描述】:

我在main() 内调用main(),递归调用10 次。现在在使用gdb (bt/backtrace) 进行调试时,我看不到main() 的多个帧。为什么?

#include<stdio.h>

int main(){

     static int i;
     int num=100;

     if(i>10)
       return 0;
     else {
       i++;
       num++;
       main();
       printf("\n%d",num); 
     }
 }

【问题讨论】:

    标签: c gdb


    【解决方案1】:

    这是记录在案的 gdbis (supposed to be) configurable 的行为。

    当我使用gcc 4.7.2 (-O3) 编译您的代码时,我得到以下程序集:

    _main:
    LFB1:
            movl    _i.2134(%rip), %eax
            cmpl    $10, %eax
            jle     L6
            xorl    %eax, %eax
            ret
    L6:
            addl    $1, %eax
            pushq   %rdx
    LCFI0:
            movl    %eax, _i.2134(%rip)
            xorl    %eax, %eax
            call    _main                    ; <=== recursive call
            popq    %rcx
    LCFI1:
            movl    $101, %esi
            xorl    %eax, %eax
            leaq    LC0(%rip), %rdi
            jmp     _printf
    LFE1:
    

    这反驳了递归调用被优化掉的假设。

    现在,如果我将二进制文件加载到gdb 并在main() 上设置断点,它会被反复命中。当我检查寄存器时,%rsp 每次调用都会递减,因此显然每个main() 都有相关的堆栈帧。

    尽管如此,bt 只显示一个帧:

    (gdb) bt
    #0  0x0000000100000f50 in main ()
    

    (在这种情况下,我知道有三个 main() 帧,而不仅仅是一个。)

    因此我得出结论,这与gdb 本身有关。

    经过进一步调查,发现这种行为是documented

    大多数程序都有一个标准的用户入口点——系统库和启动代码转换为用户代码的地方。对于 C,这是main。当gdb 在回溯中找到入口函数时,它将终止回溯,以避免追踪到高度系统特定(通常是无趣)的代码。

    当我在gdb 中设置以下内容时:

    set backtrace past-main on
    set backtrace past-entry on
    

    它开始显示两个main() 帧。由于某种原因,它仍然没有深入。

    【讨论】:

      【解决方案2】:

      您的问题与您为此使用main这一事实无关。

      一个简单的递归程序,正如您最终愿意在评论中向我们展示的那样,编译器可以很容易地通过不进行递归来优化。在您的情况下,它比通常更简单,因为您有一个预定义的递归深度10。这可以很容易地被编译器展开为静态代码。

      【讨论】:

      • 我尝试在编译过程中禁用优化,但在调试过程中再次仅显示一帧。
      • hum .. gdb disass 确实显示了 callq 0x40050c &lt;main&gt; 指令。难道是gdb故意隐藏main()上面的函数调用?
      • 查看汇编程序,对于 gcc,例如这是选项 -S 而不是 -c。首先使用另一个函数名进行递归,看看它是否来自那里。
      • 默认情况下 gdb 在 main 之前隐藏 fun 调用,但如果我们愿意,我们也可以让 gdb 显示这些 fun 调用。但是在这种情况下,我希望看到调用 main() 的多个 main() 框架并不有趣。
      • gdb 的堆栈展开代码可能会在找到main() 中的第一帧后停止。尝试将您的代码移动到另一个函数中,禁用优化并查看是否可以修复它。
      【解决方案3】:
      Here is the Answer for my own Question.
      (gdb) set backtrace past-main on
      (gdb) b main
      Breakpoint 1 at 0x4004b0: file recursion.c, line 5.
      (gdb) run
      Starting program: /nobackup/arjprasa/C/gdb/a.out
      
      Breakpoint 1, main () at recursion.c:5
      5               int num=100;
      (gdb) c
      Continuing.
      
      Breakpoint 1, main () at recursion.c:5
      5               int num=100;
      (gdb) bt
      #0  main () at recursion.c:5
      #1  0x00000000004004df in main () at recursion.c:12
      #2  0x00000034d091c4cb in __libc_start_main () from /lib64/tls/libc.so.6
      #3  0x000000000040041a in _start ()
      (gdb) c
      Continuing.
      
      Breakpoint 1, main () at recursion.c:5
      5               int num=100;
      (gdb) bt
      #0  main () at recursion.c:5
      #1  0x00000000004004df in main () at recursion.c:12
      #2  0x00000000004004df in main () at recursion.c:12
      #3  0x00000034d091c4cb in __libc_start_main () from /lib64/tls/libc.so.6
      #4  0x000000000040041a in _start ()
      (gdb) c
      Continuing.
      
      Breakpoint 1, main () at recursion.c:5
      5               int num=100;
      (gdb) bt
      #0  main () at recursion.c:5
      #1  0x00000000004004df in main () at recursion.c:12
      #2  0x00000000004004df in main () at recursion.c:12
      #3  0x00000000004004df in main () at recursion.c:12
      #4  0x00000034d091c4cb in __libc_start_main () from /lib64/tls/libc.so.6
      #5  0x000000000040041a in _start ()
      (gdb)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-02
        • 1970-01-01
        • 2016-09-15
        • 1970-01-01
        • 1970-01-01
        • 2011-07-16
        • 2014-10-09
        • 2011-09-24
        相关资源
        最近更新 更多