C/C++ call stack traces

本文介绍了一个能在VC++6.0中实现类似callstack功能的程序,可以输出调用栈上的函数名称、实参个数及值,还有函数调用处的偏移量。通过具体的源代码示例展示了如何获取这些信息。

哎,郁闷,不知道是优快云的bug还是有人故意捣乱,辛辛苦苦写的文章被替换成“自由的思维”一文,真是百思不解呀,至此也没有什么再写的激情了!(该程序能输出调用栈上的函数名称,实参个数及其值,以及函数调用处的偏移量。像VC++ 6.0中的call stack)

控制台输出如下:

StackTrace dump begin...

StackTrace(  )
Call_C( 
0x0000000A 0x00000014  )        line  448   +   0  bytes
Call_B( 
0x0000000A 0x00000014  )        line  453   +   13  bytes
Call_A( 
0x0000000A 0x00000014  )        line  458   +   13  bytes
main( 
0x00000001 0x00391380 0x003913D8  )      line  463   +   9  bytes

StackTrace dump end
!
Press any key to 
continue

 有篇文章特好,Playing with the  stack : http://www.codeguru.com/cpp/misc/misc/stack/article.php/c3875/

 下面贴个源码。

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

#define  JMP_INSTR 0xE9
#define  SYM_BUFF_SIZE 512

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(
1020);
//    CTest test;
//    test.Hello();
    return 0;
}
#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89 #define H_6B9572DA_A64B_49E6_B234_051480991C89 #ifndef __cplusplus # error "It's not going to compile without a C++ compiler..." #endif #if defined(BACKWARD_CXX11) #elif defined(BACKWARD_CXX98) #else # if __cplusplus >= 201103L # define BACKWARD_CXX11 # define BACKWARD_ATLEAST_CXX11 # define BACKWARD_ATLEAST_CXX98 # else # define BACKWARD_CXX98 # define BACKWARD_ATLEAST_CXX98 # endif #endif // You can define one of the following (or leave it to the auto-detection): // // #define BACKWARD_SYSTEM_LINUX // - specialization for linux // // #define BACKWARD_SYSTEM_UNKNOWN // - placebo implementation, does nothing. // #if defined(BACKWARD_SYSTEM_LINUX) #elif defined(BACKWARD_SYSTEM_UNKNOWN) #else # if defined(__linux) # define BACKWARD_SYSTEM_LINUX # else # define BACKWARD_SYSTEM_UNKNOWN # endif #endif #include <fstream> #include <iostream> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cstring> #include <cctype> #include <string> #include <new> #include <iomanip> #include <vector> #if defined(BACKWARD_SYSTEM_LINUX) // On linux, backtrace can back-trace or "walk" the stack using the following // libraries: // // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtine in gcc/clang for stack unwinding on // exception. // - normally libgcc is already linked to your program by default. // // #define BACKWARD_HAS_BACKTRACE == 1 // - backtrace seems to be a little bit more portable than libunwind, but on // linux, it uses unwind anyway, but abstract away a tiny information that is // sadly really important in order to get perfectly accurate stack traces. // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // Note that only one of the define should be set to 1 at a time. // # if BACKWARD_HAS_UNWIND == 1 # elif BACKWARD_HAS_BACKTRACE == 1 # else # undef BACKWARD_HAS_UNWIND # define BACKWARD_HAS_UNWIND 1 # undef BACKWARD_HAS_BACKTRACE # define BACKWARD_HAS_BACKTRACE 0 # endif // On linux, backward can extract detailed information about a stack trace // using one of the following libraries: // // #define BACKWARD_HAS_DW 1 // - libdw gives you the most juicy details out of your stack traces: // - object filename // - function name // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // - variables name and values (if not optimized out) // - You need to link with the lib "dw": // - apt-get install libdw-dev // - g++/clang++ -ldw ... // // #define BACKWARD_HAS_BFD 1 // - With libbfd, you get a fair amount of details: // - object filename // - function name // - source filename // - line numbers // - source code snippet (assuming the file is accessible) // - You need to link with the lib "bfd": // - apt-get install binutils-dev // - g++/clang++ -lbfd ... // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // // Note that only one of the define should be set to 1 at a time. // # if BACKWARD_HAS_DW == 1 # elif BACKWARD_HAS_BFD == 1 # elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 # else # undef BACKWARD_HAS_DW # define BACKWARD_HAS_DW 0 # undef BACKWARD_HAS_BFD # define BACKWARD_HAS_BFD 0 # undef BACKWARD_HAS_BACKTRACE_SYMBOL # define BACKWARD_HAS_BACKTRACE_SYMBOL 1 # endif # if BACKWARD_HAS_UNWIND == 1 # include <unwind.h> // while gcc's unwind.h defines something like that: // extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); // extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); // // clang's unwind.h defines something like this: // uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); // // Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we // cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr // anyway. // // Luckily we can play on the fact that the guard macros have a different name: #ifdef __CLANG_UNWIND_H // In fact, this function still comes from libgcc (on my different linux boxes, // clang links against libgcc). # include <inttypes.h> extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context*, int*); #endif # endif # include <cxxabi.h> # include <fcntl.h> # include <link.h> # include <sys/stat.h> # include <syscall.h> # include <unistd.h> # include <signal.h> # if BACKWARD_HAS_BFD == 1 // NOTE: defining PACKAGE{,_VERSION} is required before including // bfd.h on some platforms, see also: // https://sourceware.org/bugzilla/show_bug.cgi?id=14243 # ifndef PACKAGE # define PACKAGE # endif # ifndef PACKAGE_VERSION # define PACKAGE_VERSION # endif # include <bfd.h> # ifndef _GNU_SOURCE # define _GNU_SOURCE # include <dlfcn.h> # undef _GNU_SOURCE # else # include <dlfcn.h> # endif # endif # if BACKWARD_HAS_DW == 1 # include <elfutils/libdw.h> # include <elfutils/libdwfl.h> # include <dwarf.h> # endif # if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) // then we shall rely on backtrace # include <execinfo.h> # endif #endif // defined(BACKWARD_SYSTEM_LINUX) #ifdef BACKWARD_ATLEAST_CXX11 # include <unordered_map> # include <utility> // for std::swap namespace backward { namespace details { template <typename K, typename V> struct hashtable { typedef std::unordered_map<K, V> type; }; using std::move; } // namespace details } // namespace backward #else // NOT BACKWARD_ATLEAST_CXX11 # include <map> namespace backward { namespace details { template <typename K, typename V> struct hashtable { typedef std::map<K, V> type; }; template <typename T> const T& move(const T& v) { return v; } template <typename T> T& move(T& v) { return v; } } // namespace details } // namespace backward #endif // BACKWARD_ATLEAST_CXX11 namespace backward { namespace system_tag { struct linux_tag; // seems that I cannot call that "linux" because the name // is already defined... so I am adding _tag everywhere. struct unknown_tag; #if defined(BACKWARD_SYSTEM_LINUX) typedef linux_tag current_tag; #elif defined(BACKWARD_SYSTEM_UNKNOWN) typedef unknown_tag current_tag; #else # error "May I please get my system defines?" #endif } // namespace system_tag 注释上述代码
最新发布
09-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值