您的位置:首页 > 编程语言 > C语言/C++

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

2013-05-03 18:47 627 查看
明天游戏要封测了,感觉稍微早了点,哎,悲剧。昨天把我的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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: