/*++ Copyright (c) 1993-2002 Microsoft Corporation Module Name: debug.cpp Abstract: This file implements the debug module for drwatson. This module processes all debug events and generates the postmortem dump. Author: Wesley Witt (wesw) 1-May-1993 Environment: User Mode --*/ #include "pch.cpp" #define STATUS_POSSIBLE_DEADLOCK ((DWORD)0xC0000194L) #define STATUS_VDM_EVENT STATUS_SEGMENT_NOTIFICATION typedef struct tagSYSINFO { _TCHAR szUserName[MAX_PATH]; _TCHAR szMachineName[MAX_PATH]; } SYSINFO, *PSYSINFO; //---------------------------------------------------------------------------- // // Log output callbacks. // //---------------------------------------------------------------------------- class LogOutputCallbacks : public IDebugOutputCallbacks { public: // IUnknown. STDMETHOD(QueryInterface)( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface ); STDMETHOD_(ULONG, AddRef)( THIS ); STDMETHOD_(ULONG, Release)( THIS ); // IDebugOutputCallbacks. STDMETHOD(Output)( THIS_ IN ULONG Mask, IN PCSTR Text ); }; LogOutputCallbacks g_LogOutCb; STDMETHODIMP LogOutputCallbacks::QueryInterface( THIS_ IN REFIID InterfaceId, OUT PVOID* Interface ) { *Interface = NULL; if (IsEqualIID(InterfaceId, __uuidof(IUnknown)) || IsEqualIID(InterfaceId, __uuidof(IDebugOutputCallbacks))) { *Interface = (IDebugOutputCallbacks *)this; AddRef(); return S_OK; } else { return E_NOINTERFACE; } } STDMETHODIMP_(ULONG) LogOutputCallbacks::AddRef( THIS ) { // This class is designed to be static so // there's no true refcount. return 1; } STDMETHODIMP_(ULONG) LogOutputCallbacks::Release( THIS ) { // This class is designed to be static so // there's no true refcount. return 0; } STDMETHODIMP LogOutputCallbacks::Output( THIS_ IN ULONG Mask, IN PCSTR Text ) { PCSTR Scan; for (;;) { Scan = strchr(Text, '\n'); if (Scan == NULL) { break; } lprintfs(_T("%.*hs\r\n"), (int)(Scan - Text), Text); Text = Scan + 1; } lprintfs(_T("%hs"), Text); return S_OK; } _TCHAR * GetExceptionText( DWORD dwExceptionCode ) { static _TCHAR buf[80]; DWORD dwFormatId = 0; memset( buf, 0, sizeof(buf) ); switch (dwExceptionCode) { case STATUS_SINGLE_STEP: dwFormatId = MSG_SINGLE_STEP_EXCEPTION; break; case DBG_CONTROL_C: dwFormatId = MSG_CONTROLC_EXCEPTION; break; case DBG_CONTROL_BREAK: dwFormatId = MSG_CONTROL_BRK_EXCEPTION; break; case STATUS_ACCESS_VIOLATION: dwFormatId = MSG_ACCESS_VIOLATION_EXCEPTION; break; case STATUS_STACK_OVERFLOW: dwFormatId = MSG_STACK_OVERFLOW_EXCEPTION; break; case STATUS_INTEGER_DIVIDE_BY_ZERO: dwFormatId = MSG_INTEGER_DIVIDE_BY_ZERO_EXCEPTION; break; case STATUS_PRIVILEGED_INSTRUCTION: dwFormatId = MSG_PRIVILEGED_INSTRUCTION_EXCEPTION; break; case STATUS_ILLEGAL_INSTRUCTION: dwFormatId = MSG_ILLEGAL_INSTRUCTION_EXCEPTION; break; case STATUS_IN_PAGE_ERROR: dwFormatId = MSG_IN_PAGE_IO_EXCEPTION; break; case STATUS_DATATYPE_MISALIGNMENT: dwFormatId = MSG_DATATYPE_EXCEPTION; break; case STATUS_POSSIBLE_DEADLOCK: dwFormatId = MSG_DEADLOCK_EXCEPTION; break; case STATUS_VDM_EVENT: dwFormatId = MSG_VDM_EXCEPTION; break; case STATUS_BREAKPOINT: dwFormatId = MSG_BREAKPOINT_EXCEPTION; break; default: lprintfs( _T("\r\n") ); break; } FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, dwFormatId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language buf, sizeof(buf) / sizeof(_TCHAR), NULL ); return buf; } void CreateEngineInterfaces( PDEBUGPACKET dp ) { HRESULT Status; if ((Status = DebugCreate(__uuidof(IDebugClient2), (void **)&dp->DbgClient)) != S_OK) { goto Error; } if ((Status = dp->DbgClient-> QueryInterface(__uuidof(IDebugControl), (void **)&dp->DbgControl)) != S_OK || (Status = dp->DbgClient-> QueryInterface(__uuidof(IDebugDataSpaces), (void **)&dp->DbgData)) != S_OK || (Status = dp->DbgClient-> QueryInterface(__uuidof(IDebugRegisters), (void **)&dp->DbgRegisters)) != S_OK || (Status = dp->DbgClient-> QueryInterface(__uuidof(IDebugSymbols), (void **)&dp->DbgSymbols)) != S_OK || (Status = dp->DbgClient-> QueryInterface(__uuidof(IDebugSystemObjects), (void **)&dp->DbgSystem)) != S_OK) { goto Error; } if ((Status = dp->DbgSymbols-> AddSymbolOptions(SYMOPT_FAIL_CRITICAL_ERRORS)) != S_OK || (Status = dp->DbgControl-> AddEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK)) != S_OK || (Status = dp->DbgControl-> Execute(DEBUG_OUTCTL_IGNORE, "sxe et", DEBUG_EXECUTE_DEFAULT)) != S_OK) { goto Error; } return; Error: if (dp->options.fVisual) { FatalError( Status, LoadRcString(IDS_CANT_INIT_ENGINE) ); } else { ExitProcess( 1 ); } } void AttachToActiveProcess ( PDEBUGPACKET dp ) { HRESULT Status; if ((Status = dp->DbgClient-> AttachProcess(0, dp->dwPidToDebug, DEBUG_ATTACH_DEFAULT)) != S_OK) { if (dp->options.fVisual) { FatalError( Status, LoadRcString(IDS_ATTACHFAIL) ); } else { ExitProcess( 1 ); } } return; } DWORD SysInfoThread( PSYSINFO si ) { DWORD len; len = sizeof(si->szMachineName) / sizeof(_TCHAR); GetComputerName( si->szMachineName, &len ); len = sizeof(si->szUserName) / sizeof(_TCHAR); GetUserName( si->szUserName, &len ); return 0; } void LogSystemInformation( PDEBUGPACKET dp ) { _TCHAR buf[1024]; SYSTEM_INFO si; DWORD ver; SYSINFO mySi; SYSINFO* threadSi; DWORD dwThreadId; HANDLE hThread; DWORD TSid; lprintf( MSG_SYSINFO_HEADER ); // Initialize default unknown values. LoadRcStringBuf( IDS_UNKNOWN_MACHINE, mySi.szMachineName, _tsizeof(mySi.szMachineName) ); LoadRcStringBuf( IDS_UNKNOWN_USER, mySi.szUserName, _tsizeof(mySi.szUserName) ); // Attempt to get the actual values. // The storage passed to the get thread is not taken // from this thread's stack so that this function can exit // without leaving the other thread with stale stack pointers. threadSi = (SYSINFO*)malloc(sizeof(*threadSi)); if (threadSi != NULL) { hThread = CreateThread( NULL, 16000, (LPTHREAD_START_ROUTINE)SysInfoThread, threadSi, 0, &dwThreadId ); if (hThread != NULL) { // Let the thread run for a little while since // the get calls can be slow. If the thread doesn't // finish in the time allotted we'll just go ahead // with the default values and forget about the get thread. Sleep( 0 ); if (WaitForSingleObject( hThread, 30000 ) == WAIT_OBJECT_0) { // Thread finished so we have the real values. _tcscpy(mySi.szMachineName, threadSi->szMachineName); _tcscpy(mySi.szUserName, threadSi->szUserName); free(threadSi); } CloseHandle( hThread ); } else { free(threadSi); } } lprintf( MSG_SYSINFO_COMPUTER, mySi.szMachineName ); lprintf( MSG_SYSINFO_USER, mySi.szUserName ); ProcessIdToSessionId(dp->dwPidToDebug, &TSid); _stprintf( buf, _T("%d"), TSid ); lprintf( MSG_SYSINFO_TERMINAL_SESSION, buf ); GetSystemInfo( &si ); _stprintf( buf, _T("%d"), si.dwNumberOfProcessors ); lprintf( MSG_SYSINFO_NUM_PROC, buf ); RegLogProcessorType(); ver = GetVersion(); _stprintf( buf, _T("%d.%d"), LOBYTE(LOWORD(ver)), HIBYTE(LOWORD(ver)) ); lprintf( MSG_SYSINFO_WINVER, buf ); RegLogCurrentVersion(); lprintfs( _T("\r\n") ); } void LogTaskList( PDEBUGPACKET dp ) /*++ Routine Description: This function gets the current task list and logs the process id & process name to the log file. --*/ { HRESULT Status; #define MAX_IDS 8192 PULONG Ids = NULL; ULONG IdCount; ULONG i; Ids = (PULONG)malloc(sizeof(*Ids) * MAX_IDS); if (Ids == NULL) { goto Error; } if ((Status = dp->DbgClient-> GetRunningProcessSystemIds(0, Ids, MAX_IDS, &IdCount)) != S_OK) { goto Error; } if (IdCount > MAX_IDS) { // Incomplete process list is good enough. IdCount = MAX_IDS; } lprintf( MSG_TASK_LIST ); for (i = 0; i < IdCount; i++) { char ExeName[MAX_PATH]; if ((Status = dp->DbgClient-> GetRunningProcessDescription(0, Ids[i], DEBUG_PROC_DESC_NO_PATHS, ExeName, sizeof(ExeName), NULL, NULL, 0, NULL)) != S_OK) { lprintfs(_T("%4d Error 0x%08X\r\n"), Ids[i], Status); } else { lprintfs(_T("%4d %hs\r\n"), Ids[i], ExeName); } } lprintfs( _T("\r\n") ); free(Ids); return; Error: _tprintf( _T("ERROR: could not get the task list\n") ); free(Ids); } void LogModuleList( PDEBUGPACKET dp ) { HRESULT Status; ULONG NumMod; ULONG i; char Image[MAX_PATH]; DEBUG_MODULE_PARAMETERS Params; lprintf( MSG_MODULE_LIST ); if ((Status = dp->DbgSymbols->GetNumberModules(&NumMod, &i)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); return; } for (i = 0; i < NumMod; i++) { if ((Status = dp->DbgSymbols-> GetModuleParameters(1, NULL, i, &Params)) != S_OK || FAILED(Status = dp->DbgSymbols-> GetModuleNames(i, 0, Image, sizeof(Image), NULL, NULL, 0, NULL, NULL, 0, NULL))) { lprintfs(_T("Error 0x%08X\r\n"), Status); } else { lprintfs(_T("%016I64x - %016I64x: %hs\r\n"), Params.Base, Params.Base + Params.Size, Image); } } lprintfs( _T("\r\n") ); } void LogStackDump( PDEBUGPACKET dp ) { HRESULT Status; DWORD i; DWORD j; BYTE stack[1024] = {0}; ULONG64 StackOffset; if ((Status = dp->DbgRegisters->GetStackOffset(&StackOffset)) != S_OK || (Status = dp->DbgData->ReadVirtual(StackOffset, stack, sizeof(stack), &i)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); return; } lprintf( MSG_STACK_DUMP_HEADER ); for( i = 0; i < 20; i++ ) { j = i * 16; lprintfs( _T("%016I64x %02x %02x %02x %02x %02x %02x %02x %02x - ") _T("%02x %02x %02x %02x %02x %02x %02x %02x ") _T("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\r\n"), j + StackOffset, stack[ j + 0 ], stack[ j + 1 ], stack[ j + 2 ], stack[ j + 3 ], stack[ j + 4 ], stack[ j + 5 ], stack[ j + 6 ], stack[ j + 7 ], stack[ j + 8 ], stack[ j + 9 ], stack[ j + 10 ], stack[ j + 11 ], stack[ j + 12 ], stack[ j + 13 ], stack[ j + 14 ], stack[ j + 15 ], isprint( stack[ j + 0 ]) ? stack[ j + 0 ] : _T('.'), isprint( stack[ j + 1 ]) ? stack[ j + 1 ] : _T('.'), isprint( stack[ j + 2 ]) ? stack[ j + 2 ] : _T('.'), isprint( stack[ j + 3 ]) ? stack[ j + 3 ] : _T('.'), isprint( stack[ j + 4 ]) ? stack[ j + 4 ] : _T('.'), isprint( stack[ j + 5 ]) ? stack[ j + 5 ] : _T('.'), isprint( stack[ j + 6 ]) ? stack[ j + 6 ] : _T('.'), isprint( stack[ j + 7 ]) ? stack[ j + 7 ] : _T('.'), isprint( stack[ j + 8 ]) ? stack[ j + 8 ] : _T('.'), isprint( stack[ j + 9 ]) ? stack[ j + 9 ] : _T('.'), isprint( stack[ j + 10 ]) ? stack[ j + 10 ] : _T('.'), isprint( stack[ j + 11 ]) ? stack[ j + 11 ] : _T('.'), isprint( stack[ j + 12 ]) ? stack[ j + 12 ] : _T('.'), isprint( stack[ j + 13 ]) ? stack[ j + 13 ] : _T('.'), isprint( stack[ j + 14 ]) ? stack[ j + 14 ] : _T('.'), isprint( stack[ j + 15 ]) ? stack[ j + 15 ] : _T('.') ); } lprintfs( _T("\r\n") ); } void LogCurrentThreadInformation( PDEBUGPACKET dp, PCRASHES crash ) { HRESULT Status; ULONG ThreadId; _TCHAR IdBuf[16]; ULONG64 InstrOffs; DWORD InstrWindow; // The size should match the size of pCrash->szFunction char FuncNameA[256]; WCHAR FuncNameW[256]; ULONG64 Displ; if ((Status = dp->DbgSystem-> GetCurrentThreadSystemId(&ThreadId)) != S_OK) { ThreadId = 0xffffffff; } _stprintf( IdBuf, _T("%x"), ThreadId ); lprintf( MSG_STATE_DUMP, IdBuf ); dp->DbgClient->SetOutputCallbacks(&g_LogOutCb); if ((Status = dp->DbgRegisters-> OutputRegisters(DEBUG_OUTCTL_THIS_CLIENT, DEBUG_REGISTERS_DEFAULT)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); } lprintfs( _T("\r\n") ); InstrWindow = dp->options.dwInstructions; if (InstrWindow > 500) { InstrWindow = 500; } strcpy(FuncNameA, ""); wcscpy(FuncNameW, L""); if ((Status = dp->DbgRegisters-> GetInstructionOffset(&InstrOffs)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); } else { if (FAILED(Status = dp->DbgSymbols-> GetNameByOffset(InstrOffs, FuncNameA, sizeof(FuncNameA), NULL, &Displ))) { strcpy(FuncNameA, ""); } #ifdef UNICODE if (MultiByteToWideChar(CP_ACP, 0, FuncNameA, -1, FuncNameW, sizeof(FuncNameW) / sizeof(WCHAR)) == 0) { wcscpy(FuncNameW, L"DbgClient->SetOutputLinePrefix(" "); if ((Status = dp->DbgControl-> OutputDisassemblyLines(DEBUG_OUTCTL_THIS_CLIENT, InstrWindow, InstrWindow, InstrOffs, DEBUG_DISASM_MATCHING_SYMBOLS, NULL, NULL, NULL, NULL)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); } // If this is the event thread output a message // indicating the faulting instruction. if (crash) { dp->DbgClient->SetOutputLinePrefix(NULL); lprintf( MSG_FAULT ); } if ((Status = dp->DbgControl-> OutputDisassembly(DEBUG_OUTCTL_THIS_CLIENT, InstrOffs, DEBUG_DISASM_EFFECTIVE_ADDRESS | DEBUG_DISASM_MATCHING_SYMBOLS, &InstrOffs)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); } dp->DbgClient->SetOutputLinePrefix(" "); if ((Status = dp->DbgControl-> OutputDisassemblyLines(DEBUG_OUTCTL_THIS_CLIENT, 0, InstrWindow, InstrOffs, DEBUG_DISASM_EFFECTIVE_ADDRESS | DEBUG_DISASM_MATCHING_SYMBOLS, NULL, NULL, NULL, NULL)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); } dp->DbgClient->SetOutputLinePrefix(NULL); } lprintfs( _T("\r\n") ); if (crash) { #ifdef UNICODE wcscpy(crash->szFunction, FuncNameW); #else strcpy(crash->szFunction, FuncNameA); #endif } lprintf( MSG_STACKTRACE ); if ((Status = dp->DbgControl-> OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT, NULL, 100, DEBUG_STACK_ARGUMENTS | DEBUG_STACK_FUNCTION_INFO | DEBUG_STACK_FRAME_ADDRESSES | DEBUG_STACK_COLUMN_NAMES)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); } lprintfs( _T("\r\n") ); dp->DbgClient->SetOutputCallbacks(NULL); LogStackDump( dp ); } void LogAllThreadInformation( PDEBUGPACKET dp, PCRASHES crash ) { HRESULT Status; ULONG NumThreads; ULONG i; ULONG ThreadId; ULONG EventTid; if (!dp->options.fDumpAllThreads) { // Just log the current event thread's information. LogCurrentThreadInformation(dp, crash); return; } if ((Status = dp->DbgSystem->GetNumberThreads(&NumThreads)) != S_OK || (Status = dp->DbgSystem->GetEventThread(&EventTid)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); return; } for (i = 0; i < NumThreads; i++) { if ((Status = dp->DbgSystem-> GetThreadIdsByIndex(i, 1, &ThreadId, NULL)) != S_OK || (Status = dp->DbgSystem->SetCurrentThreadId(ThreadId)) != S_OK) { lprintfs(_T("Error 0x%08X\r\n"), Status); continue; } LogCurrentThreadInformation(dp, ThreadId == EventTid ? crash : NULL); } dp->DbgSystem->SetCurrentThreadId(EventTid); } void LogSymbols( PDEBUGPACKET dp ) { HRESULT Status; char ModName[64]; char Buf[MAX_PATH]; ULONG64 InstrOffs; ULONG64 ModBase; lprintf( MSG_SYMBOL_TABLE ); if ((Status = dp->DbgRegisters-> GetInstructionOffset(&InstrOffs)) != S_OK || (Status = dp->DbgSymbols-> GetModuleByOffset(InstrOffs, 0, NULL, &ModBase)) != S_OK || FAILED(Status = dp->DbgSymbols-> GetModuleNames(DEBUG_ANY_ID, ModBase, Buf, sizeof(Buf), NULL, ModName, sizeof(ModName), NULL, NULL, 0, NULL))) { lprintfs(_T("Error 0x%08X\r\n"), Status); return; } lprintfs(_T("%hs\r\n\r\n"), Buf); sprintf(Buf, "x %s!*", ModName); dp->DbgClient->SetOutputCallbacks(&g_LogOutCb); dp->DbgControl->Execute(DEBUG_OUTCTL_THIS_CLIENT, Buf, DEBUG_EXECUTE_DEFAULT); dp->DbgClient->SetOutputCallbacks(NULL); } void PostMortemDump( PDEBUGPACKET dp, PDEBUG_LAST_EVENT_INFO_EXCEPTION Exception ) { _TCHAR dbuf[MAX_PATH]; _TCHAR szDate[20]; _TCHAR szTime[20]; CRASHES crash = {0}; char ExeName[MAX_PATH]; GetLocalTime( &crash.time ); crash.dwExceptionCode = Exception->ExceptionRecord.ExceptionCode; crash.dwAddress = (DWORD_PTR)Exception->ExceptionRecord.ExceptionAddress; if (FAILED(dp->DbgSystem-> GetCurrentProcessExecutableName(ExeName, sizeof(ExeName), NULL))) { strcpy(ExeName, ""); } #ifdef UNICODE if (MultiByteToWideChar(CP_ACP, 0, ExeName, -1, crash.szAppName, sizeof(crash.szAppName) / sizeof(TCHAR)) == 0) { _tcscpy(crash.szAppName, _T("")); } #endif lprintf( MSG_APP_EXCEPTION ); _stprintf( dbuf, _T("%d"), dp->dwPidToDebug ); lprintf( MSG_APP_EXEP_NAME, crash.szAppName, dbuf ); GetDateFormat(LOCALE_SYSTEM_DEFAULT, DATE_SHORTDATE, &crash.time, NULL, szDate, sizeof(szDate) / sizeof(_TCHAR)); _sntprintf( szTime, _tsizeof(szTime), _T("%02d:%02d:%02d.%03d"), crash.time.wHour, crash.time.wMinute, crash.time.wSecond, crash.time.wMilliseconds ); szTime[_tsizeof(szTime) - 1] = 0; lprintf( MSG_APP_EXEP_WHEN, szDate, szTime ); _stprintf( dbuf, _T("%08lx"), Exception->ExceptionRecord.ExceptionCode ); lprintf( MSG_EXCEPTION_NUMBER, dbuf ); lprintfs( _T("(%s)\r\n\r\n"), GetExceptionText(Exception->ExceptionRecord.ExceptionCode) ); LogSystemInformation( dp ); LogTaskList( dp ); LogModuleList( dp ); LogAllThreadInformation(dp, &crash); if (dp->options.fDumpSymbols) { LogSymbols( dp ); } ElSaveCrash( &crash, dp->options.dwMaxCrashes ); dp->ExitStatus = Exception->ExceptionRecord.ExceptionCode; return; } // Valid range: [1-7] #define NUM_DIGITS_FNAME 2 void CalcNextFileName( IN PTSTR pszUserName, OUT PSTR pszFileName, IN OUT PINT pnCurrentValue, IN BOOL bUseLongFileNames ) { TCHAR szDrive[_MAX_DRIVE]; TCHAR szPath[_MAX_PATH]; TCHAR szFName[_MAX_FNAME]; TCHAR szExt[_MAX_EXT]; int nLargestPossibleNum; int nCnt; Assert(pszUserName); Assert(pnCurrentValue); Assert(1 <= NUM_DIGITS_FNAME); Assert(NUM_DIGITS_FNAME <= 7); // Given the number of digits, this is the largest number +1 // we can have. If we could raise int to an int, this would // be easy. // nLargestPossibleNum = 10^NUM_DIGITS_FNAME // We are intentionally over by one, the actual range is // [0, 10^NUM_DIGITS_FNAME-1] nLargestPossibleNum = 1; for (nCnt = 0; nCntoptions.szCrashDump ); if (!p) { return FALSE; } if (dp->options.fUseSequentialNaming) { // Figure out what the next file name should be. CalcNextFileName(p, FileName, &dp->options.nNextDumpSequence, dp->options.fUseLongFileNames ); // Save the next value of nCurrent RegSave(&dp->options); } else { #ifdef UNICODE if (WideCharToMultiByte(CP_ACP, 0, p, -1, FileName, _tsizeof(FileName), NULL, NULL) == 0) { return FALSE; } #else lstrcpyn(FileName, p, _tsizeof(FileName)); #endif } switch (dp->options.dwType) { case FullDump: Qual = DEBUG_USER_WINDOWS_DUMP; Comment = NULL; break; case FullMiniDump: Format = DEBUG_FORMAT_USER_SMALL_FULL_MEMORY | DEBUG_FORMAT_USER_SMALL_HANDLE_DATA; // Fall through. case MiniDump: Qual = DEBUG_USER_WINDOWS_SMALL_DUMP; break; default: return FALSE; } Status = dp->DbgClient->WriteDumpFile2(FileName, Qual, Format, Comment); free( p ); return Status == S_OK; } DWORD DispatchDebugEventThread( PDEBUGPACKET dp ) /*++ Routine Description: This is the entry point for DRWTSN32 Arguments: None. Return Value: None. --*/ { _TCHAR szLogFileName[1024]; _TCHAR buf[1024]; PTSTR p; if (dp->dwPidToDebug == 0) { goto exit; } CreateEngineInterfaces(dp); SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX ); AttachToActiveProcess( dp ); p = ExpandPath(dp->options.szLogPath); if (p) { lstrcpyn( szLogFileName, p, _tsizeof(szLogFileName) ); free( p ); } else { _tcscpy( szLogFileName, dp->options.szLogPath ); } MakeLogFileName( szLogFileName ); OpenLogFile( szLogFileName, dp->options.fAppendToLogFile, dp->options.fVisual ); for (;;) { ULONG Type, Process, Thread; DEBUG_LAST_EVENT_INFO_EXCEPTION LastEx; DWORD dwThreadId; HANDLE hThread; if (dp->DbgControl-> WaitForEvent(DEBUG_WAIT_DEFAULT, 30000) != S_OK || dp->DbgControl-> GetLastEventInformation(&Type, &Process, &Thread, &LastEx, sizeof(LastEx), NULL, NULL, 0, NULL) != S_OK) { break; } switch (Type) { case DEBUG_EVENT_EXCEPTION: if (LastEx.ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT) { // // If there is no event to signal, just use the first // breakpoint as the stop event. // if (dp->hEventToSignal && LastEx.FirstChance) { // // The aedebug event will be signalled AFTER this // thread exits, so that it will disappear before // the dump snapshot is taken. // dp->DbgControl->SetExecutionStatus(DEBUG_STATUS_GO_HANDLED); break; } } if (dp->options.fVisual) { // // this notification is necessary because the shell must know when // the debugee has been attached. if it doesn't know and the user is // allowed to terminate drwatson then the system may intervene with // a popup. // SendMessage( dp->hwnd, WM_ATTACHCOMPLETE, 0, 0 ); _sntprintf( buf, _tsizeof(buf), LoadRcString( IDS_AE_TEXT ), GetExceptionText(LastEx.ExceptionRecord.ExceptionCode), LastEx.ExceptionRecord.ExceptionCode, LastEx.ExceptionRecord.ExceptionAddress ); buf[_tsizeof(buf) - 1] = 0; SendMessage( dp->hwnd, WM_EXCEPTIONINFO, 0, (LPARAM) buf ); } PostMortemDump( dp, &LastEx ); if (dp->options.fCrash) { CreateDumpFile( dp ); } // // Attempt to terminate the debuggee. Continue // on if this doesn't work as the debuggee should // be killed anyway when Dr. Watson exits. // hThread = CreateThread( NULL, 16000, (LPTHREAD_START_ROUTINE)TerminationThread, dp, 0, &dwThreadId ); if (hThread) { WaitForSingleObject( hThread, 30000 ); CloseHandle( hThread ); } dp->DbgControl->SetExecutionStatus(DEBUG_STATUS_GO_NOT_HANDLED); break; case DEBUG_EVENT_EXIT_THREAD: if ( dp->hEventToSignal ) { SetEvent(dp->hEventToSignal); dp->hEventToSignal = 0L; } dp->DbgControl->SetExecutionStatus(DEBUG_STATUS_GO); break; } } exit: CloseLogFile(); if (dp->options.fVisual) { SendMessage( dp->hwnd, WM_DUMPCOMPLETE, 0, 0 ); } return 0; } DWORD TerminationThread( PDEBUGPACKET dp ) { HANDLE hProcess; hProcess = OpenProcess( PROCESS_TERMINATE, FALSE, dp->dwPidToDebug ); if (hProcess != NULL) { TerminateProcess( hProcess, dp->ExitStatus ); CloseHandle( hProcess ); } return 0; }