server-re/third-party/BugTrap-1.3-57bd515/source/SymEngine.cpp
2026-10-14 15:54:34 -04:00

3271 lines
102 KiB
C++

/*
* This is a part of the BugTrap package.
* Copyright (c) 2005-2009 IntelleSoft.
* All rights reserved.
*
* Description: Low-level wrapper for Debug Help API.
* Author: Maksim Pyatkovskiy.
* Note: Portions of this code are based on Bugslayer code developed by John Robbins.
*
* This source code is only intended as a supplement to the
* BugTrap package reference and related electronic documentation
* provided with the product. See these sources for detailed
* information regarding the BugTrap package.
*/
#include "StdAfx.h"
#include "SymEngine.h"
#include "BugTrap.h"
#include "BugTrapUtils.h"
#include "TextFormat.h"
#include "Globals.h"
#include "MemStream.h"
#include "FileStream.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
/// Maximum number of traced frames.
#define MAX_FRAME_COUNT 1000
/// Maximum number of inner errors.
#define MAX_INNER_ERROR_COUNT 10
#if defined _M_IX86
#define IMAGE_FILE_MACHINE_TYPE IMAGE_FILE_MACHINE_I386
#elif defined _M_X64
#define IMAGE_FILE_MACHINE_TYPE IMAGE_FILE_MACHINE_AMD64
#else
#error CPU architecture is not supported.
#endif
CSymEngine::CEngineParams::CEngineParams(void)
{
GetLocalTime(&m_DateTime);
m_pExceptionPointers = NULL;
m_eExceptionType = NO_EXCEPTION;
m_dwLastError = NOERROR;
MakeScreenShot();
}
/**
* @param pExceptionPointers - current exception information.
* @param eExceptionType - exception type.
*/
CSymEngine::CEngineParams::CEngineParams(PEXCEPTION_POINTERS pExceptionPointers, EXCEPTION_TYPE eExceptionType)
{
_ASSERTE(pExceptionPointers != NULL && eExceptionType != NO_EXCEPTION);
m_dwLastError = GetLastError();
GetLocalTime(&m_DateTime);
m_pExceptionPointers = pExceptionPointers;
m_eExceptionType = eExceptionType;
::GetErrorInfo(0, &m_pErrorInfo);
MakeScreenShot();
}
void CSymEngine::CEngineParams::MakeScreenShot(void)
{
if ((g_dwFlags & (BTF_DETAILEDMODE | BTF_SCREENCAPTURE)) == (BTF_DETAILEDMODE | BTF_SCREENCAPTURE))
m_pScreenShot = new CScreenShot();
else
m_pScreenShot = NULL;
}
CSymEngine::CStackTraceEntry::CStackTraceEntry(void)
{
*m_szModule = _T('\0');
*m_szAddress = _T('\0');
*m_szFunctionName = _T('\0');
*m_szFunctionOffset = _T('\0');
*m_szFunctionInfo = _T('\0');
*m_szSourceFile = _T('\0');
*m_szLineNumber = _T('\0');
*m_szLineOffset = _T('\0');
*m_szLineInfo = _T('\0');
}
CSymEngine::CErrorInfo::CErrorInfo(void)
{
m_pszWhat = NULL;
*m_szProcessName = _T('\0');
*m_szProcessID = _T('\0');
}
CSymEngine::CCpusInfo::CCpusInfo(void)
{
m_pszCpuArch = NULL;
m_dwNumCpus = 0;
}
CSymEngine::CCpuInfo::CCpuInfo(void)
{
*m_szCpuId = _T('\0');
*m_szCpuSpeed = _T('\0');
*m_szCpuDescription = _T('\0');
}
CSymEngine::COsInfo::COsInfo(void)
{
m_pszWinVersion = NULL;
*m_szSPVersion = _T('\0');
*m_szBuildNumber = _T('\0');
#ifdef _MANAGED
*m_szNetVersion = _T('\0');
#endif
}
CSymEngine::CMemInfo::CMemInfo(void)
{
*m_szMemoryLoad = _T('\0');
*m_szTotalPhys = _T('\0');
*m_szAvailPhys = _T('\0');
*m_szTotalPageFile = _T('\0');
*m_szAvailPageFile = _T('\0');
}
/**
* @param rErrorInfo - another error info object.
*/
void CSymEngine::CSysErrorInfo::CopyData(const CSysErrorInfo& rErrorInfo)
{
_tcscpy_s(m_szErrorCode, countof(m_szErrorCode), rErrorInfo.m_szErrorCode);
if (rErrorInfo.m_pszMessageBuffer)
{
size_t nMessageSize = _tcslen(rErrorInfo.m_pszMessageBuffer) + 1;
m_pszMessageBuffer = (PTSTR)LocalAlloc(LMEM_FIXED, nMessageSize);
if (m_pszMessageBuffer)
_tcscpy_s(m_pszMessageBuffer, nMessageSize, rErrorInfo.m_pszMessageBuffer);
}
else
m_pszMessageBuffer = NULL;
}
/**
* @param rErrorInfo - another error info object.
* @return reference to this object.
*/
CSymEngine::CSysErrorInfo& CSymEngine::CSysErrorInfo::operator=(const CSysErrorInfo& rErrorInfo)
{
FreeData();
CopyData(rErrorInfo);
return *this;
}
CSymEngine::CRegistersValues::CRegistersValues(void)
{
#if defined _M_IX86
*m_szEax = _T('\0');
*m_szEbx = _T('\0');
*m_szEcx = _T('\0');
*m_szEdx = _T('\0');
*m_szEsi = _T('\0');
*m_szEdi = _T('\0');
*m_szEsp = _T('\0');
*m_szEbp = _T('\0');
*m_szEip = _T('\0');
*m_szSegCs = _T('\0');
*m_szSegDs = _T('\0');
*m_szSegSs = _T('\0');
*m_szSegEs = _T('\0');
*m_szSegFs = _T('\0');
*m_szSegGs = _T('\0');
*m_szEFlags = _T('\0');
#elif defined _M_X64
*m_szRax = _T('\0');
*m_szRbx = _T('\0');
*m_szRcx = _T('\0');
*m_szRdx = _T('\0');
*m_szRsi = _T('\0');
*m_szRdi = _T('\0');
*m_szRsp = _T('\0');
*m_szRbp = _T('\0');
*m_szRip = _T('\0');
*m_szSegCs = _T('\0');
*m_szSegDs = _T('\0');
*m_szSegSs = _T('\0');
*m_szSegEs = _T('\0');
*m_szSegFs = _T('\0');
*m_szSegGs = _T('\0');
*m_szEFlags = _T('\0');
#else
#error CPU architecture is not supported.
#endif
}
/**
* @param rParams - symbolic engine parameters.
*/
void CSymEngine::SetEngineParameters(const CEngineParams& rParams)
{
m_pExceptionPointers = rParams.m_pExceptionPointers;
m_dwLastError = rParams.m_dwLastError;
m_DateTime = rParams.m_DateTime;
m_eExceptionType = rParams.m_eExceptionType;
m_dwExceptionAddress = m_pExceptionPointers != NULL ? (DWORD64)m_pExceptionPointers->ExceptionRecord->ExceptionAddress : MAXULONG_PTR;
m_pScreenShot = rParams.m_pScreenShot;
m_pErrorInfo = rParams.m_pErrorInfo;
AdjustExceptionStackFrame();
#ifdef _MANAGED
m_pNetStackTrace = new CNetStackTrace();
#endif
}
void CSymEngine::ResetEngineParameters(void)
{
m_pExceptionPointers = NULL;
m_dwLastError = NOERROR;
m_eExceptionType = NO_EXCEPTION;
m_dwExceptionAddress = MAXULONG_PTR;
m_pScreenShot = NULL;
m_pErrorInfo = NULL;
GetLocalTime(&m_DateTime);
#ifdef _MANAGED
delete m_pNetStackTrace;
m_pNetStackTrace = NULL;
#endif
}
/**
* @param rParams - symbolic engine parameters.
*/
CSymEngine::CSymEngine(const CEngineParams& rParams)
{
static const TCHAR szKernelDll[] = _T("KERNEL32.DLL");
m_hKernelDll = GetModuleHandle(szKernelDll);
FOpenThread = m_hKernelDll ? (PFOpenThread)GetProcAddress(m_hKernelDll, "OpenThread") : NULL;
static const TCHAR szDbgHelpDll[] = _T("DBGHELP.DLL");
TCHAR szDbgHelpPath[MAX_PATH];
GetModuleFileName(g_hInstance, szDbgHelpPath, countof(szDbgHelpPath));
PathRemoveFileSpec(szDbgHelpPath);
PathAppend(szDbgHelpPath, szDbgHelpDll);
m_hDbgHelpDll = LoadLibrary(szDbgHelpPath);
if (m_hDbgHelpDll == NULL)
m_hDbgHelpDll = LoadLibrary(szDbgHelpDll);
m_hSymProcess = NULL;
#ifdef _MANAGED
m_pNetStackTrace = NULL;
#endif
if (m_hDbgHelpDll != NULL)
{
FSymGetOptions = (PFSymGetOptions)GetProcAddress(m_hDbgHelpDll, "SymGetOptions");
FSymSetOptions = (PFSymSetOptions)GetProcAddress(m_hDbgHelpDll, "SymSetOptions");
FSymInitialize = (PFSymInitialize)GetProcAddress(m_hDbgHelpDll, "SymInitialize");
FSymCleanup = (PFSymCleanup)GetProcAddress(m_hDbgHelpDll, "SymCleanup");
FSymGetModuleBase64 = (PFSymGetModuleBase64)GetProcAddress(m_hDbgHelpDll, "SymGetModuleBase64");
FSymFromAddr = (PFSymFromAddr)GetProcAddress(m_hDbgHelpDll, "SymFromAddr");
FSymGetLineFromAddr64 = (PFSymGetLineFromAddr64)GetProcAddress(m_hDbgHelpDll, "SymGetLineFromAddr64");
FStackWalk64 = (PFStackWalk64)GetProcAddress(m_hDbgHelpDll, "StackWalk64");
FSymFunctionTableAccess64 = (PFSymFunctionTableAccess64)GetProcAddress(m_hDbgHelpDll, "SymFunctionTableAccess64");
FMiniDumpWriteDump = (PFMiniDumpWriteDump)GetProcAddress(m_hDbgHelpDll, "MiniDumpWriteDump");
if (FSymGetOptions && FSymSetOptions && FSymInitialize && FSymCleanup &&
FSymGetModuleBase64 && FSymFromAddr && FSymGetLineFromAddr64 &&
FStackWalk64 && FSymFunctionTableAccess64 && FMiniDumpWriteDump)
{
DWORD dwOptions = FSymGetOptions();
FSymSetOptions(dwOptions | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
m_hSymProcess = g_bWinNT ? GetCurrentProcess() : (HANDLE)GetCurrentProcessId();
if (FSymInitialize(m_hSymProcess, NULL, TRUE))
SetEngineParameters(rParams);
else
m_hSymProcess = NULL;
}
else
{
FreeLibrary(m_hDbgHelpDll);
m_hDbgHelpDll = NULL;
}
}
if (m_hDbgHelpDll == NULL)
{
FSymGetOptions = NULL;
FSymSetOptions = NULL;
FSymInitialize = NULL;
FSymCleanup = NULL;
FSymGetModuleBase64 = NULL;
FSymFromAddr = NULL;
FSymGetLineFromAddr64 = NULL;
FStackWalk64 = NULL;
FSymFunctionTableAccess64 = NULL;
FMiniDumpWriteDump = NULL;
}
if (m_hSymProcess == NULL)
ResetEngineParameters();
}
CSymEngine::~CSymEngine(void)
{
#ifdef _MANAGED
delete m_pNetStackTrace;
#endif
if (m_hSymProcess != NULL)
FSymCleanup(m_hSymProcess);
if (m_hDbgHelpDll != NULL)
FreeLibrary(m_hDbgHelpDll);
}
CSymEngine::CScreenShot::CScreenShot(void)
{
m_dwNumMonitors = 0;
DWORD dwNumMonitors = GetSystemMetrics(SM_CMONITORS);
m_arrBitmaps = new CBitmapInfo[dwNumMonitors];
if (m_arrBitmaps == NULL)
return;
DWORD dwMonitorNumber, dwDeviceNumber;
for (dwMonitorNumber = dwDeviceNumber = 0; dwMonitorNumber < dwNumMonitors; ++dwDeviceNumber)
{
DISPLAY_DEVICE DisplayDevice;
DisplayDevice.cb = sizeof(DisplayDevice);
if (! EnumDisplayDevices(NULL, dwDeviceNumber, &DisplayDevice, 0))
break;
if ((DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0)
continue;
BOOL bResult = FALSE;
HDC hDisplayDC = CreateDC(DisplayDevice.DeviceName, NULL, NULL, NULL);
if (hDisplayDC)
{
int nWidth = GetDeviceCaps(hDisplayDC, HORZRES);
int nHeight = GetDeviceCaps(hDisplayDC, VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hDisplayDC, nWidth, nHeight);
if (hBitmap)
{
HDC hMemDC = CreateCompatibleDC(hDisplayDC);
if (hMemDC)
{
CBitmapInfo* pBitmapInfo = m_arrBitmaps + dwMonitorNumber;
HBITMAP hbmpSafeBitmap = SelectBitmap(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, nWidth, nHeight, hDisplayDC, 0, 0, SRCCOPY);
BITMAP bmpInfo;
GetObject(hBitmap, sizeof(bmpInfo), &bmpInfo);
WORD wPalSize, wBmpBits = bmpInfo.bmPlanes * bmpInfo.bmBitsPixel;
if (wBmpBits <= 1)
{
wBmpBits = 1; // monochrome image
wPalSize = 2;
}
else if (wBmpBits <= 4)
{
wBmpBits = 4; // palette-based 4 bpp image
wPalSize = 16;
}
else if (wBmpBits <= 8)
{
wBmpBits = 8; // palette-based 8 bpp image
wPalSize = 256;
}
else
{
wBmpBits = 16; // force to 16 bpp image (don't allow 24 bpp)
wPalSize = 0; // don't use palette
}
pBitmapInfo->m_dwBmpHdrSize = sizeof(BITMAPINFOHEADER) + wPalSize * sizeof(RGBQUAD);
pBitmapInfo->m_pBmpInfo = (PBITMAPINFO)new BYTE[pBitmapInfo->m_dwBmpHdrSize];
if (pBitmapInfo->m_pBmpInfo)
{
ZeroMemory(pBitmapInfo->m_pBmpInfo, pBitmapInfo->m_dwBmpHdrSize);
BITMAPINFOHEADER& bmpHdr = pBitmapInfo->m_pBmpInfo->bmiHeader;
bmpHdr.biSize = sizeof(bmpHdr);
bmpHdr.biWidth = nWidth;
bmpHdr.biHeight = nHeight;
bmpHdr.biPlanes = 1;
bmpHdr.biBitCount = wBmpBits;
bmpHdr.biCompression = BI_RGB;
// call GetDIBits with a NULL bits array, so it will calculate the biSizeImage field
GetDIBits(hMemDC, hBitmap, 0, nHeight, NULL, pBitmapInfo->m_pBmpInfo, DIB_RGB_COLORS);
if (bmpHdr.biSizeImage == 0)
bmpHdr.biSizeImage = (wBmpBits * nWidth + 31) / 32 * 4 * nHeight;
pBitmapInfo->m_dwBitsArraySize = bmpHdr.biSizeImage;
pBitmapInfo->m_pBitsArray = new BYTE[pBitmapInfo->m_dwBitsArraySize];
if (pBitmapInfo->m_pBitsArray)
{
if (GetDIBits(hMemDC, hBitmap, 0, nHeight, pBitmapInfo->m_pBitsArray, pBitmapInfo->m_pBmpInfo, DIB_RGB_COLORS))
bResult = TRUE;
}
}
if (! bResult)
pBitmapInfo->Free();
else
++dwMonitorNumber;
SelectBitmap(hMemDC, hbmpSafeBitmap);
DeleteDC(hMemDC);
}
DeleteBitmap(hBitmap);
}
DeleteDC(hDisplayDC);
}
}
m_dwNumMonitors = dwMonitorNumber;
}
/**
* @param pszFileName - screen-shot file name.
* @return true if screen-shot has been written successfully.
*/
BOOL CSymEngine::CScreenShot::WriteScreenShot(PCTSTR pszFileName)
{
for (DWORD dwMonitorNumber = 0; dwMonitorNumber < m_dwNumMonitors; ++dwMonitorNumber)
{
CBitmapInfo* pBitmapInfo = m_arrBitmaps + dwMonitorNumber;
if (pBitmapInfo->m_pBmpInfo == NULL)
return FALSE;
TCHAR szFileName[MAX_PATH];
if (m_dwNumMonitors > 1)
_stprintf_s(szFileName, countof(szFileName), _T("%s%d.bmp"), pszFileName, dwMonitorNumber + 1);
else
_stprintf_s(szFileName, countof(szFileName), _T("%s.bmp"), pszFileName);
HANDLE hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
BITMAPFILEHEADER bmpfh;
ZeroMemory(&bmpfh, sizeof(bmpfh));
bmpfh.bfType = 'MB';
bmpfh.bfOffBits = sizeof(bmpfh) + pBitmapInfo->m_dwBmpHdrSize;
bmpfh.bfSize = bmpfh.bfOffBits + pBitmapInfo->m_dwBitsArraySize;
DWORD dwProcessedNumber;
WriteFile(hFile, &bmpfh, sizeof(bmpfh), &dwProcessedNumber, NULL);
WriteFile(hFile, pBitmapInfo->m_pBmpInfo, pBitmapInfo->m_dwBmpHdrSize, &dwProcessedNumber, NULL);
WriteFile(hFile, pBitmapInfo->m_pBitsArray, pBitmapInfo->m_dwBitsArraySize, &dwProcessedNumber, NULL);
CloseHandle(hFile);
}
return TRUE;
}
/**
* @return true if stack frame was adjusted.
*/
BOOL CSymEngine::AdjustExceptionStackFrame(void)
{
if (m_hDbgHelpDll == NULL)
return FALSE;
BOOL bFoundThrowingFrame = FALSE;
if (m_eExceptionType != WIN32_EXCEPTION)
{
STACKFRAME64 stFrame;
if (! InitStackTrace(&stFrame))
return FALSE;
HANDLE hThread = GetCurrentThread();
CONTEXT ctxSafe = m_StartExceptionContext;
for (;;)
{
BOOL bResult = FStackWalk64(IMAGE_FILE_MACHINE_TYPE,
m_hSymProcess,
hThread,
&stFrame,
&m_StartExceptionContext,
ReadProcessMemoryProc64,
FSymFunctionTableAccess64,
FSymGetModuleBase64,
NULL);
if (! bResult || bFoundThrowingFrame)
{
if (bFoundThrowingFrame)
m_dwExceptionAddress = stFrame.AddrPC.Offset;
else
m_StartExceptionContext = ctxSafe;
break;
}
BYTE arrSymBuffer[512];
ZeroMemory(arrSymBuffer, sizeof(arrSymBuffer));
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)arrSymBuffer;
pSymbol->SizeOfStruct = sizeof(*pSymbol);
pSymbol->MaxNameLen = sizeof(arrSymBuffer) - sizeof(*pSymbol) + 1;
DWORD64 dwDisplacement64;
if (FSymFromAddr(m_hSymProcess, stFrame.AddrPC.Offset, &dwDisplacement64, pSymbol))
{
switch (m_eExceptionType)
{
case CPP_EXCEPTION:
if (strcmp(pSymbol->Name, "_CxxThrowException") == 0 ||
strcmp(pSymbol->Name, "CxxThrowException") == 0)
{
bFoundThrowingFrame = TRUE;
}
break;
#ifdef _MANAGED
case NET_EXCEPTION:
if (strcmp(pSymbol->Name, "BT_CallNetFilter") == 0)
{
bFoundThrowingFrame = TRUE;
}
break;
#endif
case NO_EXCEPTION:
if (strcmp(pSymbol->Name, "BT_SaveSnapshot") == 0)
{
bFoundThrowingFrame = TRUE;
}
break;
}
}
}
}
return bFoundThrowingFrame;
}
/**
* @param dwException - system exception code.
* @return description of the exception.
*/
PCTSTR CSymEngine::ConvertExceptionCodeToString(DWORD dwException)
{
#define CONVERT_EXCEPTION_CODE_TO_STRING(exception) \
case EXCEPTION_##exception: return _T(#exception)
switch (dwException)
{
CONVERT_EXCEPTION_CODE_TO_STRING(ACCESS_VIOLATION);
CONVERT_EXCEPTION_CODE_TO_STRING(DATATYPE_MISALIGNMENT);
CONVERT_EXCEPTION_CODE_TO_STRING(BREAKPOINT);
CONVERT_EXCEPTION_CODE_TO_STRING(SINGLE_STEP);
CONVERT_EXCEPTION_CODE_TO_STRING(ARRAY_BOUNDS_EXCEEDED);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_DENORMAL_OPERAND);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_DIVIDE_BY_ZERO);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_INEXACT_RESULT);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_INVALID_OPERATION);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_OVERFLOW);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_STACK_CHECK);
CONVERT_EXCEPTION_CODE_TO_STRING(FLT_UNDERFLOW);
CONVERT_EXCEPTION_CODE_TO_STRING(INT_DIVIDE_BY_ZERO);
CONVERT_EXCEPTION_CODE_TO_STRING(INT_OVERFLOW);
CONVERT_EXCEPTION_CODE_TO_STRING(PRIV_INSTRUCTION);
CONVERT_EXCEPTION_CODE_TO_STRING(IN_PAGE_ERROR);
CONVERT_EXCEPTION_CODE_TO_STRING(ILLEGAL_INSTRUCTION);
CONVERT_EXCEPTION_CODE_TO_STRING(NONCONTINUABLE_EXCEPTION);
CONVERT_EXCEPTION_CODE_TO_STRING(STACK_OVERFLOW);
CONVERT_EXCEPTION_CODE_TO_STRING(INVALID_DISPOSITION);
CONVERT_EXCEPTION_CODE_TO_STRING(GUARD_PAGE);
CONVERT_EXCEPTION_CODE_TO_STRING(INVALID_HANDLE);
default: return NULL;
}
#undef CONVERT_EXCEPTION_CODE_TO_STRING
}
/**
* @param rErrorInfo - system error information.
*/
void CSymEngine::GetSysErrorInfo(CSysErrorInfo& rErrorInfo)
{
if (m_dwLastError)
{
_stprintf_s(rErrorInfo.m_szErrorCode, countof(rErrorInfo.m_szErrorCode), _T("0x%08lX"), m_dwLastError);
_ASSERTE(rErrorInfo.m_pszMessageBuffer == NULL);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, m_dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(PTSTR)&rErrorInfo.m_pszMessageBuffer, 0, NULL);
if (rErrorInfo.m_pszMessageBuffer)
TrimSpaces(rErrorInfo.m_pszMessageBuffer);
}
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetSysErrorString(CUTF8EncStream& rEncStream)
{
CSysErrorInfo ErrorInfo;
GetSysErrorInfo(ErrorInfo);
if (*ErrorInfo.m_szErrorCode)
{
rEncStream.WriteUTF8Bin(ErrorInfo.m_szErrorCode);
if (ErrorInfo.m_pszMessageBuffer && *ErrorInfo.m_pszMessageBuffer)
{
rEncStream.WriteAscii(" - ");
rEncStream.WriteUTF8Bin(ErrorInfo.m_pszMessageBuffer);
}
}
}
/**
* @param rErrorInfo - COM error information.
* @return true if error information was retrieved,
*/
BOOL CSymEngine::GetComErrorInfo(CComErrorInfo& rErrorInfo)
{
if (! m_pErrorInfo)
return FALSE;
BSTR bstrDescription = NULL;
HRESULT hRes = m_pErrorInfo->GetDescription(&bstrDescription);
if (SUCCEEDED(hRes) && bstrDescription)
{
if (*bstrDescription)
rErrorInfo.m_strDescription = bstrDescription;
SysFreeString(bstrDescription);
bstrDescription = NULL;
}
BSTR bstrHelpFile = NULL;
hRes = m_pErrorInfo->GetHelpFile(&bstrHelpFile);
if (SUCCEEDED(hRes) && bstrHelpFile)
{
if (*bstrHelpFile)
rErrorInfo.m_strHelpFile = bstrHelpFile;
SysFreeString(bstrHelpFile);
bstrHelpFile = NULL;
}
BSTR bstrSource = NULL;
hRes = m_pErrorInfo->GetSource(&bstrSource);
if (SUCCEEDED(hRes) && bstrSource)
{
if (*bstrSource)
rErrorInfo.m_strSource = bstrSource;
SysFreeString(bstrSource);
bstrSource = NULL;
}
GUID guid;
hRes = m_pErrorInfo->GetGUID(&guid);
if (SUCCEEDED(hRes))
{
OLECHAR szGuid[64];
hRes = StringFromGUID2(guid, szGuid, countof(szGuid));
if (SUCCEEDED(hRes))
rErrorInfo.m_strGuid = szGuid;
}
return TRUE;
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetComErrorString(CUTF8EncStream& rEncStream)
{
CComErrorInfo ErrorInfo;
if (! GetComErrorInfo(ErrorInfo))
return;
static const CHAR szNewLine[] = "\r\n";
static const CHAR szDescriptionMsg[] = "Description: ";
static const CHAR szHelpFileMsg[] = "Help File: ";
static const CHAR szSourceMsg[] = "Source: ";
static const CHAR szGuidMsg[] = "GUID: ";
BOOL bNotEmpty = FALSE;
if (! ErrorInfo.m_strDescription.IsEmpty())
{
bNotEmpty = TRUE;
rEncStream.WriteAscii(szDescriptionMsg);
rEncStream.WriteUTF8Bin(ErrorInfo.m_strDescription);
}
if (! ErrorInfo.m_strHelpFile.IsEmpty())
{
if (bNotEmpty)
rEncStream.WriteAscii(szNewLine);
else
bNotEmpty = TRUE;
rEncStream.WriteAscii(szHelpFileMsg);
rEncStream.WriteUTF8Bin(ErrorInfo.m_strHelpFile);
}
if (! ErrorInfo.m_strSource.IsEmpty())
{
if (bNotEmpty)
rEncStream.WriteAscii(szNewLine);
else
bNotEmpty = TRUE;
rEncStream.WriteAscii(szSourceMsg);
rEncStream.WriteUTF8Bin(ErrorInfo.m_strSource);
}
if (! ErrorInfo.m_strGuid.IsEmpty())
{
if (bNotEmpty)
rEncStream.WriteAscii(szNewLine);
else
bNotEmpty = TRUE;
rEncStream.WriteAscii(szGuidMsg);
rEncStream.WriteUTF8Bin(ErrorInfo.m_strGuid);
}
}
/**
* @param rErrorInfo - error information.
* @return true if error information is not empty.
*/
BOOL CSymEngine::GetErrorInfo(CErrorInfo& rErrorInfo)
{
if (m_hDbgHelpDll == NULL || m_eExceptionType == NO_EXCEPTION)
return FALSE;
GetModuleFileName(NULL, rErrorInfo.m_szProcessName, countof(rErrorInfo.m_szProcessName));
PCTSTR pszProcessName = PathFindFileName(rErrorInfo.m_szProcessName);
size_t nProcessNameLength = _tcslen(pszProcessName);
MoveMemory(rErrorInfo.m_szProcessName, pszProcessName, (nProcessNameLength + 1) * sizeof(TCHAR));
_ultot_s(GetCurrentProcessId(), rErrorInfo.m_szProcessID, countof(rErrorInfo.m_szProcessID), 10);
DWORD dwException;
switch (m_eExceptionType)
{
case WIN32_EXCEPTION:
dwException = m_pExceptionPointers != NULL ? m_pExceptionPointers->ExceptionRecord->ExceptionCode : 0;
rErrorInfo.m_pszWhat = ConvertExceptionCodeToString(dwException);
if (rErrorInfo.m_pszWhat == NULL)
rErrorInfo.m_pszWhat = _T("UNKNOWN_ERROR");
break;
case CPP_EXCEPTION:
rErrorInfo.m_pszWhat = _T("NATIVE_EXCEPTION");
break;
#ifdef _MANAGED
case NET_EXCEPTION:
rErrorInfo.m_pszWhat = _T("MANAGED_EXCEPTION");
break;
#endif
}
GetFirstStackTraceEntry(rErrorInfo);
return TRUE;
}
/**
* @param rStream - stream object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetWin32ErrorString(CStrStream& rStream)
{
CErrorInfo ErrorInfo;
if (! GetErrorInfo(ErrorInfo))
return FALSE;
rStream << ErrorInfo.m_szProcessName;
rStream << _T(" caused ");
rStream << ErrorInfo.m_pszWhat;
if (*ErrorInfo.m_szModule)
{
rStream << _T(" in module \"");
rStream << ErrorInfo.m_szModule;
rStream << _T('\"');
}
if (*ErrorInfo.m_szAddress)
{
rStream << _T(" at ");
rStream << ErrorInfo.m_szAddress;
}
if (*ErrorInfo.m_szFunctionInfo)
{
rStream << _T(", ");
rStream << ErrorInfo.m_szFunctionInfo;
}
if (*ErrorInfo.m_szSourceFile)
{
rStream << _T(" in \"");
rStream << ErrorInfo.m_szSourceFile;
rStream << _T('\"');
if (*ErrorInfo.m_szLineInfo)
{
rStream << _T(", ");
rStream << ErrorInfo.m_szLineInfo;
}
}
return TRUE;
}
/**
* @param rEncStream - UTF-8 encoder object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetWin32ErrorString(CUTF8EncStream& rEncStream)
{
CStrStream Stream(1024);
BOOL bResult = GetWin32ErrorString(Stream);
rEncStream.WriteUTF8Bin(Stream);
return bResult;
}
#ifdef _MANAGED
/**
* @param rStream - stream object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetNetErrorString(CStrStream& rStream)
{
if (IsNetException())
return m_pNetStackTrace->GetErrorString(rStream);
rStream.Free();
return FALSE;
}
/**
* @param rEncStream - UTF-8 encoder object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetNetErrorString(CUTF8EncStream& rEncStream)
{
CStrStream Stream(1024);
BOOL bResult = GetNetErrorString(Stream);
rEncStream.WriteUTF8Bin(Stream);
return bResult;
}
/**
* @param rStream - stream object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetNetErrorStringEx(CStrStream& rStream)
{
if (! GetNetErrorString(rStream))
return FALSE;
gcroot<Exception^> exception = NetThunks::GetNetException();
if (! NetThunks::IsNull(exception))
{
for (DWORD dwNestedErrorLevel = 1; dwNestedErrorLevel < MAX_INNER_ERROR_COUNT; ++dwNestedErrorLevel)
{
exception = NetThunks::GetInnerException(exception);
if (NetThunks::IsNull(exception))
break;
CNetStackTrace NetStackTrace(exception);
CStrStream TempStream(1024);
if (! NetStackTrace.GetErrorString(TempStream))
break;
rStream << _T("\r\n");
for (DWORD dwSpaceCounter = 0; dwSpaceCounter < dwNestedErrorLevel; ++dwSpaceCounter)
rStream << _T(" ");
rStream << TempStream;
}
}
return true;
}
/**
* @param rEncStream - UTF-8 encoder object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetNetErrorStringEx(CUTF8EncStream& rEncStream)
{
CStrStream Stream(1024);
BOOL bResult = GetNetErrorStringEx(Stream);
rEncStream.WriteUTF8Bin(Stream);
return bResult;
}
/**
* @param rStream - stream object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetErrorString(CStrStream& rStream)
{
if (IsNetException())
return GetNetErrorStringEx(rStream);
else if (m_pExceptionPointers != NULL)
return GetWin32ErrorString(rStream);
else
return FALSE;
}
/**
* @param rEncStream - UTF-8 encoder object.
* @return true if error info is not empty.
*/
BOOL CSymEngine::GetErrorString(CUTF8EncStream& rEncStream)
{
CStrStream Stream(1024);
BOOL bResult = GetErrorString(Stream);
rEncStream.WriteUTF8Bin(Stream);
return bResult;
}
#endif
/**
* @param rRegVals - registers values in string format.
*/
void CSymEngine::GetRegistersValues(CRegistersValues& rRegVals)
{
_ASSERTE(m_pExceptionPointers != NULL);
if (m_pExceptionPointers == NULL)
return;
#define CONVERT_REGISTER_VALUE_TO_STRING(reg, digits) \
_stprintf_s(rRegVals.m_sz##reg, countof(rRegVals.m_sz##reg), _T("0x%0") _T(#digits) _T("lX"), m_pExceptionPointers->ContextRecord->reg);
#if defined _M_IX86
CONVERT_REGISTER_VALUE_TO_STRING(Eax, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Ebx, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Ecx, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Edx, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Esi, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Edi, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Esp, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Ebp, 8);
CONVERT_REGISTER_VALUE_TO_STRING(Eip, 8);
CONVERT_REGISTER_VALUE_TO_STRING(SegCs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegDs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegSs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegEs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegFs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegGs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(EFlags, 8);
#elif defined _M_X64
CONVERT_REGISTER_VALUE_TO_STRING(Rax, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rbx, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rcx, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rdx, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rsi, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rdi, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rsp, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rbp, 16);
CONVERT_REGISTER_VALUE_TO_STRING(Rip, 16);
CONVERT_REGISTER_VALUE_TO_STRING(SegCs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegDs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegSs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegEs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegFs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(SegGs, 4);
CONVERT_REGISTER_VALUE_TO_STRING(EFlags, 8);
#else
#error CPU architecture is not supported.
#endif
#undef CONVERT_REGISTER_VALUE_TO_STRING
}
/**
* @param pszRegString - buffer that receives registers values.
* @param dwRegStringSize - size of buffer.
*/
void CSymEngine::GetRegistersString(PTSTR pszRegString, DWORD dwRegStringSize)
{
if (m_pExceptionPointers == NULL && dwRegStringSize)
{
*pszRegString = _T('\0');
return;
}
#if defined _M_IX86
_stprintf_s(pszRegString, dwRegStringSize,
_T("EAX=%08X EBX=%08X ECX=%08X EDX=%08X\r\n")
_T("ESI=%08X EDI=%08X FLG=%08X\r\n")
_T("EBP=%08X ESP=%08X EIP=%08X\r\n")
_T("CS=%04X DS=%04X SS=%04X ES=%04X FS=%04X GS=%04X"),
m_pExceptionPointers->ContextRecord->Eax,
m_pExceptionPointers->ContextRecord->Ebx,
m_pExceptionPointers->ContextRecord->Ecx,
m_pExceptionPointers->ContextRecord->Edx,
m_pExceptionPointers->ContextRecord->Esi,
m_pExceptionPointers->ContextRecord->Edi,
m_pExceptionPointers->ContextRecord->EFlags,
m_pExceptionPointers->ContextRecord->Ebp,
m_pExceptionPointers->ContextRecord->Esp,
m_pExceptionPointers->ContextRecord->Eip,
m_pExceptionPointers->ContextRecord->SegCs,
m_pExceptionPointers->ContextRecord->SegDs,
m_pExceptionPointers->ContextRecord->SegSs,
m_pExceptionPointers->ContextRecord->SegEs,
m_pExceptionPointers->ContextRecord->SegFs,
m_pExceptionPointers->ContextRecord->SegGs);
#elif defined _M_X64
_stprintf_s(pszRegString, dwRegStringSize,
_T("RAX=%016X RBX=%016X\r\n")
_T("RCX=%016X RDX=%016X\r\n")
_T("RSI=%016X RDI=%016X\r\n")
_T("FLG=%08X RBP=%016X\r\n")
_T("RSP=%016X RIP=%016X\r\n")
_T("CS=%04X DS=%04X SS=%04X ES=%04X FS=%04X GS=%04X"),
m_pExceptionPointers->ContextRecord->Rax,
m_pExceptionPointers->ContextRecord->Rbx,
m_pExceptionPointers->ContextRecord->Rcx,
m_pExceptionPointers->ContextRecord->Rdx,
m_pExceptionPointers->ContextRecord->Rsi,
m_pExceptionPointers->ContextRecord->Rdi,
m_pExceptionPointers->ContextRecord->EFlags,
m_pExceptionPointers->ContextRecord->Rbp,
m_pExceptionPointers->ContextRecord->Rsp,
m_pExceptionPointers->ContextRecord->Rip,
m_pExceptionPointers->ContextRecord->SegCs,
m_pExceptionPointers->ContextRecord->SegDs,
m_pExceptionPointers->ContextRecord->SegSs,
m_pExceptionPointers->ContextRecord->SegEs,
m_pExceptionPointers->ContextRecord->SegFs,
m_pExceptionPointers->ContextRecord->SegGs);
#else
#error CPU architecture is not supported.
#endif
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetRegistersString(CUTF8EncStream& rEncStream)
{
TCHAR szRegString[256];
GetRegistersString(szRegString, countof(szRegString));
rEncStream.WriteUTF8Bin(szRegString);
}
/**
* @param rCpusInfo - CPUs information.
*/
void CSymEngine::GetCpusInfo(CCpusInfo& rCpusInfo)
{
static const TCHAR szIntel[] = _T("Intel-x86");
static const TCHAR szIA64[] = _T("IA-64");
static const TCHAR szAMD64[] = _T("AMD-x64");
static const TCHAR szUnknown[] = _T("Unknown");
rCpusInfo.m_pszCpuArch = szUnknown;
SYSTEM_INFO si;
GetSystemInfo(&si);
if (g_bWinNT)
{
switch (si.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL:
rCpusInfo.m_pszCpuArch = szIntel;
break;
case PROCESSOR_ARCHITECTURE_IA64:
rCpusInfo.m_pszCpuArch = szIA64;
break;
case PROCESSOR_ARCHITECTURE_AMD64:
rCpusInfo.m_pszCpuArch = szAMD64;
break;
default: // PROCESSOR_ARCHITECTURE_UNKNOWN
rCpusInfo.m_pszCpuArch = szUnknown;
break;
}
}
else
rCpusInfo.m_pszCpuArch = szIntel;
rCpusInfo.m_dwNumCpus = si.dwNumberOfProcessors;
}
/**
* @param dwCpuNum - CPU number.
* @param rCpuInfo - CPU information.
* @return true if cpu info was successfully retrieved.
*/
BOOL CSymEngine::GetCpuInfo(DWORD dwCpuNum, CCpuInfo& rCpuInfo)
{
static const TCHAR szQuestion[] = _T("?");
static const TCHAR szUnknown[] = _T("Unknown");
_tcscpy_s(rCpuInfo.m_szCpuId, countof(rCpuInfo.m_szCpuId), szUnknown);
_tcscpy_s(rCpuInfo.m_szCpuSpeed, countof(rCpuInfo.m_szCpuSpeed), szQuestion);
_tcscpy_s(rCpuInfo.m_szCpuDescription, countof(rCpuInfo.m_szCpuId), szUnknown);
BOOL bResult = TRUE;
if (g_bWinNT)
{
TCHAR szCentralProcessorPath[MAX_PATH];
_stprintf_s(szCentralProcessorPath, countof(szCentralProcessorPath), _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%lu"), dwCpuNum);
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szCentralProcessorPath, 0l, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD dwValue, dwValueType, dwValueSize;
dwValueSize = sizeof(dwValue);
if (RegQueryValueEx(hKey, _T("~MHz"), NULL, &dwValueType, (PBYTE)&dwValue, &dwValueSize) == ERROR_SUCCESS && dwValueType == REG_DWORD)
_ultot_s(dwValue, rCpuInfo.m_szCpuSpeed, countof(rCpuInfo.m_szCpuSpeed), 10);
else
*rCpuInfo.m_szCpuSpeed = _T('\0');
dwValueSize = sizeof(rCpuInfo.m_szCpuDescription);
if (RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, &dwValueType, (PBYTE)rCpuInfo.m_szCpuDescription, &dwValueSize) == ERROR_SUCCESS && dwValueType == REG_SZ)
TrimSpaces(rCpuInfo.m_szCpuDescription);
else
*rCpuInfo.m_szCpuDescription = _T('\0');
dwValueSize = sizeof(rCpuInfo.m_szCpuId);
if (RegQueryValueEx(hKey, _T("Identifier"), NULL, &dwValueType, (PBYTE)rCpuInfo.m_szCpuId, &dwValueSize) == ERROR_SUCCESS && dwValueType == REG_SZ)
TrimSpaces(rCpuInfo.m_szCpuId);
else
*rCpuInfo.m_szCpuId = _T('\0');
RegCloseKey(hKey);
}
else
bResult = FALSE;
}
return bResult;
}
/**
* @param rStream - buffer that receives CPU information.
*/
void CSymEngine::GetCpuString(CStrStream& rStream)
{
CCpusInfo CpusInfo;
GetCpusInfo(CpusInfo);
rStream << _T("Architecture: ");
rStream << CpusInfo.m_pszCpuArch;
rStream << _T("\r\nNumber of Processors: ");
TCHAR szTempBuf[16];
_ultot_s(CpusInfo.m_dwNumCpus, szTempBuf, countof(szTempBuf), 10);
rStream << szTempBuf;
rStream << _T("\r\nProcessors Descriptions:");
for (DWORD dwCpuNum = 0; dwCpuNum < CpusInfo.m_dwNumCpus; ++dwCpuNum)
{
CCpuInfo CpuInfo;
if (GetCpuInfo(dwCpuNum, CpuInfo))
{
rStream << _T("\r\n");
_ultot_s(dwCpuNum + 1, szTempBuf, countof(szTempBuf), 10);
rStream << szTempBuf;
rStream << _T(". ");
rStream << CpuInfo.m_szCpuDescription;
}
}
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetCpuString(CUTF8EncStream& rEncStream)
{
CStrStream Stream(1024);
GetCpuString(Stream);
rEncStream.WriteUTF8Bin(Stream);
}
/**
* @param rOsInfo - OS information.
*/
void CSymEngine::GetOsInfo(COsInfo& rOsInfo)
{
static const TCHAR szUnknown[] = _T("Unknown");
static const TCHAR szWindowsNT351[] = _T("Windows NT 3.51");
static const TCHAR szWindowsNT40[] = _T("Windows NT 4.0");
static const TCHAR szWindows95[] = _T("Windows 95");
static const TCHAR szWindows98[] = _T("Windows 98");
static const TCHAR szWindowsMe[] = _T("Windows Me");
static const TCHAR szWindows2000[] = _T("Windows 2000");
static const TCHAR szWindowsXP[] = _T("Windows XP");
static const TCHAR szWindowsVista[] = _T("Windows Vista");
static const TCHAR szWindowsServer2003[] = _T("Windows Server 2003");
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
rOsInfo.m_pszWinVersion = szUnknown;
switch (osvi.dwMajorVersion)
{
case 3:
if (osvi.dwMinorVersion == 51 && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
rOsInfo.m_pszWinVersion = szWindowsNT351;
break;
case 4:
switch (osvi.dwMinorVersion)
{
case 0:
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
rOsInfo.m_pszWinVersion = szWindowsNT40;
else if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
rOsInfo.m_pszWinVersion = szWindows95;
break;
case 10:
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
rOsInfo.m_pszWinVersion = szWindows98;
break;
case 90:
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
rOsInfo.m_pszWinVersion = szWindowsMe;
break;
}
break;
case 5:
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
switch (osvi.dwMinorVersion)
{
case 0:
rOsInfo.m_pszWinVersion = szWindows2000;
break;
case 1:
rOsInfo.m_pszWinVersion = szWindowsXP;
break;
case 2:
rOsInfo.m_pszWinVersion = szWindowsServer2003;
break;
}
}
break;
case 6:
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
switch (osvi.dwMinorVersion)
{
case 0:
rOsInfo.m_pszWinVersion = szWindowsVista;
break;
}
}
break;
}
_tcscpy_s(rOsInfo.m_szSPVersion, countof(rOsInfo.m_szSPVersion), osvi.szCSDVersion);
_ultot_s(osvi.dwBuildNumber, rOsInfo.m_szBuildNumber, countof(rOsInfo.m_szBuildNumber), 10);
#ifdef _MANAGED
NetThunks::GetNetVersion(rOsInfo.m_szNetVersion, countof(rOsInfo.m_szNetVersion));
#endif
}
/**
* @param pszOSString - pointer to OS string.
* @param dwOSStringSize - size of OS string buffer.
*/
void CSymEngine::GetOsString(PTSTR pszOSString, DWORD dwOSStringSize)
{
COsInfo OsInfo;
GetOsInfo(OsInfo);
#ifdef _MANAGED
_stprintf_s(pszOSString, dwOSStringSize,
_T("OS Version: %s %s\r\n")
_T("Build Number: %s\r\n")
_T("CLR Version: %s"),
OsInfo.m_pszWinVersion,
OsInfo.m_szSPVersion,
OsInfo.m_szBuildNumber,
OsInfo.m_szNetVersion);
#else
_stprintf_s(pszOSString, dwOSStringSize,
_T("OS Version: %s %s\r\n")
_T("Build Number: %s"),
OsInfo.m_pszWinVersion,
OsInfo.m_szSPVersion,
OsInfo.m_szBuildNumber);
#endif
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetOsString(CUTF8EncStream& rEncStream)
{
TCHAR szOSString[256];
GetOsString(szOSString, countof(szOSString));
rEncStream.WriteUTF8Bin(szOSString);
}
/**
* @param rMemInfo - memory information.
*/
void CSymEngine::GetMemInfo(CMemInfo& rMemInfo)
{
MEMORYSTATUS ms;
GlobalMemoryStatus(&ms);
_ultot_s(ms.dwMemoryLoad, rMemInfo.m_szMemoryLoad, countof(rMemInfo.m_szMemoryLoad), 10);
_ultot_s((DWORD)ms.dwTotalPhys, rMemInfo.m_szTotalPhys, countof(rMemInfo.m_szTotalPhys), 10);
_ultot_s((DWORD)ms.dwAvailPhys, rMemInfo.m_szAvailPhys, countof(rMemInfo.m_szAvailPhys), 10);
_ultot_s((DWORD)ms.dwTotalPageFile, rMemInfo.m_szTotalPageFile, countof(rMemInfo.m_szTotalPageFile), 10);
_ultot_s((DWORD)ms.dwAvailPageFile, rMemInfo.m_szAvailPageFile, countof(rMemInfo.m_szAvailPageFile), 10);
}
/**
* @param pszMemString - pointer to memory status string.
* @param dwMemStringSize - size of memory status string buffer.
*/
void CSymEngine::GetMemString(PTSTR pszMemString, DWORD dwMemStringSize)
{
MEMORYSTATUS ms;
GlobalMemoryStatus(&ms);
_stprintf_s(pszMemString, dwMemStringSize,
_T("Current Memory Load: %lu%%\r\n")
_T("Total Physical Memory: %lu MB\r\n")
_T("Available Physical Memory: %lu MB\r\n")
_T("Total Page File Memory: %lu MB\r\n")
_T("Available Page File Memory: %lu MB"),
ms.dwMemoryLoad,
ms.dwTotalPhys / (1024 * 1024),
ms.dwAvailPhys / (1024 * 1024),
ms.dwTotalPageFile / (1024 * 1024),
ms.dwAvailPageFile / (1024 * 1024));
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetMemString(CUTF8EncStream& rEncStream)
{
TCHAR szMemString[256];
GetMemString(szMemString, countof(szMemString));
rEncStream.WriteUTF8Bin(szMemString);
}
/**
* @param pDestination - destination address.
* @param pSource - source address.
* @param dwSize - number of bytes to copy.
*/
void CSymEngine::SafeCopy(PVOID pDestination, PVOID pSource, DWORD dwSize)
{
__try {
for (DWORD dwBytePos = 0; dwBytePos < dwSize; ++dwBytePos)
((PBYTE)pDestination)[dwBytePos] = ((PBYTE)pSource)[dwBytePos];
} __except (EXCEPTION_EXECUTE_HANDLER) {
// do nothing - just ignore the exception...
}
}
/**
* @param pStackFrame - pointer stack frame that is being initialized.
* @param pContext - pointer to thread/exception context record.
*/
void CSymEngine::InitStackFrame(LPSTACKFRAME64 pStackFrame, const CONTEXT* pContext)
{
ZeroMemory(pStackFrame, sizeof(*pStackFrame));
#if defined _M_IX86
pStackFrame->AddrPC.Mode = AddrModeFlat;
pStackFrame->AddrPC.Offset = pContext->Eip;
pStackFrame->AddrPC.Segment = (WORD)pContext->SegCs;
pStackFrame->AddrStack.Mode = AddrModeFlat;
pStackFrame->AddrStack.Offset = pContext->Esp;
pStackFrame->AddrStack.Segment = (WORD)pContext->SegSs;
pStackFrame->AddrFrame.Mode = AddrModeFlat;
pStackFrame->AddrFrame.Offset = pContext->Ebp;
pStackFrame->AddrFrame.Segment = (WORD)pContext->SegEs;
#elif defined _M_X64
pStackFrame->AddrPC.Mode = AddrModeFlat;
pStackFrame->AddrPC.Offset = pContext->Rip;
pStackFrame->AddrPC.Segment = (WORD)pContext->SegCs;
pStackFrame->AddrStack.Mode = AddrModeFlat;
pStackFrame->AddrStack.Offset = pContext->Rsp;
pStackFrame->AddrStack.Segment = (WORD)pContext->SegSs;
pStackFrame->AddrFrame.Mode = AddrModeFlat;
pStackFrame->AddrFrame.Offset = pContext->Rbp;
pStackFrame->AddrFrame.Segment = (WORD)pContext->SegEs;
#else
#error CPU architecture is not supported.
#endif
}
/**
* @param pContext - pointer to thread context record.
* @return true if thread context was successfully resolved.
*/
BOOL CSymEngine::GetCurrentThreadContext(PCONTEXT pContext)
{
#if defined _M_IX86
__asm
{
push eax
push edi
push es
push ecx
// EDI = pContext
mov edi, [pContext]
// ES = DS
push ds
pop es
// ECX = sizeof(*pContext)
mov ecx, size CONTEXT
// EAX = 0
xor eax, eax
// store EDI
push edi
// ZeroMemory(pContext, sizeof(*pContext))
cld
rep stosb
// restore EDI
pop edi
pop ecx
pop es
// fill pContext
mov dword ptr [edi] CONTEXT.ContextFlags, CONTEXT_FULL
mov dword ptr [edi] CONTEXT.Eax, eax
mov dword ptr [edi] CONTEXT.Ecx, ecx
mov dword ptr [edi] CONTEXT.Edx, edx
mov dword ptr [edi] CONTEXT.Ebx, ebx
mov dword ptr [edi] CONTEXT.Esi, esi
mov dword ptr [edi] CONTEXT.Edi, edi
mov word ptr [edi] CONTEXT.SegSs, ss
mov word ptr [edi] CONTEXT.SegCs, cs
mov word ptr [edi] CONTEXT.SegDs, ds
mov word ptr [edi] CONTEXT.SegEs, es
mov word ptr [edi] CONTEXT.SegFs, fs
mov word ptr [edi] CONTEXT.SegGs, gs
// pContext->EFlags = flags
pushfd
pop [edi] CONTEXT.EFlags
// extract caller's EBP, EIP (return address) and ESP
mov eax, [ebp]
mov dword ptr [edi] CONTEXT.Ebp, eax
mov eax, [ebp + 4]
mov dword ptr [edi] CONTEXT.Eip, eax
lea eax, [ebp + 8]
mov dword ptr [edi] CONTEXT.Esp, eax
pop edi
pop eax
}
#elif defined _M_X64
RtlCaptureContext(pContext);
#else
#error CPU architecture is not supported.
#endif
return TRUE;
}
/**
* @param pStackFrame - pointer to the exception stack frame.
* @return true if thread context was successfully resolved.
*/
BOOL CSymEngine::InitStackTrace(LPSTACKFRAME64 pStackFrame)
{
if (m_pExceptionPointers != NULL)
{
// It's necessary to make a copy of CONTEXT structure because StackWalk64() changes it.
// However this line of code doesn't work on Windows 9x:
// m_StartExceptionContext = *m_pExceptionPointers->ContextRecord;
// I think the size of CONTEXT structure was increased since Windows 9x
// and m_pExceptionPointers->ContextRecord points to smaller buffer.
// Windows 9x considers such copy attempt as access violation, so it's safer
// to define custom copy function and catch there all errors. It's not the
// problem to copy smaller chunk to larger buffer, because Windows 9x uses
// only properly copied part of larger memory block.
SafeCopy(&m_StartExceptionContext, m_pExceptionPointers->ContextRecord, sizeof(m_StartExceptionContext));
}
else
{
if (! GetCurrentThreadContext(&m_StartExceptionContext))
return FALSE;
}
InitStackFrame(pStackFrame, &m_StartExceptionContext);
return TRUE;
}
/**
* @param hThread - handle of examined thread; pass NULL if you want to get exception stack trace.
* @return true if thread context was successfully resolved.
*/
BOOL CSymEngine::InitStackTrace(HANDLE hThread)
{
if (! hThread)
{
hThread = GetCurrentThread();
// It's necessary to make a copy of CONTEXT structure because StackWalk64() changes it.
// However this line of code doesn't work on Windows 9x:
// m_swContext.m_context = *m_pExceptionPointers->ContextRecord;
// I think the size of CONTEXT structure was increased since Windows 9x
// and m_pExceptionPointers->ContextRecord points to smaller buffer.
// Windows 9x considers such copy attempt as access violation, so it's safer
// to define custom copy function and catch there all errors. It's not the
// problem to copy smaller chunk to larger buffer, because Windows 9x uses
// only properly copied part of larger memory block.
if (m_eExceptionType == WIN32_EXCEPTION)
{
_ASSERTE(m_pExceptionPointers != NULL);
SafeCopy(&m_swContext.m_context, m_pExceptionPointers->ContextRecord, sizeof(m_swContext.m_context));
}
else
m_swContext.m_context = m_StartExceptionContext;
}
else
{
m_swContext.m_context.ContextFlags = CONTEXT_FULL;
if (! GetThreadContext(hThread, &m_swContext.m_context))
return FALSE;
}
m_swContext.m_hThread = hThread;
InitStackFrame(&m_swContext.m_stFrame, &m_swContext.m_context);
m_dwFrameCount = 0;
return TRUE;
}
/**
* @param rEncStream - UTF-8 encoder object.
* @return true if thread context was successfully resolved.
*/
BOOL CSymEngine::GetNextWin32StackTraceString(CUTF8EncStream& rEncStream)
{
CStackTraceEntry Entry;
if (! GetNextStackTraceEntry(Entry))
return FALSE;
if (*Entry.m_szModule)
{
rEncStream.WriteByte('\"');
rEncStream.WriteUTF8Bin(Entry.m_szModule);
rEncStream.WriteByte('\"');
}
if (*Entry.m_szAddress)
{
rEncStream.WriteAscii(" at ");
rEncStream.WriteUTF8Bin(Entry.m_szAddress);
}
if (*Entry.m_szFunctionInfo)
{
rEncStream.WriteAscii(", ");
rEncStream.WriteUTF8Bin(Entry.m_szFunctionInfo);
}
if (*Entry.m_szSourceFile)
{
rEncStream.WriteAscii(" in \"");
rEncStream.WriteUTF8Bin(Entry.m_szSourceFile);
rEncStream.WriteByte('\"');
}
if (*Entry.m_szLineInfo)
{
rEncStream.WriteAscii(", ");
rEncStream.WriteUTF8Bin(Entry.m_szLineInfo);
}
return TRUE;
}
/**
* @param rStream - stream object.
*/
void CSymEngine::GetEnvironmentStrings(CStrStream& rStream)
{
TCHAR* pchEnvironment = ::GetEnvironmentStrings();
if (pchEnvironment)
{
const TCHAR* pchEnvironmentPair = pchEnvironment;
while (*pchEnvironmentPair)
{
size_t nItemLength = _tcslen(pchEnvironmentPair);
rStream << pchEnvironmentPair << _T("\r\n");
pchEnvironmentPair += nItemLength + 1;
}
FreeEnvironmentStrings(pchEnvironment);
}
}
/**
* @param rXmlWriter - XML writer.
*/
void CSymEngine::GetEnvironmentStrings(CXmlWriter& rXmlWriter)
{
rXmlWriter.WriteStartElement(_T("environment")); // <environment>
TCHAR* pchEnvironment = ::GetEnvironmentStrings();
if (pchEnvironment)
{
const TCHAR* pchEnvironmentPair = pchEnvironment;
while (*pchEnvironmentPair)
{
const TCHAR* pchEnd;
const TCHAR* pchEquation = _tcschr(pchEnvironmentPair, _T('='));
if (pchEquation)
{
pchEnd = _tcschr(pchEquation, _T('\0'));
TCHAR szVariableName[MAX_PATH];
_tcsncpy_s(szVariableName, countof(szVariableName), pchEnvironmentPair, pchEquation - pchEnvironmentPair);
rXmlWriter.WriteStartElement(_T("variable")); // <variable>
rXmlWriter.WriteElementString(_T("name"), szVariableName); // <name>...</name>
rXmlWriter.WriteElementString(_T("value"), pchEquation + 1); // <value>...</value>
rXmlWriter.WriteEndElement(); // </variable>
}
else
pchEnd = _tcschr(pchEnvironmentPair, _T('\0'));
pchEnvironmentPair = pchEnd + 1;
}
FreeEnvironmentStrings(pchEnvironment);
}
rXmlWriter.WriteEndElement(); // </environment>
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetEnvironmentStrings(CUTF8EncStream& rEncStream)
{
CStrStream Stream(8 * 1024);
GetEnvironmentStrings(Stream);
rEncStream.WriteUTF8Bin(Stream);
}
#ifdef _MANAGED
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetAssemblyList(CUTF8EncStream& rEncStream)
{
static const CHAR szAppDomainMsg[] = "\r\nApplication Domain: ";
static const CHAR szAssembliesMsg[] = ", Assemblies:\r\n";
static const CHAR szAppDomainIDMsg[] = ", ID: ";
static const CHAR szAssemblyNameMsg[] = "Assembly Name: ";
static const CHAR szAssemblyVersionMsg[] = "\r\nAssembly Version: ";
static const CHAR szFileVersionMsg[] = "\r\nFile Version: ";
static const CHAR szCodeBaseMsg[] = "\r\nCode Base: ";
static const CHAR szDividerMsg[] = "----------------------------------------\r\n";
static const CHAR szNewLine[] = "\r\n";
DWORD dwAppDomainID;
WCHAR szAppDomainName[MAX_PATH];
NetThunks::GetAppDomainInfo(dwAppDomainID, szAppDomainName, countof(szAppDomainName));
CHAR szAppDomainID[32];
_ultoa_s(dwAppDomainID, szAppDomainID, countof(szAppDomainID), 10);
rEncStream.WriteAscii(szAppDomainMsg);
rEncStream.WriteUTF8Bin(szAppDomainName);
rEncStream.WriteAscii(szAppDomainIDMsg);
rEncStream.WriteAscii(szAppDomainID);
CNetAssemblies NetAssemblies;
CNetAssemblies::CAssemblyInfo AssemblyInfo;
if (NetAssemblies.GetFirstAssembly(AssemblyInfo))
{
rEncStream.WriteAscii(szAssembliesMsg);
rEncStream.WriteAscii(szDividerMsg);
do
{
rEncStream.WriteAscii(szAssemblyNameMsg);
rEncStream.WriteUTF8Bin(AssemblyInfo.m_szName);
rEncStream.WriteAscii(szAssemblyVersionMsg);
rEncStream.WriteUTF8Bin(AssemblyInfo.m_szVersion);
rEncStream.WriteAscii(szFileVersionMsg);
rEncStream.WriteUTF8Bin(AssemblyInfo.m_szFileVersion);
rEncStream.WriteAscii(szCodeBaseMsg);
rEncStream.WriteUTF8Bin(AssemblyInfo.m_szCodeBase);
rEncStream.WriteAscii(szNewLine);
rEncStream.WriteAscii(szNewLine);
}
while (NetAssemblies.GetNextAssembly(AssemblyInfo));
}
else
rEncStream.WriteAscii(szNewLine);
}
/**
* @param rXmlWriter - XML writer.
*/
void CSymEngine::GetAssemblyList(CXmlWriter& rXmlWriter)
{
DWORD dwAppDomainID;
WCHAR szAppDomainName[MAX_PATH];
NetThunks::GetAppDomainInfo(dwAppDomainID, szAppDomainName, countof(szAppDomainName));
TCHAR szAppDomainID[32];
_ultot_s(dwAppDomainID, szAppDomainID, countof(szAppDomainID), 10);
rXmlWriter.WriteStartElement(_T("app-domain")); // <app-domain>
rXmlWriter.WriteElementString(_T("name"), szAppDomainName); // <name>...</name>
rXmlWriter.WriteElementString(_T("id"), szAppDomainID); // <id>...</id>
rXmlWriter.WriteStartElement(_T("assemblies")); // <assemblies>
CNetAssemblies NetAssemblies;
CNetAssemblies::CAssemblyInfo AssemblyInfo;
if (NetAssemblies.GetFirstAssembly(AssemblyInfo))
{
do
{
rXmlWriter.WriteStartElement(_T("assembly")); // <assembly>
rXmlWriter.WriteElementString(_T("name"), AssemblyInfo.m_szName); // <name>...</name>
rXmlWriter.WriteElementString(_T("version"), AssemblyInfo.m_szVersion); // <version>...</version>
rXmlWriter.WriteElementString(_T("file-version"), AssemblyInfo.m_szFileVersion); // <file-version>...</file-version>
rXmlWriter.WriteElementString(_T("code-base"), AssemblyInfo.m_szCodeBase); // <code-base>...</code-base>
rXmlWriter.WriteEndElement(); // </assembly>
}
while (NetAssemblies.GetNextAssembly(AssemblyInfo));
}
rXmlWriter.WriteEndElement(); // </assemblies>
rXmlWriter.WriteEndElement(); // </app-domain>
}
#endif
/**
* @param rEncStream - UTF-8 encoder object.
* @param pEnumProcess - pointer to the process enumerator;
* @param rProcEntry - process entry.
*/
void CSymEngine::GetModuleList(CUTF8EncStream& rEncStream, CEnumProcess* pEnumProcess, CEnumProcess::CProcessEntry& rProcEntry)
{
_ASSERTE(pEnumProcess != NULL);
static const CHAR szProcessMsg[] = "Process: ";
static const CHAR szModulesMsg[] = ", Modules:\r\n";
static const CHAR szProcessIDMsg[] = ", PID: ";
static const CHAR szBaseMsg[] = ", Base: ";
static const CHAR szSizeMsg[] = ", Size: ";
static const CHAR szDividerMsg[] = "----------------------------------------\r\n";
static const CHAR szNewLine[] = "\r\n";
CHAR szTempBuf[64];
_ultoa_s(rProcEntry.m_dwProcessID, szTempBuf, countof(szTempBuf), 10);
rEncStream.WriteAscii(szProcessMsg);
rEncStream.WriteUTF8Bin(rProcEntry.m_szProcessName);
rEncStream.WriteAscii(szProcessIDMsg);
rEncStream.WriteAscii(szTempBuf);
CEnumProcess::CModuleEntry ModuleEntry;
if (pEnumProcess->GetModuleFirst(rProcEntry.m_dwProcessID, ModuleEntry))
{
rEncStream.WriteAscii(szModulesMsg);
rEncStream.WriteAscii(szDividerMsg);
do
{
rEncStream.WriteUTF8Bin(ModuleEntry.m_szModuleName);
TCHAR szVersionString[64];
if (GetVersionString(ModuleEntry.m_szModuleName, szVersionString, countof(szVersionString)))
{
rEncStream.WriteAscii(" (");
rEncStream.WriteUTF8Bin(szVersionString);
rEncStream.WriteByte(_T(')'));
}
rEncStream.WriteAscii(szBaseMsg);
#if defined _WIN64
sprintf_s(szTempBuf, countof(szTempBuf), "%016lX", (DWORD_PTR)ModuleEntry.m_pLoadBase);
#elif defined _WIN32
sprintf_s(szTempBuf, countof(szTempBuf), "%08lX", (DWORD_PTR)ModuleEntry.m_pLoadBase);
#endif
rEncStream.WriteAscii(szTempBuf);
rEncStream.WriteAscii(szSizeMsg);
sprintf_s(szTempBuf, countof(szTempBuf), "%08lX", (DWORD_PTR)ModuleEntry.m_dwModuleSize);
rEncStream.WriteAscii(szTempBuf);
rEncStream.WriteAscii(szNewLine);
}
while (pEnumProcess->GetModuleNext(rProcEntry.m_dwProcessID, ModuleEntry));
rEncStream.WriteAscii(szNewLine);
}
else
rEncStream.WriteAscii(szNewLine);
}
/**
* @param rXmlWriter - XML writer.
* @param pEnumProcess - pointer to the process enumerator;
* @param rProcEntry - process entry.
*/
void CSymEngine::GetModuleList(CXmlWriter& rXmlWriter, CEnumProcess* pEnumProcess, CEnumProcess::CProcessEntry& rProcEntry)
{
_ASSERTE(pEnumProcess != NULL);
rXmlWriter.WriteStartElement(_T("process")); // <process>
rXmlWriter.WriteElementString(_T("name"), rProcEntry.m_szProcessName); // <name>...</name>
TCHAR szTempBuf[64];
_ultot_s(rProcEntry.m_dwProcessID, szTempBuf, countof(szTempBuf), 10);
rXmlWriter.WriteElementString(_T("id"), szTempBuf); // <id>...</id>
rXmlWriter.WriteStartElement(_T("modules")); // <modules>
CEnumProcess::CModuleEntry ModuleEntry;
BOOL bContinue = pEnumProcess->GetModuleFirst(rProcEntry.m_dwProcessID, ModuleEntry);
while (bContinue)
{
rXmlWriter.WriteStartElement(_T("module")); // <module>
rXmlWriter.WriteElementString(_T("name"), ModuleEntry.m_szModuleName); // <name>...</name>
TCHAR szVersionString[64];
if (! GetVersionString(ModuleEntry.m_szModuleName, szVersionString, countof(szVersionString)))
*szVersionString = _T('\0');
rXmlWriter.WriteElementString(_T("version"), szVersionString); // <version>...</version>
#if defined _WIN64
_stprintf_s(szTempBuf, countof(szTempBuf), _T("0x%016lX"), (DWORD_PTR)ModuleEntry.m_pLoadBase);
#elif defined _WIN32
_stprintf_s(szTempBuf, countof(szTempBuf), _T("0x%08lX"), (DWORD_PTR)ModuleEntry.m_pLoadBase);
#endif
rXmlWriter.WriteElementString(_T("base"), szTempBuf); // <base>...</base>
_stprintf_s(szTempBuf, countof(szTempBuf), _T("0x%08lX"), (DWORD_PTR)ModuleEntry.m_dwModuleSize);
rXmlWriter.WriteElementString(_T("size"), szTempBuf); // <size>...</size>
rXmlWriter.WriteEndElement(); // </module>
bContinue = pEnumProcess->GetModuleNext(rProcEntry.m_dwProcessID, ModuleEntry);
}
rXmlWriter.WriteEndElement(); // </modules>
rXmlWriter.WriteEndElement(); // </process>
}
/**
* @param rXmlWriter - XML writer.
* @param pEnumProcess - pointer to the process enumerator;
*/
void CSymEngine::GetProcessList(CXmlWriter& rXmlWriter, CEnumProcess* pEnumProcess)
{
rXmlWriter.WriteStartElement(_T("processes")); // <processes>
_ASSERTE(pEnumProcess != NULL);
CEnumProcess::CProcessEntry ProcEntry;
if (g_dwFlags & BTF_LISTPROCESSES)
{
if (pEnumProcess->GetProcessFirst(ProcEntry))
{
do
GetModuleList(rXmlWriter, pEnumProcess, ProcEntry);
while (pEnumProcess->GetProcessNext(ProcEntry));
g_pEnumProc->Close();
}
}
else
{
pEnumProcess->GetCurrentProcess(ProcEntry);
GetModuleList(rXmlWriter, pEnumProcess, ProcEntry);
}
rXmlWriter.WriteEndElement(); // </processes>
}
/**
* @param rEncStream - UTF-8 encoder object.
* @param pEnumProcess - pointer to the process enumerator;
*/
void CSymEngine::GetProcessList(CUTF8EncStream& rEncStream, CEnumProcess* pEnumProcess)
{
_ASSERTE(pEnumProcess != NULL);
CEnumProcess::CProcessEntry ProcEntry;
if (g_dwFlags & BTF_LISTPROCESSES)
{
if (pEnumProcess->GetProcessFirst(ProcEntry))
{
do
GetModuleList(rEncStream, pEnumProcess, ProcEntry);
while (pEnumProcess->GetProcessNext(ProcEntry));
g_pEnumProc->Close();
}
}
else
{
pEnumProcess->GetCurrentProcess(ProcEntry);
GetModuleList(rEncStream, pEnumProcess, ProcEntry);
}
}
/**
* @param rEncStream - UTF-8 encoder object.
* @param dwThreadID - thread ID.
* @param hThread - thread handle.
* @param pszThreadStatus - thread status.
*/
void CSymEngine::GetWin32StackTrace(CUTF8EncStream& rEncStream, DWORD dwThreadID, HANDLE hThread, PCSTR pszThreadStatus)
{
static const CHAR szTraceMsg[] = "Stack Trace: ";
static const CHAR szThreadIDMsg[] = ", TID: ";
static const CHAR szDividerMsg[] = "----------------------------------------\r\n";
static const CHAR szNewLine[] = "\r\n";
CHAR szThreadID[32];
_ultoa_s(dwThreadID, szThreadID, countof(szThreadID), 10);
rEncStream.WriteAscii(szTraceMsg);
rEncStream.WriteAscii(pszThreadStatus);
rEncStream.WriteAscii(szThreadIDMsg);
rEncStream.WriteAscii(szThreadID);
rEncStream.WriteAscii(szNewLine);
rEncStream.WriteAscii(szDividerMsg);
BOOL bContinue = GetFirstWin32StackTraceString(rEncStream, hThread);
while (bContinue)
{
rEncStream.WriteAscii(szNewLine);
bContinue = GetNextWin32StackTraceString(rEncStream);
}
rEncStream.WriteAscii(szNewLine);
}
/**
* @param rXmlWriter - XML writer.
* @param dwThreadID - thread ID.
* @param hThread - thread handle.
* @param pszThreadStatus - thread status.
*/
void CSymEngine::GetWin32StackTrace(CXmlWriter& rXmlWriter, DWORD dwThreadID, HANDLE hThread, PCTSTR pszThreadStatus)
{
rXmlWriter.WriteStartElement(_T("thread")); // <thread>
TCHAR szThreadID[32];
_ultot_s(dwThreadID, szThreadID, countof(szThreadID), 10);
rXmlWriter.WriteElementString(_T("id"), szThreadID); // <id>...</id>
rXmlWriter.WriteElementString(_T("status"), pszThreadStatus); // <status>...</status>
rXmlWriter.WriteStartElement(_T("stack")); // <stack>
CStackTraceEntry Entry;
BOOL bContinue = GetFirstStackTraceEntry(Entry, hThread);
while (bContinue)
{
rXmlWriter.WriteStartElement(_T("frame")); // <frame>
rXmlWriter.WriteElementString(_T("module"), Entry.m_szModule); // <module>...</module>
rXmlWriter.WriteElementString(_T("address"), Entry.m_szAddress); // <address>...</address>
rXmlWriter.WriteStartElement(_T("function")); // <function>
rXmlWriter.WriteElementString(_T("name"), Entry.m_szFunctionName); // <name>...</name>
rXmlWriter.WriteElementString(_T("offset"), Entry.m_szFunctionOffset); // <offset>...</offset>
rXmlWriter.WriteEndElement(); // </function>
rXmlWriter.WriteElementString(_T("file"), Entry.m_szSourceFile); // <file>...</file>
rXmlWriter.WriteStartElement(_T("line")); // <line>
rXmlWriter.WriteElementString(_T("number"), Entry.m_szLineNumber); // <number>...</number>
rXmlWriter.WriteElementString(_T("offset"), Entry.m_szLineOffset); // <offset>...</offset>
rXmlWriter.WriteEndElement(); // </line>
rXmlWriter.WriteEndElement(); // </frame>
bContinue = GetNextStackTraceEntry(Entry);
}
rXmlWriter.WriteEndElement(); // </stack>
rXmlWriter.WriteEndElement(); // </thread>
}
#ifdef _MANAGED
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetNetStackTrace(CUTF8EncStream& rEncStream)
{
_ASSERTE(m_pNetStackTrace != NULL);
static const CHAR szTraceMsg[] = "\r\nCLR Stack Trace: ";
static const CHAR szInterruptedStateMsg[] = "Interrupted Thread";
static const CHAR szActiveStateMsg[] = "Active Thread";
static const CHAR szThreadIDMsg[] = ", TID: ";
static const CHAR szThreadNameMsg[] = ", Name: ";
static const CHAR szDividerMsg[] = "----------------------------------------\r\n";
static const CHAR szNewLine[] = "\r\n";
PCSTR pszThreadStatus = IsNetException() ? szInterruptedStateMsg : szActiveStateMsg;
DWORD dwThreadID;
WCHAR szThreadName[128];
NetThunks::GetThreadInfo(dwThreadID, szThreadName, countof(szThreadName));
CHAR szThreadID[32];
_ultoa_s(dwThreadID, szThreadID, countof(szThreadID), 10);
rEncStream.WriteAscii(szTraceMsg);
rEncStream.WriteAscii(pszThreadStatus);
if (*szThreadName)
{
rEncStream.WriteAscii(szThreadNameMsg);
rEncStream.WriteUTF8Bin(szThreadName);
}
rEncStream.WriteAscii(szThreadIDMsg);
rEncStream.WriteAscii(szThreadID);
rEncStream.WriteAscii(szNewLine);
rEncStream.WriteAscii(szDividerMsg);
bool bContinue = m_pNetStackTrace->GetFirstStackTraceString(rEncStream);
while (bContinue)
{
rEncStream.WriteAscii(szNewLine);
bContinue = m_pNetStackTrace->GetNextStackTraceString(rEncStream);
}
rEncStream.WriteAscii(szNewLine);
}
/**
* @param rXmlWriter - XML writer.
*/
void CSymEngine::GetNetStackTrace(CXmlWriter& rXmlWriter)
{
_ASSERTE(m_pNetStackTrace != NULL);
static const TCHAR szInterruptedState[] = _T("interrupted");
static const TCHAR szActiveState[] = _T("active");
PCTSTR pszThreadStatus = IsNetException() ? szInterruptedState : szActiveState;
rXmlWriter.WriteStartElement(_T("clr-thread")); // <clr-thread>
DWORD dwThreadID;
WCHAR szThreadName[128];
NetThunks::GetThreadInfo(dwThreadID, szThreadName, countof(szThreadName));
rXmlWriter.WriteElementString(_T("name"), szThreadName); // <name>...</name>
TCHAR szThreadID[32];
_ultot_s(dwThreadID, szThreadID, countof(szThreadID), 10);
rXmlWriter.WriteElementString(_T("id"), szThreadID); // <id>...</id>
rXmlWriter.WriteElementString(_T("status"), pszThreadStatus); // <status>...</status>
rXmlWriter.WriteStartElement(_T("stack")); // <stack>
CNetStackTrace::CNetStackTraceEntry Entry;
BOOL bContinue = m_pNetStackTrace->GetFirstStackTraceEntry(Entry);
while (bContinue)
{
rXmlWriter.WriteStartElement(_T("frame")); // <frame>
rXmlWriter.WriteElementString(_T("assembly"), Entry.m_szAssembly); // <assembly>...</assembly>
rXmlWriter.WriteElementString(_T("native-offset"), Entry.m_szNativeOffset); // <native-offset>...</native-offset>
rXmlWriter.WriteElementString(_T("il-offset"), Entry.m_szILOffset); // <il-offset>...</il-offset>
rXmlWriter.WriteElementString(_T("type"), Entry.m_szType); // <type>...</type>
rXmlWriter.WriteElementString(_T("method"), Entry.m_szMethod); // <method>...</method>
rXmlWriter.WriteElementString(_T("file"), Entry.m_szSourceFile); // <file>...</file>
rXmlWriter.WriteElementString(_T("line"), Entry.m_szLineNumber); // <line>...</line>
rXmlWriter.WriteElementString(_T("column"), Entry.m_szColumnNumber); // <column>...</column>
rXmlWriter.WriteEndElement(); // </frame>
bContinue = m_pNetStackTrace->GetNextStackTraceEntry(Entry);
}
rXmlWriter.WriteEndElement(); // </stack>
rXmlWriter.WriteEndElement(); // </clr-thread>
}
#endif
/**
* @param rEncStream - UTF-8 encoder object.
* @param pEnumProcess - pointer to the process enumerator;
*/
void CSymEngine::GetWin32ThreadsList(CUTF8EncStream& rEncStream, CEnumProcess* pEnumProcess)
{
_ASSERTE(pEnumProcess != NULL);
if (! FOpenThread)
return;
static const CHAR szSuspendedStateMsg[] = "Suspended Thread";
static const CHAR szRunningStateMsg[] = "Running Thread";
static const CHAR szActiveStateMsg[] = "Active Thread";
DWORD dwCurrentThreadID = GetCurrentThreadId();
DWORD dwCurrentProcessID = GetCurrentProcessId();
CEnumProcess::CThreadEntry thr;
if (pEnumProcess->GetThreadFirst(dwCurrentProcessID, thr))
{
do
{
bool bActiveThread = thr.m_dwThreadID == dwCurrentThreadID;
if (m_pExceptionPointers != NULL && bActiveThread)
continue;
PCSTR pszThreadStatus;
if (bActiveThread)
{
pszThreadStatus = szActiveStateMsg;
GetWin32StackTrace(rEncStream, thr.m_dwThreadID, NULL, pszThreadStatus);
}
else
{
HANDLE hThread = FOpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME, FALSE, thr.m_dwThreadID);
if (hThread == NULL)
continue;
pszThreadStatus = SuspendThread(hThread) ? szSuspendedStateMsg : szRunningStateMsg;
GetWin32StackTrace(rEncStream, thr.m_dwThreadID, hThread, pszThreadStatus);
ResumeThread(hThread);
CloseHandle(hThread);
}
}
while (pEnumProcess->GetThreadNext(dwCurrentProcessID, thr));
}
}
/**
* @param rXmlWriter - XML writer.
* @param pEnumProcess - pointer to the process enumerator;
*/
void CSymEngine::GetWin32ThreadsList(CXmlWriter& rXmlWriter, CEnumProcess* pEnumProcess)
{
_ASSERTE(pEnumProcess != NULL);
if (! FOpenThread)
return;
static const TCHAR szSuspendedState[] = _T("suspended");
static const TCHAR szRunningState[] = _T("running");
static const TCHAR szActiveState[] = _T("active");
DWORD dwCurrentThreadID = GetCurrentThreadId();
DWORD dwCurrentProcessID = GetCurrentProcessId();
CEnumProcess::CThreadEntry thr;
if (pEnumProcess->GetThreadFirst(dwCurrentProcessID, thr))
{
do
{
bool bActiveThread = thr.m_dwThreadID == dwCurrentThreadID;
if (m_pExceptionPointers != NULL && bActiveThread)
continue;
PCTSTR pszThreadStatus;
if (bActiveThread)
{
pszThreadStatus = szActiveState;
GetWin32StackTrace(rXmlWriter, thr.m_dwThreadID, NULL, pszThreadStatus);
}
else
{
HANDLE hThread = FOpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME, FALSE, thr.m_dwThreadID);
if (hThread == NULL)
continue;
pszThreadStatus = SuspendThread(hThread) ? szSuspendedState : szRunningState;
GetWin32StackTrace(rXmlWriter, thr.m_dwThreadID, hThread, pszThreadStatus);
ResumeThread(hThread);
CloseHandle(hThread);
}
}
while (pEnumProcess->GetThreadNext(dwCurrentProcessID, thr));
}
}
/**
* @param pszModuleName - module file name.
* @param pszVersionString - place to store version information.
* @param dwVersionStringSize - size of version string buffer.
* @return true if operation was completed successfully.
*/
BOOL CSymEngine::GetVersionString(PCTSTR pszModuleName, PTSTR pszVersionString, DWORD dwVersionStringSize)
{
if (dwVersionStringSize == 0)
return FALSE;
*pszVersionString = _T('\0');
DWORD dwSize, dwHandle;
dwSize = GetFileVersionInfoSize((PTSTR)pszModuleName, &dwHandle);
if (dwSize == 0)
return FALSE;
UINT uLength;
VS_FIXEDFILEINFO* pFileVerInfo;
PBYTE pVersionInfo = new BYTE[dwSize];
if (pVersionInfo == NULL)
return FALSE;
BOOL bResult = FALSE;
if (GetFileVersionInfo((PTSTR)pszModuleName, dwHandle, dwSize, pVersionInfo) &&
VerQueryValue(pVersionInfo, _T("\\"), (PVOID*)&pFileVerInfo, &uLength))
{
_ASSERTE(uLength == sizeof(*pFileVerInfo));
_stprintf_s(pszVersionString, dwVersionStringSize,
_T("%lu.%lu.%lu.%lu"),
HIWORD(pFileVerInfo->dwFileVersionMS),
LOWORD(pFileVerInfo->dwFileVersionMS),
HIWORD(pFileVerInfo->dwFileVersionLS),
LOWORD(pFileVerInfo->dwFileVersionLS));
bResult = TRUE;
}
delete[] pVersionInfo;
return bResult;
}
/**
* @param rStream - stream object.
*/
void CSymEngine::GetComputerIPs(CStrStream& rStream)
{
WSADATA wd;
if (WSAStartup(MAKEWORD(2, 0), &wd) == ERROR_SUCCESS)
{
CHAR szHostName[256];
if (gethostname(szHostName, countof(szHostName)) == ERROR_SUCCESS)
{
hostent* pHostEnt = gethostbyname(szHostName);
if (pHostEnt != NULL && pHostEnt->h_addrtype == AF_INET)
{
DWORD dwAddrPos = 0;
for (;;)
{
void *pIPAddr = pHostEnt->h_addr_list[dwAddrPos];
if (pIPAddr == NULL)
break;
IN_ADDR sin_addr;
CopyMemory(&sin_addr, pIPAddr, pHostEnt->h_length);
char* pszIPAddrA = inet_ntoa(sin_addr);
if (dwAddrPos)
rStream << _T(", ");
rStream << pszIPAddrA;
++dwAddrPos;
}
}
}
WSACleanup();
}
}
/**
* @param rEncStream - UTF-8 encoder object.
*/
void CSymEngine::GetComputerIPs(CUTF8EncStream& rEncStream)
{
CStrStream Stream(256);
GetComputerIPs(Stream);
rEncStream.WriteUTF8Bin(Stream);
}
/**
* @param rXmlWriter - XML writer.
*/
void CSymEngine::GetComputerIPs(CXmlWriter& rXmlWriter)
{
WSADATA wd;
if (WSAStartup(MAKEWORD(2, 0), &wd) == ERROR_SUCCESS)
{
CHAR szHostName[256];
if (gethostname(szHostName, countof(szHostName)) == ERROR_SUCCESS)
{
hostent* pHostEnt = gethostbyname(szHostName);
if (pHostEnt != NULL && pHostEnt->h_addrtype == AF_INET)
{
rXmlWriter.WriteStartElement(_T("ips")); // <ips>
DWORD dwAddrPos = 0;
for (;;)
{
void *pIPAddr = pHostEnt->h_addr_list[dwAddrPos];
if (pIPAddr == NULL)
break;
IN_ADDR sin_addr;
CopyMemory(&sin_addr, pIPAddr, pHostEnt->h_length);
char* pszIPAddrA = inet_ntoa(sin_addr);
#ifdef _UNICODE
TCHAR szIPAddr[32];
MultiByteToWideChar(CP_ACP, 0, pszIPAddrA, -1, szIPAddr, countof(szIPAddr));
PWSTR pszIPAddr = szIPAddr;
#else
PSTR pszIPAddr = pszIPAddrA;
#endif
rXmlWriter.WriteElementString(_T("ip"), pszIPAddr); // <ip>...</ip>
++dwAddrPos;
}
rXmlWriter.WriteEndElement(); // </ips>
}
}
WSACleanup();
}
}
/**
* @param pszDateTime - pointer to the date-time buffer.
* @param dwDateTimeSize - date-time buffer size.
*/
void CSymEngine::GetDateTime(PTSTR pszDateTime, DWORD dwDateTimeSize)
{
_ASSERTE(dwDateTimeSize > 0);
// Computers use different locales, so it's desirable to use universal date and time format.
const LCID lcidEnglishUS = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
DWORD dwOutputSize = GetDateFormat(lcidEnglishUS, LOCALE_USE_CP_ACP | DATE_LONGDATE | LOCALE_NOUSEROVERRIDE, &m_DateTime, NULL, pszDateTime, dwDateTimeSize);
if (dwOutputSize < dwDateTimeSize)
pszDateTime[dwOutputSize - 1] = _T(' ');
GetTimeFormat(lcidEnglishUS, LOCALE_USE_CP_ACP | LOCALE_NOUSEROVERRIDE, &m_DateTime, NULL, pszDateTime + dwOutputSize, dwDateTimeSize - dwOutputSize);
}
/**
* @param pszTimeStamp - pointer to the time-stamp buffer.
* @param dwTimeStampSize - time-stamp buffer size.
*/
void CSymEngine::GetTimeStamp(PTSTR pszTimeStamp, DWORD dwTimeStampSize)
{
FILETIME ftime;
SystemTimeToFileTime(&m_DateTime, &ftime);
ULARGE_INTEGER uint;
uint.LowPart = ftime.dwLowDateTime;
uint.HighPart = ftime.dwHighDateTime;
_ui64tot_s(uint.QuadPart, pszTimeStamp, dwTimeStampSize, 10);
}
/**
* @param rEncStream - UTF-8 encoder object.
* @param pEnumProcess - pointer to the process enumerator;
* pass NULL pointer if you want to skip process and module list.
*/
void CSymEngine::GetErrorLog(CUTF8EncStream& rEncStream, CEnumProcess* pEnumProcess)
{
static const CHAR szAppMsg[] = "Application: ";
static const CHAR szVersionMsg[] = "Version: ";
static const CHAR szComputerNameMsg[] = "Computer: ";
static const CHAR szComputerIPMsg[] = "IP Address: ";
static const CHAR szUserNameMsg[] = "User: ";
static const CHAR szDateTimeMsg[] = "Date: ";
static const CHAR szDividerMsg[] = "----------------------------------------\r\n";
static const CHAR szErrorMsg[] = "\r\n\r\nError Reason:\r\n";
static const CHAR szCommandLineMsg[] = "Command Line:\r\n";
static const CHAR szCurrentDirMsg[] = "\r\n\r\nCurrent Directory:\r\n";
static const CHAR szEnvironmentMsg[] = "\r\n\r\nEnvironment Variables:\r\n";
static const CHAR szInterruptedStateMsg[] = "Interrupted Thread";
static const CHAR szUserMsg[] = "\r\n\r\nUser Message:\r\n";
static const CHAR szSysErrorMsg[] = "\r\n\r\nSystem Error:\r\n";
static const CHAR szCOMErrorMsg[] = "\r\n\r\nCOM Error:\r\n";
static const CHAR szRegistersMsg[] = "\r\n\r\nRegisters:\r\n";
static const CHAR szCpuMsg[] = "\r\n\r\nCPU:\r\n";
static const CHAR szOSMsg[] = "\r\n\r\nOperating System:\r\n";
static const CHAR szMemMsg[] = "\r\n\r\nMemory Usage:\r\n";
static const CHAR szNewLine[] = "\r\n";
#ifdef _MANAGED
bool bNativeInfo = m_pNetStackTrace == NULL || (g_dwFlags & BTF_NATIVEINFO) != 0;
#endif
if (*g_szAppName)
{
rEncStream.WriteAscii(szAppMsg);
rEncStream.WriteUTF8Bin(g_szAppName);
rEncStream.WriteAscii(szNewLine);
}
if (*g_szAppVersion)
{
rEncStream.WriteAscii(szVersionMsg);
rEncStream.WriteUTF8Bin(g_szAppVersion);
rEncStream.WriteAscii(szNewLine);
}
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD dwSize = countof(szComputerName);
if (GetComputerName(szComputerName, &dwSize))
{
rEncStream.WriteAscii(szComputerNameMsg);
rEncStream.WriteUTF8Bin(szComputerName);
rEncStream.WriteAscii(szNewLine);
}
// Temporary encoder object.
CMemStream MemStream(1024);
CUTF8EncStream TmpEncStream(&MemStream);
TmpEncStream.Reset();
GetComputerIPs(TmpEncStream);
if (MemStream.GetLength())
{
MemStream.SetPosition(0, FILE_BEGIN);
rEncStream.WriteAscii(szComputerIPMsg);
rEncStream.Write(TmpEncStream);
rEncStream.WriteAscii(szNewLine);
}
TCHAR szUserName[UNLEN + 1];
dwSize = countof(szUserName);
if (GetUserName(szUserName, &dwSize))
{
rEncStream.WriteAscii(szUserNameMsg);
rEncStream.WriteUTF8Bin(szUserName);
rEncStream.WriteAscii(szNewLine);
}
TCHAR szDateTime[64];
GetDateTime(szDateTime, countof(szDateTime));
rEncStream.WriteAscii(szDateTimeMsg);
rEncStream.WriteUTF8Bin(szDateTime);
if (
#ifdef _MANAGED
IsNetException() ||
#endif
m_pExceptionPointers != NULL)
{
rEncStream.WriteAscii(szErrorMsg);
rEncStream.WriteAscii(szDividerMsg);
GetErrorString(rEncStream);
}
if (! g_strUserMessage.IsEmpty())
{
rEncStream.WriteAscii(szUserMsg);
rEncStream.WriteAscii(szDividerMsg);
rEncStream.WriteUTF8Bin(g_strUserMessage);
}
TmpEncStream.Reset();
GetSysErrorString(TmpEncStream);
if (MemStream.GetLength())
{
MemStream.SetPosition(0, FILE_BEGIN);
rEncStream.WriteAscii(szSysErrorMsg);
rEncStream.WriteAscii(szDividerMsg);
rEncStream.Write(TmpEncStream);
}
TmpEncStream.Reset();
GetComErrorString(TmpEncStream);
if (MemStream.GetLength())
{
MemStream.SetPosition(0, FILE_BEGIN);
rEncStream.WriteAscii(szCOMErrorMsg);
rEncStream.WriteAscii(szDividerMsg);
rEncStream.Write(TmpEncStream);
}
if (m_pExceptionPointers != NULL
#ifdef _MANAGED
&& bNativeInfo
#endif
)
{
rEncStream.WriteAscii(szRegistersMsg);
rEncStream.WriteAscii(szDividerMsg);
GetRegistersString(rEncStream);
}
rEncStream.WriteAscii(szCpuMsg);
rEncStream.WriteAscii(szDividerMsg);
GetCpuString(rEncStream);
rEncStream.WriteAscii(szOSMsg);
rEncStream.WriteAscii(szDividerMsg);
GetOsString(rEncStream);
rEncStream.WriteAscii(szMemMsg);
rEncStream.WriteAscii(szDividerMsg);
GetMemString(rEncStream);
rEncStream.WriteAscii(szNewLine);
#ifdef _MANAGED
if (m_pNetStackTrace != NULL)
{
GetNetStackTrace(rEncStream);
GetNetThreadsList(rEncStream);
}
if (bNativeInfo)
{
#endif
if (m_pExceptionPointers != NULL)
{
DWORD dwCurrentThreadID = GetCurrentThreadId();
GetWin32StackTrace(rEncStream, dwCurrentThreadID, NULL, szInterruptedStateMsg);
}
if (pEnumProcess != NULL)
GetWin32ThreadsList(rEncStream, pEnumProcess);
#ifdef _MANAGED
}
#endif
PCTSTR pszCommandLine = GetCommandLine();
_ASSERTE(pszCommandLine != NULL);
rEncStream.WriteAscii(szCommandLineMsg);
rEncStream.WriteAscii(szDividerMsg);
rEncStream.WriteUTF8Bin(pszCommandLine);
TCHAR szCurrentDirectory[MAX_PATH];
if (GetCurrentDirectory(countof(szCurrentDirectory), szCurrentDirectory) > 0)
{
rEncStream.WriteAscii(szCurrentDirMsg);
rEncStream.WriteAscii(szDividerMsg);
rEncStream.WriteUTF8Bin(szCurrentDirectory);
}
rEncStream.WriteAscii(szEnvironmentMsg);
rEncStream.WriteAscii(szDividerMsg);
GetEnvironmentStrings(rEncStream);
#ifdef _MANAGED
GetAssemblyList(rEncStream);
if (bNativeInfo)
{
#endif
if (pEnumProcess != NULL)
GetProcessList(rEncStream, pEnumProcess);
#ifdef _MANAGED
}
#endif
}
#ifdef _MANAGED
/**
* @param rXmlWriter - XML writer object.
* @param pNetStackTrace - current stack trace.
* @param exception - managed exception object.
* @param dwNestedErrorLevel - nested error level.
* @return true if error information is not empty.
*/
BOOL CSymEngine::GetErrorInfo(CXmlWriter& rXmlWriter, CNetStackTrace* pNetStackTrace, gcroot<Exception^> exception, DWORD dwNestedErrorLevel)
{
if (dwNestedErrorLevel >= MAX_INNER_ERROR_COUNT)
return FALSE;
_ASSERTE(pNetStackTrace != NULL);
CNetStackTrace::CNetErrorInfo ErrorInfo;
if (! pNetStackTrace->GetErrorInfo(ErrorInfo))
return FALSE;
if (dwNestedErrorLevel == 0)
rXmlWriter.WriteElementString(_T("what"), _T("MANAGED_EXCEPTION")); // <what>...</what>
rXmlWriter.WriteStartElement(_T("exception")); // <exception>
rXmlWriter.WriteElementString(_T("type"), ErrorInfo.m_szException); // <type>...</type>
rXmlWriter.WriteElementString(_T("message"), ErrorInfo.m_szMessage); // <message>...</message>
rXmlWriter.WriteEndElement(); // </exception>
if (dwNestedErrorLevel == 0)
{
rXmlWriter.WriteStartElement(_T("process")); // <process>
rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szProcessName); // <name>...</name>
rXmlWriter.WriteElementString(_T("id"), ErrorInfo.m_szProcessID); // <id>...</id>
rXmlWriter.WriteEndElement(); // </process>
rXmlWriter.WriteStartElement(_T("app-domain")); // <app-domain>
rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szAppDomainName); // <name>...</name>
rXmlWriter.WriteElementString(_T("id"), ErrorInfo.m_szAppDomainID); // <id>...</id>
rXmlWriter.WriteEndElement(); // </app-domain>
}
rXmlWriter.WriteElementString(_T("assembly"), ErrorInfo.m_szAssembly); // <assembly>...</assembly>
rXmlWriter.WriteElementString(_T("native-offset"), ErrorInfo.m_szNativeOffset); // <native-offset>...</native-offset>
rXmlWriter.WriteElementString(_T("il-offset"), ErrorInfo.m_szILOffset); // <il-offset>...</il-offset>
rXmlWriter.WriteElementString(_T("type"), ErrorInfo.m_szType); // <type>...</type>
rXmlWriter.WriteElementString(_T("method"), ErrorInfo.m_szMethod); // <method>...</method>
rXmlWriter.WriteElementString(_T("file"), ErrorInfo.m_szSourceFile); // <file>...</file>
rXmlWriter.WriteElementString(_T("line"), ErrorInfo.m_szLineNumber); // <line>...</line>
rXmlWriter.WriteElementString(_T("column"), ErrorInfo.m_szColumnNumber); // <column>...</column>
if (! NetThunks::IsNull(exception))
{
exception = NetThunks::GetInnerException(exception);
if (! NetThunks::IsNull(exception))
{
rXmlWriter.WriteStartElement(_T("inner-error")); // <inner-error>
CNetStackTrace NetStackTrace(exception);
GetErrorInfo(rXmlWriter, &NetStackTrace, exception, dwNestedErrorLevel + 1);
rXmlWriter.WriteEndElement(); // </inner-error>
}
}
return TRUE;
}
#endif
/**
* @param rXmlWriter - XML writer object.
* @return true if error information is not empty.
*/
BOOL CSymEngine::GetErrorInfo(CXmlWriter& rXmlWriter)
{
#ifdef _MANAGED
if (IsNetException())
{
rXmlWriter.WriteStartElement(_T("error")); // <error>
BOOL bResult = GetErrorInfo(rXmlWriter, m_pNetStackTrace, NetThunks::GetNetException(), 0);
rXmlWriter.WriteEndElement(); // </error>
return bResult;
}
else if (m_pExceptionPointers != NULL)
{
#endif
CErrorInfo ErrorInfo;
if (! GetErrorInfo(ErrorInfo))
return FALSE;
rXmlWriter.WriteStartElement(_T("error")); // <error>
rXmlWriter.WriteElementString(_T("what"), ErrorInfo.m_pszWhat); // <what>...</what>
rXmlWriter.WriteStartElement(_T("process")); // <process>
rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szProcessName); // <name>...</name>
rXmlWriter.WriteElementString(_T("id"), ErrorInfo.m_szProcessID); // <id>...</id>
rXmlWriter.WriteEndElement(); // </process>
rXmlWriter.WriteElementString(_T("module"), ErrorInfo.m_szModule); // <module>...</module>
rXmlWriter.WriteElementString(_T("address"), ErrorInfo.m_szAddress); // <address>...</address>
rXmlWriter.WriteStartElement(_T("function")); // <function>
rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szFunctionName); // <name>...</name>
rXmlWriter.WriteElementString(_T("offset"), ErrorInfo.m_szFunctionOffset); // <offset>...</offset>
rXmlWriter.WriteEndElement(); // </function>
rXmlWriter.WriteElementString(_T("file"), ErrorInfo.m_szSourceFile); // <file>...</file>
rXmlWriter.WriteStartElement(_T("line")); // <line>
rXmlWriter.WriteElementString(_T("number"), ErrorInfo.m_szLineNumber); // <number>...</number>
rXmlWriter.WriteElementString(_T("offset"), ErrorInfo.m_szLineOffset); // <offset>...</offset>
rXmlWriter.WriteEndElement(); // </line>
rXmlWriter.WriteEndElement(); // </error>
return TRUE;
#ifdef _MANAGED
}
return FALSE;
#endif
}
/**
* @param rXmlWriter - XML writer object.
*/
void CSymEngine::GetRegistersInfo(CXmlWriter& rXmlWriter)
{
_ASSERTE(m_pExceptionPointers != NULL);
CRegistersValues RegVals;
GetRegistersValues(RegVals);
rXmlWriter.WriteStartElement(_T("registers")); // <registers>
#if defined _M_IX86
rXmlWriter.WriteElementString(_T("eax"), RegVals.m_szEax); // <eax>...</eax>
rXmlWriter.WriteElementString(_T("ebx"), RegVals.m_szEbx); // <ebx>...</ebx>
rXmlWriter.WriteElementString(_T("ecx"), RegVals.m_szEcx); // <ecx>...</ecx>
rXmlWriter.WriteElementString(_T("edx"), RegVals.m_szEdx); // <edx>...</edx>
rXmlWriter.WriteElementString(_T("esi"), RegVals.m_szEsi); // <esi>...</esi>
rXmlWriter.WriteElementString(_T("edi"), RegVals.m_szEdi); // <edi>...</edi>
rXmlWriter.WriteElementString(_T("esp"), RegVals.m_szEsp); // <esp>...</esp>
rXmlWriter.WriteElementString(_T("ebp"), RegVals.m_szEbp); // <ebp>...</ebp>
rXmlWriter.WriteElementString(_T("eip"), RegVals.m_szEip); // <eip>...</eip>
rXmlWriter.WriteElementString(_T("cs"), RegVals.m_szSegCs); // <cs>...</cs>
rXmlWriter.WriteElementString(_T("ds"), RegVals.m_szSegDs); // <ds>...</ds>
rXmlWriter.WriteElementString(_T("ss"), RegVals.m_szSegSs); // <ss>...</ss>
rXmlWriter.WriteElementString(_T("es"), RegVals.m_szSegEs); // <es>...</es>
rXmlWriter.WriteElementString(_T("fs"), RegVals.m_szSegFs); // <fs>...</fs>
rXmlWriter.WriteElementString(_T("gs"), RegVals.m_szSegGs); // <gs>...</gs>
rXmlWriter.WriteElementString(_T("eflags"), RegVals.m_szEFlags); // <eflags>...</eflags>
#elif defined _M_X64
rXmlWriter.WriteElementString(_T("rax"), RegVals.m_szRax); // <rax>...</rax>
rXmlWriter.WriteElementString(_T("rbx"), RegVals.m_szRbx); // <rbx>...</rbx>
rXmlWriter.WriteElementString(_T("rcx"), RegVals.m_szRcx); // <rcx>...</rcx>
rXmlWriter.WriteElementString(_T("rdx"), RegVals.m_szRdx); // <rdx>...</rdx>
rXmlWriter.WriteElementString(_T("rsi"), RegVals.m_szRsi); // <rsi>...</rsi>
rXmlWriter.WriteElementString(_T("rdi"), RegVals.m_szRdi); // <rdi>...</rdi>
rXmlWriter.WriteElementString(_T("rsp"), RegVals.m_szRsp); // <rsp>...</rsp>
rXmlWriter.WriteElementString(_T("rbp"), RegVals.m_szRbp); // <rbp>...</rbp>
rXmlWriter.WriteElementString(_T("rip"), RegVals.m_szRip); // <rip>...</rip>
rXmlWriter.WriteElementString(_T("cs"), RegVals.m_szSegCs); // <cs>...</cs>
rXmlWriter.WriteElementString(_T("ds"), RegVals.m_szSegDs); // <ds>...</ds>
rXmlWriter.WriteElementString(_T("ss"), RegVals.m_szSegSs); // <ss>...</ss>
rXmlWriter.WriteElementString(_T("es"), RegVals.m_szSegEs); // <es>...</es>
rXmlWriter.WriteElementString(_T("fs"), RegVals.m_szSegFs); // <fs>...</fs>
rXmlWriter.WriteElementString(_T("gs"), RegVals.m_szSegGs); // <gs>...</gs>
rXmlWriter.WriteElementString(_T("eflags"), RegVals.m_szEFlags); // <eflags>...</eflags>
#else
#error CPU architecture is not supported.
#endif
rXmlWriter.WriteEndElement(); // </registers>
}
/**
* @param rXmlWriter - XML writer object.
*/
void CSymEngine::GetSysErrorInfo(CXmlWriter& rXmlWriter)
{
CSysErrorInfo SysErrorInfo;
GetSysErrorInfo(SysErrorInfo);
rXmlWriter.WriteStartElement(_T("syserror")); // <syserror>
rXmlWriter.WriteElementString(_T("code"), SysErrorInfo.m_szErrorCode); // <code>...</code>
PCTSTR pszMessageBuffer = SysErrorInfo.m_pszMessageBuffer ? SysErrorInfo.m_pszMessageBuffer : _T("");
rXmlWriter.WriteElementString(_T("description"), pszMessageBuffer); // <description>...</description>
rXmlWriter.WriteEndElement(); // </syserror>
}
/**
* @param rXmlWriter - XML writer object.
*/
void CSymEngine::GetComErrorInfo(CXmlWriter& rXmlWriter)
{
CComErrorInfo ComErrorInfo;
GetComErrorInfo(ComErrorInfo);
rXmlWriter.WriteStartElement(_T("comerror")); // <comerror>
rXmlWriter.WriteElementString(_T("description"), ComErrorInfo.m_strDescription); // <description>...</description>
rXmlWriter.WriteElementString(_T("helpfile"), ComErrorInfo.m_strHelpFile); // <helpfile>...</helpfile>
rXmlWriter.WriteElementString(_T("source"), ComErrorInfo.m_strSource); // <source>...</source>
rXmlWriter.WriteElementString(_T("guid"), ComErrorInfo.m_strGuid); // <guid>...</guid>
rXmlWriter.WriteEndElement(); // </comerror>
}
/**
* @param rXmlWriter - XML writer object.
*/
void CSymEngine::GetCpusInfo(CXmlWriter& rXmlWriter)
{
CCpusInfo CpusInfo;
GetCpusInfo(CpusInfo);
rXmlWriter.WriteStartElement(_T("cpus")); // <cpus>
TCHAR szNumCpus[16];
_ultot_s(CpusInfo.m_dwNumCpus, szNumCpus, countof(szNumCpus), 10);
rXmlWriter.WriteElementString(_T("number"), szNumCpus); // <number>...</number>
rXmlWriter.WriteElementString(_T("architecture"), CpusInfo.m_pszCpuArch); // <architecture>...</architecture>
for (DWORD dwCpuNum = 0; dwCpuNum < CpusInfo.m_dwNumCpus; ++dwCpuNum)
{
CCpuInfo CpuInfo;
if (GetCpuInfo(dwCpuNum, CpuInfo))
{
rXmlWriter.WriteStartElement(_T("cpu")); // <cpu>
rXmlWriter.WriteElementString(_T("id"), CpuInfo.m_szCpuId); // <id>...</id>
rXmlWriter.WriteElementString(_T("speed"), CpuInfo.m_szCpuSpeed); // <speed>...</speed>
rXmlWriter.WriteElementString(_T("description"), CpuInfo.m_szCpuDescription); // <description>...</description>
rXmlWriter.WriteEndElement(); // </cpu>
}
}
rXmlWriter.WriteEndElement(); // </cpus>
}
/**
* @param rXmlWriter - XML writer object.
*/
void CSymEngine::GetOsInfo(CXmlWriter& rXmlWriter)
{
COsInfo OsInfo;
GetOsInfo(OsInfo);
rXmlWriter.WriteStartElement(_T("os")); // <os>
rXmlWriter.WriteElementString(_T("version"), OsInfo.m_pszWinVersion); // <version>...</version>
rXmlWriter.WriteElementString(_T("spack"), OsInfo.m_szSPVersion); // <spack>...</spack>
rXmlWriter.WriteElementString(_T("build"), OsInfo.m_szBuildNumber); // <build>...</build>
#ifdef _MANAGED
rXmlWriter.WriteElementString(_T("clr-version"), OsInfo.m_szNetVersion); // <clr-version>...</clr-version>
#endif
rXmlWriter.WriteEndElement(); // </os>
}
/**
* @param rXmlWriter - XML writer object.
*/
void CSymEngine::GetMemInfo(CXmlWriter& rXmlWriter)
{
CMemInfo MemInfo;
GetMemInfo(MemInfo);
rXmlWriter.WriteStartElement(_T("memory")); // <memory>
rXmlWriter.WriteElementString(_T("load"), MemInfo.m_szMemoryLoad); // <load>...</load>
rXmlWriter.WriteElementString(_T("totalphys"), MemInfo.m_szTotalPhys); // <totalphys>...</totalphys>
rXmlWriter.WriteElementString(_T("availphys"), MemInfo.m_szAvailPhys); // <availphys>...</availphys>
rXmlWriter.WriteElementString(_T("totalpage"), MemInfo.m_szTotalPageFile); // <totalpage>...</totalpage>
rXmlWriter.WriteElementString(_T("availpage"), MemInfo.m_szAvailPageFile); // <availpage>...</availpage>
rXmlWriter.WriteEndElement(); // </memory>
}
/**
* @param rXmlWriter - XML writer object.
* @param pEnumProcess - pointer to the process enumerator;
* pass NULL pointer if you want to skip process and module list.
*/
void CSymEngine::GetErrorLog(CXmlWriter& rXmlWriter, CEnumProcess* pEnumProcess)
{
static const TCHAR szInterruptedState[] = _T("interrupted");
#ifdef _MANAGED
bool bNativeInfo = m_pNetStackTrace == NULL || (g_dwFlags & BTF_NATIVEINFO) != 0;
#endif
rXmlWriter.SetIndentation(_T(' '), 2);
rXmlWriter.WriteStartDocument();
TCHAR szDateTime[64];
GetDateTime(szDateTime, countof(szDateTime));
TCHAR szHeader[256];
_stprintf_s(szHeader, countof(szHeader), _T("\r\n")
_T(" This %s was automatically generated\r\n")
_T(" by ") BUGTRAP_TITLE _T(" on %s\r\n"),
m_pExceptionPointers != NULL ? _T("error report") : _T("snapshot"), szDateTime);
rXmlWriter.WriteComment(szHeader); // <!--...-->
rXmlWriter.WriteStartElement(_T("report")); // <report>
rXmlWriter.WriteAttributeString(_T("version"), _T("1"));
rXmlWriter.WriteElementString(_T("platform"), BUGTRAP_PLATFORM); // <platform>...</platform>
rXmlWriter.WriteElementString(_T("application"), g_szAppName); // <application>...</application>
rXmlWriter.WriteElementString(_T("version"), g_szAppVersion); // <version>...</version>
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD dwSize = countof(szComputerName);
if (! GetComputerName(szComputerName, &dwSize))
*szComputerName = _T('\0');
rXmlWriter.WriteElementString(_T("computer"), szComputerName); // <computer>...</computer>
GetComputerIPs(rXmlWriter);
TCHAR szUserName[UNLEN + 1];
dwSize = countof(szUserName);
if (! GetUserName(szUserName, &dwSize))
*szUserName = _T('\0');
rXmlWriter.WriteElementString(_T("user"), szUserName); // <user>...</user>
TCHAR szTimeStamp[64];
GetTimeStamp(szTimeStamp, countof(szTimeStamp));
rXmlWriter.WriteElementString(_T("timestamp"), szTimeStamp); // <timestamp>...</timestamp>
GetErrorInfo(rXmlWriter);
rXmlWriter.WriteElementString(_T("usermsg"), g_strUserMessage); // <usermsg>...</usermsg>
GetSysErrorInfo(rXmlWriter);
GetComErrorInfo(rXmlWriter);
if (m_pExceptionPointers != NULL
#ifdef _MANAGED
&& bNativeInfo
#endif
)
{
GetRegistersInfo(rXmlWriter);
}
GetCpusInfo(rXmlWriter);
GetOsInfo(rXmlWriter);
GetMemInfo(rXmlWriter);
#ifdef _MANAGED
if (m_pNetStackTrace != NULL)
{
rXmlWriter.WriteStartElement(_T("clr-threads")); // <clr-threads>
GetNetStackTrace(rXmlWriter);
GetNetThreadsList(rXmlWriter);
rXmlWriter.WriteEndElement(); // </clr-threads>
}
if (bNativeInfo)
{
#endif
rXmlWriter.WriteStartElement(_T("threads")); // <threads>
if (m_pExceptionPointers != NULL)
{
DWORD dwCurrentThreadID = GetCurrentThreadId();
GetWin32StackTrace(rXmlWriter, dwCurrentThreadID, NULL, szInterruptedState);
}
if (pEnumProcess != NULL)
GetWin32ThreadsList(rXmlWriter, pEnumProcess);
rXmlWriter.WriteEndElement(); // </threads>
#ifdef _MANAGED
}
#endif
PCTSTR pszCommandLine = GetCommandLine();
_ASSERTE(pszCommandLine != NULL);
rXmlWriter.WriteElementString(_T("cmdline"), pszCommandLine); // <cmdline>...</cmdline>
TCHAR szCurrentDirectory[MAX_PATH];
if (GetCurrentDirectory(countof(szCurrentDirectory), szCurrentDirectory) == 0)
*szCurrentDirectory = _T('\0');
rXmlWriter.WriteElementString(_T("curdir"), szCurrentDirectory); // <curdir>...</curdir>
GetEnvironmentStrings(rXmlWriter);
#ifdef _MANAGED
GetAssemblyList(rXmlWriter);
if (bNativeInfo)
{
#endif
if (pEnumProcess != NULL)
GetProcessList(rXmlWriter, pEnumProcess);
#ifdef _MANAGED
}
#endif
rXmlWriter.WriteEndElement(); // </report>
rXmlWriter.WriteEndDocument();
}
/**
* @param hZipFile - zip archive handle.
* @param pszFilePath - name of added file.
* @param pszFileName - file name stored in archive.
* @return true if file was successfully added.
*/
BOOL CSymEngine::AddFileToArchive(zipFile hZipFile, PCTSTR pszFilePath, PCTSTR pszFileName)
{
PCSTR pszFileNameA;
#ifdef _UNICODE
CHAR szFileNameA[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, pszFileName, -1, szFileNameA, countof(szFileNameA), NULL, NULL);
pszFileNameA = szFileNameA;
#else
pszFileNameA = pszFileName;
#endif
BOOL bResult = FALSE;
HANDLE hFile = CreateFile(pszFilePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwFileSize = GetFileSize(hFile, NULL);
DWORD dwBufferSize = min(dwFileSize, g_dwMaxBufferSize);
PBYTE pFileBuffer = new BYTE[dwBufferSize];
if (pFileBuffer)
{
if (zipOpenNewFileInZip(hZipFile, pszFileNameA, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION) == Z_OK)
{
bResult = TRUE;
for (;;)
{
DWORD dwProcessedNumber = 0;
bResult = ReadFile(hFile, pFileBuffer, dwBufferSize, &dwProcessedNumber, NULL);
if (! bResult || dwProcessedNumber == 0)
break;
bResult = zipWriteInFileInZip(hZipFile, pFileBuffer, dwProcessedNumber) == Z_OK;
if (! bResult)
break;
}
if (zipCloseFileInZip(hZipFile) != Z_OK)
bResult = FALSE;
}
delete[] pFileBuffer;
}
CloseHandle(hFile);
}
else
{
DWORD dwLastError = GetLastError();
if (dwLastError == ERROR_FILE_NOT_FOUND ||
dwLastError == ERROR_PATH_NOT_FOUND ||
GetFileAttributes(pszFilePath) == INVALID_FILE_ATTRIBUTES)
{
bResult = TRUE; // ignore missing files
}
}
return bResult;
}
/**
* @param pszFileName - dump file name.
* @return true if crash information has been written successfully.
*/
BOOL CSymEngine::WriteDump(PCTSTR pszFileName)
{
_ASSERTE(FMiniDumpWriteDump != NULL);
if (FMiniDumpWriteDump == NULL)
return FALSE;
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
HANDLE hProcess = GetCurrentProcess();
DWORD dwProcessID = GetCurrentProcessId();
BOOL bResult;
if (m_pExceptionPointers != NULL)
{
MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = GetCurrentThreadId();
ExInfo.ExceptionPointers = m_pExceptionPointers;
ExInfo.ClientPointers = TRUE;
bResult = FMiniDumpWriteDump(hProcess, dwProcessID, hFile, g_eDumpType, &ExInfo, NULL, NULL);
}
else
bResult = FMiniDumpWriteDump(hProcess, dwProcessID, hFile, g_eDumpType, NULL, NULL, NULL);
CloseHandle(hFile);
if (! bResult)
DeleteFile(pszFileName);
return bResult;
}
/**
* @param pszFolderName - parent folder name.
* @param pEnumProcess - pointer to the process enumerator;
* pass NULL pointer if you want to skip process and module list.
* @return true if crash information has been stored successfully.
*/
BOOL CSymEngine::WriteReportFiles(PCTSTR pszFolderName, CEnumProcess* pEnumProcess)
{
PCTSTR pszLogExtension = GetLogFileExtension();
if (pszLogExtension == NULL)
return FALSE;
TCHAR szLogFileName[MAX_PATH];
_stprintf_s(szLogFileName, countof(szLogFileName), _T("errorlog.%s"), pszLogExtension);
TCHAR szFullLogFileName[MAX_PATH];
PathCombine(szFullLogFileName, pszFolderName, szLogFileName);
if (! WriteLog(szFullLogFileName, pEnumProcess))
return FALSE;
if (g_eDumpType != MiniDumpNoDump)
{
TCHAR szFullDumpFileName[MAX_PATH];
PathCombine(szFullDumpFileName, pszFolderName, _T("crashdump.dmp"));
if (FMiniDumpWriteDump != NULL && ! WriteDump(szFullDumpFileName))
return FALSE;
}
if (m_pScreenShot)
{
TCHAR szFullScreenShotFileName[MAX_PATH];
PathCombine(szFullScreenShotFileName, pszFolderName, _T("screenshot"));
if (! m_pScreenShot->WriteScreenShot(szFullScreenShotFileName))
return FALSE;
}
return TRUE;
}
/**
* @param pszReportFolder - path to folder containing report files.
* @param pszArchiveFileName - zip file name.
* @return true if crash information has been archived successfully.
*/
BOOL CSymEngine::ArchiveReportFiles(PCTSTR pszReportFolder, PCTSTR pszArchiveFileName)
{
PCSTR pszArchiveFileNameA;
#ifdef _UNICODE
CHAR szArchiveFileNameA[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, pszArchiveFileName, -1, szArchiveFileNameA, countof(szArchiveFileNameA), NULL, NULL);
pszArchiveFileNameA = szArchiveFileNameA;
#else
pszArchiveFileNameA = pszArchiveFileName;
#endif
zipFile hZipFile = zipOpen(pszArchiveFileNameA, APPEND_STATUS_CREATE);
if (! hZipFile)
return FALSE;
BOOL bResult = TRUE;
TCHAR szFindFileTemplate[MAX_PATH];
PathCombine(szFindFileTemplate, pszReportFolder, _T("*"));
WIN32_FIND_DATA FindData;
HANDLE hFindFile = FindFirstFile(szFindFileTemplate, &FindData);
if (hFindFile != INVALID_HANDLE_VALUE)
{
BOOL bMore = TRUE;
while (bMore)
{
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
{
TCHAR szFilePath[MAX_PATH];
PathCombine(szFilePath, pszReportFolder, FindData.cFileName);
bResult = AddFileToArchive(hZipFile, szFilePath, FindData.cFileName);
if (! bResult)
break;
}
bMore = FindNextFile(hFindFile, &FindData);
}
FindClose(hFindFile);
}
if (bResult)
{
size_t nFileCount = g_arrLogLinks.GetCount();
for (size_t nFilePos = 0; nFilePos < nFileCount; ++nFilePos)
{
CLogLink* pLogLink = g_arrLogLinks[nFilePos];
_ASSERTE(pLogLink != NULL);
PCTSTR pszFilePath = pLogLink->GetLogFileName();
PCTSTR pszFileName = PathFindFileName(pszFilePath);
_ASSERTE(pszFileName != NULL);
bResult = AddFileToArchive(hZipFile, pszFilePath, pszFileName);
if (! bResult)
break;
}
}
if (zipClose(hZipFile, NULL) != ZIP_OK)
bResult = FALSE;
if (! bResult)
DeleteFile(pszArchiveFileName);
return bResult;
}
/**
* @param pszArchiveFileName - zip file name.
* @param pEnumProcess - pointer to the process enumerator;
* pass NULL pointer if you want to skip process and module list.
* @return true if crash information has been archived successfully.
*/
BOOL CSymEngine::WriteReportArchive(PCTSTR pszArchiveFileName, CEnumProcess* pEnumProcess)
{
TCHAR szTempPath[MAX_PATH];
CreateTempFolder(szTempPath, countof(szTempPath));
BOOL bResult = WriteReportFiles(szTempPath, pEnumProcess) &&
ArchiveReportFiles(szTempPath, pszArchiveFileName);
DeleteFolder(szTempPath);
return bResult;
}
/**
* @param pszExtension - file extension.
* @param pszFileName - buffer for resulting file name.
* @param dwBufferSize - size of file name buffer.
*/
void CSymEngine::GetReportFileName(PCTSTR pszExtension, PTSTR pszFileName, DWORD dwBufferSize) const
{
if (pszExtension == NULL)
pszExtension = _T("");
size_t nFileNameLen = GetCanonicalAppName(pszFileName, dwBufferSize, FALSE);
if (nFileNameLen > 0 && nFileNameLen + 1 < dwBufferSize)
{
pszFileName[nFileNameLen++] = _T('_');
pszFileName[nFileNameLen] = _T('\0');
}
_stprintf_s(pszFileName + nFileNameLen, dwBufferSize - nFileNameLen,
_T("%s_%02d%02d%02d-%02d%02d%02d.%s"),
m_pExceptionPointers != NULL ? _T("error_report") : _T("snapshot"),
m_DateTime.wYear % 100, m_DateTime.wMonth, m_DateTime.wDay,
m_DateTime.wHour, m_DateTime.wMinute, m_DateTime.wSecond,
pszExtension);
}
/**
* @param pszFileName - log file name.
* @param pEnumProcess - pointer to the process enumerator;
* pass NULL pointer if you want to skip process and module list.
* @return true if crash information has been archived successfully.
*/
BOOL CSymEngine::WriteLog(PCTSTR pszFileName, CEnumProcess* pEnumProcess)
{
CFileStream FileStream(1024);
if (! FileStream.Open(pszFileName, CREATE_ALWAYS, GENERIC_WRITE))
return FALSE;
if (g_eReportFormat == BTRF_TEXT)
{
CUTF8EncStream EncStream(&FileStream);
GetErrorLog(EncStream, pEnumProcess);
return TRUE;
}
else if (g_eReportFormat == BTRF_XML)
{
CXmlWriter XmlWriter(&FileStream);
GetErrorLog(XmlWriter, pEnumProcess);
return TRUE;
}
else
{
_ASSERT(FALSE);
return FALSE;
}
}
/**
* @param rEntry - stack entry information.
* @return true if there is information about stack entry.
*/
BOOL CSymEngine::GetNextStackTraceEntry(CStackTraceEntry& rEntry)
{
if (m_hDbgHelpDll == NULL)
return FALSE;
if (++m_dwFrameCount > MAX_FRAME_COUNT)
return FALSE;
BOOL bResult = FStackWalk64(IMAGE_FILE_MACHINE_TYPE,
m_hSymProcess,
m_swContext.m_hThread,
&m_swContext.m_stFrame,
&m_swContext.m_context,
ReadProcessMemoryProc64,
FSymFunctionTableAccess64,
FSymGetModuleBase64,
NULL);
if (! bResult)
return FALSE;
HINSTANCE hModule = (HINSTANCE)FSymGetModuleBase64(m_hSymProcess, m_swContext.m_stFrame.AddrPC.Offset);
if (hModule != NULL)
GetModuleFileName(hModule, rEntry.m_szModule, countof(rEntry.m_szModule));
else
*rEntry.m_szModule = _T('\0');
DWORD64 dwExceptionAddress = m_swContext.m_stFrame.AddrPC.Offset;
WORD wExceptionSegment = m_swContext.m_stFrame.AddrPC.Segment;
#if defined _WIN64
_stprintf_s(rEntry.m_szAddress, countof(rEntry.m_szAddress),
_T("%04lX:%016lX"), wExceptionSegment, dwExceptionAddress);
#elif defined _WIN32
_stprintf_s(rEntry.m_szAddress, countof(rEntry.m_szAddress),
_T("%04lX:%08lX"), wExceptionSegment, dwExceptionAddress);
#endif
BYTE arrSymBuffer[512];
ZeroMemory(arrSymBuffer, sizeof(arrSymBuffer));
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)arrSymBuffer;
pSymbol->SizeOfStruct = sizeof(*pSymbol);
pSymbol->MaxNameLen = sizeof(arrSymBuffer) - sizeof(*pSymbol) + 1;
DWORD64 dwDisplacement64;
if (FSymFromAddr(m_hSymProcess, dwExceptionAddress, &dwDisplacement64, pSymbol))
{
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP, 0, pSymbol->Name, -1, rEntry.m_szFunctionName, countof(rEntry.m_szFunctionName));
#else
_tcscpy_s(rEntry.m_szFunctionName, countof(rEntry.m_szFunctionName), pSymbol->Name);
#endif
if (dwDisplacement64)
{
_ui64tot_s(dwDisplacement64, rEntry.m_szFunctionOffset, countof(rEntry.m_szFunctionOffset), 10);
_stprintf_s(rEntry.m_szFunctionInfo, countof(rEntry.m_szFunctionInfo), _T("%s()+%s byte(s)"), rEntry.m_szFunctionName, rEntry.m_szFunctionOffset);
}
else
{
*rEntry.m_szFunctionOffset = _T('\0');
_stprintf_s(rEntry.m_szFunctionInfo, countof(rEntry.m_szFunctionInfo), _T("%s()"), rEntry.m_szFunctionName);
}
}
else
{
*rEntry.m_szFunctionInfo = _T('\0');
*rEntry.m_szFunctionName = _T('\0');
*rEntry.m_szFunctionOffset = _T('\0');
}
DWORD dwDisplacement32;
IMAGEHLP_LINE64 il;
ZeroMemory(&il, sizeof(il));
il.SizeOfStruct = sizeof(il);
if (FSymGetLineFromAddr64(m_hSymProcess, dwExceptionAddress, &dwDisplacement32, &il))
{
#ifdef _UNICODE
MultiByteToWideChar(CP_ACP, 0, il.FileName, -1, rEntry.m_szSourceFile, countof(rEntry.m_szSourceFile));
#else
_tcscpy_s(rEntry.m_szSourceFile, countof(rEntry.m_szSourceFile), il.FileName);
#endif
_ultot_s(il.LineNumber, rEntry.m_szLineNumber, countof(rEntry.m_szLineNumber), 10);
if (dwDisplacement32)
{
_ultot_s(dwDisplacement32, rEntry.m_szLineOffset, countof(rEntry.m_szLineOffset), 10);
_stprintf_s(rEntry.m_szLineInfo, countof(rEntry.m_szLineInfo), _T("line %s+%s byte(s)"), rEntry.m_szLineNumber, rEntry.m_szLineOffset);
}
else
{
*rEntry.m_szLineOffset = _T('\0');
_stprintf_s(rEntry.m_szLineInfo, countof(rEntry.m_szLineInfo), _T("line %s"), rEntry.m_szLineNumber);
}
}
else
{
*rEntry.m_szSourceFile = _T('\0');
*rEntry.m_szLineInfo = _T('\0');
*rEntry.m_szLineNumber = _T('\0');
*rEntry.m_szLineOffset = _T('\0');
}
return TRUE;
}
/**
* @param pszFileName - report file name.
* @param pEnumProcess - pointer to the process enumerator;
* pass NULL pointer if you want to skip process and module list.
* @return true if crash information has been archived successfully.
*/
BOOL CSymEngine::WriteReport(PCTSTR pszFileName, CEnumProcess* pEnumProcess)
{
return (g_dwFlags & BTF_DETAILEDMODE ?
WriteReportArchive(pszFileName, pEnumProcess) :
WriteLog(pszFileName, pEnumProcess));
}
/**
* @param pszArchiveFileName - report file name.
* @param pszFileName - file name.
* @return true if user comment has been appended to successfully.
*/
BOOL CSymEngine::AppendFileToReport(PCTSTR pszArchiveFileName, PCTSTR pszFileName)
{
if ((g_dwFlags & BTF_DETAILEDMODE) == 0)
return FALSE;
PCSTR pszArchiveFileNameA;
#ifdef _UNICODE
CHAR szArchiveFileNameA[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, pszArchiveFileName, -1, szArchiveFileNameA, countof(szArchiveFileNameA), NULL, NULL);
pszArchiveFileNameA = szArchiveFileNameA;
#else
pszArchiveFileNameA = pszArchiveFileName;
#endif
zipFile hZipFile = zipOpen(pszArchiveFileNameA, APPEND_STATUS_ADDINZIP);
if (! hZipFile)
return FALSE;
BOOL bResult = AddFileToArchive(hZipFile, pszFileName, PathFindFileName(pszFileName));
if (zipClose(hZipFile, NULL) != ZIP_OK)
bResult = FALSE;
return bResult;
}
/**
* @param rEncStream - UTF-8 encoder object.
* @param hThread - handle of examined thread; pass NULL if you want to get exception stack trace.
* @return true if thread context was successfully resolved.
*/
BOOL CSymEngine::GetFirstWin32StackTraceString(CUTF8EncStream& rEncStream, HANDLE hThread)
{
if (! InitStackTrace(hThread))
return FALSE;
return GetNextWin32StackTraceString(rEncStream);
}
/**
* @param rEntry - stack entry information.
* @param hThread - handle of examined thread; pass NULL if you want to get exception stack trace.
* @return true if there is information about stack entry.
*/
BOOL CSymEngine::GetFirstStackTraceEntry(CStackTraceEntry& rEntry, HANDLE hThread)
{
if (! InitStackTrace(hThread))
return FALSE;
return GetNextStackTraceEntry(rEntry);
}
/**
* @return log file extension.
*/
PCTSTR CSymEngine::GetLogFileExtension(void)
{
if (g_eReportFormat == BTRF_TEXT)
return _T("log");
else if (g_eReportFormat == BTRF_XML)
return _T("xml");
else
{
_ASSERT(FALSE);
return NULL;
}
}
/**
* @return report file extension.
*/
PCTSTR CSymEngine::GetReportFileExtension(void)
{
return (g_dwFlags & BTF_DETAILEDMODE ? _T("zip") : GetLogFileExtension());
}