明天游戏要封测了,感觉稍微早了点,哎,悲剧。昨天把我的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 );
}
}
相信有了例子在,应该很好懂怎么用的了,呵呵。希望对你有所帮助。
如果您比我还懒,那么就下载这个试试看吧: