/* * 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 = 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")); // 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")); // rXmlWriter.WriteElementString(_T("name"), szVariableName); // ... rXmlWriter.WriteElementString(_T("value"), pchEquation + 1); // ... rXmlWriter.WriteEndElement(); // } else pchEnd = _tcschr(pchEnvironmentPair, _T('\0')); pchEnvironmentPair = pchEnd + 1; } FreeEnvironmentStrings(pchEnvironment); } rXmlWriter.WriteEndElement(); // } /** * @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")); // rXmlWriter.WriteElementString(_T("name"), szAppDomainName); // ... rXmlWriter.WriteElementString(_T("id"), szAppDomainID); // ... rXmlWriter.WriteStartElement(_T("assemblies")); // CNetAssemblies NetAssemblies; CNetAssemblies::CAssemblyInfo AssemblyInfo; if (NetAssemblies.GetFirstAssembly(AssemblyInfo)) { do { rXmlWriter.WriteStartElement(_T("assembly")); // rXmlWriter.WriteElementString(_T("name"), AssemblyInfo.m_szName); // ... rXmlWriter.WriteElementString(_T("version"), AssemblyInfo.m_szVersion); // ... rXmlWriter.WriteElementString(_T("file-version"), AssemblyInfo.m_szFileVersion); // ... rXmlWriter.WriteElementString(_T("code-base"), AssemblyInfo.m_szCodeBase); // ... rXmlWriter.WriteEndElement(); // } while (NetAssemblies.GetNextAssembly(AssemblyInfo)); } rXmlWriter.WriteEndElement(); // rXmlWriter.WriteEndElement(); // } #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")); // rXmlWriter.WriteElementString(_T("name"), rProcEntry.m_szProcessName); // ... TCHAR szTempBuf[64]; _ultot_s(rProcEntry.m_dwProcessID, szTempBuf, countof(szTempBuf), 10); rXmlWriter.WriteElementString(_T("id"), szTempBuf); // ... rXmlWriter.WriteStartElement(_T("modules")); // CEnumProcess::CModuleEntry ModuleEntry; BOOL bContinue = pEnumProcess->GetModuleFirst(rProcEntry.m_dwProcessID, ModuleEntry); while (bContinue) { rXmlWriter.WriteStartElement(_T("module")); // rXmlWriter.WriteElementString(_T("name"), ModuleEntry.m_szModuleName); // ... TCHAR szVersionString[64]; if (! GetVersionString(ModuleEntry.m_szModuleName, szVersionString, countof(szVersionString))) *szVersionString = _T('\0'); rXmlWriter.WriteElementString(_T("version"), szVersionString); // ... #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); // ... _stprintf_s(szTempBuf, countof(szTempBuf), _T("0x%08lX"), (DWORD_PTR)ModuleEntry.m_dwModuleSize); rXmlWriter.WriteElementString(_T("size"), szTempBuf); // ... rXmlWriter.WriteEndElement(); // bContinue = pEnumProcess->GetModuleNext(rProcEntry.m_dwProcessID, ModuleEntry); } rXmlWriter.WriteEndElement(); // rXmlWriter.WriteEndElement(); // } /** * @param rXmlWriter - XML writer. * @param pEnumProcess - pointer to the process enumerator; */ void CSymEngine::GetProcessList(CXmlWriter& rXmlWriter, CEnumProcess* pEnumProcess) { rXmlWriter.WriteStartElement(_T("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(); // } /** * @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")); // TCHAR szThreadID[32]; _ultot_s(dwThreadID, szThreadID, countof(szThreadID), 10); rXmlWriter.WriteElementString(_T("id"), szThreadID); // ... rXmlWriter.WriteElementString(_T("status"), pszThreadStatus); // ... rXmlWriter.WriteStartElement(_T("stack")); // CStackTraceEntry Entry; BOOL bContinue = GetFirstStackTraceEntry(Entry, hThread); while (bContinue) { rXmlWriter.WriteStartElement(_T("frame")); // rXmlWriter.WriteElementString(_T("module"), Entry.m_szModule); // ... rXmlWriter.WriteElementString(_T("address"), Entry.m_szAddress); //
...
rXmlWriter.WriteStartElement(_T("function")); // rXmlWriter.WriteElementString(_T("name"), Entry.m_szFunctionName); // ... rXmlWriter.WriteElementString(_T("offset"), Entry.m_szFunctionOffset); // ... rXmlWriter.WriteEndElement(); // rXmlWriter.WriteElementString(_T("file"), Entry.m_szSourceFile); // ... rXmlWriter.WriteStartElement(_T("line")); // rXmlWriter.WriteElementString(_T("number"), Entry.m_szLineNumber); // ... rXmlWriter.WriteElementString(_T("offset"), Entry.m_szLineOffset); // ... rXmlWriter.WriteEndElement(); // rXmlWriter.WriteEndElement(); // bContinue = GetNextStackTraceEntry(Entry); } rXmlWriter.WriteEndElement(); //
rXmlWriter.WriteEndElement(); //
} #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")); // DWORD dwThreadID; WCHAR szThreadName[128]; NetThunks::GetThreadInfo(dwThreadID, szThreadName, countof(szThreadName)); rXmlWriter.WriteElementString(_T("name"), szThreadName); // ... TCHAR szThreadID[32]; _ultot_s(dwThreadID, szThreadID, countof(szThreadID), 10); rXmlWriter.WriteElementString(_T("id"), szThreadID); // ... rXmlWriter.WriteElementString(_T("status"), pszThreadStatus); // ... rXmlWriter.WriteStartElement(_T("stack")); // CNetStackTrace::CNetStackTraceEntry Entry; BOOL bContinue = m_pNetStackTrace->GetFirstStackTraceEntry(Entry); while (bContinue) { rXmlWriter.WriteStartElement(_T("frame")); // rXmlWriter.WriteElementString(_T("assembly"), Entry.m_szAssembly); // ... rXmlWriter.WriteElementString(_T("native-offset"), Entry.m_szNativeOffset); // ... rXmlWriter.WriteElementString(_T("il-offset"), Entry.m_szILOffset); // ... rXmlWriter.WriteElementString(_T("type"), Entry.m_szType); // ... rXmlWriter.WriteElementString(_T("method"), Entry.m_szMethod); // ... rXmlWriter.WriteElementString(_T("file"), Entry.m_szSourceFile); // ... rXmlWriter.WriteElementString(_T("line"), Entry.m_szLineNumber); // ... rXmlWriter.WriteElementString(_T("column"), Entry.m_szColumnNumber); // ... rXmlWriter.WriteEndElement(); // bContinue = m_pNetStackTrace->GetNextStackTraceEntry(Entry); } rXmlWriter.WriteEndElement(); // rXmlWriter.WriteEndElement(); // } #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")); // 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); // ... ++dwAddrPos; } rXmlWriter.WriteEndElement(); // } } 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, 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")); // ... rXmlWriter.WriteStartElement(_T("exception")); // rXmlWriter.WriteElementString(_T("type"), ErrorInfo.m_szException); // ... rXmlWriter.WriteElementString(_T("message"), ErrorInfo.m_szMessage); // ... rXmlWriter.WriteEndElement(); // if (dwNestedErrorLevel == 0) { rXmlWriter.WriteStartElement(_T("process")); // rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szProcessName); // ... rXmlWriter.WriteElementString(_T("id"), ErrorInfo.m_szProcessID); // ... rXmlWriter.WriteEndElement(); // rXmlWriter.WriteStartElement(_T("app-domain")); // rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szAppDomainName); // ... rXmlWriter.WriteElementString(_T("id"), ErrorInfo.m_szAppDomainID); // ... rXmlWriter.WriteEndElement(); // } rXmlWriter.WriteElementString(_T("assembly"), ErrorInfo.m_szAssembly); // ... rXmlWriter.WriteElementString(_T("native-offset"), ErrorInfo.m_szNativeOffset); // ... rXmlWriter.WriteElementString(_T("il-offset"), ErrorInfo.m_szILOffset); // ... rXmlWriter.WriteElementString(_T("type"), ErrorInfo.m_szType); // ... rXmlWriter.WriteElementString(_T("method"), ErrorInfo.m_szMethod); // ... rXmlWriter.WriteElementString(_T("file"), ErrorInfo.m_szSourceFile); // ... rXmlWriter.WriteElementString(_T("line"), ErrorInfo.m_szLineNumber); // ... rXmlWriter.WriteElementString(_T("column"), ErrorInfo.m_szColumnNumber); // ... if (! NetThunks::IsNull(exception)) { exception = NetThunks::GetInnerException(exception); if (! NetThunks::IsNull(exception)) { rXmlWriter.WriteStartElement(_T("inner-error")); // CNetStackTrace NetStackTrace(exception); GetErrorInfo(rXmlWriter, &NetStackTrace, exception, dwNestedErrorLevel + 1); rXmlWriter.WriteEndElement(); // } } 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")); // BOOL bResult = GetErrorInfo(rXmlWriter, m_pNetStackTrace, NetThunks::GetNetException(), 0); rXmlWriter.WriteEndElement(); // return bResult; } else if (m_pExceptionPointers != NULL) { #endif CErrorInfo ErrorInfo; if (! GetErrorInfo(ErrorInfo)) return FALSE; rXmlWriter.WriteStartElement(_T("error")); // rXmlWriter.WriteElementString(_T("what"), ErrorInfo.m_pszWhat); // ... rXmlWriter.WriteStartElement(_T("process")); // rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szProcessName); // ... rXmlWriter.WriteElementString(_T("id"), ErrorInfo.m_szProcessID); // ... rXmlWriter.WriteEndElement(); // rXmlWriter.WriteElementString(_T("module"), ErrorInfo.m_szModule); // ... rXmlWriter.WriteElementString(_T("address"), ErrorInfo.m_szAddress); //
...
rXmlWriter.WriteStartElement(_T("function")); // rXmlWriter.WriteElementString(_T("name"), ErrorInfo.m_szFunctionName); // ... rXmlWriter.WriteElementString(_T("offset"), ErrorInfo.m_szFunctionOffset); // ... rXmlWriter.WriteEndElement(); // rXmlWriter.WriteElementString(_T("file"), ErrorInfo.m_szSourceFile); // ... rXmlWriter.WriteStartElement(_T("line")); // rXmlWriter.WriteElementString(_T("number"), ErrorInfo.m_szLineNumber); // ... rXmlWriter.WriteElementString(_T("offset"), ErrorInfo.m_szLineOffset); // ... rXmlWriter.WriteEndElement(); // rXmlWriter.WriteEndElement(); //
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")); // #if defined _M_IX86 rXmlWriter.WriteElementString(_T("eax"), RegVals.m_szEax); // ... rXmlWriter.WriteElementString(_T("ebx"), RegVals.m_szEbx); // ... rXmlWriter.WriteElementString(_T("ecx"), RegVals.m_szEcx); // ... rXmlWriter.WriteElementString(_T("edx"), RegVals.m_szEdx); // ... rXmlWriter.WriteElementString(_T("esi"), RegVals.m_szEsi); // ... rXmlWriter.WriteElementString(_T("edi"), RegVals.m_szEdi); // ... rXmlWriter.WriteElementString(_T("esp"), RegVals.m_szEsp); // ... rXmlWriter.WriteElementString(_T("ebp"), RegVals.m_szEbp); // ... rXmlWriter.WriteElementString(_T("eip"), RegVals.m_szEip); // ... rXmlWriter.WriteElementString(_T("cs"), RegVals.m_szSegCs); // ... rXmlWriter.WriteElementString(_T("ds"), RegVals.m_szSegDs); // ... rXmlWriter.WriteElementString(_T("ss"), RegVals.m_szSegSs); // ... rXmlWriter.WriteElementString(_T("es"), RegVals.m_szSegEs); // ... rXmlWriter.WriteElementString(_T("fs"), RegVals.m_szSegFs); // ... rXmlWriter.WriteElementString(_T("gs"), RegVals.m_szSegGs); // ... rXmlWriter.WriteElementString(_T("eflags"), RegVals.m_szEFlags); // ... #elif defined _M_X64 rXmlWriter.WriteElementString(_T("rax"), RegVals.m_szRax); // ... rXmlWriter.WriteElementString(_T("rbx"), RegVals.m_szRbx); // ... rXmlWriter.WriteElementString(_T("rcx"), RegVals.m_szRcx); // ... rXmlWriter.WriteElementString(_T("rdx"), RegVals.m_szRdx); // ... rXmlWriter.WriteElementString(_T("rsi"), RegVals.m_szRsi); // ... rXmlWriter.WriteElementString(_T("rdi"), RegVals.m_szRdi); // ... rXmlWriter.WriteElementString(_T("rsp"), RegVals.m_szRsp); // ... rXmlWriter.WriteElementString(_T("rbp"), RegVals.m_szRbp); // ... rXmlWriter.WriteElementString(_T("rip"), RegVals.m_szRip); // ... rXmlWriter.WriteElementString(_T("cs"), RegVals.m_szSegCs); // ... rXmlWriter.WriteElementString(_T("ds"), RegVals.m_szSegDs); // ... rXmlWriter.WriteElementString(_T("ss"), RegVals.m_szSegSs); // ... rXmlWriter.WriteElementString(_T("es"), RegVals.m_szSegEs); // ... rXmlWriter.WriteElementString(_T("fs"), RegVals.m_szSegFs); // ... rXmlWriter.WriteElementString(_T("gs"), RegVals.m_szSegGs); // ... rXmlWriter.WriteElementString(_T("eflags"), RegVals.m_szEFlags); // ... #else #error CPU architecture is not supported. #endif rXmlWriter.WriteEndElement(); // } /** * @param rXmlWriter - XML writer object. */ void CSymEngine::GetSysErrorInfo(CXmlWriter& rXmlWriter) { CSysErrorInfo SysErrorInfo; GetSysErrorInfo(SysErrorInfo); rXmlWriter.WriteStartElement(_T("syserror")); // rXmlWriter.WriteElementString(_T("code"), SysErrorInfo.m_szErrorCode); // ... PCTSTR pszMessageBuffer = SysErrorInfo.m_pszMessageBuffer ? SysErrorInfo.m_pszMessageBuffer : _T(""); rXmlWriter.WriteElementString(_T("description"), pszMessageBuffer); // ... rXmlWriter.WriteEndElement(); // } /** * @param rXmlWriter - XML writer object. */ void CSymEngine::GetComErrorInfo(CXmlWriter& rXmlWriter) { CComErrorInfo ComErrorInfo; GetComErrorInfo(ComErrorInfo); rXmlWriter.WriteStartElement(_T("comerror")); // rXmlWriter.WriteElementString(_T("description"), ComErrorInfo.m_strDescription); // ... rXmlWriter.WriteElementString(_T("helpfile"), ComErrorInfo.m_strHelpFile); // ... rXmlWriter.WriteElementString(_T("source"), ComErrorInfo.m_strSource); // ... rXmlWriter.WriteElementString(_T("guid"), ComErrorInfo.m_strGuid); // ... rXmlWriter.WriteEndElement(); // } /** * @param rXmlWriter - XML writer object. */ void CSymEngine::GetCpusInfo(CXmlWriter& rXmlWriter) { CCpusInfo CpusInfo; GetCpusInfo(CpusInfo); rXmlWriter.WriteStartElement(_T("cpus")); // TCHAR szNumCpus[16]; _ultot_s(CpusInfo.m_dwNumCpus, szNumCpus, countof(szNumCpus), 10); rXmlWriter.WriteElementString(_T("number"), szNumCpus); // ... rXmlWriter.WriteElementString(_T("architecture"), CpusInfo.m_pszCpuArch); // ... for (DWORD dwCpuNum = 0; dwCpuNum < CpusInfo.m_dwNumCpus; ++dwCpuNum) { CCpuInfo CpuInfo; if (GetCpuInfo(dwCpuNum, CpuInfo)) { rXmlWriter.WriteStartElement(_T("cpu")); // rXmlWriter.WriteElementString(_T("id"), CpuInfo.m_szCpuId); // ... rXmlWriter.WriteElementString(_T("speed"), CpuInfo.m_szCpuSpeed); // ... rXmlWriter.WriteElementString(_T("description"), CpuInfo.m_szCpuDescription); // ... rXmlWriter.WriteEndElement(); // } } rXmlWriter.WriteEndElement(); // } /** * @param rXmlWriter - XML writer object. */ void CSymEngine::GetOsInfo(CXmlWriter& rXmlWriter) { COsInfo OsInfo; GetOsInfo(OsInfo); rXmlWriter.WriteStartElement(_T("os")); // rXmlWriter.WriteElementString(_T("version"), OsInfo.m_pszWinVersion); // ... rXmlWriter.WriteElementString(_T("spack"), OsInfo.m_szSPVersion); // ... rXmlWriter.WriteElementString(_T("build"), OsInfo.m_szBuildNumber); // ... #ifdef _MANAGED rXmlWriter.WriteElementString(_T("clr-version"), OsInfo.m_szNetVersion); // ... #endif rXmlWriter.WriteEndElement(); // } /** * @param rXmlWriter - XML writer object. */ void CSymEngine::GetMemInfo(CXmlWriter& rXmlWriter) { CMemInfo MemInfo; GetMemInfo(MemInfo); rXmlWriter.WriteStartElement(_T("memory")); // rXmlWriter.WriteElementString(_T("load"), MemInfo.m_szMemoryLoad); // ... rXmlWriter.WriteElementString(_T("totalphys"), MemInfo.m_szTotalPhys); // ... rXmlWriter.WriteElementString(_T("availphys"), MemInfo.m_szAvailPhys); // ... rXmlWriter.WriteElementString(_T("totalpage"), MemInfo.m_szTotalPageFile); // ... rXmlWriter.WriteElementString(_T("availpage"), MemInfo.m_szAvailPageFile); // ... rXmlWriter.WriteEndElement(); // } /** * @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")); // rXmlWriter.WriteAttributeString(_T("version"), _T("1")); rXmlWriter.WriteElementString(_T("platform"), BUGTRAP_PLATFORM); // ... rXmlWriter.WriteElementString(_T("application"), g_szAppName); // ... rXmlWriter.WriteElementString(_T("version"), g_szAppVersion); // ... TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwSize = countof(szComputerName); if (! GetComputerName(szComputerName, &dwSize)) *szComputerName = _T('\0'); rXmlWriter.WriteElementString(_T("computer"), szComputerName); // ... GetComputerIPs(rXmlWriter); TCHAR szUserName[UNLEN + 1]; dwSize = countof(szUserName); if (! GetUserName(szUserName, &dwSize)) *szUserName = _T('\0'); rXmlWriter.WriteElementString(_T("user"), szUserName); // ... TCHAR szTimeStamp[64]; GetTimeStamp(szTimeStamp, countof(szTimeStamp)); rXmlWriter.WriteElementString(_T("timestamp"), szTimeStamp); // ... GetErrorInfo(rXmlWriter); rXmlWriter.WriteElementString(_T("usermsg"), g_strUserMessage); // ... 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")); // GetNetStackTrace(rXmlWriter); GetNetThreadsList(rXmlWriter); rXmlWriter.WriteEndElement(); // } if (bNativeInfo) { #endif rXmlWriter.WriteStartElement(_T("threads")); // if (m_pExceptionPointers != NULL) { DWORD dwCurrentThreadID = GetCurrentThreadId(); GetWin32StackTrace(rXmlWriter, dwCurrentThreadID, NULL, szInterruptedState); } if (pEnumProcess != NULL) GetWin32ThreadsList(rXmlWriter, pEnumProcess); rXmlWriter.WriteEndElement(); // #ifdef _MANAGED } #endif PCTSTR pszCommandLine = GetCommandLine(); _ASSERTE(pszCommandLine != NULL); rXmlWriter.WriteElementString(_T("cmdline"), pszCommandLine); // ... TCHAR szCurrentDirectory[MAX_PATH]; if (GetCurrentDirectory(countof(szCurrentDirectory), szCurrentDirectory) == 0) *szCurrentDirectory = _T('\0'); rXmlWriter.WriteElementString(_T("curdir"), szCurrentDirectory); // ... GetEnvironmentStrings(rXmlWriter); #ifdef _MANAGED GetAssemblyList(rXmlWriter); if (bNativeInfo) { #endif if (pEnumProcess != NULL) GetProcessList(rXmlWriter, pEnumProcess); #ifdef _MANAGED } #endif rXmlWriter.WriteEndElement(); // 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()); }