bool CSymbolEngine::Init( HANDLE hProcess, PCTSTR SearchPath, bool Invade, bool Notify ) { // Check parameters and preconditions if( m_hProcess != NULL ) { _ASSERTE( !_T("Already initialized.") ); if( hProcess != m_hProcess ) { m_LastError = ERROR_INVALID_FUNCTION; return false; } else { return true; } } // Call SymInitialize if( !SymInitialize( hProcess, SearchPath, Invade ? TRUE : FALSE ) ) { m_LastError = GetLastError(); _ASSERTE( !_T("SymInitialize failed.") ); return false; } // Register the notification callback, if requested if( Notify ) { if( !SymRegisterCallback64( hProcess, DebugInfoCallback, (ULONG64)this ) ) { m_LastError = GetLastError(); _ASSERTE( !_T("SymInitialize failed.") ); return false; } } // Complete m_hProcess = hProcess; return true; }
BOOL DebugMainLoop(const DebugOptions *pOptions) { BOOL fFinished = FALSE; BOOL fBreakpointSignalled = FALSE; BOOL fWowBreakpointSignalled = FALSE; BOOL fTerminating = FALSE; while(!fFinished) { DEBUG_EVENT DebugEvent; // debugging event information DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation PPROCESS_INFO pProcessInfo; PTHREAD_INFO pThreadInfo; HANDLE hProcess; // Wait for a debugging event to occur. The second parameter indicates // that the function does not return until a debugging event occurs. if(!WaitForDebugEvent(&DebugEvent, INFINITE)) { OutputDebug("WaitForDebugEvent: 0x%08lx", GetLastError()); return FALSE; } // Process the debugging event code. switch (DebugEvent.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: { PEXCEPTION_RECORD pExceptionRecord = &DebugEvent.u.Exception.ExceptionRecord; NTSTATUS ExceptionCode = pExceptionRecord->ExceptionCode; // Process the exception code. When handling // exceptions, remember to set the continuation // status parameter (dwContinueStatus). This value // is used by the ContinueDebugEvent function. if (pOptions->verbose_flag) { lprintf("EXCEPTION PID=%lu TID=%lu ExceptionCode=0x%lx dwFirstChance=%lu\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId, pExceptionRecord->ExceptionCode, DebugEvent.u.Exception.dwFirstChance ); } // Find the process in the process list pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED; if (DebugEvent.u.Exception.dwFirstChance) { if (pExceptionRecord->ExceptionCode == (DWORD)STATUS_BREAKPOINT) { // Signal the aedebug event if (!fBreakpointSignalled) { fBreakpointSignalled = TRUE; if (pOptions->hEvent) { SetEvent(pOptions->hEvent); CloseHandle(pOptions->hEvent); } if (pOptions->dwThreadId) { DWORD dwThreadId = pOptions->dwThreadId; const DWORD dwFailed = (DWORD)-1; DWORD dwRet = dwFailed; pThreadInfo = &pProcessInfo->Threads[dwThreadId]; HANDLE hThread = pThreadInfo->hThread; if (hThread != NULL) { dwRet = ResumeThread(hThread); } if (dwRet == dwFailed) { lprintf("error: failed to resume thread %lu\n", dwThreadId); } } /* * We ignore first-chance breakpoints by default. * * We get one of these whenever we attach to a process. * But in some cases, we never get a second-chance, e.g., * when we're attached through MSVCRT's abort(). */ if (!pOptions->breakpoint_flag) { dwContinueStatus = DBG_CONTINUE; break; } } } if (ExceptionCode == STATUS_WX86_BREAKPOINT) { if (!fWowBreakpointSignalled) { fWowBreakpointSignalled = TRUE; dwContinueStatus = DBG_CONTINUE; break; } } /* * Ignore thread naming exception. * * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx * * TODO: Note down the thread name */ if (ExceptionCode == 0x406d1388) { dwContinueStatus = DBG_CONTINUE; break; } if (!pOptions->first_chance) { break; } } // XXX: Deferred symbols don't get loaded without this SymRefreshModuleList(pProcessInfo->hProcess); dumpException(pProcessInfo->hProcess, &DebugEvent.u.Exception.ExceptionRecord); // Find the thread in the thread list THREAD_INFO_LIST::const_iterator it; for (it = pProcessInfo->Threads.begin(); it != pProcessInfo->Threads.end(); ++it) { DWORD dwThreadId = it->first; HANDLE hThread = it->second.hThread; if (dwThreadId != DebugEvent.dwThreadId && ExceptionCode != STATUS_BREAKPOINT && ExceptionCode != STATUS_WX86_BREAKPOINT) { continue; } dumpStack(pProcessInfo->hProcess, hThread, NULL); } if (!DebugEvent.u.Exception.dwFirstChance) { /* * Terminate the process. As continuing would cause the JIT debugger * to be invoked again. */ fTerminating = TRUE; TerminateProcess(pProcessInfo->hProcess, (UINT)ExceptionCode); } break; } case CREATE_THREAD_DEBUG_EVENT: if (pOptions->verbose_flag) { lprintf("CREATE_THREAD PID=%lu TID=%lu\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId ); } // Add the thread to the thread list pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId]; pThreadInfo->hThread = DebugEvent.u.CreateThread.hThread; break; case CREATE_PROCESS_DEBUG_EVENT: { HANDLE hFile = DebugEvent.u.CreateProcessInfo.hFile; char szImageName[MAX_PATH]; char *lpImageName = NULL; if (hFile && GetFileNameFromHandle(hFile, szImageName, _countof(szImageName))) { lpImageName = szImageName; } if (pOptions->verbose_flag) { PCSTR lpModuleName = lpImageName ? getBaseName(lpImageName) : ""; lprintf("CREATE_PROCESS PID=%lu TID=%lu lpBaseOfImage=%p %s\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId, DebugEvent.u.CreateProcessInfo.lpBaseOfImage, lpModuleName ); } hProcess = DebugEvent.u.CreateProcessInfo.hProcess; pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; pProcessInfo->hProcess = hProcess; pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId]; pThreadInfo->hThread = DebugEvent.u.CreateProcessInfo.hThread; if (!InitializeSym(hProcess, FALSE)) { OutputDebug("error: SymInitialize failed: 0x%08lx\n", GetLastError()); exit(EXIT_FAILURE); } SymRegisterCallback64(hProcess, &symCallback, 0); loadModule(hProcess, hFile, lpImageName, DebugEvent.u.CreateProcessInfo.lpBaseOfImage); break; } case EXIT_THREAD_DEBUG_EVENT: if (pOptions->verbose_flag) { lprintf("EXIT_THREAD PID=%lu TID=%lu dwExitCode=0x%lx\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId, DebugEvent.u.ExitThread.dwExitCode ); } // Remove the thread from the thread list pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; hProcess = pProcessInfo->hProcess; // Dump the stack on abort() if (!fTerminating && isAbnormalExitCode(DebugEvent.u.ExitThread.dwExitCode)) { pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId]; refreshSymbolsAndDumpStack(hProcess, pThreadInfo->hThread); } pProcessInfo->Threads.erase(DebugEvent.dwThreadId); break; case EXIT_PROCESS_DEBUG_EVENT: { if (pOptions->verbose_flag) { lprintf("EXIT_PROCESS PID=%lu TID=%lu dwExitCode=0x%lx\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId, DebugEvent.u.ExitProcess.dwExitCode ); } pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; hProcess = pProcessInfo->hProcess; // Dump the stack on abort() if (!fTerminating && isAbnormalExitCode(DebugEvent.u.ExitThread.dwExitCode)) { pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId]; refreshSymbolsAndDumpStack(hProcess, pThreadInfo->hThread); } // Remove the process from the process list g_Processes.erase(DebugEvent.dwProcessId); if (!SymCleanup(hProcess)) { OutputDebug("SymCleanup failed with 0x%08lx\n", GetLastError()); } if (g_Processes.empty()) { fFinished = TRUE; } break; } case LOAD_DLL_DEBUG_EVENT: { HANDLE hFile = DebugEvent.u.LoadDll.hFile; char szImageName[MAX_PATH]; char *lpImageName = NULL; if (hFile && GetFileNameFromHandle(hFile, szImageName, _countof(szImageName))) { lpImageName = szImageName; } if (pOptions->verbose_flag) { PCSTR lpModuleName = lpImageName ? getBaseName(lpImageName) : ""; lprintf("LOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p %s\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId, DebugEvent.u.LoadDll.lpBaseOfDll, lpModuleName ); } pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; hProcess = pProcessInfo->hProcess; loadModule(hProcess, hFile, lpImageName, DebugEvent.u.LoadDll.lpBaseOfDll); break; } case UNLOAD_DLL_DEBUG_EVENT: if (pOptions->verbose_flag) { lprintf("UNLOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId, DebugEvent.u.UnloadDll.lpBaseOfDll ); } pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; hProcess = pProcessInfo->hProcess; SymUnloadModule64(hProcess, (UINT_PTR)DebugEvent.u.UnloadDll.lpBaseOfDll); break; case OUTPUT_DEBUG_STRING_EVENT: { if (pOptions->verbose_flag) { lprintf("OUTPUT_DEBUG_STRING PID=%lu TID=%lu\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId ); } pProcessInfo = &g_Processes[DebugEvent.dwProcessId]; assert(!DebugEvent.u.DebugString.fUnicode); LPSTR lpDebugStringData = readProcessString(pProcessInfo->hProcess, DebugEvent.u.DebugString.lpDebugStringData, DebugEvent.u.DebugString.nDebugStringLength); lprintf("%s", lpDebugStringData); free(lpDebugStringData); break; } case RIP_EVENT: if (pOptions->verbose_flag) { lprintf("RIP PID=%lu TID=%lu\r\n", DebugEvent.dwProcessId, DebugEvent.dwThreadId ); } break; default: if (pOptions->verbose_flag) { lprintf("EVENT%lu PID=%lu TID=%lu\r\n", DebugEvent.dwDebugEventCode, DebugEvent.dwProcessId, DebugEvent.dwThreadId ); } break; } // Resume executing the thread that reported the debugging event. ContinueDebugEvent( DebugEvent.dwProcessId, DebugEvent.dwThreadId, dwContinueStatus ); } return TRUE; }
int main(int argc, char **argv) { BOOL bRet; DWORD dwRet; bool debug = false; char *szModule = nullptr; bool functions = false; bool demangle = false; bool pretty = false; while (1) { int opt = getopt(argc, argv, "?CDe:fHp"); switch (opt) { case 'C': demangle = true; break; case 'D': debug = true; break; case 'e': szModule = optarg; break; case 'f': functions = true; break; case 'H': usage(argv[0]); return EXIT_SUCCESS; case 'p': pretty = true; break; case '?': fprintf(stderr, "error: invalid option `%c`\n", optopt); /* pass-through */ default: usage(argv[0]); return EXIT_FAILURE; case -1: break; } if (opt == -1) { break; } } if (szModule == nullptr) { usage(argv[0]); return EXIT_FAILURE; } // Load the module HMODULE hModule = nullptr; #ifdef _WIN64 hModule = LoadLibraryExA(szModule, NULL, LOAD_LIBRARY_AS_DATAFILE); #endif if (!hModule) { hModule = LoadLibraryExA(szModule, NULL, DONT_RESOLVE_DLL_REFERENCES); } if (!hModule) { fprintf(stderr, "error: failed to load %s\n", szModule); return EXIT_FAILURE; } DWORD dwSymOptions = SymGetOptions(); dwSymOptions |= SYMOPT_LOAD_LINES; #ifndef NDEBUG dwSymOptions |= SYMOPT_DEBUG; #endif // We can get more information by calling UnDecorateSymbolName() ourselves. dwSymOptions &= ~SYMOPT_UNDNAME; SymSetOptions(dwSymOptions); HANDLE hProcess = GetCurrentProcess(); bRet = InitializeSym(hProcess, FALSE); assert(bRet); if (debug) { SymRegisterCallback64(hProcess, &callback, 0); } dwRet = SymLoadModuleEx(hProcess, NULL, szModule, NULL, (DWORD64)(UINT_PTR)hModule, 0, NULL, 0); if (!dwRet) { fprintf(stderr, "warning: failed to load module symbols\n"); } if (!GetModuleHandleA("symsrv.dll")) { fprintf(stderr, "warning: symbol server not loaded\n"); } while (optind < argc) { const char *arg = argv[optind++]; DWORD64 dwRelAddr; if (arg[0] == '0' && arg[1] == 'x') { sscanf(&arg[2], "%08" PRIX64, &dwRelAddr); } else { dwRelAddr = atol(arg); } UINT_PTR dwAddr = (UINT_PTR)hModule + dwRelAddr; if (functions) { struct { SYMBOL_INFO Symbol; CHAR Name[512]; } sym; char UnDecoratedName[512]; const char *function = "??"; ZeroMemory(&sym, sizeof sym); sym.Symbol.SizeOfStruct = sizeof sym.Symbol; sym.Symbol.MaxNameLen = sizeof sym.Symbol.Name + sizeof sym.Name; DWORD64 dwSymDisplacement = 0; bRet = SymFromAddr(hProcess, dwAddr, &dwSymDisplacement, &sym.Symbol); if (bRet) { function = sym.Symbol.Name; if (demangle) { if (UnDecorateSymbolName( sym.Symbol.Name, UnDecoratedName, sizeof UnDecoratedName, UNDNAME_COMPLETE)) { function = UnDecoratedName; } } } fputs(function, stdout); fputs(pretty ? " at " : "\n", stdout); } IMAGEHLP_LINE64 line; ZeroMemory(&line, sizeof line); line.SizeOfStruct = sizeof line; DWORD dwLineDisplacement = 0; bRet = SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &line); if (bRet) { fprintf(stdout, "%s:%lu\n", line.FileName, line.LineNumber); } else { fputs("??:?\n", stdout); } fflush(stdout); } SymCleanup(hProcess); FreeLibrary(hModule); return 0; }
void ConfigDlg::LoadSymbols() { g_StackDepth = GetDlgItemInt( IDC_EDIT1, NULL, FALSE ); if( m_bChanged ) { CString csExeName; GetModuleFileName( 0, csExeName.GetBuffer( MAX_PATH), MAX_PATH ); csExeName.ReleaseBuffer(); csExeName = csExeName.MakeLower(); csExeName.Replace( _T(".exe"), _T("Mem.ini")); CStdioFile File; if( !File.Open( csExeName, CFile::modeCreate|CFile::modeWrite )) { goto LoadDll; } int nCount = m_List.GetItemCount(); m_csPath.Empty(); for( int nId = 0;nId < nCount; nId++ ) { CString csItem = m_List.GetItemText( nId ,0 ); m_csPath += csItem + _T(";"); csItem += _T("\r\n"); File.WriteString( csItem ); } File.Close(); } LoadDll: HMODULE hModule = GetModuleHandle( _T("dbghelp.dll")); SymRefreshModuleListDef pSymRefreshModuleList; if( hModule ) { pSymRefreshModuleList = (SymRefreshModuleListDef)GetProcAddress( hModule, _T("SymRefreshModuleList")); CString csLoadedDll; GetModuleFileName( hModule, csLoadedDll.GetBuffer(MAX_PATH), MAX_PATH ); csLoadedDll.ReleaseBuffer(); if( !pSymRefreshModuleList ) { // old version of dbghelp :( MessageBox( "Your application has already loaded dbghelp.dll from " + csLoadedDll + "\n\nFor acqurate results, replace this dll with the latest version of dbghelp.dll" "coming with \"Debugging tools for windows\" or with the dll the application folder of this utility", "Error", MB_OK ); } else { MessageBox( "Your application has already loaded dbghelp.dll from " + csLoadedDll + " Please confirm that the symsrv.dll exists in th same folder.\n" "Otherwise symbol server will not work", "Warning", MB_OK ); } } else { // static int nTempvariable; // MEMORY_BASIC_INFORMATION MemInfo; // CString csDllPath; // if( !VirtualQuery( &nTempvariable, &MemInfo, sizeof(MemInfo))) // { // goto LoadDll; // } CString csDllPath; HMODULE hHookDll = GetModuleHandle( _T("HookDll.dll")); if( !GetModuleFileName( hHookDll, csDllPath.GetBuffer( MAX_PATH), MAX_PATH )) { goto LoadDll; } csDllPath.ReleaseBuffer(); int nPos = csDllPath.ReverseFind( _T('\\')); if( 0 >= nPos ) { goto LoadDll; } csDllPath = csDllPath.Left( nPos + 1 ); //csDllPath = "C:\\Program Files\\Debugging Tools for Windows (x86)\\"; csDllPath += _T("dbghelp.dll"); hModule = LoadLibrary( csDllPath ); if( !hModule) { hModule = LoadLibrary( _T("dbghelp.dll")); pSymRefreshModuleList = (SymRefreshModuleListDef)GetProcAddress( hModule, _T("SymRefreshModuleList")); if( !pSymRefreshModuleList ) { MessageBox( "Failed to load the dbghelp.dll from the local directory\n\n" "The application will continue with the default dbghelp.dll. But some feature may" "be unavailable", "Error", MB_OK ); } } else { pSymRefreshModuleList = (SymRefreshModuleListDef)GetProcAddress( hModule, _T("SymRefreshModuleList")); } } SymCleanup(GetCurrentProcess()); CString csWholePath = m_csPath; csWholePath.TrimRight( ';' ); DWORD dwOption = 0;//SymGetOptions(); dwOption |= SYMOPT_CASE_INSENSITIVE|SYMOPT_LOAD_LINES|SYMOPT_FAIL_CRITICAL_ERRORS| SYMOPT_LOAD_ANYTHING|SYMOPT_UNDNAME; SymSetOptions( dwOption ); CWinThread* pThread = AfxBeginThread( ThreadEntry, this ); HANDLE hThread = pThread->m_hThread; BOOL fInvadeProcess = (0 == pSymRefreshModuleList)?TRUE:FALSE; BOOL bRet = SymInitialize(GetCurrentProcess(), (LPTSTR)csWholePath.operator LPCTSTR() , fInvadeProcess ); SymRegisterCallback64( GetCurrentProcess(),SymRegisterCallbackProc64,(ULONG64 )this ); while( !m_ProgressDlg.m_hWnd )// wait untill the dialog is created { Sleep( 50 ); } if( pSymRefreshModuleList ) { pSymRefreshModuleList( GetCurrentProcess()); } //SymRefreshModuleList( GetCurrentProcess()); m_ProgressDlg.SendMessage( WM_CLOSE ); WaitForSingleObject( hThread, 10000 ); }