2013-02-06 225 views
4

如果我不使用-O參數進行編譯,附加的代碼就能正常工作。但是,如果我使用-O2進行編譯,則無法打印出追蹤中的中間函數。最初,我認爲所有的東西都被優化了,所以我給printf調用了每個例程來排除這個問題。它仍然有相同的輸出。如何讓gcc的__builtin_frame_address與-O2一起使用?

預期結果:GCC -rdynamic -g test.c的-o測試-L/USR /本地/ lib中-lexecinfo

./test
DEPTH = 11
./test:F0(0x40d952)
./test:F1(0x40da0e)
./test:F2(0x40da1e)
./test:F3(0x40da2e)
./test:F4(0x40da3e)
./test:F5(0x40da4e)
./test:f6(0x40da5e)
./test:F7(0x40da6e)
./test:主(0x40da89)
./test:_start(0x40080e)

意外結果:GCC -O2 -rdynamic -g test.c的-o測試-L/USR /本地/ lib中-lexecinfo

./test
DEPTH = 2
./test:F0(0x40794b)

#include <stdio.h> 
#include <dlfcn.h> 

#define CALLSTACK_MAXLEN 64 

// 
// We use this macro instead of a for loop in backtrace() because the 
// documentation says that you have to use a constant, not a variable. 
// 
#define BT(X) {               \ 
     case X:               \ 
       if (!__builtin_frame_address(X)) {      \ 
         return X;          \ 
       }              \ 
                     \ 
       trace[X].address = __builtin_return_address(X);   \ 
       break;             \ 
} 

struct call { 
     const void *address; 
     const char *function; 
     const char *object; 
}; 

struct call trace[CALLSTACK_MAXLEN]; 

int 
backtrace(int depth) { 
     int   i; 
     Dl_info  dlinfo; 

     for (i = 0; i < depth; i++) { 
       switch (i) { 
         BT( 0); 
         BT( 1); 
         BT( 2); 
         BT( 3); 
         BT( 4); 
         BT( 5); 
         BT( 6); 
         BT( 7); 
         BT( 8); 
         BT( 9); 
         BT(10); 
         BT(11); 
         BT(12); 
         BT(13); 
         BT(14); 
         BT(15); 
         BT(16); 
         BT(17); 
         BT(18); 
         BT(19); 
         default: return i; 
       } 

       if (dladdr(trace[i].address, &dlinfo) != 0) { 
         trace[i].function = dlinfo.dli_sname; 
         trace[i].object = dlinfo.dli_fname; 
       } 
     } 

     return i; 
} 

void 
f0() { 
     int i; 
     int depth; 

     depth = backtrace(CALLSTACK_MAXLEN); 
     printf("DEPTH=%d\n", depth); 

     for (i = 0 ; trace[i].object != NULL; i++) { 
       printf("%s: %s (%p)\n", trace[i].object, trace[i].function, trace[i].address); 
     } 
} 

void f1() { f0(); } 
void f2() { f1(); } 
void f3() { f2(); } 
void f4() { f3(); } 
void f5() { f4(); } 
void f6() { f5(); } 
void f7() { f6(); } 

int main(int argc, char **argv) { 
     f7(); 
     return 0; 
} 

回答

5

原因是尾遞歸優化。即使內聯被關閉,尾遞歸調用的變化跳,像

f6: 
.LFB29: 
    .cfi_startproc 
    xorl %eax, %eax 
    jmp f5 

所以你必須:

  1. 排除內聯

    void __attribute__ ((noinline)) f1() { f0(); } 
    void __attribute__ ((noinline)) f2() { f1(); } 
    void __attribute__ ((noinline)) f3() { f2(); } 
    void __attribute__ ((noinline)) f4() { f3(); } 
    void __attribute__ ((noinline)) f5() { f4(); } 
    void __attribute__ ((noinline)) f6() { f5(); } 
    void __attribute__ ((noinline)) f7() { f6(); } 
    
  2. 編譯-fno-optimize-同級呼叫和保留幀指針

    gcc -O2 -rdynamic -g -o bfa bfa.c -ldl -fno-opti邁茲同胞-調用-fno-省略幀指針

輸出是:

$ ./bfa 
DEPTH=10 
./bfa: f0 (0x400f23) 
./bfa: f1 (0x400f8b) 
./bfa: f2 (0x400f9b) 
./bfa: f3 (0x400fab) 
./bfa: f4 (0x400fbb) 
./bfa: f5 (0x400fcb) 
./bfa: f6 (0x400fdb) 
./bfa: f7 (0x400feb) 
./bfa: main (0x400ffb) 
/lib/libc.so.6: __libc_start_main (0x7fdfbae51c4d) 

如所希望的。

+0

感謝您的解釋和新的gcc params(-f)的使用。 –

相關問題