一个简单的使用C++在运行时获取调用堆栈的类

     明天游戏要封测了,感觉稍微早了点,哎,悲剧。昨天把我的Bug改完了,今天在公司闲了一天,写文档差点写得睡着。没事所以回来得早点,7点过就到家了,郁闷的是回来又被新闻联播着实恶心了一把。闲的蛋疼只好写点程序了。最近一直在整理过去写过的东西,希望能够达到拿到哪里都可以直接用,不需要配置什么,动机主要是最近自己写了一些小东西,发现很多东西我都是在做重复劳动,比如说string_shim、C++ string和String^的桥接等等。为了避免这些没有必要的重复劳动,所以就动手整理啦。

       我给它起了个名字叫dbsoft。感觉小写还是比较和谐的,嘿嘿。目前已经整理了不少公共组件,透露下:

      由于个人水平有限,代码当然不怎么样,但是我一直在努力,期待着进步。

      分享一个今晚刚写好的类,在C++中用于获取调用堆栈的信息。我写的最开始就是为VS2005和2008写的,所以VC6或者VC7.1等就不要尝试了,另外呢还是依赖于Boost的智能指针。如果你是VS2008 SP1的话可以用#include <memory>代替#include<boost/tr1/tr1/memory>。这本来是属于dbsoft的一部分,我专门把它提取了出来独立出来,这样方便大家整合。

简单的应用实例:

main.cpp

#include <iostream>

using namespace std;

#include "debug_tool.hpp"

#include <windows.h>
#include <DbgHelp.h>

#pragma comment( lib, "Dbghelp.lib" )

inline std::string GetLastErrorDescA( unsigned uErrorCode = ::GetLastError() )
{
LPVOID lpMsg = NULL;
::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
   NULL, uErrorCode, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
   (LPSTR)&lpMsg,
   0,
   NULL
   );

if( lpMsg == NULL )
{
   return "δ֪";
}

std::string strReason = (const char*)lpMsg;

::LocalFree( lpMsg );

return strReason;
}

int SEHHanlerTest( LPEXCEPTION_POINTERS pep )
{
dbsoft::callstack::callstack_ptr pCall = dbsoft::callstack::generate( pep->ContextRecord );

cout<<" Error:: " << GetLastErrorDescA( pep->ExceptionRecord->ExceptionCode&0x0fffffff ) << endl;

if( pCall )
{
   for( dbsoft::callstack::const_iterator it = pCall->begin();
    it != pCall->end();
    ++it )
   {
    cout<< *it << endl;
   }
}

return 1;
}

void test_main()
{
int* p = NULL;

__try
{
   *p = 100;
}
__except( SEHHanlerTest( GetExceptionInformation() ) )
{

}
}

int main()
{
dbsoft::callstack::callstack_ptr call = dbsoft::callstack::generate();

if( call )
{
   for( dbsoft::callstack::const_iterator it = call->begin();
    it != call->end();
    ++it )
   {
    cout<< *it << endl;
   }
}

printf_s( "........................................................\n" );

test_main();

return 0;
}

输出:

dbsoft::detail::callstack_Imp::generate文件:e:\documents\visual studio 2008\proj
ects\callstacktest\debug_tool.cpp, 行数:209
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
dbsoft::callstack::generate文件:e:\documents\visual studio 2008\projects\callsta
cktest\debug_tool.cpp, 行数:312
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
main文件:e:\documents\visual studio 2008\projects\callstacktest\main.cpp, 行数:
69
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
__tmainCRTStartup文件:f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c, 行数:586

模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
mainCRTStartup文件:f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c, 行数:403
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
BaseThreadInitThunk 模块: C:\Windows\system32\kernel32.dll
RtlInitializeExceptionChain 模块: C:\Windows\SYSTEM32\ntdll.dll
RtlInitializeExceptionChain 模块: C:\Windows\SYSTEM32\ntdll.dll
RtlInitializeExceptionChain 模块: C:\Windows\SYSTEM32\ntdll.dll
........................................................
Error:: 拒绝访问。

