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

朋友,有空来看看,喜欢的朋友请收藏

你可能感兴趣的:(c,汇编,null,byte)