C/C++ call stack traces

2008/5/9

這兩天研究了一下C/C++ call stack traces,遂寫了個小程序來輸出一下call stack,該程序比較簡單,只能輸出調用棧上的函數名稱,至於複雜點的輸出請看下面2008/5/10處的修改 。該程序用到了Dbghelp.dll 相信各位達人都知道Dbghelp,這裏就不多說了。採用__cdecl 堆棧調用標準,在VC++ 6.0下編譯通過,下面貼出代碼。



#include  < stdio.h >
#include 
< windows.h >
#include 
< Dbghelp.h >

#define  JMP_INSTR 0xE9
#define  SYM_BUFF_SIZE 512
#pragma comment(lib,"imagehlp.lib")

static  BYTE g_stSymbol [ SYM_BUFF_SIZE ] ;

void  CallStack( void )
{
    
int  retAddrs;
    
int  saved_ebp;
    
int  callFuncAddrs;
    HANDLE hCurProcess;

    
// Fill the IMAGEHLP_SYMBOL struct
    PIMAGEHLP_SYMBOL pSym  =  (PIMAGEHLP_SYMBOL) & g_stSymbol ;
    FillMemory ( pSym , NULL , SYM_BUFF_SIZE ) ;
    pSym
-> SizeOfStruct  =   sizeof  ( IMAGEHLP_SYMBOL ) ;
    pSym
-> MaxNameLength  =  SYM_BUFF_SIZE  -   sizeof  ( IMAGEHLP_SYMBOL);

    
// Initialize & load the symbol table
    hCurProcess  =  (HANDLE)::GetCurrentProcessId();
    
if ( ! ::SymInitialize(hCurProcess, NULL, TRUE))
    
{
        
return ;
    }


    __asm
    
{
        mov eax, dword ptr [ebp
+ 4 ]     // Get the return address
        mov retAddrs, eax
        mov eax, dword ptr [ebp]
        mov saved_ebp, eax
    }


    printf(
" CallSatck dump begin... " );
    
while (saved_ebp  >   0 )
    
{
        
if (retAddrs  !=   0 )
        
{
            callFuncAddrs 
=   * ( int   * )(retAddrs  -   4 +  retAddrs;
            
if ((unsigned  int )callFuncAddrs  >   0x80000000 )
            
{
                
break ;
            }


            
// Jump over the JMP instruction to the real function address
             while (JMP_INSTR  ==   * (unsigned  char   * )callFuncAddrs) 
            
{
                
int  offset  =  callFuncAddrs  +   1 ;
                callFuncAddrs 
=   * ( int   * )(offset)  +  (callFuncAddrs  +   5 );
            }


            
if ( ! ::SymGetSymFromAddr(hCurProcess, (DWORD)callFuncAddrs, NULL, pSym))
            
{
                
break ;
            }

        
//             printf("0x%08X <--", callFuncAddrs);
            printf( " %s( ) " , pSym -> Name);
        }


        retAddrs 
=   * ( int   * )(saved_ebp  +   4 );
        saved_ebp 
=   * ( int   * )saved_ebp;
    }


    printf(
" CallSatck dump end! " );
}


class  CTest
{
public :
    
void  Hello()
    
{
        printf(
" Hello World " );
        CallStack();
    }
;

    
void  SetPrivateVal( int  nVal)
    
{
        y 
=  nVal;
    }


public :
    
int  x;

private :
    
int  y;
}
;

int  Call_C( int  a,  int  b)
{
    CallStack();
    
return  (a  +  b);
}


int  Call_B( int  a,  int  b)
{
    
return  Call_C(a, b);
}


int  Call_A( int  a,  int  b)
{
    
return  Call_B(a, b);
}


// #include <assert.h>
// #include <BugsLayerUtil.h>

main()
{
//     Call_A(10, 20);
    CTest test;
    test.Hello();
    
return   0 ;
}

控制檯輸出如下信息:



Hello World
CallSatck dump begin...

CallStack( )
CTest::Hello( )
main( )

CallSatck dump end
!
Press any key to 
continue

 如果改一下main( ) 函數如:



main()
{
    Call_A(
10 20 );
//     CTest test;
//     test.Hello();
     return   0 ;
}

 則控制檯輸出如下信息



CallSatck dump begin...

CallStack( )
Call_C( )
Call_B( )
Call_A( )
main( )

CallSatck dump end
!
Press any key to 
continue

 2008/5/10

       今天把程序修改了一下,可以輸出函數所傳遞的實參個數及值,還增加了函數調用處的偏移量,總之輸出有點像VC++ 6.0種調試時候的call stack,不過函數的參數類型我還是沒有得到。本來想通過讀取符號表中的decorated symbols 來自己undecorated 一下就知道了各個參數的類型,可不管我怎麼調用SymSetOptions()函數進行設置也不好用,只能先擱一下了。當然,能通過反彙編來識別出參數類 型,但這麼做有點太繁瑣了。

修改後控制檯能輸出如下信息:



CallSatck dump begin...

StackTrace(  )
Call_C( 
0x0000000A 0x00000014  )        line  466   +   0  bytes
Call_B( 
0x0000000A 0x00000014  )        line  471   +   13  bytes
Call_A( 
0x0000000A 0x00000014  )        line  476   +   13  bytes
main( 
0x00000001 0x003913A0 0x003913F8  )      line  484   +   9  bytes

CallSatck dump end
!
Press any key to 
continue

說了這麼多,貼個修改後的代碼先。(仍需要完善......)



#include  < stdio.h >
#include 
< windows.h >
#include 
< Dbghelp.h >

#define  JMP_INSTR 0xE9
#define  SYM_BUFF_SIZE 512
#pragma comment(lib,"imagehlp.lib")

static  BYTE g_stSymbol [ SYM_BUFF_SIZE ] ;

int  GetFuncArgsNum( int  retAddrs)
{
    
if ( 0xC483   ==   * (WORD  * )retAddrs)  //  0xC483 ''Add esp, xxx''
         return   * (BYTE * )(retAddrs  +   2 >>   2 ;
    
else
        
return   0 ;
}


int  GetFuncArgVal( int  ebp,  int  index)
{
    
return   * ( int   * )(ebp  +  ((index  +   2 <<   2 ));
}


int  GetCallFuncAddrs( int  retAddrs)
{
    
int  callFuncAddrs  =   * ( int   * )(retAddrs  -   4 +  retAddrs;
    
    
// callFuncAddrs > 0x80000000 can cause the ''can not read the memory'' error!
     if ((DWORD)callFuncAddrs  >   0x80000000 )
    
{
        
return   0 ;
    }

    
    
// Jump over the JMP instruction to the real address of function
     while (JMP_INSTR  ==   * (BYTE  * )callFuncAddrs) 
    
{
        
int  offset  =  callFuncAddrs  +   1 ;
        callFuncAddrs 
=   * ( int   * )(offset)  +  (callFuncAddrs  +   5 );
    }


    
return  callFuncAddrs;
}


void  PrintCallFuncOffset( int  retAddrs)
{
    HANDLE hCurProcess 
=  (HANDLE) ::GetCurrentProcessId();
    
static  saved_retAddrs  =   0 ;

    
if ( 0   ==  saved_retAddrs)
    
{
        saved_retAddrs 
=  retAddrs;
        printf(
" %s " " " );
        
return ;
    }


    DWORD dwDisp;
    IMAGEHLP_LINE stLine;

    FillMemory(
& stLine, NULL,  sizeof (IMAGEHLP_LINE));
    stLine.SizeOfStruct 
=   sizeof (stLine);
    
if ( ! ::SymGetLineFromAddr(hCurProcess, (DWORD)saved_retAddrs,  & dwDisp,  & stLine))
    
{
        printf(
" %s " " " );
        
return ;
    }


    printf(
" line %d + %d bytes " , stLine.LineNumber, dwDisp);
    saved_retAddrs 
=  retAddrs;
}


void  PrintCallFunc( int  ebp)
{
    
int  callFuncAddrs;
    DWORD dwDisp 
=   0 ;
    
int  retAddrs  =   * ( int   * )(ebp  +   4 );
    HANDLE hCurProcess 
=  (HANDLE) ::GetCurrentProcessId();
    PIMAGEHLP_SYMBOL pSym 
=  (PIMAGEHLP_SYMBOL) & g_stSymbol ;
    
int  argsNum  =  ::GetFuncArgsNum(retAddrs);

    callFuncAddrs 
=  GetCallFuncAddrs(retAddrs);
    
if ( ! ::SymGetSymFromAddr(hCurProcess, (DWORD)callFuncAddrs,  & dwDisp, pSym))
    
{
        
return ;
    }

    
    
// Print the name of call function
    printf( " %s(  " , pSym -> Name);
    
    
// Print the args
     if (argsNum  >   0 )
    
{
        argsNum
-- ;
        
for ( int  i  =   0 ; i  <  argsNum; i ++ )
        
{
            printf(
" 0x%08X,  " , GetFuncArgVal(ebp, i));
        }


        
// print the last arg.
        printf( " 0x%08X ) " , GetFuncArgVal(ebp, i));
    }

    
else
    
{
        printf(
" %s " "  ) " );
    }


    
// Print the line number
    PrintCallFuncOffset(retAddrs);
}


void  StackTrace( void )
{
    
int  saved_ebp;
    
int  cur_ebp;
    HANDLE hCurProcess;

    
// Fill the IMAGEHLP_SYMBOL struct
    PIMAGEHLP_SYMBOL pSym  =  (PIMAGEHLP_SYMBOL) & g_stSymbol ;
    FillMemory ( pSym , NULL , SYM_BUFF_SIZE ) ;
    pSym
-> SizeOfStruct  =   sizeof  ( IMAGEHLP_SYMBOL ) ;
    pSym
-> MaxNameLength  =  SYM_BUFF_SIZE  -   sizeof  ( IMAGEHLP_SYMBOL );

    
// Initialize & load the symbol table
    hCurProcess  =  (HANDLE)::GetCurrentProcessId();
    ::SymSetOptions ( SYMOPT_UNDNAME 
|  SYMOPT_LOAD_LINES ) ;
    
if ( ! ::SymInitialize(hCurProcess, NULL, TRUE)) //  Load the module automatically
     {
        
return ;
    }


    __asm
    
{
        
// Get the current ebp
        mov cur_ebp, ebp
    }


    
// Get the saved ebp
    saved_ebp  =   * ( int   * )cur_ebp;

    
// Print the call stack
    printf( " StackTrace dump begin... " );
    
while (saved_ebp  !=   0 )
    
{
        PrintCallFunc(cur_ebp);

        
// Move to the next caller''s stack frame
        cur_ebp  =  saved_ebp;
        saved_ebp 
=   * ( int   * )saved_ebp;
    }


    printf(
" StackTrace dump end! " );
    ::SymCleanup(hCurProcess);
}


class  CTest
{
public :
    
void  Hello()
    
{
        StackTrace();
    }
;
}
;

int  Call_C( int  a,  int  b)
{
    StackTrace();
    
return  (a  +  b);
}


int  Call_B( int  a,  int  b)
{
    
return  Call_C(a, b);
}


int  Call_A( int  a,  int  b)
{
    
return  Call_B(a, b);
}


main()
{
    Call_A(
10 20 );
//     CTest test;
//     test.Hello();
     return   0 ;
}

 


聞香止步 收集於:http://www.diybl.com/course/3_program/c++/cppjs/2008510/115247.html
淘寶店 擺件 飾品 *木雕系列*:海南黃花梨、越南黃花梨、草花梨、小葉紫檀、黑檀、綠檀木、黃楊木、桃木髮簪  木梳 樟木壁掛 佛珠 車飾 擺件

http://shop36570193.taobao.com

朋友,有空來看看,喜歡的朋友請收藏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章