test_main文件:e:\documents\visual studio 2008\projects\callstacktest\main.cpp,
行数:59
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
main文件:e:\documents\visual studio 2008\projects\callstacktest\main.cpp, 行数:
85
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
__tmainCRTStartup文件:f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c, 行数:586

模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
mainCRTStartup文件:f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c, 行数:403
模块: e:\Documents\Visual Studio 2008\Projects\callstacktest\Debug\callstacktes
t.exe
BaseThreadInitThunk 模块: C:\Windows\system32\kernel32.dll
RtlInitializeExceptionChain 模块: C:\Windows\SYSTEM32\ntdll.dll
RtlInitializeExceptionChain 模块: C:\Windows\SYSTEM32\ntdll.dll
RtlInitializeExceptionChain 模块: C:\Windows\SYSTEM32\ntdll.dll
请按任意键继续. . .


最后发上这个类吧:

// debug_tool.hpp

/**
* @brief   调试工具
* @author dongbo
* @date    2010-3-17
* @remarks
**/

#pragma once

#include <list>
#include <string>

#include <boost/tr1/tr1/memory>

namespace dbsoft
{
namespace detail
{
   class callstack_Imp;
}

class callstack
{
public:
   typedef std::string             func_name;
   typedef std::list< func_name >                 func_name_list;
   typedef func_name_list::const_iterator         const_iterator;
   typedef std::tr1::shared_ptr<callstack>        callstack_ptr;

   friend class detail::callstack_Imp;

public:
   callstack();
public:
   const_iterator begin() const;
   const_iterator end() const;

public:
   /** 
   * @brief 如果你不传递参数,那么你将得到当前时刻的调用堆栈
   *        如果你使用它来处理SEH异常,那么请将LPEXCEPTION_POINTERS->ContextRecord传进去
   **/
   static callstack_ptr generate( const void* pContext = NULL );

private:
   std::tr1::shared_ptr<detail::callstack_Imp>         m_spImp;
}; 
}

// debug_tool.cpp

/**
* @brief   调试工具
* @author dongbo
* @date    2010-3-17
* @remarks
**/

#include "debug_tool.hpp"

#include <windows.h>
#include <WinDNS.h>
#include <DbgHelp.h>
#include <Psapi.h>
#pragma comment( lib, "Dbghelp.lib" )
#pragma comment( lib, "Psapi.lib" )

namespace dbsoft
{
namespace detail
{
   class callstack_Imp
   {
   public:
    typedef callstack::func_name            func_name;
    typedef callstack::func_name_list                      func_name_list;
    typedef callstack::const_iterator                      const_iterator;
    typedef callstack::callstack_ptr                       callstack_ptr;

   public:
    callstack_Imp();

   public:
    const_iterator begin() const
    {
     return m_lstFunc.begin();
    }

    const_iterator end() const
    {
     return m_lstFunc.end();
    }

   private:
    func_name_list   m_lstFunc;

   public:
    static callstack_ptr generate( const void* pContext );
   protected:
    static void           _initialize();
    static bool           _loadAllModules();
    static void           _stackwalk( QWORD* pTrace, DWORD dwMaxDepth, CONTEXT* pContext );
    static func_name      _getfuncname( QWORD dwFunc );
   private:
    static bool           m_bInitialized;
   };

   bool callstack_Imp::m_bInitialized = false;

   callstack_Imp::callstack_Imp()
   {
    if( !m_bInitialized )
    {
     _initialize();
    }
   }

