//==============================================================================
//
// 项目名 :
// 文件名 :XDBExceptionReport.cpp
// 作 者 :
// 用 途 :异常报告输出实现类。
//
//==============================================================================
// 版本记录
//==============================================================================
//
// V0.1 2005-5-19 9:49:44
// V0.9 2005-6-20 8:54:52 RC1
//
//
//==============================================================================
#include "stdafx.h"
#include <malloc.h>
#include "KDSExceptionReport.h"
#include "KDSOsUtils.h"
#include "KingDataServer/KDSCommonPublic.h"
//==============================================================================
// 需要DBGHELP.LIB文件
//==============================================================================
#pragma comment( linker, "/defaultlib:dbghelp.lib")
#pragma comment( lib,"version.lib" )
using namespace KING_DATA_SERVER;
//==============================================================================
// 静态成员
//==============================================================================
WCHAR XDBExceptionReport::m_szReportFileName[MAX_PATH] = { 0 };
LPTOP_LEVEL_EXCEPTION_FILTER XDBExceptionReport::m_previousFilter = NULL;
HANDLE XDBExceptionReport::m_hReportFile = INVALID_HANDLE_VALUE;
HANDLE XDBExceptionReport::m_hProcess = NULL;
CRITICAL_SECTION XDBExceptionReport::m_csDbgHelp;
#define XDB_ASSERT KDS_ASSERT
//==============================================================================
// 全局唯一实例
//==============================================================================
static XDBExceptionReport _kdsExceptionReport;
//==============================================================================
// UNICODE字节序标志(BOM:Byte Order Mark)
//==============================================================================
#define UNICODE_BOM "\xFF\xFE"
//=========================================================
// 嵌套层次深度
//=========================================================
#define MAX_NESTING_LEVEL 2
//=============================================================
// 最多处理的子节点数目
//=============================================================
#define MAX_CHILDREN_NUM 16
//=============================================================
// 最多处理的数组元素个数
//=============================================================
#define MAX_ELEMENT_NUM 16
//=============================================================
// 异常输出文件的最大尺寸
//=============================================================
// 注: 只在每次处理异常之前检查尺寸,因此文件可能暂时出现尺寸
// 大于该值的现象,不过下次再发生异常时,文件尺寸将被收缩。
//=============================================================
#define MAX_REPORT_FILE_SIZE ((DWORD)16*1024*1024) // 16M
//=========================================================
// 字符输出缓冲区大小
//=========================================================
#define MAX_OUT_BUFFER 32767
//==============================================================================
//
// 类成员实现
//
//==============================================================================
// 枚举符号信息的回调参数结构
struct EnumerateSymbolsCallbackPara
{
LPSTACKFRAME64 stackFrame; // 调用栈信息
HANDLE reportFile; // 输出文件句柄
};
/// <summary>
/// 构造函数。
/// </summary>
XDBExceptionReport::XDBExceptionReport( )
{
// 生成默认的异常报告输出文件
// 默认的文件路径与可执行文件路径相同,文件后缀为RPT
::GetModuleFileName( 0, m_szReportFileName, MAX_PATH );
// 得到进程ID
DWORD ProcessId = GetCurrentProcessId();
// 生成合适的后缀
LPWSTR pszDot = wcsrchr( m_szReportFileName, WSTR('.') );
if ( pszDot != NULL ) *pszDot = WSTR('\0');
wchar_t wStr[ 32] = { 0 };
swprintf( wStr,32, WSTR("_%u.RPT"),ProcessId );
size_t MaxCount = MAX_PATH - wcslen( m_szReportFileName ) - 1;
if( MaxCount > 0 ) wcsncat( m_szReportFileName,wStr,MaxCount );
// 获得进程句柄
m_hProcess = GetCurrentProcess();
// 初始化临界区
::InitializeCriticalSection( &m_csDbgHelp );
// 安装未处理异常过滤函数
m_previousFilter = ::SetUnhandledExceptionFilter(
XDBUnhandledExceptionFilter );
}
/// <summary>
/// 析构函数。
/// </summary>
XDBExceptionReport::~XDBExceptionReport( )
{
// 恢复默认的未处理异常过滤函数
SetUnhandledExceptionFilter( m_previousFilter );
// 删除临界区
::DeleteCriticalSection( &m_csDbgHelp );
}
/// <summary>
/// 设置异常报告输出文件的路径。
/// </summary>
/// <param name="pszReportFileName">
/// 文件路径。
/// </param>
void XDBExceptionReport::SetReportFileName( LPWSTR pszReportFileName )
{
XDB_ASSERT( pszReportFileName != NULL );
if( pszReportFileName != NULL )
wcscpy( m_szReportFileName, pszReportFileName );
}
/// <summary>
/// 获得异常报告输出文件的路径。
/// </summary>
/// <returns>
/// 返回文件路径。
/// </returns>
WCHAR* XDBExceptionReport::GetReportFileName( )
{
return m_szReportFileName;
}
/// <summary>
/// 未处理异常入口函数。
/// </summary>
/// <param name="pExceptionInfo">
/// 异常信息。
/// </param>
/// <returns>
/// 返回值为EXCEPTION_CONTINUE_SEARCH或EXCEPTION_EXECUTE_HANDLER。
/// </returns>
LONG WINAPI XDBExceptionReport::XDBUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo )
{
LONG Result = EXCEPTION_CONTINUE_SEARCH;
DWORD FileSize= 0;
__try
{
// 进入临界区(DBGHELP库只能被单线程访问)
::EnterCriticalSection( &m_csDbgHelp );
// 打开文件
m_hReportFile = ::CreateFile( m_szReportFileName,
GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
// 检查是否成功打开文件
if ( m_hReportFile != INVALID_HANDLE_VALUE )
{
// 检查文件大小
FileSize = GetFileSize( m_hReportFile,NULL );
//==============================================================================
#ifdef _UNICODE
//==============================================================================
if( FileSize == 0 )
{
DWORD Bytes = 0;
::WriteFile( m_hReportFile,UNICODE_BOM,2,&Bytes,NULL );
}
//==============================================================================
#endif // _UNICODE
//==============================================================================
// 检查是否需要收缩文件尺寸
if( FileSize > MAX_REPORT_FILE_SIZE )
{
// 收缩文件
if( !ShrinkReportFile( m_hReportFile, m_szReportFileName ) )
{
XDB_ASSERT( m_hReportFile == INVALID_HANDLE_VALUE );
__leave; // 跳出__try块
}
}
// 不覆盖原有内容,移动到文件的最后
SetFilePointer( m_hReportFile, 0, 0, FILE_END );
// 生成异常报告
GenerateExceptionReport( m_hReportFile, pExceptionInfo,false );
// 关闭文件句柄
CloseHandle( m_hReportFile );
m_hReportFile = INVALID_HANDLE_VALUE;
}
// 检查之前是否存在过滤函数
if ( m_previousFilter )
Result = m_previousFilter( pExceptionInfo );
} // __try
__finally
{
// 释放临界区
::LeaveCriticalSection( &m_csDbgHelp );
}
// 返回
return Result;
}
/// <summary>
/// 异常过滤函数。
/// </summary>
/// <param name="pszReportFileName">
/// 异常报告输出的文件。
/// </param>
/// <param name="pExceptionInfo">
/// 异常信息。
/// </param>
/// <param name="bWriteVariables">
/// 是否输出参数信息。
/// </param>
/// <returns>
/// 返回值为EXCEPTION_EXECUTE_HANDLER。
/// </returns>
LONG WINAPI XDBExceptionReport::XDBHandledExceptionFilter(
LPCWSTR pszReportFileName,
PEXCEPTION_POINTERS pExceptionInfo,
bool bWriteVariables /*=false*/)
{
// Release版不输出参数信息
#ifndef _DEBUG
bWriteVariables = false;
#endif
// 检查参数
if( pszReportFileName == NULL ) return EXCEPTION_EXECUTE_HANDLER;
HANDLE pReportFile = INVALID_HANDLE_VALUE;
DWORD FileSize = 0;
__try
{
// 进入临界区(DBGHELP库只能被单线程访问)
::EnterCriticalSection( &m_csDbgHelp );
// 打开文件
pReportFile = ::CreateFile(
pszReportFileName,
GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( pReportFile != INVALID_HANDLE_VALUE )
{
// 检查文件大小
FileSize = GetFileSize( pReportFile,NULL );
//==============================================================================
#ifdef _UNICODE
//==============================================================================
if( FileSize == 0 )
{
DWORD Bytes = 0;
::WriteFile( pReportFile,UNICODE_BOM,2,&Bytes,NULL );
}
//==============================================================================
#endif // _UNICODE
//==============================================================================
// 检查是否需要收缩文件尺寸
if( FileSize > MAX_REPORT_FILE_SIZE )
{
// 收缩文件
if( !ShrinkReportFile( pReportFile, pszReportFileName ) )
{
XDB_ASSERT( pReportFile == INVALID_HANDLE_VALUE );
__leave; // 跳出__try块
}
}
// 不覆盖原有内容,移动到文件的最后
SetFilePointer( pReportFile, 0, 0, FILE_END );
// 生成异常报告
GenerateExceptionReport( pReportFile, pExceptionInfo,bWriteVariables);
// 关闭文件句柄
CloseHandle( pReportFile );
} // pReportFile != INVALID_HANDLE_VALUE
}// __try
__finally
{
// 释放临界区
::LeaveCriticalSection( &m_csDbgHelp );
}
// 返回异常已经处理标志
return EXCEPTION_EXECUTE_HANDLER;
}
/// <summary>
/// 当文件尺寸过大时,收缩异常报告输出文件。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出文件句柄。
/// </param>
/// <param name="pszReportFileName">
/// 异常报告输出文件路径。
/// </param>
/// <returns>
/// 指明操作是否成功。
/// </returns>
BOOL XDBExceptionReport::ShrinkReportFile(
HANDLE& pReportFile,
LPCWSTR pszReportFileName )
{
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
XDB_ASSERT( pszReportFileName != NULL );
// 关闭文件
::CloseHandle( pReportFile );
// 删除文件
::DeleteFile( pszReportFileName );
// 重新创建文件
pReportFile = ::CreateFile(
pszReportFileName,
GENERIC_WRITE|GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
// 写入字节序标志
if( pReportFile != INVALID_HANDLE_VALUE )
{
DWORD Bytes = 0;
::WriteFile( pReportFile,UNICODE_BOM,2,&Bytes,NULL );
}
// 返回
return ( pReportFile != INVALID_HANDLE_VALUE );
}
/// <summary>
/// 生成异常报告。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="pExceptionInfo">
/// 异常信息。
/// </param>
/// <param name="bWriteVariables">
/// 是否输出参数信息。
/// </param>
void XDBExceptionReport::GenerateExceptionReport(
HANDLE pReportFile,
PEXCEPTION_POINTERS pExceptionInfo,
bool bWriteVariables)
{
// 检查参数
if( pReportFile == INVALID_HANDLE_VALUE ||
pExceptionInfo == NULL )
return;
// Release版不输出参数信息
#ifndef _DEBUG
bWriteVariables = false;
#endif
WCHAR szBuffer[4096] = { 0 };
//==============================================================================
// 生成文件头,记录系统信息
//==============================================================================
PrintOut( pReportFile , WSTR("Exception report created by %s\r\n\r\n"), GetApplicationInfo( szBuffer ,4096) );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
PrintOut( pReportFile , WSTR("System details:\r\n") );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
PrintOut( pReportFile , WSTR("%s\r\n"), GetOperationSystemInfo( szBuffer,4096 ) );
PrintOut( pReportFile , WSTR("%s\r\n"), GetProcessorInfo( szBuffer,4096 ) );
PrintOut( pReportFile , WSTR("%s\r\n"), GetMemoryInfo( szBuffer,4096 ) );
//==============================================================================
// 输出异常信息
//==============================================================================
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
PrintOut( pReportFile , WSTR("Exception Details:\r\n" ) );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
PrintOut( pReportFile , WSTR("Exception code: %08X %s\r\n"),
pExceptionRecord->ExceptionCode,
GetExceptionString(pExceptionRecord->ExceptionCode) );
//==============================================================================
// 设置符号引擎属性
//==============================================================================
DWORD dwOptions = SymGetOptions() ;
// 需要装载行号信息
SymSetOptions(dwOptions | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
// 初始化DBGHELP库
if ( !SymInitialize( m_hProcess, NULL , TRUE ) )
return;
//==============================================================================
// 输出引起异常的地址信息
//==============================================================================
WCHAR szFaultingModule[MAX_PATH] = { 0 };
DWORD_PTR section =0, offset = 0;
// 获得地址
if( GetLogicalAddress(
pExceptionRecord->ExceptionAddress,
szFaultingModule,
sizeof( szFaultingModule ),
section,
offset ) )
{
// 输出地址信息
PrintOut( pReportFile ,
WSTR("Fault address: %p %IX:%IX %s\r\n"),
pExceptionRecord->ExceptionAddress,
section, offset, szFaultingModule );
}
else
{
PrintOut( pReportFile ,
WSTR( "Failed to get module info,Fault address: %p\r\n" ),
pExceptionRecord->ExceptionAddress );
// 返回
return;
}
//==============================================================================
// 输出寄存器信息
//==============================================================================
PCONTEXT pCtx = pExceptionInfo->ContextRecord;
XDB_ASSERT( pCtx != NULL );
PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
PrintOut( pReportFile ,WSTR("Registers:\r\n") );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
#ifdef _M_IX86
PrintOut( pReportFile , WSTR("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n"),
pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
pCtx->Esi, pCtx->Edi );
PrintOut( pReportFile , WSTR("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip );
PrintOut( pReportFile , WSTR("SS:ESP:%04X:%08X EBP:%08X\r\n"),
pCtx->SegSs, pCtx->Esp, pCtx->Ebp );
PrintOut( pReportFile , WSTR("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"),
pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs );
PrintOut( pReportFile , WSTR("Flags:%08X\r\n"), pCtx->EFlags );
#endif // TODO: other platform
//==============================================================================
// 输出调用堆栈
//==============================================================================
CONTEXT trashableContext = *pCtx;
WriteStackDetails( pReportFile, &trashableContext, false );
//==============================================================================
// 输出参数信息
//==============================================================================
if( bWriteVariables )
{
// 输出局部变量和函数参数
PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
PrintOut( pReportFile , WSTR("Local Variables And Parameters:\r\n") );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
trashableContext = *pCtx;
WriteStackDetails( pReportFile,&trashableContext, true );
// 输出全局变量
#ifdef DBGHELP_PRINTOUT_GLOBAL
{
PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
PrintOut( pReportFile , WSTR("Global Variables:\r\n") );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
EnumerateSymbolsCallbackPara stParam = { NULL, pReportFile };
SymEnumSymbols( m_hProcess,
(DWORD64)GetModuleHandle(szFaultingModule),
NULL, EnumerateSymbolsCallback, &stParam );
}
#endif // DBGHELP_PRINTOUT_GLOBAL
}
// 释放资源
SymCleanup( m_hProcess );
// 结束输出
PrintOut( pReportFile ,WSTR("\r\n") );
}
/// <summary>
/// 获得运行程序的版本信息。
/// </summary>
/// <param name="szBuffer">
/// 用于保存信息的缓冲区(必须足够大)。
/// </param>
/// <returns>
/// 返回程序信息。
/// </returns>
WCHAR* XDBExceptionReport::GetApplicationInfo( WCHAR* szBuffer,size_t szLength )
{
XDB_ASSERT( szBuffer != NULL );
// 获得全路径
WCHAR szModuleName[MAX_PATH]= { 0 };
WCHAR szAppName[MAX_PATH] = { 0 };
::GetModuleFileName( NULL,szModuleName,MAX_PATH );
// 获得程序名称
WCHAR* p = wcsrchr( szModuleName, WSTR('\\') );
if( p == NULL )
wcscpy( szAppName, szModuleName );
else
wcscpy( szAppName, p+1 );
// 获得版本信息
WCHAR szVersion[256] = { 0 };
GetFileVersion( szModuleName,WSTR("FileVersion"),szVersion );
// 获得时间信息
SYSTEMTIME localTime = { 0 };
GetLocalTime( &localTime );
WCHAR szTime[128] ={ 0 };
swprintf( szTime , 128,L"%04d-%02d-%02d %02d:%02d:%02d.%03d", localTime.wYear , localTime.wMonth , localTime.wDay , localTime.wHour , localTime.wMinute, localTime.wSecond, localTime.wMilliseconds );
// 构造返回信息
swprintf( szBuffer,szLength,WSTR("%s %s [%s]"),szAppName,szVersion ,szTime );
return szBuffer;
}
/// <summary>
/// 获得文件版本信息。
/// </summary>
/// <param name="lpszFileName">
/// 文件名。
/// </param>
/// <param name="lpszVersionName">
/// 要获取的版本信息。
/// </param>
/// <param name="lpszVersion">
/// 用于返回版本信息,必须具有足够的空间。
/// </param>
/// <returns>
/// 是否成功。
/// </returns>
BOOL XDBExceptionReport::GetFileVersion(
LPCWSTR lpszFileName,
LPCWSTR lpszVersionName,
LPWSTR lpszVersion )
{
// 检查参数
if( lpszFileName == NULL || lpszVersionName == NULL || lpszVersion == NULL )
return FALSE;
// 局部变量
DWORD dwVerInfoSize = 0; // Size of version information block
DWORD dwVerHnd = 0; // An 'ignored' parameter, always '0'
BOOL bRet = FALSE;
// 获取该文件的版本信息资源的大小
dwVerInfoSize = ::GetFileVersionInfoSize( (LPWSTR)lpszFileName, &dwVerHnd );
if(dwVerInfoSize == 0)
return FALSE;
//获取该文件的版本信息资源
WCHAR *szVersionInfo = new WCHAR[dwVerInfoSize];
XDB_ASSERT( szVersionInfo != NULL );
ZeroMemory( szVersionInfo,dwVerInfoSize );
bRet = ::GetFileVersionInfo( (LPWSTR)lpszFileName, dwVerHnd, dwVerInfoSize, szVersionInfo );
if (!bRet)
{
delete []szVersionInfo;
return FALSE;
}
// 获取该文件的版本信息资源中的语言代号
DWORD *lpLanguageCode = NULL;
UINT nLen = 0;
bRet = ::VerQueryValue(
szVersionInfo,
WSTR("\\VarFileInfo\\Translation"),
(LPVOID *)&lpLanguageCode,
&nLen);
if (!bRet)
{
delete []szVersionInfo;
return FALSE;
}
// 生成版本信息字符串
WCHAR szVersionName[64] = { 0 };
swprintf( szVersionName,64, WSTR("\\StringFileInfo\\%04hX%04hX\\%s"), LOWORD(*lpLanguageCode), HIWORD(*lpLanguageCode), lpszVersionName) ;
// 获取该文件的版本信息资源中指定的某项版本信息
LPWSTR szVersion = NULL; // String pointer to 'version' text
bRet = ::VerQueryValue(
szVersionInfo,
szVersionName,
(LPVOID *)&szVersion,
&nLen );
if (!bRet)
{
delete []szVersionInfo;
return FALSE;
}
// 拷贝版本信息
wcscpy( lpszVersion,szVersion );
if ( wcsstr( lpszVersionName, WSTR("Version")) != NULL )
{
// 如果指定的版本信息名称中包含"Version"的话,则将版本号中的","更换为"."
for ( WCHAR *p = lpszVersion; *p != WSTR('\0'); p++ )
{
if( *p == WSTR(',') )
*p = WSTR('.');
}
}
// 释放内存
delete []szVersionInfo;
return TRUE;
}
/// <summary>
/// 获得异常代码的字符串描述。
/// </summary>
/// <param name="dwCode">
/// 异常代码。
/// </param>
/// <returns>
/// 异常代码的字符串描述。
/// </returns>
LPWSTR XDBExceptionReport::GetExceptionString( DWORD dwCode )
{
#define EXCEPTION( x ) case EXCEPTION_##x: return WSTR(#x);
switch ( dwCode )
{
EXCEPTION( ACCESS_VIOLATION )
EXCEPTION( DATATYPE_MISALIGNMENT )
EXCEPTION( BREAKPOINT )
EXCEPTION( SINGLE_STEP )
EXCEPTION( ARRAY_BOUNDS_EXCEEDED )
EXCEPTION( FLT_DENORMAL_OPERAND )
EXCEPTION( FLT_DIVIDE_BY_ZERO )
EXCEPTION( FLT_INEXACT_RESULT )
EXCEPTION( FLT_INVALID_OPERATION )
EXCEPTION( FLT_OVERFLOW )
EXCEPTION( FLT_STACK_CHECK )
EXCEPTION( FLT_UNDERFLOW )
EXCEPTION( INT_DIVIDE_BY_ZERO )
EXCEPTION( INT_OVERFLOW )
EXCEPTION( PRIV_INSTRUCTION )
EXCEPTION( IN_PAGE_ERROR )
EXCEPTION( ILLEGAL_INSTRUCTION )
EXCEPTION( NONCONTINUABLE_EXCEPTION )
EXCEPTION( STACK_OVERFLOW )
EXCEPTION( INVALID_DISPOSITION )
EXCEPTION( GUARD_PAGE )
EXCEPTION( INVALID_HANDLE )
}
// If not one of the "known" exceptions, try to get the string
// from NTDLL.DLL's message table.
static WCHAR szBuffer[512] = { 0 };
::FormatMessage(
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle( WSTR("NTDLL.DLL") ),
dwCode, 0, szBuffer, sizeof( szBuffer ), 0 );
return szBuffer;
}
/// <summary>
/// 获得基本类型的字符串描述。
/// </summary>
/// <param name="dwBasicType">
/// 基本类型。
/// </param>
/// <returns>
/// 基本类型的字符串表示。
/// </returns>
WCHAR* XDBExceptionReport::GetBasicTypeString(
DWORD dwBasicType )
{
#define BASE_TYPE_CASE(x) case bt##x : return WSTR(#x);break;
switch( dwBasicType )
{
BASE_TYPE_CASE( NoType )
BASE_TYPE_CASE( Void )
BASE_TYPE_CASE( Char )
BASE_TYPE_CASE( WChar )
BASE_TYPE_CASE( Int )
BASE_TYPE_CASE( UInt )
BASE_TYPE_CASE( Float )
BASE_TYPE_CASE( BCD )
BASE_TYPE_CASE( Bool )
BASE_TYPE_CASE( Long )
BASE_TYPE_CASE( ULong )
BASE_TYPE_CASE( Currency )
BASE_TYPE_CASE( Date )
BASE_TYPE_CASE( Variant )
BASE_TYPE_CASE( Complex )
BASE_TYPE_CASE( Bit )
BASE_TYPE_CASE( BSTR )
BASE_TYPE_CASE( Hresult )
}
return WSTR("(UnknownType)");
}
/// <summary>
/// 获得符号类型的字符串描述。
/// </summary>
/// <param name="dwSymbolTag">
/// 符号类型。
/// </param>
/// <returns>
/// 符号类型的字符串描述。
/// </returns>
WCHAR* XDBExceptionReport::GetSymbolTagString(
DWORD dwSymbolTag )
{
#define SYM_TAG_CASE(x) case SymTag##x : return WSTR(#x);break;
switch( dwSymbolTag )
{
SYM_TAG_CASE( Null )
SYM_TAG_CASE( Exe )
SYM_TAG_CASE( Compiland )
SYM_TAG_CASE( CompilandDetails )
SYM_TAG_CASE( CompilandEnv )
SYM_TAG_CASE( Function )
SYM_TAG_CASE( Block )
SYM_TAG_CASE( Data )
SYM_TAG_CASE( Annotation )
SYM_TAG_CASE( Label )
SYM_TAG_CASE( PublicSymbol )
SYM_TAG_CASE( UDT )
SYM_TAG_CASE( Enum )
SYM_TAG_CASE( FunctionType )
SYM_TAG_CASE( PointerType )
SYM_TAG_CASE( ArrayType )
SYM_TAG_CASE( BaseType )
SYM_TAG_CASE( Typedef )
SYM_TAG_CASE( BaseClass )
SYM_TAG_CASE( Friend )
SYM_TAG_CASE( FunctionArgType )
SYM_TAG_CASE( FuncDebugStart )
SYM_TAG_CASE( FuncDebugEnd )
SYM_TAG_CASE( UsingNamespace )
SYM_TAG_CASE( VTableShape )
SYM_TAG_CASE( VTable )
SYM_TAG_CASE( Custom )
SYM_TAG_CASE( Thunk )
SYM_TAG_CASE( CustomType )
SYM_TAG_CASE( ManagedType )
SYM_TAG_CASE( Dimension )
}
return WSTR("(UnknownTag)");
}
/// <summary>
/// 获得引起异常的地址信息。
/// </summary>
/// <param name="addr">
/// 地址信息。
/// </param>
/// <param name="szModule">
/// 用于保存模块名的缓冲。
/// </param>
/// <param name="len">
/// 缓冲区大小。
/// </param>
/// <param name="section">
/// 地址所位于的节。
/// </param>
/// <param name="offset">
/// 地址偏移。
/// </param>
/// <returns>
/// 成功返回TRUE,否则返回FALSE。
/// </returns>
BOOL XDBExceptionReport::GetLogicalAddress(
PVOID addr, PWSTR szModule, DWORD len,
DWORD_PTR& section, DWORD_PTR& offset )
{
// XDB_ASSERT( addr != NULL );
XDB_ASSERT( szModule != NULL );
XDB_ASSERT( len > 0 );
section = 0;
offset = 0;
HMODULE hMod = NULL;
hMod = (HMODULE)SymGetModuleBase64( m_hProcess, (DWORD64)addr );
if( hMod == NULL )
{
// 尝试另外的方法
MEMORY_BASIC_INFORMATION mbi = { 0 };
if( 0 == ::VirtualQuery( addr, &mbi, sizeof(MEMORY_BASIC_INFORMATION) ) )
return FALSE;
// 模块地址就是AllocationBase
hMod = (HMODULE) mbi.AllocationBase;
}
// 获得模块名称
if ( !GetModuleFileName( hMod, szModule, len ) )
return FALSE;
// 获得PE Header
PIMAGE_NT_HEADERS pNtHdr = ImageNtHeader( hMod );
if( pNtHdr == NULL ) return FALSE;
// 获得SECTION
DWORD rva = ( DWORD )( (BYTE*)addr - (BYTE*)hMod );
PIMAGE_SECTION_HEADER pFirst = IMAGE_FIRST_SECTION( pNtHdr );
PIMAGE_SECTION_HEADER pSection = ImageRvaToSection( pNtHdr,hMod,rva );
if( pSection == NULL ) return FALSE;
// 计算节号和偏移
section = pSection - pFirst + 1;
offset = rva - pSection->VirtualAddress;
return TRUE;
}
/// <summary>
/// 输出调用堆栈信息。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="pContext">
/// 上下文环境句柄。
/// </param>
/// <param name="bWriteVariables">
/// 是否输出参数信息。
/// </param>
void XDBExceptionReport::WriteStackDetails(
HANDLE pReportFile,
PCONTEXT pContext,
bool bWriteVariables )
{
DWORD dwMachineType = 0; // CPU类型
STACKFRAME64 sf = { 0 } ; // 调用堆栈帧
WCHAR szModule[MAX_PATH] = WSTR(""); // 模块路径
WCHAR szError[MAX_TRACE_BUFFER_LEN] = { 0 };// 错误信息
DWORD_PTR section = 0, offset = 0; // 逻辑地址
DWORD64 symDisplacement = 0; // 相对于符号起始位置的偏移
BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 1024 ] = { 0 };// 用于保存符号名的缓冲区
IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE64) }; // 源文件及行号信息
DWORD dwLineDisplacement = 0; // 相对偏移
// 输出分隔头
PrintOut( pReportFile , WSTR("\r\n=====================================================\r\n") );
PrintOut( pReportFile , WSTR("Call stack:\r\n") );
PrintOut( pReportFile , WSTR("=====================================================\r\n") );
PrintOut( pReportFile , WSTR("Address Frame Function SourceFile\r\n") );
//=============================================================
//
// 对于Intel CPU,需要在首次调用StackWalk64函数之前初始化
// STACKFRAME64成员的AddrPC和AddrFrame成员
//
//=============================================================
#if defined(_M_IX86) // 32bit
sf.AddrPC.Offset = pContext->Eip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Offset = pContext->Esp;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = pContext->Ebp;
sf.AddrFrame.Mode = AddrModeFlat;
dwMachineType = IMAGE_FILE_MACHINE_I386;
#elif defined( _M_IA64 ) // Itanium Processor Family
sf.AddrPC.Offset = pContext->StIIP;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Offset = pContext->IntSp;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = pContext->IntSp;
sf.AddrFrame.Mode = AddrModeFlat;
sf.AddrBStore.Offset = pContext->RsBSP;
sf.AddrBStore.Mode = AddrModeFlat;
dwMachineType = IMAGE_FILE_MACHINE_IA64;
#elif defined( _M_X64 ) // X64
sf.AddrPC.Offset = pContext->Rip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrStack.Offset = pContext->Rsp;
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = pContext->Rsp;
sf.AddrFrame.Mode = AddrModeFlat;
dwMachineType = IMAGE_FILE_MACHINE_AMD64;
#else
#error "OS not supported!"
#endif
// 遍历调用堆栈
while ( true )
{
// 获取下一个调用帧
if ( !StackWalk64(dwMachineType,
m_hProcess,
GetCurrentThread(),
&sf,
pContext,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL ) )
break;// 结束循环
// 检查该帧是否正常( EBP != 0 )
if ( 0 == sf.AddrFrame.Offset )
break;// 结束循环
// 输出EIP及EBP
PrintOut( pReportFile , WSTR("%I64X %I64X "),
sf.AddrPC.Offset,
sf.AddrFrame.Offset );
// 获取该调用帧对应的函数名
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); // !! sizeof(symbolBuffer);
pSymbol->MaxNameLen = 1024;
DWORD64 symDisplacement = 0;
if ( SymFromAddr(m_hProcess,sf.AddrPC.Offset,&symDisplacement,pSymbol))
{
// 如果找到了符号,则输出其名称及偏移
#ifdef DBGHELP_TRANSLATE_TCHAR
PrintOut( pReportFile , WSTR("%ls+%I64X"), pSymbol->Name, symDisplacement );
#else
PrintOut( pReportFile , WSTR("%hs+%I64X"), pSymbol->Name, symDisplacement );
#endif
}
else // 如果无法找到符号,则返回其对应的逻辑(虚拟)地址
{
// 当没有相应的PDB文件时,就会走到这儿
GetLogicalAddress( (PVOID)sf.AddrPC.Offset,
szModule, sizeof(szModule), section, offset );
PrintOut( pReportFile , WSTR("%IX:%IX %s\r\n"), section, offset, szModule );
// 继续处理下一个堆栈帧
continue;
}
// 获取该调用帧对应的源文件及行号信息
if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset,
&dwLineDisplacement, &lineInfo ) )
{
#ifdef DBGHELP_TRANSLATE_TCHAR
PrintOut( pReportFile , WSTR(" %ls line %u"), lineInfo.FileName,lineInfo.LineNumber);
#else
PrintOut( pReportFile , WSTR(" %hs line %u"), lineInfo.FileName,lineInfo.LineNumber);
#endif
}
else // 如果不存在相应的PDB文件,则输出模块信息
{
// 打印模块信息
GetLogicalAddress( (PVOID)sf.AddrPC.Offset,
szModule, sizeof(szModule), section, offset );
PrintOut( pReportFile ,WSTR( " %s %IX:%IX \r\n"), szModule, section, offset );
// 继续处理下一个调用帧
continue;
}
PrintOut( pReportFile , WSTR("\r\n") );
// 检查是否需要输出局部变量及函数参数信息
if ( bWriteVariables )
{
// 设置局部变量对应的上下文
IMAGEHLP_STACK_FRAME imagehlpStackFrame = { 0 };
imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
if( SymSetContext( m_hProcess, &imagehlpStackFrame, NULL ) )
{
// 枚举局部变量及函数参数
EnumerateSymbolsCallbackPara stParam = { &sf, pReportFile };
SymEnumSymbols( m_hProcess, 0, NULL, EnumerateSymbolsCallback, &stParam ) ;
PrintOut( pReportFile , WSTR("\r\n") );
}
else // 如果没有对应的PDB文件,则无法获得局部变量及函数参数信息
{
// 输出错误信息
::FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
0,szError,MAX_TRACE_BUFFER_LEN-1,NULL );
PrintOut( pReportFile , WSTR("SymSetContext Failed: %X : %s\r\n"),GetLastError(),szError );
}
}// bWriteVariables
}// while loop
}
/// <summary>
/// 枚举符号信息的回调函数。
/// </summary>
/// <param name="pSymInfo">
/// 符号信息。
/// </param>
/// <param name="SymbolSize">
/// 符号尺寸。
/// </param>
/// <param name="UserContext">
/// 用户参数。
/// </param>
/// <returns>
/// 返回TRUE时,继续枚举;否则,终止枚举过程。
/// </returns>
BOOL CALLBACK XDBExceptionReport::EnumerateSymbolsCallback(
PSYMBOL_INFO pSymInfo,
ULONG SymbolSize,
PVOID UserContext )
{
XDB_ASSERT( pSymInfo != NULL );
BOOL ContinueEnum = TRUE;
EnumerateSymbolsCallbackPara* pParam = ( EnumerateSymbolsCallbackPara * )UserContext;
// 过滤掉不准备处理的类型
if( ( pSymInfo->Tag != SymTagUDT ) && // 11:自定义类型
( pSymInfo->Tag != SymTagData ) && // 07:数据
( pSymInfo->Tag != SymTagEnum ) && // 12:枚举
( pSymInfo->Tag != SymTagPointerType) && // 14:指针
( pSymInfo->Tag != SymTagArrayType ) && // 15:数组
( pSymInfo->Tag != SymTagBaseType ) && // 16:基本类型
( pSymInfo->Tag != SymTagBaseClass ) ) // 18:自定义类型的基类
return TRUE;
// 查询符号信息
__try
{
// (调试)输出符号信息
// DumpSymbolInfo( pParam->reportFile , pSymInfo );
// 尝试获得变量的值
FormatSymbolValue( pSymInfo, pParam->stackFrame, pParam->reportFile );
PrintOut( pParam->reportFile, WSTR("\r\n" ) );
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
PrintOut( pParam->reportFile ,WSTR("\tError on symbol:\'%hs\'.\r\n"), pSymInfo->Name );
ContinueEnum = FALSE;
}
return ContinueEnum;
}
/// <summary>
/// 格式化符号值。
/// </summary>
/// <param name="pSym">
/// 符号信息。
/// </param>
/// <param name="sf">
/// 堆栈帧。
/// </param>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <returns>
/// 格式化结果字符串。
/// </returns>
bool XDBExceptionReport::FormatSymbolValue(
PSYMBOL_INFO pSym,
LPSTACKFRAME64 sf,
HANDLE pReportFile )
{
// 注意:sf可以为NULL
XDB_ASSERT( pSym != NULL );
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
// 确定属性
if( pSym->Flags & SYMFLAG_PARAMETER ) // 参数
PrintOut( pReportFile, WSTR("Parameter ") );
else if ( pSym->Flags & SYMFLAG_LOCAL ) // 局部变量
PrintOut( pReportFile, WSTR("Local " ) );
// 显示变量名称
#ifdef DBGHELP_TRANSLATE_TCHAR
PrintOut( pReportFile, WSTR("\'%ls\'"), pSym->Name );
#else
PrintOut( pReportFile, WSTR("\'%hs\'"), pSym->Name );
#endif
// 如果是寄存器变量,则返回(无法得到当时的寄存器值)
if( pSym->Flags & SYMFLAG_REGISTER )
{
PrintOut( pReportFile, WSTR(" (register variable) ") );
return false;
}
// 确定变量在内存中的地址
DWORD_PTR pVariable = 0;
if ( pSym->Flags & SYMFLAG_REGREL ) // 相对(栈基址寄存器)的偏移(局部变量或参数)
{
if( sf == NULL ) // 无效参数
{
PrintOut( pReportFile, WSTR(" (null stack frame) ") );
return false;
}
pVariable = (DWORD_PTR)sf->AddrFrame.Offset; // EBP
pVariable += (DWORD_PTR)pSym->Address;
}
else // 绝对地址(全局变量才会使用绝对地址)
pVariable = (DWORD_PTR)pSym->Address;
// 输出变量信息
return DumpTypeIndex(pReportFile,pSym->ModBase, pSym->TypeIndex,0, pVariable );
}
/// <summary>
/// 输出类型的属性信息。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="modBase">
/// 模块基址。
/// </param>
/// <param name="dwTypeIndex">
/// 类型索引。
/// </param>
/// <param name="dwOffset">
/// 变量地址。
/// </param>
void XDBExceptionReport::DumpTypeInfo(
HANDLE pReportFile,
DWORD64 modBase,
DWORD dwTypeIndex ,
DWORD_PTR dwOffset )
{
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
DWORD dwValue = 0;
DWORD64 dwValue64 = 0;
VARIANT varValue = { 0 };
WCHAR* wszValue = NULL;
// DWORD类型的数据
#define PRINT_OUT_DWORD( x ) \
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,x,&dwValue ) ) \
PrintOut( pReportFile,WSTR("\t%s = %d\n"),WSTR(#x),dwValue );
// DWORD64类型的数据
#define PRINT_OUT_DWORD64( x ) \
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,x,&dwValue64 ) ) \
PrintOut( pReportFile,WSTR("\t%s = %I64X\n"),WSTR(#x),dwValue64 );
// 字符串类型的数据
#define PRINT_OUT_WSTR( x ) \
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,x,&wszValue ) ) \
{ \
PrintOut( pReportFile,WSTR("\t%s = %ls\n"),WSTR(#x),wszValue ); \
LocalFree( wszValue ); \
wszValue = NULL; \
}
// 输出头部信息
PrintOut( pReportFile, WSTR("\r\n\t=========TypeInfo BEGIN==========\r\n" ));
PrintOut( pReportFile, WSTR("\tTypeIndex = %d\n"), dwTypeIndex );
PrintOut( pReportFile, WSTR("\tOffset = %IX\n"), dwOffset );
// 输出详细信息
// 类型(或成员)名称
PRINT_OUT_WSTR( TI_GET_SYMNAME );
// 符号类型
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_SYMTAG,&dwValue ) )
PrintOut( pReportFile, WSTR("\tTI_GET_SYMTAG\t = %ls\n"), GetSymbolTagString( dwValue ) );
// 符号基本数据类型
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_BASETYPE,&dwValue ) )
PrintOut( pReportFile, WSTR("\tTI_GET_BASETYPE\t = %ls\n"), GetBasicTypeString(dwValue) );
// 符号的值(如果存在的话)
::VariantInit( &varValue );
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_VALUE,&varValue) )
{
PrintOut( pReportFile, WSTR("\tTI_GET_VALUE\t"));
FormatVariantValue( pReportFile,&varValue );
PrintOut( pReportFile, WSTR("\r\n"));
::VariantClear( &varValue );
}
// 其他信息
PRINT_OUT_DWORD64( TI_GET_LENGTH )
PRINT_OUT_DWORD64( TI_GET_ADDRESS)
PRINT_OUT_DWORD( TI_GET_TYPE )
PRINT_OUT_DWORD( TI_GET_TYPEID )
PRINT_OUT_DWORD( TI_GET_SYMINDEX )
PRINT_OUT_DWORD( TI_GET_ARRAYINDEXTYPEID )
PRINT_OUT_DWORD( TI_GET_DATAKIND )
PRINT_OUT_DWORD( TI_GET_ADDRESSOFFSET )
PRINT_OUT_DWORD( TI_GET_OFFSET )
PRINT_OUT_DWORD( TI_GET_COUNT )
PRINT_OUT_DWORD( TI_GET_CHILDRENCOUNT )
PRINT_OUT_DWORD( TI_GET_BITPOSITION )
PRINT_OUT_DWORD( TI_GET_VIRTUALBASECLASS )
PRINT_OUT_DWORD( TI_GET_VIRTUALBASEPOINTEROFFSET )
PRINT_OUT_DWORD( TI_GET_CLASSPARENTID )
PRINT_OUT_DWORD( TI_GET_NESTED )
PRINT_OUT_DWORD( TI_GET_LEXICALPARENT )
PRINT_OUT_DWORD( TI_GET_THISADJUST )
PRINT_OUT_DWORD( TI_GET_UDTKIND )
PRINT_OUT_DWORD( TI_IS_EQUIV_TO )
PRINT_OUT_DWORD( TI_GET_CALLING_CONVENTION )
// 未输出的信息
// TI_FINDCHILDREN
// TI_GET_VIRTUALTABLESHAPEID
// TI_GTIEX_REQS_VALID
// TI_IS_CLOSE_EQUIV_TO
// 打印结束空行
PrintOut( pReportFile, WSTR("\t=========TypeInfo END============\r\n" ));
}
/// <summary>
/// 输出符号信息。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="pSymInfo">
/// 符号信息结构。
/// </param>
void XDBExceptionReport::DumpSymbolInfo(
HANDLE pReportFile,
PSYMBOL_INFO pSymInfo )
{
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
XDB_ASSERT( pSymInfo != NULL );
PrintOut( pReportFile, WSTR("\t=========SYMBOL BEGIN==========\n") );
PrintOut( pReportFile, WSTR("\tTypeIndex = %d\n"),pSymInfo->TypeIndex );
// PrintOut( pReportFile, WSTR("\tIndex = %d\n"),pSymInfo->info );
PrintOut( pReportFile, WSTR("\tReserved[0] = %d\n"),pSymInfo->Reserved[0] );
PrintOut( pReportFile, WSTR("\tReserved[1] = %d\n"),pSymInfo->Reserved[1] );
PrintOut( pReportFile, WSTR("\tSize = %d\n"),pSymInfo->Size );
PrintOut( pReportFile, WSTR("\tFlags = %08X\n"),pSymInfo->Flags );
PrintOut( pReportFile, WSTR("\tValue = %d\n"),pSymInfo->Value );
PrintOut( pReportFile, WSTR("\tAddress = %I64X\n"),pSymInfo->Address );
PrintOut( pReportFile, WSTR("\tRegister = %d\n"),pSymInfo->Register);
PrintOut( pReportFile, WSTR("\tScope = %d\n"),pSymInfo->Scope );
PrintOut( pReportFile, WSTR("\tTag = %d\n"),pSymInfo->Tag );
#ifdef DBGHELP_TRANSLATE_TCHAR
PrintOut( pReportFile, WSTR("\tName = %ls\n"),pSymInfo->Name);
#else
PrintOut( pReportFile, WSTR("\tName = %hs\n"),pSymInfo->Name);
#endif
PrintOut( pReportFile,WSTR("\t=========SYMBOL END============\n") );
}
/// <summary>
/// 输出类型信息。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="modBase">
/// 模块基址。
/// </param>
/// <param name="dwTypeIndex">
/// 类型索引。
/// </param>
/// <param name="nestingLevel">
/// 嵌套层次。
/// </param>
/// <param name="offset">
/// 偏移。
/// </param>
/// <returns>
/// 成功或失败标志。
/// </returns>
bool XDBExceptionReport::DumpTypeIndex(
HANDLE pReportFile,
DWORD64 modBase,
DWORD dwTypeIndex,
unsigned nestingLevel,
DWORD_PTR offset )
{
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
// 检查嵌套层次
if( nestingLevel > MAX_NESTING_LEVEL )
return false;
// (调试)输出类型信息
// DumpTypeInfo( pReportFile,modBase,dwTypeIndex,offset );
// 首先获得符号的类型
DWORD dwSymbolTag = 0;
if( !SymGetTypeInfo( m_hProcess, modBase,dwTypeIndex,TI_GET_SYMTAG,&dwSymbolTag ) )
return false; // 无法获得符号的类型
// 获得长度
DWORD64 dwLength = 0; // 下列函数有可能失败
SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_LENGTH,&dwLength );
// 过滤掉不准备处理的类型
if( ( dwSymbolTag != SymTagUDT ) && // 11:自定义类型
( dwSymbolTag != SymTagData ) && // 07:数据
( dwSymbolTag != SymTagEnum ) && // 12:枚举
( dwSymbolTag != SymTagPointerType ) && // 14:指针
( dwSymbolTag != SymTagArrayType ) && // 15:数组
( dwSymbolTag != SymTagBaseType ) && // 16:基本类型
( dwSymbolTag != SymTagBaseClass ) ) // 18:自定义类型的基类
return false;
// 获得符号类型的名称(类型名称或结构成员,对于基本类型会失败)
WCHAR * pwszTypeName = NULL;
if ( SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME,&pwszTypeName ) )
{
XDB_ASSERT( pwszTypeName != NULL );
PrintOut( pReportFile, WSTR(" %ls"), pwszTypeName );
LocalFree( pwszTypeName );
}
// 检查地址是否合法
if( ::IsBadReadPtr( (LPCVOID) offset, sizeof(DWORD_PTR)) )
{
PrintOut( pReportFile, WSTR("(E_ADDR:%IX)\n"),offset );
return false;
}
// 根据不同的类型进行处理
VARIANT varValue = { 0 };
DWORD dwDataKind = 0;
DWORD dwMemberOffset = 0;
DWORD dwChildrenCount = 0;
DWORD dwTypeId = 0;
DWORD64 dwMemberLength = 0;
DWORD dwBasicType = btNoType;
DWORD dwMemberTag = 0;
switch( dwSymbolTag )
{
case SymTagUDT: // 自定义类型
{
// 打印空行
PrintOut( pReportFile, WSTR("\r\n") );
// 检查嵌套级别
if( nestingLevel+1 > MAX_NESTING_LEVEL )
break;
// 确定子节点的个数
if( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,&dwChildrenCount ) )
break;
// 如果没有子节点,则返回
if ( 0 == dwChildrenCount )break;
// 以继承方式扩展TI_FINDCHILDREN_PARAMS结构
struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS
{
ULONG MoreChildIds[MAX_CHILDREN_NUM-1];
FINDCHILDREN(){ Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]); }
} children;
// 调整处理的子节点数目
if( dwChildrenCount > MAX_CHILDREN_NUM )
dwChildrenCount = MAX_CHILDREN_NUM;
children.Count = dwChildrenCount;
children.Start = 0;
// 获得每个子节点的类型索引
if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,&children ) )
break;
// 遍历每个子节点
for ( unsigned i = 0; i < dwChildrenCount; i++ )
{
// 获得成员的偏移
if( !SymGetTypeInfo( m_hProcess, modBase,children.ChildId[i] ,
TI_GET_OFFSET, &dwMemberOffset ) )
{
// 对于函数成员,可能没有偏移
continue;
}
// 获得SymTag
if( !SymGetTypeInfo( m_hProcess,modBase,children.ChildId[i],TI_GET_SYMTAG,&dwMemberTag ) )
{
// 无法获得符号类型,继续处理下一个
continue;
}
// 过滤掉不准备处理的类型
if( ( dwMemberTag != SymTagUDT ) && // 11:自定义类型
( dwMemberTag != SymTagData ) && // 07:数据
( dwMemberTag != SymTagEnum ) && // 12:枚举
( dwMemberTag != SymTagPointerType ) && // 14:指针
( dwMemberTag != SymTagArrayType ) && // 15:数组
( dwMemberTag != SymTagBaseType ) && // 16:基本类型
( dwMemberTag != SymTagBaseClass ) ) // 18:自定义类型的基类
{
// 不是我们想处理的类型,继续下一个
continue;
}
// 根据嵌套级别缩进
PrintOut( pReportFile,WSTR("%*s"),(nestingLevel+1)*4,WSTR(" ") );
// 递归处理每个成员,嵌套层次递增
DumpTypeIndex( pReportFile,modBase,
children.ChildId[i],nestingLevel+1,
offset+dwMemberOffset ) ;
// 输出空行
PrintOut( pReportFile, WSTR("\r\n" ) );
} // FOR LOOP
} // SymTagUDT BLOCK
break;
case SymTagEnum: // 枚举类型的值
case SymTagData: // 数据
case SymTagBaseClass: // 基类
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_TYPEID,&dwTypeId ) )
DumpTypeIndex( pReportFile,modBase,dwTypeId,nestingLevel,offset );
break;
case SymTagBaseType: // 基本类型
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_BASETYPE,&dwBasicType ) )
FormatOutputValue( pReportFile,dwBasicType,dwLength,(LPVOID)offset);
break;
case SymTagPointerType: // 指针类型
// 打印地址及指针类型标识
PrintOut( pReportFile,WSTR("\t(*[%IX])\t"),offset );
// 获得数据的类型
if( !SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_TYPEID,&dwTypeId ) )
break;
// 获得内部的类型(特别处理字符串的问题)
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_BASETYPE,&dwBasicType ) )
{
if( dwBasicType == btChar || dwBasicType == btWChar )
{
FormatPointerValue( pReportFile,dwBasicType,dwLength,(LPVOID)offset );
break; // 结束处理
}
}
// 获得内部的类型
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_SYMTAG,&dwMemberTag ) )
{
// 过滤掉不准备处理的类型
if( ( dwMemberTag != SymTagUDT ) && // 11:自定义类型
( dwMemberTag != SymTagData ) && // 07:数据
( dwMemberTag != SymTagEnum ) && // 12:枚举
( dwMemberTag != SymTagPointerType ) && // 14:指针
( dwMemberTag != SymTagArrayType ) && // 15:数组
( dwMemberTag != SymTagBaseType ) && // 16:基本类型
( dwMemberTag != SymTagBaseClass ) ) // 18:自定义类型的基类
break; // 结束处理
}
// 递归调用
DumpTypeIndex( pReportFile,modBase,dwTypeId,nestingLevel,*((LPDWORD)offset) );
break;
case SymTagArrayType: // 数组类型
{
// 打印空行
PrintOut( pReportFile, WSTR("\r\n") );
// 检查嵌套级别
if( nestingLevel+1 > MAX_NESTING_LEVEL )
break;
// 确定子节点的个数,注意是TI_GET_COUNT,而不是TI_GET_CHILDRENCOUNT
if( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_COUNT ,&dwChildrenCount ) )
break;
// 如果没有子节点,则返回
if ( 0 == dwChildrenCount ) break;
if( dwChildrenCount > MAX_ELEMENT_NUM )
dwChildrenCount = MAX_ELEMENT_NUM;
// 获得元素的类型
if( !SymGetTypeInfo( m_hProcess,modBase,dwTypeIndex,TI_GET_TYPEID,&dwTypeId ) )
break;
// 获得内部的类型
if( SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_SYMTAG,&dwMemberTag ) )
{
// 过滤掉不准备处理的类型
if( ( dwMemberTag != SymTagUDT ) && // 11:自定义类型
( dwMemberTag != SymTagData ) && // 07:数据
( dwMemberTag != SymTagEnum ) && // 12:枚举
( dwMemberTag != SymTagPointerType ) && // 14:指针
( dwMemberTag != SymTagArrayType ) && // 15:数组
( dwMemberTag != SymTagBaseType ) && // 16:基本类型
( dwMemberTag != SymTagBaseClass ) ) // 18:自定义类型的基类
break; // 结束处理
}
// 获得元素的长度
if( !SymGetTypeInfo( m_hProcess,modBase,dwTypeId,TI_GET_LENGTH,&dwMemberLength ) )
break;
// 遍历每个子节点
dwMemberOffset = 0;
for ( DWORD i = 0; i < dwChildrenCount; i++ )
{
// 根据嵌套级别缩进
PrintOut( pReportFile,WSTR("%*s"),(nestingLevel+1)*4,WSTR(" ") );
// 打印索引
PrintOut( pReportFile,WSTR("[%d]") , i );
// 输出子节点信息
DumpTypeIndex( pReportFile,modBase,
dwTypeId,nestingLevel+1,
offset+dwMemberOffset );
// 输出空行
PrintOut( pReportFile, WSTR("\r\n" ) );
// 更新偏移
dwMemberOffset += (DWORD)dwMemberLength ;
} // FOR LOOP
}// BLOCK
break;
default:
return false;
break;
}
// 返回
return true;
}
/// <summary>
/// 输出可变类型的数据。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="pVal">
/// 待输出数值。
/// </param>
void XDBExceptionReport::FormatVariantValue(
HANDLE pReportFile,
const VARIANT* pVal )
{
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
XDB_ASSERT( pVal != NULL );
switch( V_VT( pVal ) )
{
case VT_I8:
PrintOut( pReportFile, WSTR(" = %ld"), V_I8(pVal) );
break;
case VT_UI8:
PrintOut( pReportFile, WSTR(" = %ld"), V_UI8(pVal) );
break;
case VT_I4:
PrintOut( pReportFile, WSTR(" = %d"), V_I4(pVal) );
break;
case VT_UI4:
PrintOut( pReportFile, WSTR(" = %d"), V_UI4(pVal) );
break;
case VT_I2:
PrintOut( pReportFile, WSTR(" = %d"), V_I2(pVal) );
break;
case VT_UI2:
PrintOut( pReportFile, WSTR(" = %d"), V_UI2(pVal) );
break;
case VT_I1:
PrintOut( pReportFile, WSTR(" = %d"), V_I1(pVal) );
break;
case VT_UI1:
PrintOut( pReportFile, WSTR(" = %d"), V_UI1(pVal) );
break;
case VT_INT:
PrintOut( pReportFile, WSTR(" = %d"), V_INT(pVal) );
break;
case VT_UINT:
PrintOut( pReportFile, WSTR(" = %d"), V_UINT(pVal) );
break;
case VT_R4:
PrintOut( pReportFile, WSTR(" = %f"), V_R4(pVal) );
break;
case VT_R8:
PrintOut( pReportFile, WSTR(" = %lf"), V_R8(pVal) );
break;
case VT_BSTR:
PrintOut( pReportFile, WSTR(" = \"%ls\""),V_BSTR( pVal ) );
break;
case VT_BOOL:
if( V_BOOL( pVal ) == VARIANT_FALSE )
PrintOut( pReportFile, WSTR(" = VARIANT_FALSE") );
else
PrintOut( pReportFile, WSTR(" = VARIANT_TRUE") );
break;
default:
PrintOut( pReportFile, WSTR(" (TYPE NOT SUPPROTED)") );
break;
}
}
/// <summary>
/// 格式化指针参数值。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="basicType">
/// 参数类型。
/// </param>
/// <param name="length">
/// 值长度。
/// </param>
/// <param name="pAddress">
/// 值地址。
/// </param>
void XDBExceptionReport::FormatPointerValue(
HANDLE pReportFile,
DWORD basicType,
DWORD64 length,
PVOID pAddress )
{
XDB_ASSERT( pAddress != NULL );
// 注意:length长度没有使用,无法区分是单个元素的指针还是数组
if( basicType == btChar )
{
LPSTR pszText = *( LPSTR*)pAddress;
// 检查指针是否有效
// 最多输出64个字符
if( IsBadStringPtrA( pszText,64 ) )
PrintOut( pReportFile, WSTR(" = (E_ADDR:%p)"),pAddress );
else
{
if( pszText[0] == '\0' )
PrintOut( pReportFile, WSTR(" = \"\"") );
else
PrintOut( pReportFile, WSTR(" = \"%.63hs\""),pszText );
}
}
else if( basicType == btWChar )
{
LPWSTR pwszText = *( LPWSTR*)pAddress;
// 检查指针是否有效
// 最多输出64个字符
if( IsBadStringPtrW( pwszText,64 ) )
PrintOut( pReportFile, WSTR(" = (E_ADDR:%p)"),pAddress) ;
else
{
if( pwszText[0] == L'\0' )
PrintOut( pReportFile, WSTR(" = \"\"") );
else
PrintOut( pReportFile, WSTR(" = \"%.63ls\""),pwszText );
}
}
else
PrintOut( pReportFile, WSTR(" = (PTR:%p)"), pAddress);
}
/// <summary>
/// 格式化参数值。
/// </summary>
/// <param name="pReportFile">
/// 异常报告输出的文件句柄。
/// </param>
/// <param name="basicType">
/// 参数类型。
/// </param>
/// <param name="length">
/// 值长度。
/// </param>
/// <param name="pAddress">
/// 值地址。
/// </param>
void XDBExceptionReport::FormatOutputValue(
HANDLE pReportFile,
DWORD basicType,
DWORD64 length,
PVOID pAddress )
{
XDB_ASSERT( pAddress != NULL );
XDB_ASSERT( pReportFile != INVALID_HANDLE_VALUE );
// 检查地址
if( pAddress == NULL )
{
PrintOut( pReportFile,WSTR(" = (E_ADDR:%p)"), pAddress );
return;
}
// 根据数据类型进行处理
CHAR ch = 0;
WCHAR wch= 0;
switch( basicType )
{
case btChar : /// ANSI字符
ch = *(CHAR*)pAddress;
if( ch != 0 )
PrintOut( pReportFile, WSTR(" = '%hc'"),ch );
else
PrintOut( pReportFile, WSTR(" = ''") );
break;
case btWChar : /// UNICODE字符
wch = *(WCHAR*)pAddress;
if( wch != 0 )
PrintOut( pReportFile, WSTR(" = '%lc'"),wch );
else
PrintOut( pReportFile, WSTR(" = ''") );
break;
case btBSTR : /// 字符串
if( !IsBadStringPtrW( *(LPWSTR*)pAddress,64 ) )
PrintOut( pReportFile, WSTR(" = \"%.63ls\""),*(LPWSTR*)pAddress );
else
PrintOut( pReportFile, WSTR(" = [%p]"),pAddress );
break;
case btInt : /// 有符号整数
case btUInt : /// 无符号整数
case btLong : /// 有符号长整数
case btULong : /// 无符号长整数
case btHresult : /// HRESULT
case btBool : /// 布尔值
if( length == sizeof(BYTE) )
PrintOut( pReportFile, WSTR(" = %d"), *(BYTE*)pAddress );
else if( length == sizeof(SHORT) )
PrintOut( pReportFile, WSTR(" = %d"), *(WORD*)pAddress );
else if( length == sizeof( DWORD ) )
PrintOut( pReportFile, WSTR(" = %d"), *(DWORD*)pAddress );
else if( length == sizeof( DWORD64 ) )
PrintOut( pReportFile, WSTR(" = %ld"), *(DWORD64*)pAddress );
else
PrintOut( pReportFile, WSTR(" = [%p]"), pAddress);
break;
case btFloat : /// 浮点数
if( length == 4 )
PrintOut( pReportFile, WSTR(" = %f"), *(float*)pAddress);
else if( length == 8 )
PrintOut( pReportFile, WSTR(" = %lf"),*(double *)pAddress );
else
PrintOut( pReportFile, WSTR(" = [%p]"), pAddress);
break;
case btVariant : /// 可变数据类型
{
VARIANT* pVal = ( VARIANT * )pAddress ;
FormatVariantValue( pReportFile,pVal );
}
break;
case btDate : /// 日期类型
case btBCD : /// BCD类型
case btCurrency : /// 货币类型
case btComplex : /// 复数
case btBit : /// 二进制
case btNoType : /// 没有数据类型
default:
PrintOut( pReportFile, WSTR(" = [%p]"), pAddress);
break;
}
}
/// <summary>
/// 输出信息到文件。
/// </summary>
/// <param name="pReportFile">
/// 文件句柄。
/// </param>
/// <param name="format">
/// 格式化字符串。
/// </param>
/// <returns>
/// 写入的字符个数。
/// </returns>
int __cdecl XDBExceptionReport::PrintOut( HANDLE pReportFile,const WCHAR * format, ...)
{
WCHAR szBuff[MAX_OUT_BUFFER+1] = { 0 };
int retValue = 0;
DWORD cbWritten = 0;
va_list argptr;
va_start( argptr, format );
retValue = _vsntprintf( szBuff,MAX_OUT_BUFFER,format, argptr );
va_end( argptr );
::WriteFile( pReportFile, szBuff, retValue * sizeof(WCHAR), &cbWritten, 0 );
return retValue;
}
SEH转化为C++异常<三>
最新推荐文章于 2025-03-11 12:17:08 发布