void ADM_backTrack(const char *info,int lineno,const char *file) { fflush(stderr); fflush(stdout); if (mysaveFunction) mysaveFunction(); if (myFatalFunction) myFatalFunction("Crash", "Press OK to build crash info"); void* currentProcessId = GetCurrentProcess(); SymInitialize(currentProcessId, NULL, TRUE); dumpBackTrace(currentProcessId); SymCleanup(currentProcessId); printf("Assert failed at file %s, line %d\n\n",file,lineno); exit(-1); }
// Load and initialize dbghelp.dll. Returns false if failed. // To simplify callers, it can be called multiple times - it only does the // work the first time, unless force is true, in which case we re-initialize // the library (needed in crash dump where we re-initialize dbghelp.dll after // downloading symbols) bool Initialize(const WCHAR *symPathW, bool force) { if (gSymInitializeOk && !force) return true; bool needsCleanup = gSymInitializeOk; if (!Load()) return false; if (!_SymInitializeW && !_SymInitialize) { plog("dbghelp::Initialize(): SymInitializeW() and SymInitialize() not present in dbghelp.dll"); return false; } if (needsCleanup) SymCleanup(); if (_SymInitializeW) { gSymInitializeOk = _SymInitializeW(GetCurrentProcess(), symPathW, TRUE); } else { // SymInitializeW() is not present on some XP systems char symPathA[MAX_PATH]; if (0 != str::conv::ToCodePageBuf(symPathA, dimof(symPathA), symPathW, CP_ACP)) gSymInitializeOk = _SymInitialize(GetCurrentProcess(), symPathA, TRUE); } if (!gSymInitializeOk) { plog("dbghelp::Initialize(): _SymInitialize() failed"); return false; } DWORD symOptions = _SymGetOptions(); symOptions = (SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME); symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; // don't show system msg box on errors _SymSetOptions(symOptions); //SetupSymbolPath(); return true; }
void windows_print_stacktrace(CONTEXT* context) { SymInitialize(GetCurrentProcess(), 0, true); STACKFRAME frame = { 0 }; /* setup initial stack frame */ #ifdef AMD64 frame.AddrPC.Offset = context->Rip; frame.AddrStack.Offset = context->Rsp; frame.AddrFrame.Offset = context->Rsp; #else frame.AddrPC.Offset = context->Eip; frame.AddrStack.Offset = context->Esp; frame.AddrFrame.Offset = context->Ebp; #endif frame.AddrPC.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat; #ifdef AMD64 while (StackWalk64(IMAGE_FILE_MACHINE_AMD64, #else while (StackWalk(IMAGE_FILE_MACHINE_I386, #endif GetCurrentProcess(), GetCurrentThread(), &frame, context, 0, SymFunctionTableAccess, SymGetModuleBase, 0 ) ) { addr2line(icky_global_program_name, (void*)frame.AddrPC.Offset); } SymCleanup( GetCurrentProcess() ); }
void CMiniDumpReader::Close() { UnmapViewOfFile(m_pMiniDumpStartPtr); if(m_hFileMapping!=NULL) { CloseHandle(m_hFileMapping); } if(m_hFileMiniDump!=INVALID_HANDLE_VALUE) { CloseHandle(m_hFileMiniDump); m_hFileMiniDump = INVALID_HANDLE_VALUE; } m_pMiniDumpStartPtr = NULL; if(m_DumpData.m_hProcess!=NULL) { SymCleanup(m_DumpData.m_hProcess); } }
bool CATDbgHelper::Close( void ) { LOG_FUNC_ENTRY("CATDbgHelper::Close"); if ( ! SymUnloadModule64( GetCurrentProcess(), m_BaseAddress ) ) { LOG_STRING("Dbghelp:Module unload failed."); } CDBGHELPER_CLIENTS--; if ( CDBGHELPER_OPEN && CDBGHELPER_CLIENTS == 0) { // Cleanup dbghelper. if ( ! SymCleanup( GetCurrentProcess() ) ) { LOG_STRING("dbghelper cleanup failed."); return false; } LOG_STRING("dbghelper closed."); // Set state not opened. CDBGHELPER_OPEN = false; } return true; }
static LONG CALLBACK ctrl_handler_w32(LPEXCEPTION_POINTERS info) { switch (info->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: switch (info->ExceptionRecord->ExceptionInformation[0]) { case 0: LbErrorLog("Attempt to read from inaccessible memory address.\n"); break; case 1: LbErrorLog("Attempt to write to inaccessible memory address.\n"); break; case 8: LbErrorLog("User-mode data execution prevention (DEP) violation.\n"); break; default: LbErrorLog("Memory access voilation, code %d.\n",(int)info->ExceptionRecord->ExceptionInformation[0]); break; } break; case EXCEPTION_INT_DIVIDE_BY_ZERO: LbErrorLog("Attempt of integer division by zero.\n"); break; default: LbErrorLog("Failure code %x received.\n",info->ExceptionRecord->ExceptionCode); break; } if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) { LbErrorLog("Failed to init symbol context\n"); } else { _backtrace(16 , info->ContextRecord); SymCleanup(GetCurrentProcess()); } LbScreenReset(); LbErrorLogClose(); return EXCEPTION_EXECUTE_HANDLER; }
void OsDestroy(OsContext* aContext) { if (aContext == NULL) { return; } (void)SymCleanup(aContext->iDebugSymbolHandle); if (NULL != aContext->iInterfaceChangeObserver) { aContext->iInterfaceChangeObserver->iShutdown = 1; (void)WSASetEvent(aContext->iInterfaceChangeObserver->iShutdownEvent); (void)WaitForSingleObject(aContext->iInterfaceChangeObserver->iSem, INFINITE); CloseHandle(aContext->iInterfaceChangeObserver->iSem); WSACloseEvent(aContext->iInterfaceChangeObserver->iEvent); WSACloseEvent(aContext->iInterfaceChangeObserver->iShutdownEvent); (void)closesocket(aContext->iInterfaceChangeObserver->iSocket); free(aContext->iInterfaceChangeObserver); aContext->iInterfaceChangeObserver = NULL; } (void)WSACleanup(); OsMutexDestroy(aContext->iMutex); aContext->iMutex = kHandleNull; TlsFree(aContext->iTlsIndex); free(aContext); }
static LONG WINAPI exception_filter(LPEXCEPTION_POINTERS info) { struct output_buffer ob; output_init(&ob, g_output, BUFFER_MAX); if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) { output_print(&ob,"Failed to init symbol context\n"); } else { struct bfd_ctx bc; _backtrace(&ob , &bc , 128 , info->ContextRecord); close_bfd_ctx(&bc); SymCleanup(GetCurrentProcess()); } fputs(g_output , stderr); exit(1); return 0; }
BOOLEAN MspAcquireContext( IN PMSP_STACKTRACE_OBJECT Object, IN ULONG ProcessId ) { PMSP_STACKTRACE_CONTEXT Old; PMSP_STACKTRACE_CONTEXT New; WCHAR SymbolPath[MAX_PATH * 2]; Old = Object->Context; if (Old != NULL) { if (Old->ProcessId == ProcessId) { return TRUE; } else { SymCleanup(Old->ProcessHandle); } } New = MspLookupStackTraceContext(Object, ProcessId, TRUE); if (!New) { __debugbreak(); return FALSE; } ASSERT(New->ProcessHandle != NULL); MdbGetSymbolPath(SymbolPath, MAX_PATH * 2); SymInitializeW(New->ProcessHandle, SymbolPath, TRUE); SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); Object->Context = New; return TRUE; }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // const char* USER_SYMBOL_SEARCH_PATH = "http://msdl.microsoft.com/download/symbols"; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void SymEngine::Init() { if (!isInitialized) { previousOptions = SymGetOptions(); memset(previousSearchPath, 0, MAX_SEARCH_PATH_LENGTH); SymGetSearchPath(hProcess, previousSearchPath, MAX_SEARCH_PATH_LENGTH); SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_LOAD_ANYTHING); if (!SymInitialize(hProcess, NULL, TRUE)) { needRestorePreviousSettings = true; SymCleanup(hProcess); if (SymInitialize(hProcess, NULL, TRUE)) isInitialized = true; } else { isInitialized = true; } } }
LONG WINAPI exceptionFilter(LPEXCEPTION_POINTERS info) { if (g_output == NULL) { g_output = (char*) malloc(BUFFER_MAX); } struct output_buffer ob; output_init(&ob, g_output, BUFFER_MAX); if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) { output_print(&ob,"Failed to init symbol context\n"); } else { bfd_init(); struct bfd_set *set = (bfd_set*)calloc(1,sizeof(*set)); _backtrace(&ob , set , 128 , info->ContextRecord); release_set(set); SymCleanup(GetCurrentProcess()); } // Dump a stack trace to a file. QFile stackTraceFile; stackTraceFile.setFileName(QString("%1/openmodelica.stacktrace.%2").arg(OpenModelica::tempDirectory()).arg(Helper::OMCServerName)); if (stackTraceFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&stackTraceFile); out.setCodec(Helper::utf8.toStdString().data()); out.setGenerateByteOrderMark(false); out << g_output; out.flush(); stackTraceFile.close(); } CrashReportDialog *pCrashReportDialog = new CrashReportDialog; pCrashReportDialog->exec(); exit(1); }
static LONG WINAPI exception_filter(LPEXCEPTION_POINTERS info) { struct output_buffer ob; output_init(&ob, g_output, BUFFER_MAX); if (!SymInitialize(GetCurrentProcess(), 0, TRUE)) { output_print(&ob,"Failed to init symbol context\n"); } else { bfd_init(); struct bfd_set *set = calloc(1,sizeof(*set)); _backtrace(&ob , set , 128 , info->ContextRecord); release_set(set); SymCleanup(GetCurrentProcess()); } fputs(g_output , stderr); exit(1); return 0; }
LPVOID getCtrlRoutine() { LPVOID ctrlRoutine; // CtrlRoutine --> MyHandle --> getCtrlRoutine // set the CaptureStackBackTrace's first param to 2 to ingore the MyHandler and getCtrlRoutine calls. // should disable complier optimization on Release version. USHORT count = CaptureStackBackTrace((ULONG) 2, (ULONG) 1, &ctrlRoutine, NULL); if (count != 1) { _tprintf(__T("CaptureStackBackTrace error\n")); goto error; } HANDLE hProcess = GetCurrentProcess(); if (!SymInitialize(hProcess, NULL, TRUE)) { RETVAL rv; _HandleLastError(rv, __T("SymInitialize")); } ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME*sizeof(TCHAR) + sizeof(ULONG64)-1)/sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO) buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; LPVOID funcCtrlRoutine = NULL; DWORD64 dwDisplacement = 0; if(!SymFromAddr(hProcess, (DWORD64) ctrlRoutine, &dwDisplacement, pSymbol)) { RETVAL rv; _HandleLastError(rv, __T("SymFromAddr")); } funcCtrlRoutine = reinterpret_cast<LPVOID>(pSymbol->Address); SymCleanup(hProcess); return funcCtrlRoutine; error: return NULL; }
BOOL CShadowSSDTMgr::GetShadowSSDTNativeAddrBySymbol() { BOOL boRetn; PDWORD pW32pServiceTable = 0; DWORD dwload = 0; PLOADED_IMAGE ploadImage = {0}; char imgPath[MAX_PATH]; // 初始化 if ( !InitSymbolsHandler() ) { return FALSE; } // 获取系统目录"C:\WINDOWS\system32" GetSystemDirectory( imgPath, MAX_PATH ); // 加载模块 ploadImage = ImageLoad( "win32k.sys", imgPath ); // 加载符号"C:\WINDOWS\system32\win32k.sys" strcat( imgPath, "\\win32k.sys" ); dwload=SymLoadModule ( g_ixerhProc, ploadImage->hFile, imgPath, "win32k.pdb", 0, ploadImage->SizeOfImage ); boRetn=SymEnumSymbols( g_ixerhProc, dwload, NULL, (PSYM_ENUMERATESYMBOLS_CALLBACK)EnumSymRoutine, NULL ); if (g_W32pServiceTable) { pW32pServiceTable = (PDWORD)( g_W32pServiceTable - dwload + (DWORD)ploadImage->MappedAddress ); boRetn=SymEnumSymbols( g_ixerhProc, dwload, NULL, (PSYM_ENUMERATESYMBOLS_CALLBACK)EnumSymRoutine, pW32pServiceTable ); } ImageUnload(ploadImage); SymCleanup(g_ixerhProc); return boRetn; }
LONG WINAPI MyExceptionFilter ( EXCEPTION_POINTERS * lpep) { BOOL rVal; STACKFRAME StackFrame; CONTEXT Context; IMAGEHLP_SYMBOL *pImagehlpSymbol; ULONG Displacement; BOOL fReturn; CHAR szUndecoratedName[MAXSYMBOLNAMELENGTH]; FILE * flog; SymSetOptions(0); SymInitialize(SYM_HANDLE, NULL, TRUE); flog = fopen("c:\\Except.log","a"); if (!flog) return EXCEPTION_CONTINUE_SEARCH; printf("\ndumping stack trace\n"); ZeroMemory(&StackFrame, sizeof(StackFrame)); Context = *lpep->ContextRecord; #if defined(_M_IX86) StackFrame.AddrPC.Offset = Context.Eip; StackFrame.AddrPC.Mode = AddrModeFlat; StackFrame.AddrFrame.Offset = Context.Ebp; StackFrame.AddrFrame.Mode = AddrModeFlat; StackFrame.AddrStack.Offset = Context.Esp; StackFrame.AddrStack.Mode = AddrModeFlat; #endif pImagehlpSymbol = (IMAGEHLP_SYMBOL *) xmalloc(sizeof(IMAGEHLP_SYMBOL) + MAXSYMBOLNAMELENGTH - 1); ZeroMemory(pImagehlpSymbol, sizeof(IMAGEHLP_SYMBOL) + MAXSYMBOLNAMELENGTH - 1); pImagehlpSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); pImagehlpSymbol->MaxNameLength = MAXSYMBOLNAMELENGTH; do { rVal = StackWalk ( MACHINE_TYPE, SYM_HANDLE, 0, &StackFrame, &Context, ReadProcessMemory, SymFunctionTableAccess, SymGetModuleBase, NULL); if (rVal) { pImagehlpSymbol->Address = StackFrame.AddrPC.Offset; fReturn = SymGetSymFromAddr ( SYM_HANDLE, StackFrame.AddrPC.Offset, &Displacement, pImagehlpSymbol ); fprintf(flog,"%08x %08x ", StackFrame.AddrFrame.Offset, StackFrame.AddrReturn.Offset); printf("%08x %08x ", StackFrame.AddrFrame.Offset, StackFrame.AddrReturn.Offset); if (fReturn) { fReturn = SymUnDName ( pImagehlpSymbol, szUndecoratedName, MAXSYMBOLNAMELENGTH); if (fReturn) { fprintf(flog,"%s", szUndecoratedName); printf("%s", szUndecoratedName); if (Displacement){ fprintf(flog,"+%x", Displacement); printf("+%x", Displacement); } } } else{ fprintf(flog,"0x%08x", StackFrame.AddrPC.Offset); printf("0x%08x", StackFrame.AddrPC.Offset); } fprintf(flog,"\n"); printf("\n"); } } while (rVal); SymCleanup(SYM_HANDLE); fprintf(flog,"----Hit ^c to exit----\n"); fclose(flog); ExitProcess((DWORD)-1); return EXCEPTION_CONTINUE_SEARCH;//EXCEPTION_EXECUTE_HANDLER; }
int main(int argc, char* argv[]) { if (argc != 2) { printf("\n *** OffsetToSource by Sasha Goldshtein (blog.sashag.net), (C) 2013 ***\n\n"); printf(" Usage: OffsetToSource module[.exe|.dll][!function]+0x<offset>\n\n"); printf(" Example: OffsetToSource myapp!main+0xd0\n"); printf(" Example: OffsetToSource myapp+0x441d0\n"); return 2; } const HANDLE hProcess = GetCurrentProcess(); SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES); if (FALSE == SymInitialize(hProcess, NULL, FALSE)) { printf("*** Error initializing symbol engine: 0x%x\n", GetLastError()); return 1; } CHAR symbolPath[2048], path[2048]; GetEnvironmentVariableA("PATH", path, ARRAYSIZE(symbolPath)); GetEnvironmentVariableA("_NT_SYMBOL_PATH", symbolPath, ARRAYSIZE(symbolPath)); strcat_s(path, ";.;"); strcat_s(path, symbolPath); SymSetSearchPath(hProcess, path); //Parse argv[1] to obtain the module name, symbol name, and offset // Example format: module!Class::Method+0x40 // Another option: module+0x1000 BOOL symbolNameSpecified; CHAR moduleName[200], symName[200]; DWORD offset = 0; CHAR* bang = strchr(argv[1], '!'); if (bang != NULL) { strncpy_s(moduleName, argv[1], bang - argv[1]); CHAR* plus = strchr(bang, '+'); if (plus != NULL) { strncpy_s(symName, bang + 1, plus - bang - 1); sscanf_s(plus + 1, "0x%x", &offset); } else { strcpy_s(symName, bang + 1); } symbolNameSpecified = TRUE; } else { CHAR* plus = strchr(argv[1], '+'); if (plus == NULL) { printf("*** Invalid input: %s\n", argv[1]); return 1; } strncpy_s(moduleName, argv[1], plus - argv[1]); sscanf_s(plus + 1, "0x%x", &offset); symbolNameSpecified = FALSE; } BOOL nakedName = strstr(moduleName, ".dll") == NULL && strstr(moduleName, ".exe") == NULL; if (nakedName) { strcat_s(moduleName, ".dll"); } DWORD64 moduleBase; while (0 == (moduleBase = SymLoadModule64(hProcess, NULL, moduleName, NULL, 0, 0))) { if (nakedName) { strcpy(strstr(moduleName, ".dll"), ".exe"); nakedName = FALSE; continue; } printf("*** Error loading symbols: 0x%x\n", GetLastError()); return 1; } DWORD64 symbolAddress; if (symbolNameSpecified) { ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; if (FALSE == SymFromName(hProcess, symName, pSymbol)) { printf("*** Error retrieving symbol information for symbol %s: 0x%x\n", argv[1], GetLastError()); return 1; } symbolAddress = pSymbol->Address + offset; } else { symbolAddress = moduleBase + offset; } DWORD displacement; IMAGEHLP_LINE64 line; RtlZeroMemory(&line, sizeof(line)); line.SizeOfStruct = sizeof(line); if (FALSE == SymGetLineFromAddr64(hProcess, symbolAddress, &displacement, &line)) { printf("*** Error retrieving source line for %s: 0x%x\n", argv[1], GetLastError()); return 1; } printf("%s [0x%I64x] = %s line %d (+0x%x)\n", argv[1], symbolAddress, line.FileName, line.LineNumber, displacement); SymCleanup(hProcess); return 0; }
LONG WINAPI PtokaX_UnhandledExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo) { static volatile LONG PermLock = 0; // When unhandled exception happen then permanently 'lock' here. We terminate after first exception. while(InterlockedExchange(&PermLock, 1) == 1) { ::Sleep(10); } // Set failure hook __pfnDliFailureHook2 = PtokaX_FailHook; // Check if we have debug symbols if(FileExist(sDebugSymbolsFile.c_str()) == false) { #ifdef _BUILD_GUI ::MessageBox(NULL, "Something bad happen and PtokaX crashed. PtokaX was not able to collect any information why this happen because file with debug symbols" " (PtokaX.pdb) is missing. If you know why this crash happen then please report it as bug to [email protected]!", "PtokaX crashed!", MB_OK | MB_ICONERROR); #else AppendLog("Something bad happen and PtokaX crashed. PtokaX was not able to collect any information why this happen because file with debug symbols" " (PtokaX.pdb) is missing. If you know why this crash happen then please report it as bug to [email protected]!"); #endif ExceptionHandlingUnitialize(); exit(EXIT_FAILURE); } // Initialize debug symbols SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_LINES); if(SymInitialize(GetCurrentProcess(), PATH.c_str(), TRUE) == FALSE) { #ifdef _BUILD_GUI ::MessageBox(NULL, "Something bad happen and PtokaX crashed. PtokaX was not able to collect any information why this happen because initializatin of" " debug symbols failed. If you know why this crash happen then please report it as bug to [email protected]!", "PtokaX crashed!", MB_OK | MB_ICONERROR); #else AppendLog("Something bad happen and PtokaX crashed. PtokaX was not able to collect any information why this happen because initializatin of" " debug symbols failed. If you know why this crash happen then please report it as bug to [email protected]!"); #endif ExceptionHandlingUnitialize(); exit(EXIT_FAILURE); } // Generate crash log filename time_t acc_time; time(&acc_time); struct tm *tm = localtime(&acc_time); strftime(sDebugBuf, 128, "Crash-%d.%m.%Y-%H.%M.%S.log", tm); // Open crash file FILE * fw = fopen((sLogPath + sDebugBuf).c_str(), "w"); if(fw == NULL) { #ifdef _BUILD_GUI ::MessageBox(NULL, "Something bad happen and PtokaX crashed. PtokaX was not able to create file with information why this crash happen." " If you know why this crash happen then please report it as bug to [email protected]!", "PtokaX crashed!", MB_OK | MB_ICONERROR); #else AppendLog("Something bad happen and PtokaX crashed. PtokaX was not able to create file with information why this crash happen." " If you know why this crash happen then please report it as bug to [email protected]!"); #endif ExceptionHandlingUnitialize(); SymCleanup(GetCurrentProcess()); exit(EXIT_FAILURE); } string sCrashMsg = "Something bad happen and PtokaX crashed. PtokaX collected information why this crash happen to file "; sCrashMsg += string(sDebugBuf); sCrashMsg += ", please send that file to [email protected]!"; // Write PtokaX version, build and exception code fprintf(fw, "PtokaX version: " PtokaXVersionString " [build " BUILD_NUMBER "]" #ifdef _M_X64 " (x64)" #endif "\nException Code: %x\n", ExceptionInfo->ExceptionRecord->ExceptionCode); { // Write windoze version where we crashed if is possible OSVERSIONINFOEX ver; memset(&ver, 0, sizeof(OSVERSIONINFOEX)); ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if(GetVersionEx((OSVERSIONINFO*)&ver) != 0) { fprintf(fw, "Windows version: %d.%d SP: %d\n", ver.dwMajorVersion, ver.dwMinorVersion, ver.wServicePackMajor); } } // Write date and time when crash happen strftime(sDebugBuf, 128, "Date and time: %d.%m.%Y %H:%M:%S\n\n", tm); fprintf(fw, sDebugBuf); STACKFRAME64 sf64CallStack; memset(&sf64CallStack, 0, sizeof(STACKFRAME64)); sf64CallStack.AddrPC.Mode = AddrModeFlat; sf64CallStack.AddrStack.Mode = AddrModeFlat; sf64CallStack.AddrFrame.Mode = AddrModeFlat; #ifdef _M_X64 sf64CallStack.AddrPC.Offset = ExceptionInfo->ContextRecord->Rip; sf64CallStack.AddrStack.Offset = ExceptionInfo->ContextRecord->Rsp; sf64CallStack.AddrFrame.Offset = ExceptionInfo->ContextRecord->Rbp; #else sf64CallStack.AddrPC.Offset = ExceptionInfo->ContextRecord->Eip; sf64CallStack.AddrStack.Offset = ExceptionInfo->ContextRecord->Esp; sf64CallStack.AddrFrame.Offset = ExceptionInfo->ContextRecord->Ebp; #endif // Write where crash happen fprintf(fw, "Exception location:\n"); GetSourceFileInfo(sf64CallStack.AddrPC.Offset, fw); GetFunctionInfo(sf64CallStack.AddrPC.Offset, fw); // Try to write callstack fprintf(fw, "\nCall stack:\n"); // We don't want it like never ending story, limit call stack to 100 lines for(uint32_t ui32i = 0; ui32i < 100; ui32i++) { if(StackWalk64( #ifdef _M_X64 IMAGE_FILE_MACHINE_AMD64, #else IMAGE_FILE_MACHINE_I386, #endif GetCurrentProcess(), GetCurrentThread(), &sf64CallStack, ExceptionInfo->ContextRecord, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL) == FALSE || sf64CallStack.AddrFrame.Offset == 0) { break; } GetSourceFileInfo(sf64CallStack.AddrPC.Offset, fw); GetFunctionInfo(sf64CallStack.AddrPC.Offset, fw); } fclose(fw); #ifdef _BUILD_GUI ::MessageBox(NULL, sCrashMsg.c_str(), "PtokaX crashed!", MB_OK | MB_ICONERROR); #else AppendLog(sCrashMsg.c_str()); #endif ExceptionHandlingUnitialize(); SymCleanup(GetCurrentProcess()); exit(EXIT_FAILURE); }
CExceptionParser::~CExceptionParser(void) { if(m_hProcess) SymCleanup(m_hProcess); }
//************************************************************************* // Method: GetStackInfo // Description: Gets the call stack for the specified thread // // Parameters: // hThread - the handle to the target thread // threadContext - the context of the target thread // // Return Value: (FrameInfo *) An array containing stack trace data //************************************************************************* SiUtils::SiArray <FrameInfo *> StackWalker::GetStackInfo(HANDLE hThread, CONTEXT &threadContext) { //Clear the frame array of any previous data frameArray.Clear(); DWORD imageType = IMAGE_FILE_MACHINE_I386; HANDLE hProcess = GetCurrentProcess(); int frameNum; // counts walked frames DWORD offsetFromSymbol; // tells us how far from the symbol we were DWORD symOptions; // symbol handler settings IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN ); char undName[MAXNAMELEN]; // undecorated name char undFullName[MAXNAMELEN]; // undecorated name with all shenanigans IMAGEHLP_MODULE Module; IMAGEHLP_LINE Line; std::string symSearchPath; char *tt = 0, *p; STACKFRAME s; // in/out stackframe memset( &s, '\0', sizeof s ); tt = new char[TTBUFLEN]; // build symbol search path from: symSearchPath = ""; // current directory if (GetCurrentDirectory(TTBUFLEN, tt )) symSearchPath += tt + std::string( ";" ); // dir with executable if (GetModuleFileName(0, tt, TTBUFLEN)) { for ( p = tt + strlen( tt ) - 1; p >= tt; -- p ) { // locate the rightmost path separator if ( *p == '\\' || *p == '/' || *p == ':' ) break; } if ( p != tt ) { if ( *p == ':' ) // we leave colons in place ++ p; *p = '\0'; // eliminate the exe name and last path sep symSearchPath += tt + std::string( ";" ); } } // environment variable _NT_SYMBOL_PATH if ( GetEnvironmentVariable( "_NT_SYMBOL_PATH", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); // environment variable _NT_ALTERNATE_SYMBOL_PATH if ( GetEnvironmentVariable( "_NT_ALTERNATE_SYMBOL_PATH", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); // environment variable SYSTEMROOT if ( GetEnvironmentVariable( "SYSTEMROOT", tt, TTBUFLEN ) ) symSearchPath += tt + std::string( ";" ); if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 ); strncpy( tt, symSearchPath.c_str(), TTBUFLEN ); tt[TTBUFLEN - 1] = '\0'; // if strncpy() overruns, it doesn't add the null terminator // init symbol handler stuff (SymInitialize()) if (!SymInitialize(hProcess, tt, false )) goto cleanup; // SymGetOptions() symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions( symOptions ); // Enumerate modules and tell imagehlp.dll about them. enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() ); // init STACKFRAME for first call // Notes: AddrModeFlat is just an assumption. I hate VDM debugging. // Notes: will have to be #ifdef-ed for Alphas; MIPSes are dead anyway, // and good riddance. s.AddrPC.Offset = threadContext.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = threadContext.Ebp; s.AddrFrame.Mode = AddrModeFlat; memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN ); pSym->SizeOfStruct = IMGSYMLEN; pSym->MaxNameLength = MAXNAMELEN; memset( &Line, '\0', sizeof Line ); Line.SizeOfStruct = sizeof Line; memset( &Module, '\0', sizeof Module ); Module.SizeOfStruct = sizeof Module; offsetFromSymbol = 0; for ( frameNum = 0; ; ++ frameNum ) { FrameInfo * frameInfo = new FrameInfo(); if (!StackWalk( imageType, hProcess, hThread, &s, &threadContext, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL ) ) break; frameInfo->frameNumber = frameNum; frameInfo->IsWOWFarCall = s.Far; frameInfo->IsVirtualFrame = s.Virtual; frameInfo->Eip = s.AddrPC.Offset; //if 0, the no symbols frameInfo->ReturnAddr = s.AddrReturn.Offset; frameInfo->FramePtr = s.AddrFrame.Offset; frameInfo->StackPtr = s.AddrStack.Offset; if (s.AddrPC.Offset == 0) { //printf( "(-nosymbols- PC == 0)\n" ); } else { // we seem to have a valid PC // show procedure info (SymGetSymFromAddr()) if ( ! SymGetSymFromAddr( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) ) { /*if ( GetLastError() != 487 ) printf( "SymGetSymFromAddr(): GetLastError() = %lu\n", GetLastError() );*/ } else { // UnDecorateSymbolName() UnDecorateSymbolName( pSym->Name, undName, MAXNAMELEN, UNDNAME_NAME_ONLY ); UnDecorateSymbolName( pSym->Name, undFullName, MAXNAMELEN, UNDNAME_COMPLETE ); strcpy (frameInfo->undecoratedName, undName); strcpy (frameInfo->undecoratedFullName, undFullName); strcpy (frameInfo->signature, pSym->Name ); frameInfo->offsetFromSymbol = offsetFromSymbol; } // show module info (SymGetModuleInfo()) if (SymGetModuleInfo( hProcess, s.AddrPC.Offset, &Module ) ) { strcpy (frameInfo->ModuleName, Module.ModuleName); strcpy (frameInfo->ImageName, Module.ImageName); frameInfo->BaseOfImage = Module.BaseOfImage; } } // we seem to have a valid PC // no return address means no deeper stackframe if ( s.AddrReturn.Offset == 0 ) { // avoid misunderstandings in the printf() following the loop SetLastError( 0 ); break; } this->frameArray.Add (frameInfo); } // for ( frameNum ) cleanup: // de-init symbol handler etc. (SymCleanup()) SymCleanup( hProcess ); free( pSym ); delete [] tt; return frameArray; }
int ZCE_LIB::backtrace_stack(ZCE_LOG_PRIORITY dbg_lvl, const char *dbg_info) { //跟踪函数的层数 const size_t SIZE_OF_BACKTRACE_FUNC = 100; #if defined(ZCE_OS_LINUX) ZCE_LOG(dbg_lvl, "[BACKTRACE]This program compiled by Linux GCC. %s", dbg_info); //Windows 下必须是2008或者VISTA之后的SDK才支持, #elif defined(ZCE_OS_WINDOWS) && ZCE_SUPPORT_WINSVR2008 == 1 ZCE_LOG(dbg_lvl, "[BACKTRACE]This program compiled by Windows Visual studio .%s", dbg_info); #else ZCE_UNUSED_ARG(SIZE_OF_BACKTRACE_FUNC); ZCE_LOG(dbg_lvl, "[BACKTRACE]back_trace_stack don't support this system.%s", dbg_info); #endif //这个方法是提供给Linux 下的GCC使用的 #if defined(ZCE_OS_LINUX) void *backtrace_buffer[SIZE_OF_BACKTRACE_FUNC]; char **symbols_strings; int sz_of_stack = ::backtrace(backtrace_buffer, SIZE_OF_BACKTRACE_FUNC); // symbols_strings = ::backtrace_symbols(backtrace_buffer, sz_of_stack); if (symbols_strings == NULL) { ZCE_LOG(dbg_lvl, "%s", "[BACKTRACE] backtrace_symbols return fail."); } //打印所有的堆栈信息,有些时候信息无法显示符号表,建议使用 for (int j = 0; j < sz_of_stack; j++) { ZCE_LOG(dbg_lvl, "[BACKTRACE] %u, %s.", j + 1, symbols_strings[j]); } //释放空间 ::free(symbols_strings); #elif defined(ZCE_OS_WINDOWS) && ZCE_SUPPORT_WINSVR2008 == 1 //我还没有时间看完dbghelp所有的东西,目前的代码参考后一个版本居多,目前这个东东必须有pdb文件, //http://blog.csdn.net/skies457/article/details/7201185 // Max length of symbols' name. const size_t MAX_NAME_LENGTH = 256; // Store register addresses. CONTEXT context; // Call stack. STACKFRAME64 stackframe; // Handle to current process & thread. HANDLE process, cur_thread; // Generally it can be subsitituted with 0xFFFFFFFF & 0xFFFFFFFE. // Debugging symbol's information. PSYMBOL_INFO symbol; // Source information (file name & line number) IMAGEHLP_LINE64 source_info; // Source line displacement. DWORD displacement; // Initialize PSYMBOL_INFO structure. // Allocate a properly-sized block. symbol = (PSYMBOL_INFO)malloc(sizeof(SYMBOL_INFO) + (MAX_NAME_LENGTH) * sizeof(char)); memset(symbol, 0, sizeof(SYMBOL_INFO) + (MAX_NAME_LENGTH) * sizeof(TCHAR)); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); // SizeOfStruct *MUST BE* set to sizeof(SYMBOL_INFO). symbol->MaxNameLen = MAX_NAME_LENGTH; // Initialize IMAGEHLP_LINE64 structure. memset(&source_info, 0, sizeof(IMAGEHLP_LINE64)); source_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); // Initialize STACKFRAME64 structure. RtlCaptureContext(&context); // Get context. memset(&stackframe, 0, sizeof(STACKFRAME64)); // Fill in register addresses (EIP, ESP, EBP). stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrStack.Mode = AddrModeFlat; stackframe.AddrFrame.Mode = AddrModeFlat; #if defined ZCE_WIN32 stackframe.AddrPC.Offset = context.Eip; stackframe.AddrStack.Offset = context.Esp; stackframe.AddrFrame.Offset = context.Ebp; #elif defined ZCE_WIN64 stackframe.AddrPC.Offset = context.Rip; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrFrame.Offset = context.Rbp; #else #endif // Get current process & thread. process = GetCurrentProcess(); cur_thread = GetCurrentThread(); // Initialize dbghelp library. if (!SymInitialize(process, NULL, TRUE)) { return -1; } //这些空间是绝对足够的,我也不做详细的检查了 const size_t LINE_OUTLEN = 1024; char line_out[LINE_OUTLEN]; int use_len = 0; uint32_t k = 0; // Enumerate call stack frame. while (StackWalk64(IMAGE_FILE_MACHINE_I386, process, cur_thread, &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { use_len = 0; // End reaches. if (stackframe.AddrFrame.Offset == 0 || k > SIZE_OF_BACKTRACE_FUNC) { break; } // Get symbol. if (SymFromAddr(process, stackframe.AddrPC.Offset, NULL, symbol)) { use_len += snprintf(line_out + use_len, LINE_OUTLEN - use_len, " %s", symbol->Name); } if (SymGetLineFromAddr64(process, stackframe.AddrPC.Offset, &displacement, &source_info)) { // Get source information. use_len += snprintf(line_out + use_len, LINE_OUTLEN - use_len, "\t[ %s: %d] at addr 0x % 08LX", source_info.FileName, source_info.LineNumber, stackframe.AddrPC.Offset); } else { // If err_code == 0x1e7, no symbol was found. if (GetLastError() == 0x1E7) { use_len += snprintf(line_out + use_len, LINE_OUTLEN - use_len, "%s", "\tNo debug symbol loaded for this function."); } } ZCE_LOG(dbg_lvl, "[BACKTRACE] %u, %s.", k + 1, line_out); ++k; } SymCleanup(process); // Clean up and exit. free(symbol); #endif // return 0; }
void PrintCallStack(const CONTEXT *pContext) { HANDLE hProcess = GetCurrentProcess(); SymInitialize(hProcess, NULL, TRUE); CONTEXT c = *pContext; STACKFRAME64 sf; memset(&sf, 0, sizeof(STACKFRAME64)); DWORD dwImageType = IMAGE_FILE_MACHINE_I386; #ifdef _M_IX86 sf.AddrPC.Offset = c.Eip; sf.AddrPC.Mode = AddrModeFlat; sf.AddrStack.Offset = c.Esp; sf.AddrStack.Mode = AddrModeFlat; sf.AddrFrame.Offset = c.Ebp; sf.AddrFrame.Mode = AddrModeFlat; #elif _M_X64 dwImageType = IMAGE_FILE_MACHINE_AMD64; sf.AddrPC.Offset = c.Rip; sf.AddrPC.Mode = AddrModeFlat; sf.AddrFrame.Offset = c.Rsp; sf.AddrFrame.Mode = AddrModeFlat; sf.AddrStack.Offset = c.Rsp; sf.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 dwImageType = IMAGE_FILE_MACHINE_IA64; sf.AddrPC.Offset = c.StIIP; sf.AddrPC.Mode = AddrModeFlat; sf.AddrFrame.Offset = c.IntSp; sf.AddrFrame.Mode = AddrModeFlat; sf.AddrBStore.Offset = c.RsBSP; sf.AddrBStore.Mode = AddrModeFlat; sf.AddrStack.Offset = c.IntSp; sf.AddrStack.Mode = AddrModeFlat; #else #error "Platform not supported!" #endif HANDLE hThread = GetCurrentThread(); while (true) { if (!StackWalk64(dwImageType, hProcess, hThread, &sf, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) break; DWORD64 address = sf.AddrPC.Offset; if (address == 0) break; // Get symbol name char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)] = {0}; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; pSymbol->MaxNameLen = MAX_SYM_NAME ; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); SymFromAddr(hProcess, address, NULL, pSymbol); // Get module name IMAGEHLP_MODULE64 moduleInfo = {0}; moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); TCHAR* szModule = _T(""); // SymGetModuleInfo64 will fail in unicode version // http://msdn.microsoft.com/en-us/library/windows/desktop/ms681336(v=vs.85).aspx if (SymGetModuleInfo64(hProcess, address, &moduleInfo)) szModule = moduleInfo.ModuleName; // Get file name and line count IMAGEHLP_LINE64 lineInfo = {0}; lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64); DWORD displacement; if (SymGetLineFromAddr64(hProcess, address, &displacement, &lineInfo)) { _tprintf(_T("%016llX %s!%s [%s @ %lu]\n"), pSymbol->Address, szModule, pSymbol->Name, lineInfo.FileName, lineInfo.LineNumber); } else { _tprintf(_T("%016llX %s!%s\n"), pSymbol->Address, szModule, pSymbol->Name); } } SymCleanup(hProcess); }
/** * Print stack trace (using a specified stack context) to "os" * * @param context CONTEXT record for stack trace * @param os ostream& to receive printed stack backtrace */ void printWindowsStackTrace( CONTEXT& context, std::ostream& os ) { SimpleMutex::scoped_lock lk(_stackTraceMutex); HANDLE process = GetCurrentProcess(); BOOL ret = SymInitialize(process, getSymbolSearchPath(process), TRUE); if ( ret == FALSE ) { DWORD dosError = GetLastError(); log() << "Stack trace failed, SymInitialize failed with error " << std::dec << dosError << std::endl; return; } DWORD options = SymGetOptions(); options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS; SymSetOptions( options ); STACKFRAME64 frame64; memset( &frame64, 0, sizeof(frame64) ); #if defined(_M_AMD64) DWORD imageType = IMAGE_FILE_MACHINE_AMD64; frame64.AddrPC.Offset = context.Rip; frame64.AddrFrame.Offset = context.Rbp; frame64.AddrStack.Offset = context.Rsp; #elif defined(_M_IX86) DWORD imageType = IMAGE_FILE_MACHINE_I386; frame64.AddrPC.Offset = context.Eip; frame64.AddrFrame.Offset = context.Ebp; frame64.AddrStack.Offset = context.Esp; #else #error Neither _M_IX86 nor _M_AMD64 were defined #endif frame64.AddrPC.Mode = AddrModeFlat; frame64.AddrFrame.Mode = AddrModeFlat; frame64.AddrStack.Mode = AddrModeFlat; const size_t nameSize = 1024; const size_t symbolBufferSize = sizeof(SYMBOL_INFO) + nameSize; boost::scoped_array<char> symbolCharBuffer( new char[symbolBufferSize] ); memset( symbolCharBuffer.get(), 0, symbolBufferSize ); SYMBOL_INFO* symbolBuffer = reinterpret_cast<SYMBOL_INFO*>( symbolCharBuffer.get() ); symbolBuffer->SizeOfStruct = sizeof(SYMBOL_INFO); symbolBuffer->MaxNameLen = nameSize; // build list std::vector<TraceItem> traceList; TraceItem traceItem; size_t moduleWidth = 0; size_t sourceWidth = 0; for ( size_t i = 0; i < maxBackTraceFrames; ++i ) { ret = StackWalk64( imageType, process, GetCurrentThread(), &frame64, &context, NULL, NULL, NULL, NULL ); if ( ret == FALSE || frame64.AddrReturn.Offset == 0 ) { break; } DWORD64 address = frame64.AddrPC.Offset; getModuleName( process, address, &traceItem.moduleName ); size_t width = traceItem.moduleName.length(); if ( width > moduleWidth ) { moduleWidth = width; } getSourceFileAndLineNumber( process, address, &traceItem.sourceAndLine ); width = traceItem.sourceAndLine.length(); if ( width > sourceWidth ) { sourceWidth = width; } getsymbolAndOffset( process, address, symbolBuffer, &traceItem.symbolAndOffset ); traceList.push_back( traceItem ); } SymCleanup( process ); // print list ++moduleWidth; ++sourceWidth; size_t frameCount = traceList.size(); for ( size_t i = 0; i < frameCount; ++i ) { std::stringstream ss; ss << traceList[i].moduleName << " "; size_t width = traceList[i].moduleName.length(); while ( width < moduleWidth ) { ss << " "; ++width; } ss << traceList[i].sourceAndLine << " "; width = traceList[i].sourceAndLine.length(); while ( width < sourceWidth ) { ss << " "; ++width; } ss << traceList[i].symbolAndOffset; log() << ss.str() << std::endl; } }
int main(int argc, char *argv[]) { DWORD error; HANDLE process; ULONG64 module_base; int i; char* search; char buf[256]; /* Enough to hold one hex address, I trust! */ int rv = 0; /* We may add SYMOPT_UNDNAME if --demangle is specified: */ DWORD symopts = SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES; char* filename = "a.out"; /* The default if -e isn't specified */ int print_function_name = 0; /* Set to 1 if -f is specified */ for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--functions") == 0 || strcmp(argv[i], "-f") == 0) { print_function_name = 1; } else if (strcmp(argv[i], "--demangle") == 0 || strcmp(argv[i], "-C") == 0) { symopts |= SYMOPT_UNDNAME; } else if (strcmp(argv[i], "-e") == 0) { if (i + 1 >= argc) { fprintf(stderr, "FATAL ERROR: -e must be followed by a filename\n"); return 1; } filename = argv[i+1]; i++; /* to skip over filename too */ } else if (strcmp(argv[i], "--help") == 0) { usage(); exit(0); } else { usage(); exit(1); } } process = GetCurrentProcess(); if (!SymInitialize(process, NULL, FALSE)) { error = GetLastError(); fprintf(stderr, "SymInitialize returned error : %d\n", error); return 1; } search = malloc(SEARCH_CAP); if (SymGetSearchPath(process, search, SEARCH_CAP)) { if (strlen(search) + sizeof(";" WEBSYM) > SEARCH_CAP) { fprintf(stderr, "Search path too long\n"); SymCleanup(process); return 1; } strcat(search, ";" WEBSYM); } else { error = GetLastError(); fprintf(stderr, "SymGetSearchPath returned error : %d\n", error); rv = 1; /* An error, but not a fatal one */ strcpy(search, WEBSYM); /* Use a default value */ } if (!SymSetSearchPath(process, search)) { error = GetLastError(); fprintf(stderr, "SymSetSearchPath returned error : %d\n", error); rv = 1; /* An error, but not a fatal one */ } SymSetOptions(symopts); module_base = SymLoadModuleEx(process, NULL, filename, NULL, 0, 0, NULL, 0); if (!module_base) { /* SymLoadModuleEx failed */ error = GetLastError(); fprintf(stderr, "SymLoadModuleEx returned error : %d for %s\n", error, filename); SymCleanup(process); return 1; } buf[sizeof(buf)-1] = '\0'; /* Just to be safe */ while (fgets(buf, sizeof(buf)-1, stdin)) { /* GNU addr2line seems to just do a strtol and ignore any * weird characters it gets, so we will too. */ unsigned __int64 addr = _strtoui64(buf, NULL, 16); ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME*sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; IMAGEHLP_LINE64 line; DWORD dummy; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = MAX_SYM_NAME; if (print_function_name) { if (SymFromAddr(process, (DWORD64)addr, NULL, pSymbol)) { printf("%s\n", pSymbol->Name); } else { printf("??\n"); } } line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); if (SymGetLineFromAddr64(process, (DWORD64)addr, &dummy, &line)) { printf("%s:%d\n", line.FileName, (int)line.LineNumber); } else { printf("??:0\n"); } } SymUnloadModule64(process, module_base); SymCleanup(process); return rv; }
std::vector<CL_String> CL_System::get_stack_frames_text(void **frames, int num_frames) { #ifdef WIN32 static CL_Mutex mutex; CL_MutexSection mutex_lock(&mutex); BOOL result = SymInitialize(GetCurrentProcess(), NULL, TRUE); if (!result) return std::vector<CL_String>(); std::vector<CL_String> backtrace_text; for (unsigned short i = 0; i < num_frames; i++) { unsigned char buffer[sizeof(IMAGEHLP_SYMBOL64) + 128]; IMAGEHLP_SYMBOL64 *symbol64 = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer); memset(symbol64, 0, sizeof(IMAGEHLP_SYMBOL64) + 128); symbol64->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); symbol64->MaxNameLength = 128; DWORD64 displacement = 0; BOOL result = SymGetSymFromAddr64(GetCurrentProcess(), (DWORD64) frames[i], &displacement, symbol64); if (result) { IMAGEHLP_LINE64 line64; DWORD displacement = 0; memset(&line64, 0, sizeof(IMAGEHLP_LINE64)); line64.SizeOfStruct = sizeof(IMAGEHLP_LINE64); result = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64) frames[i], &displacement, &line64); if (result) { backtrace_text.push_back( cl_format( "%1 (%2, line %3)", CL_StringHelp::local8_to_text(symbol64->Name), CL_StringHelp::local8_to_text(line64.FileName), (int) line64.LineNumber)); } else { backtrace_text.push_back(symbol64->Name); } } } SymCleanup(GetCurrentProcess()); return backtrace_text; #elif !defined(__APPLE__) char **strings; strings = backtrace_symbols(frames, num_frames); if (!strings) { return std::vector<CL_String>(); } std::vector<CL_String> backtrace_text; for (int cnt = 0; cnt < num_frames; cnt++) { // Decode the strings char *ptr = strings[cnt]; char *filename = ptr; const char *function = ""; // Find function name while(*ptr) { if (*ptr=='(') // Found function name { *(ptr++) = 0; function = ptr; break; } ptr++; } // Find offset if (function[0]) // Only if function was found { while(*ptr) { if (*ptr=='+') // Found function offset { *(ptr++) = 0; break; } if (*ptr==')') // Not found function offset, but found, end of function { *(ptr++) = 0; break; } ptr++; } } int status; char *new_function = abi::__cxa_demangle(function, 0, 0, &status); if (new_function) // Was correctly decoded { function = new_function; } backtrace_text.push_back( cl_format("%1 (%2)", function, filename)); if (new_function) { free(new_function); } } free (strings); return backtrace_text; #else return std::vector<CL_String>(); #endif }
void printTrace() { SymInitialize(GetCurrentProcess(), NULL, TRUE); UINT32 maxframes = 62; UINT_PTR myFrames[62]; ZeroMemory(myFrames, sizeof(UINT_PTR) * maxframes); ULONG BackTraceHash; maxframes = CaptureStackBackTrace(0, maxframes, reinterpret_cast<PVOID*>(myFrames), &BackTraceHash); const UINT_PTR* pFrame = myFrames; const size_t frameSize = maxframes; UINT32 startIndex = 0; int unresolvedFunctionsCount = 0; IMAGEHLP_LINE sourceInfo = { 0 }; sourceInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE); // Use static here to increase performance, and avoid heap allocs. // It's thread safe because of g_heapMapLock lock. static char stack_line[1024] = ""; bool isPrevFrameInternal = false; DWORD NumChars = 0; const size_t max_line_length = 512; const int resolvedCapacity = 62 * max_line_length; const size_t allocedBytes = resolvedCapacity * sizeof(char); char resolved[resolvedCapacity]; if (resolved) { ZeroMemory(resolved, allocedBytes); } HANDLE hProcess = GetCurrentProcess(); int resolvedLength = 0; // Iterate through each frame in the call stack. for (UINT32 frame = 0; frame < frameSize; frame++) { if (pFrame[frame] == 0) break; // Try to get the source file and line number associated with // this program counter address. SIZE_T programCounter = pFrame[frame]; DWORD64 displacement64; BYTE symbolBuffer[sizeof(SYMBOL_INFO) + 256 * sizeof(char)]; LPCSTR functionName = getFunctionName(programCounter, displacement64, (SYMBOL_INFO*)&symbolBuffer); // It turns out that calls to SymGetLineFromAddrW64 may free the very memory we are scrutinizing here // in this method. If this is the case, m_Resolved will be null after SymGetLineFromAddrW64 returns. // When that happens there is nothing we can do except crash. DWORD displacement = 0; BOOL foundline = SymGetLineFromAddr(hProcess, programCounter, &displacement, &sourceInfo); bool isFrameInternal = false; // show one allocation function for context if (NumChars > 0 && !isFrameInternal && isPrevFrameInternal) { resolvedLength += NumChars; if (resolved) { strncat_s(resolved, resolvedCapacity, stack_line, NumChars); } } isPrevFrameInternal = isFrameInternal; if (!foundline) displacement = (DWORD)displacement64; NumChars = resolveFunction(programCounter, foundline ? &sourceInfo : NULL, displacement, functionName, stack_line, _countof(stack_line)); if (NumChars > 0 && !isFrameInternal) { resolvedLength += NumChars; if (resolved) { strncat_s(resolved, resolvedCapacity, stack_line, NumChars); } } } // end for loop printLog(resolved); SymCleanup(GetCurrentProcess()); return; }
static void print_backtrace(CONTEXT *pctx) { SymGetLineFromAddrW64_func_t zbx_SymGetLineFromAddrW64 = NULL; SymFromAddr_func_t zbx_SymFromAddr = NULL; CONTEXT ctx, ctxcount; STACKFRAME64 s, scount; PSYMBOL_INFO pSym = NULL; HMODULE hModule; HANDLE hProcess, hThread; DWORD64 offset; wchar_t szProcessName[MAX_PATH]; char *process_name = NULL, *process_path = NULL, *frame = NULL; size_t frame_alloc = 0, frame_offset; int nframes = 0; ctx = *pctx; zabbix_log(LOG_LEVEL_CRIT, "=== Backtrace: ==="); memset(&s, 0, sizeof(s)); s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Mode = AddrModeFlat; #ifdef _M_X64 s.AddrPC.Offset = ctx.Rip; s.AddrFrame.Offset = ctx.Rbp; s.AddrStack.Offset = ctx.Rsp; #else s.AddrPC.Offset = ctx.Eip; s.AddrFrame.Offset = ctx.Ebp; s.AddrStack.Offset = ctx.Esp; #endif hProcess = GetCurrentProcess(); hThread = GetCurrentThread(); if (0 != GetModuleFileNameEx(hProcess, NULL, szProcessName, ARRSIZE(szProcessName))) { char *ptr; int path_alloc = 0, path_offset = 0; process_name = zbx_unicode_to_utf8(szProcessName); if (NULL != (ptr = strstr(process_name, progname))) zbx_strncpy_alloc(&process_path, &path_alloc, &path_offset, process_name, ptr - process_name); } if (NULL != (hModule = GetModuleHandle(TEXT("DbgHelp.DLL")))) { zbx_SymGetLineFromAddrW64 = (SymGetLineFromAddrW64_func_t)GetProcAddress(hModule, "SymGetLineFromAddr64"); zbx_SymFromAddr = (SymFromAddr_func_t)GetProcAddress(hModule, "SymFromAddr"); } if (NULL != zbx_SymFromAddr || NULL != zbx_SymGetLineFromAddrW64) { SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES); if (FALSE != SymInitialize(hProcess, process_path, TRUE)) { pSym = (PSYMBOL_INFO) zbx_malloc(NULL, sizeof(SYMBOL_INFO) + MAX_SYM_NAME); memset(pSym, 0, sizeof(SYMBOL_INFO) + MAX_SYM_NAME); pSym->SizeOfStruct = sizeof(SYMBOL_INFO); pSym->MaxNameLen = MAX_SYM_NAME; } } scount = s; ctxcount = ctx; /* get number of frames, ctxcount may be modified during StackWalk64() calls */ while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &scount, &ctxcount, NULL, NULL, NULL, NULL)) { if (0 == scount.AddrReturn.Offset) break; nframes++; } while (TRUE == StackWalk64(ZBX_IMAGE_FILE_MACHINE, hProcess, hThread, &s, &ctx, NULL, NULL, NULL, NULL)) { frame_offset = 0; zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, "%d: %s", nframes--, NULL == process_name ? "(unknown)" : process_name); if (NULL != pSym) { DWORD dwDisplacement; IMAGEHLP_LINE64 line = {sizeof(IMAGEHLP_LINE64)}; zbx_chrcpy_alloc(&frame, &frame_alloc, &frame_offset, '('); if (NULL != zbx_SymFromAddr && TRUE == zbx_SymFromAddr(hProcess, s.AddrPC.Offset, &offset, pSym)) { zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, "%s+0x%lx", pSym->Name, offset); } if (NULL != zbx_SymGetLineFromAddrW64 && TRUE == zbx_SymGetLineFromAddrW64(hProcess, s.AddrPC.Offset, &dwDisplacement, &line)) { zbx_snprintf_alloc(&frame, &frame_alloc, &frame_offset, " %s:%d", line.FileName, line.LineNumber); } zbx_chrcpy_alloc(&frame, &frame_alloc, &frame_offset, ')'); } zabbix_log(LOG_LEVEL_CRIT, "%s [0x%lx]", frame, s.AddrPC.Offset); if (0 == s.AddrReturn.Offset) break; } SymCleanup(hProcess); zbx_free(frame); zbx_free(process_path); zbx_free(process_name); zbx_free(pSym); }
/* ================== Sym_Shutdown ================== */ void Sym_Shutdown( void ) { SymUnloadModule( GetCurrentProcess(), lastAllocationBase ); SymCleanup( GetCurrentProcess() ); lastAllocationBase = -1; }
void CleanupStacktrace() { // Unintialize IMAGEHLP.DLL SymCleanup(GetCurrentProcess()); }
void printStackTrace( std::ostream &os ) { HANDLE process = GetCurrentProcess(); BOOL ret = SymInitialize( process, NULL, TRUE ); if ( ret == FALSE ) { DWORD dosError = GetLastError(); os << "Stack trace failed, SymInitialize failed with error " << std::dec << dosError << std::endl; return; } DWORD options = SymGetOptions(); options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS; SymSetOptions( options ); CONTEXT context; memset( &context, 0, sizeof(context) ); context.ContextFlags = CONTEXT_CONTROL; RtlCaptureContext( &context ); STACKFRAME64 frame64; memset( &frame64, 0, sizeof(frame64) ); #if defined(_M_AMD64) DWORD imageType = IMAGE_FILE_MACHINE_AMD64; frame64.AddrPC.Offset = context.Rip; frame64.AddrFrame.Offset = context.Rbp; frame64.AddrStack.Offset = context.Rsp; #elif defined(_M_IX86) DWORD imageType = IMAGE_FILE_MACHINE_I386; frame64.AddrPC.Offset = context.Eip; frame64.AddrFrame.Offset = context.Ebp; frame64.AddrStack.Offset = context.Esp; #else #error Neither _M_IX86 nor _M_AMD64 were defined #endif frame64.AddrPC.Mode = AddrModeFlat; frame64.AddrFrame.Mode = AddrModeFlat; frame64.AddrStack.Mode = AddrModeFlat; const size_t nameSize = 1024; const size_t symbolRecordSize = sizeof(SYMBOL_INFO) + nameSize; boost::scoped_array<char> symbolBuffer( new char[symbolRecordSize] ); memset( symbolBuffer.get(), 0, symbolRecordSize ); SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>( symbolBuffer.get() ); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = nameSize; std::vector<TraceItem> traceList; TraceItem traceItem; size_t moduleWidth = 0; size_t fileWidth = 0; size_t frameCount = 0; for ( size_t i = 0; i < maxBackTraceFrames; ++i ) { ret = StackWalk64( imageType, process, GetCurrentThread(), &frame64, &context, NULL, NULL, NULL, NULL ); if ( ret == FALSE || frame64.AddrReturn.Offset == 0 ) { frameCount = i; break; } // module (executable) name IMAGEHLP_MODULE64 module; memset ( &module, 0, sizeof(module) ); module.SizeOfStruct = sizeof(module); ret = SymGetModuleInfo64( process, frame64.AddrPC.Offset, &module ); char* moduleName = module.LoadedImageName; char* backslash = strrchr( moduleName, '\\' ); if ( backslash ) { moduleName = backslash + 1; } traceItem.module = moduleName; size_t len = traceItem.module.length(); if ( len > moduleWidth ) { moduleWidth = len; } // source code filename and line number IMAGEHLP_LINE64 line; memset( &line, 0, sizeof(line) ); line.SizeOfStruct = sizeof(line); DWORD displacement32; ret = SymGetLineFromAddr64( process, frame64.AddrPC.Offset, &displacement32, &line ); if ( ret ) { std::string filename( line.FileName ); std::string::size_type start = filename.find( "\\src\\mongo\\" ); if ( start == std::string::npos ) { start = filename.find( "\\src\\third_party\\" ); } if ( start != std::string::npos ) { std::string shorter( "..." ); shorter += filename.substr( start ); traceItem.sourceFile.swap( shorter ); } else { traceItem.sourceFile.swap( filename ); } len = traceItem.sourceFile.length() + 3; traceItem.lineNumber = line.LineNumber; if ( traceItem.lineNumber < 10 ) { len += 1; } else if ( traceItem.lineNumber < 100 ) { len += 2; } else if ( traceItem.lineNumber < 1000 ) { len += 3; } else if ( traceItem.lineNumber < 10000 ) { len += 4; } else { len += 5; } traceItem.sourceLength = len; if ( len > fileWidth ) { fileWidth = len; } } else { traceItem.sourceFile.clear(); traceItem.sourceLength = 0; } // symbol name and offset from symbol DWORD64 displacement; ret = SymFromAddr( process, frame64.AddrPC.Offset, &displacement, symbol ); if ( ret ) { traceItem.symbol = symbol->Name; traceItem.instructionOffset = displacement; } else { traceItem.symbol = "???"; traceItem.instructionOffset = 0; } // add to list traceList.push_back( traceItem ); } SymCleanup( process ); // print list ++moduleWidth; ++fileWidth; for ( size_t i = 0; i < frameCount; ++i ) { os << traceList[i].module << " "; size_t width = traceList[i].module.length(); while ( width < moduleWidth ) { os << " "; ++width; } if ( traceList[i].sourceFile.length() ) { os << traceList[i].sourceFile << "(" << std::dec << traceList[i].lineNumber << ") "; } width = traceList[i].sourceLength; while ( width < fileWidth ) { os << " "; ++width; } os << traceList[i].symbol << "+0x" << std::hex << traceList[i].instructionOffset; os << std::endl; } }
GCallStack::GCallStack(HANDLE hThread, CONTEXT& c) { if (!_bLockInit) // only init the single instance of the CRITICAL_SECTION 1 time for the many instances of GCallStack { InitializeCriticalSection(&_DbgHelpLock); _bLockInit = true; } DWORD imageType = IMAGE_FILE_MACHINE_I386; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); int frameNum = 0; // counts walked frames int MAX_STACK_FRAMES = 7777; // in C# the maximum stack frames imposed by the language is 1000. Arbitrary limit to guarantee no condition of infinate walking in corrupted memory. DWORD offsetFromLine; // tells us line number in the source file #if defined(_LINUX64) || defined(_WIN64) || defined(_IOS) unsigned __int64 offsetFromSymbol; // tells us how far from the symbol we were #else DWORD offsetFromSymbol; // tells us how far from the symbol we were #endif DWORD symOptions; // symbol handler settings IMAGEHLP_SYMBOL *pSym = (IMAGEHLP_SYMBOL *) malloc( IMGSYMLEN + MAXNAMELEN ); GString strStackName(MAXNAMELEN + 512); // undecorated method/function name + Source file and line number IMAGEHLP_MODULE Module; IMAGEHLP_LINE Line; STACKFRAME64 s; // in/out stackframe memset( &s, '\0', sizeof s ); // note: converted code from [std::string symSearchPath] to [GString symSearchPath] so it will compile with the _UNICODE build flag - 8/18/2014 GString symSearchPath; #ifdef _UNICODE wchar_t *tt = 0, *p; tt = new wchar_t[TTBUFLEN]; #else char *tt = 0, *p; tt = new char[TTBUFLEN]; #endif // build symbol search path from: symSearchPath = ""; // current directory if (GetCurrentDirectory(TTBUFLEN, tt)) symSearchPath << tt << "; "; // dir with executable if ( GetModuleFileName( 0, tt, TTBUFLEN ) ) { #ifdef _UNICODE for (p = tt + wcslen(tt) - 1; p >= tt; --p) #else for (p = tt + strlen(tt) - 1; p >= tt; --p) // VC6 does not have a _tcsclen() and we still support VC6 #endif { // locate the rightmost path separator if ( *p == '\\' || *p == '/' || *p == ':' ) break; } // if we found one, p is pointing at it; if not, tt only contains an exe name (no path), and p points before its first byte if ( p != tt ) // path sep found? { if ( *p == ':' ) // we leave colons in place ++ p; *p = '\0'; // eliminate the exe name and last path sep symSearchPath << tt << "; "; } } // environment variable _NT_SYMBOL_PATH GString g("_NT_SYMBOL_PATH"); if (GetEnvironmentVariable(g, tt, TTBUFLEN)) symSearchPath << tt << "; "; // environment variable _NT_ALTERNATE_SYMBOL_PATH g = "_NT_ALTERNATE_SYMBOL_PATH"; if (GetEnvironmentVariable(g, tt, TTBUFLEN)) symSearchPath << tt << "; "; // environment variable SYSTEMROOT g = "SYSTEMROOT"; if (GetEnvironmentVariable(g, tt, TTBUFLEN)) symSearchPath << tt << "; "; if ( symSearchPath.size() > 0 ) // if we added anything, we have a trailing semicolon symSearchPath = symSearchPath.substr( 0, symSearchPath.size() - 1 ); // 8/20/2014 note: In older Windows API's SymInitialize()'s 2nd argument was not defined as "const char *", it was only "char *" // Although "const" was not defined, the API call is "const" in behavior. In newer versions of the Windows API this has been fixed. // In newer versions - SymInitialize's 2nd argument may resolve to either "const char *" OR "const wchar_t *", and in those builds the // GString has a default conversion to the correct string type, however in the older build configurations, GString does not (and should not) // know how to resolve to a "char *" by default, so in that case the preprocessor directive isolates the code needed to convert to "char *" #if defined(_MSC_VER) && _MSC_VER <= 1200 if (!SymInitialize(hProcess, symSearchPath.Buf(), false)) // symSearchPath == (char *) #else if (!SymInitialize(hProcess, symSearchPath, true)) // symSearchPath == (const char *) --OR-- (const wchar_t *) depending on the _UNICODE preprocessor definition #endif { goto tagCleanUp; } symOptions = SymGetOptions(); symOptions |= SYMOPT_LOAD_LINES; symOptions &= ~SYMOPT_UNDNAME; SymSetOptions( symOptions ); enumAndLoadModuleSymbols( hProcess, GetCurrentProcessId() ); // init STACKFRAME for first call, definitions found in ImageHlp.h #ifdef _M_IX86 imageType = IMAGE_FILE_MACHINE_I386; s.AddrPC.Offset = c.Eip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Ebp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Esp; s.AddrStack.Mode = AddrModeFlat; #elif _M_X64 imageType = IMAGE_FILE_MACHINE_AMD64; s.AddrPC.Offset = c.Rip; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.Rsp; s.AddrFrame.Mode = AddrModeFlat; s.AddrStack.Offset = c.Rsp; s.AddrStack.Mode = AddrModeFlat; #elif _M_IA64 imageType = IMAGE_FILE_MACHINE_IA64; s.AddrPC.Offset = c.StIIP; s.AddrPC.Mode = AddrModeFlat; s.AddrFrame.Offset = c.IntSp; s.AddrFrame.Mode = AddrModeFlat; s.AddrBStore.Offset = c.RsBSP; s.AddrBStore.Mode = AddrModeFlat; s.AddrStack.Offset = c.IntSp; s.AddrStack.Mode = AddrModeFlat; #endif memset( pSym, '\0', IMGSYMLEN + MAXNAMELEN ); pSym->SizeOfStruct = IMGSYMLEN; pSym->MaxNameLength = MAXNAMELEN; memset( &Line, '\0', sizeof Line ); Line.SizeOfStruct = sizeof Line; memset( &Module, '\0', sizeof Module ); Module.SizeOfStruct = sizeof Module; offsetFromSymbol = 0; // DbgHelp is single threaded, so acquire a lock. EnterCriticalSection(&_DbgHelpLock); while ( frameNum < MAX_STACK_FRAMES ) { // get next stack frame (StackWalk(), SymFunctionTableAccess(), SymGetModuleBase()) // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can // assume that either you are done, or that the stack is so hosed that the next deeper frame could not be found. #ifdef _WIN64 if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) #else if (!StackWalk(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL)) #endif break; // Maybe it failed, maybe we have finished walking the stack if ( s.AddrPC.Offset != 0 ) { // Most likely a valid stack rame // show procedure info if ( ! SymGetSymFromAddr64( hProcess, s.AddrPC.Offset, &offsetFromSymbol, pSym ) ) { break; } else { // UnDecorateSymbolName() to get the Class::Method or function() name in tyhe callstack strStackName.Empty(); UnDecorateSymbolName(pSym->Name, strStackName._str, MAXNAMELEN, UNDNAME_COMPLETE); strStackName.SetLength(strlen(strStackName._str)); // SymGetLineFromAddr() to get the source.cpp and the line number IMAGEHLP_LINE64 Line; if (SymGetLineFromAddr64(hProcess, s.AddrPC.Offset, &offsetFromLine, &Line) != FALSE) { GString g(Line.FileName); // Line.FileName conains the "c:\Full\Path\TO\Source.cpp" // Builds string "Foo::Bar[Source.cpp]@777" strStackName << "[" << g.StartingAt(g.ReverseFind("\\") + 1) << "]@" << Line.LineNumber; } // add the GString to the GStringList, do not add frame 0 because it will always be GException::GSeception where we divided by 0 if (frameNum > 0) _stk += strStackName; } } else { // base reached SetLastError(0); break; } ++frameNum; } LeaveCriticalSection(&_DbgHelpLock); // de-init symbol handler etc. (SymCleanup()) SymCleanup( hProcess ); free( pSym ); tagCleanUp:; delete [] tt; CloseHandle(hProcess); }