   void callstack_Imp::_stackwalk(QWORD *pTrace, DWORD dwMaxDepth, CONTEXT *pContext)
   {
    STACKFRAME64        sfStackFrame64;
    HANDLE              hProcess = ::GetCurrentProcess();
    HANDLE              hThread = ::GetCurrentThread();

    DWORD               dwDepth = 0;

    ::ZeroMemory( &sfStackFrame64, sizeof(sfStackFrame64) );

    __try
    {
     sfStackFrame64.AddrPC.Offset        = pContext->Eip;
     sfStackFrame64.AddrPC.Mode          = AddrModeFlat;
     sfStackFrame64.AddrStack.Offset     = pContext->Esp;
     sfStackFrame64.AddrStack.Mode       = AddrModeFlat;
     sfStackFrame64.AddrFrame.Offset     = pContext->Ebp;
     sfStackFrame64.AddrFrame.Mode       = AddrModeFlat;

     bool bSuccessed = true;

     while( bSuccessed && (dwDepth < dwMaxDepth) )
     {
      bSuccessed = ::StackWalk64( 
       IMAGE_FILE_MACHINE_I386,
       hProcess,
       hThread,
       &sfStackFrame64,
       pContext,
       NULL,
       SymFunctionTableAccess64,
       SymGetModuleBase64,
       NULL
       ) != FALSE ;

      pTrace[ dwDepth ] = sfStackFrame64.AddrPC.Offset;
      ++dwDepth;

      if( !bSuccessed )
      {
       break;
      }

      if( sfStackFrame64.AddrFrame.Offset == 0 )
      {
       break;
      }
     }
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
    }
   }

   callstack_Imp::func_name callstack_Imp::_getfuncname( QWORD dwFunc )
   {
    static const int gc_iMaxNameLength = 4096;
    char                    szSymbol[ sizeof(IMAGEHLP_SYMBOL64) + gc_iMaxNameLength ] = {0};
    PIMAGEHLP_SYMBOL64      Symbol;
    DWORD                   dwSymbolDisplacement = 0;
    DWORD64                 dw64SymbolDisplacement = 0;

    HANDLE                  hProcess = ::GetCurrentProcess();

    Symbol                  = (PIMAGEHLP_SYMBOL64)szSymbol;
    Symbol->SizeOfStruct    = sizeof(szSymbol);
    Symbol->MaxNameLength   = gc_iMaxNameLength;

    func_name              strResult;

    if( ::SymGetSymFromAddr64( 
     hProcess,
     dwFunc,
     &dw64SymbolDisplacement,
     Symbol )
     )
    {
     int i=0;
     for( ; Symbol->Name[i] < 32 || Symbol->Name[i] > 127; )
     {
      ++i;
     }

     strResult += ( const char* )( Symbol->Name + i );
    }
    else
    {
     return "未知函数";
    }

   IMAGEHLP_LINE64            ImageHelpLine;
   ImageHelpLine.SizeOfStruct = sizeof(ImageHelpLine);

   if( ::SymGetLineFromAddr64( hProcess, dwFunc, &dwSymbolDisplacement, &ImageHelpLine ) )
   {
     // 还是删掉吧,虽然这个操作很好用,但是过多的依赖我不想看到
    // fmt::WritePipe( strResult, "文件: ", ImageHelpLine.FileName, " 行数:", ImageHelpLine.LineNumber );

     // 我就不信,4096都不够
     char szBuf[4096] = {0};
     sprintf_s( szBuf, "文件:%s, 行数:%d\n", ImageHelpLine.FileName, ImageHelpLine.LineNumber );

     strResult += szBuf;
   }

    IMAGEHLP_MODULE64 ImageHelpModule;
    ImageHelpModule.SizeOfStruct = sizeof(ImageHelpModule);

    if( ::SymGetModuleInfo64( hProcess, dwFunc, &ImageHelpModule ) )
    {
     char               szModuleName[1024];
     sprintf_s( szModuleName, " 模块: %s", ImageHelpModule.ImageName );

     strResult += szModuleName;
    }

    return strResult;
   }

   callstack_Imp::callstack_ptr callstack_Imp::generate( const void* pContext )
   {
    if( !m_bInitialized )
    {
     _initialize();
    }

    CONTEXT Context;

    if( pContext != NULL )
    {
     ::memcpy_s( &Context, sizeof(CONTEXT), pContext, sizeof(CONTEXT) );
    }
    else
    {
     ::ZeroMemory( &Context, sizeof(Context) );
     Context.ContextFlags = CONTEXT_FULL;

     __asm
     {
      call FakeFuncCall
FakeFuncCall:

      pop eax
       mov Context.Eip, eax
       mov Context.Ebp, ebp
       mov Context.Esp, esp
     }
    }

    static const int gc_iMaxStackDepth = 512;
    QWORD aryStack[gc_iMaxStackDepth] = {0};

    // 由于_stackwalk内部使用SEH 因此不能在其内部使用C++类
    _stackwalk( aryStack, gc_iMaxStackDepth, &Context );

    callstack_ptr spCallStack( new callstack() );

    for( int i=0; i<gc_iMaxStackDepth && aryStack[i] != 0; ++i )
    {
     func_name name = _getfuncname(aryStack[i]);
     spCallStack->m_spImp->m_lstFunc.push_back( name );
    }  

    return spCallStack;
   }

   void callstack_Imp::_initialize()
   {
    if( m_bInitialized )
    {
     return;
    }

    // 设置符号引擎
    DWORD SymOpts = ::SymGetOptions();
    SymOpts |= SYMOPT_LOAD_LINES;
    SymOpts |= SYMOPT_DEBUG;
    ::SymSetOptions( SymOpts );

    if( FALSE == ::SymInitialize( ::GetCurrentProcess(), NULL, TRUE ) )
    {
     // DBSOFT_LogMsg( "::SymInitialize初始化失败..." );
     return;
    }

    if( !_loadAllModules() )
    {
     // DBSOFT_LogMsg( "LoadModules发生了错了" );
    }

    m_bInitialized = true;
   }

   bool callstack_Imp::_loadAllModules()
   {
    HANDLE                 hProcess = ::GetCurrentProcess();
    static const int       gc_iMaxHandles = 4096;
    HMODULE                aryHandles[ gc_iMaxHandles ] = {0};

    unsigned               uBytes = 0;

    BOOL bResult = ::EnumProcessModules(
     hProcess, aryHandles, sizeof(aryHandles), (LPDWORD)&uBytes );

    if( FALSE == bResult )
    {
     // DBSOFT_LogMsg( "::EnumProcessModules失败 Msg:", GetLastErrorDescA() );
     return false;
    }

    const int iCount = uBytes/sizeof(HMODULE);

    for( int i=0; i < iCount; ++i )
    {
     char szModuleName[4096] = {0};
     char szImageName[4096] ={0};
     MODULEINFO Info;

     ::GetModuleInformation( hProcess, aryHandles[i], &Info, sizeof(Info) );
     ::GetModuleFileNameExA( hProcess, aryHandles[i], szImageName, 4096 );
     ::GetModuleBaseNameA( hProcess, aryHandles[i], szModuleName, 4096 );

     ::SymLoadModule64( hProcess, aryHandles[i], szImageName, szModuleName, (DWORD64)Info.lpBaseOfDll, (DWORD)Info.SizeOfImage );
    }

    return true;
   }
}

callstack::callstack():m_spImp( new detail::callstack_Imp() )
{  
}

callstack::const_iterator callstack::begin() const
{
   return m_spImp->begin();
}

callstack::const_iterator callstack::end() const
{
   return m_spImp->end();
}

callstack::callstack_ptr callstack::generate( const void* pContext )
{
   return detail::callstack_Imp::generate( pContext );
}
}

        相信有了例子在,应该很好懂怎么用的了,呵呵。希望对你有所帮助。

如果您比我还懒,那么就下载这个试试看吧:

http://ishare.iask.sina.com.cn/f/7090776.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值