Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12 kB · 388 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389/* * PROJECT: Dr. Watson crash reporter * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Entrypoint / main print function * COPYRIGHT: Copyright 2017 Mark Jansen <mark.jansen@reactos.org> */
#include "precomp.h"#include <winuser.h>#include <algorithm>#include <shlobj.h>#include <shlwapi.h>#include <tchar.h>#include <strsafe.h>#include <tlhelp32.h>#include <dbghelp.h>#include <conio.h>#include <atlbase.h>#include <atlstr.h>#include "resource.h"
static const char szUsage[] = "Usage: DrWtsn32 [-i] [-g] [-p dddd] [-e dddd] [-?]\n" " -i: Install DrWtsn32 as the postmortem debugger\n" " -g: Ignored, Provided for compatibility with WinDbg and CDB.\n" " -p dddd: Attach to process dddd.\n" " -e dddd: Signal the event dddd.\n" " -?: This help.\n";
extern "C"NTSYSAPI ULONG NTAPI vDbgPrintEx(_In_ ULONG ComponentId, _In_ ULONG Level, _In_z_ PCCH Format, _In_ va_list ap);#define DPFLTR_ERROR_LEVEL 0
void xfprintf(FILE* stream, const char *fmt, ...){ va_list ap;
va_start(ap, fmt); vfprintf(stream, fmt, ap); vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL, fmt, ap); va_end(ap);}
static bool SortModules(const ModuleData& left, const ModuleData& right){ return left.BaseAddress < right.BaseAddress;}
static void PrintThread(FILE* output, DumpData& data, DWORD tid, ThreadData& thread){ thread.Update();
xfprintf(output, NEWLINE "State Dump for Thread Id 0x%x%s" NEWLINE NEWLINE, tid, (tid == data.ThreadID) ? " (CRASH)" : "");
const CONTEXT& ctx = thread.Context; if ((ctx.ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {#if defined(_M_IX86) xfprintf(output, "eax:%p ebx:%p ecx:%p edx:%p esi:%p edi:%p" NEWLINE, ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx, ctx.Esi, ctx.Edi);#elif defined(_M_AMD64) xfprintf(output, "rax:%p rbx:%p rcx:%p rdx:%p rsi:%p rdi:%p rbp:%p rsp:%p" NEWLINE, ctx.Rax, ctx.Rbx, ctx.Rcx, ctx.Rdx, ctx.Rsi, ctx.Rdi, ctx.Rbp, ctx.Rsp); xfprintf(output, "r8:%p r9:%p r10:%p r11:%p r12:%p r13:%p r14:%p r15:%p" NEWLINE, ctx.R8, ctx.R9, ctx.R10, ctx.R11, ctx.R12, ctx.R13, ctx.R14, ctx.R15);#elif defined(_M_ARM) xfprintf(output, "r0:%p r1:%p r2:%p r3:%p r4:%p r5:%p r6:%p" NEWLINE, ctx.R0, ctx.R1, ctx.R2, ctx.R3, ctx.R4, ctx.R5, ctx.R6); xfprintf(output, "r7:%p r8:%p r9:%p r10:%p r11:%p r12:%p" NEWLINE, ctx.R7, ctx.R8, ctx.R9, ctx.R10, ctx.R11, ctx.R12);#else#error Unknown architecture#endif }
if ((ctx.ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {#if defined(_M_IX86) xfprintf(output, "eip:%p esp:%p ebp:%p" NEWLINE, ctx.Eip, ctx.Esp, ctx.Ebp);#elif defined(_M_AMD64) xfprintf(output, "rip:%p rsp:%p rbp:%p" NEWLINE, ctx.Rip, ctx.Rsp, ctx.Rbp);#elif defined(_M_ARM) xfprintf(output, "sp:%p lr:%p pc:%p cpsr:%p" NEWLINE, ctx.Sp, ctx.Lr, ctx.Pc, ctx.Cpsr);#else#error Unknown architecture#endif }
if ((ctx.ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS) {#if defined(_M_IX86) || defined(_M_AMD64) xfprintf(output, "dr0:%p dr1:%p dr2:%p dr3:%p dr6:%p dr7:%p" NEWLINE, ctx.Dr0, ctx.Dr1, ctx.Dr2, ctx.Dr3, ctx.Dr6, ctx.Dr7);#elif defined(_M_ARM) for (int n = 0; n < ARM_MAX_BREAKPOINTS; ++n) xfprintf(output, "Bvr%d:%p%s", n, ctx.Bvr[n], ((n + 1) == ARM_MAX_BREAKPOINTS) ? NEWLINE : " "); for (int n = 0; n < ARM_MAX_BREAKPOINTS; ++n) xfprintf(output, "Bcr%d:%p%s", n, ctx.Bcr[n], ((n + 1) == ARM_MAX_BREAKPOINTS) ? NEWLINE : " ");
for (int n = 0; n < ARM_MAX_WATCHPOINTS; ++n) xfprintf(output, "Wvr%d:%p%s", n, ctx.Wvr[n], ((n + 1) == ARM_MAX_WATCHPOINTS) ? NEWLINE : " "); for (int n = 0; n < ARM_MAX_WATCHPOINTS; ++n) xfprintf(output, "Wcr%d:%p%s", n, ctx.Wcr[n], ((n + 1) == ARM_MAX_WATCHPOINTS) ? NEWLINE : " ");#else#error Unknown architecture#endif }
PrintStackBacktrace(output, data, thread);}
void PrintBugreport(FILE* output, DumpData& data){ PrintSystemInfo(output, data); xfprintf(output, NEWLINE "*----> Task List <----*" NEWLINE NEWLINE); HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnap != INVALID_HANDLE_VALUE) { PROCESSENTRY32 pe; pe.dwSize = sizeof(pe); if (Process32First(hSnap, &pe)) { do { xfprintf(output, "%5d: %ls" NEWLINE, pe.th32ProcessID, pe.szExeFile); } while (Process32Next(hSnap, &pe)); } CloseHandle(hSnap); }
xfprintf(output, NEWLINE "*----> Module List <----*" NEWLINE NEWLINE); std::sort(data.Modules.begin(), data.Modules.end(), SortModules);
ModuleData mainModule(NULL); mainModule.Update(data.ProcessHandle); xfprintf(output, "(%p - %p) %ls" NEWLINE, mainModule.BaseAddress, (PBYTE)mainModule.BaseAddress + mainModule.Size, data.ProcessPath.c_str());
for (size_t n = 0; n < data.Modules.size(); ++n) { ModuleData& mod = data.Modules[n]; if (!mod.Unloaded) { mod.Update(data.ProcessHandle); xfprintf(output, "(%p - %p) %s" NEWLINE, mod.BaseAddress, (PBYTE)mod.BaseAddress + mod.Size, mod.ModuleName.c_str()); } }
BeginStackBacktrace(data);
// First print the thread that crashed ThreadMap::iterator crash = data.Threads.find(data.ThreadID); if (crash != data.Threads.end()) PrintThread(output, data, crash->first, crash->second);
// Print the other threads for (ThreadMap::iterator it = data.Threads.begin(); it != data.Threads.end(); ++it) { if (it->first != data.ThreadID) PrintThread(output, data, it->first, it->second); } EndStackBacktrace(data);}
int abort(FILE* output, int err){ if (output != stdout) fclose(output); else _getch();
return err;}
std::wstring Settings_GetOutputPath(void){ WCHAR Buffer[MAX_PATH] = L""; ULONG BufferSize = _countof(Buffer); BOOL UseDefaultPath = TRUE;
CRegKey key; if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\ReactOS\\Crash Reporter", KEY_READ) == ERROR_SUCCESS && key.QueryStringValue(L"Dump Directory", Buffer, &BufferSize) == ERROR_SUCCESS) { UseDefaultPath = FALSE; }
if (UseDefaultPath) { if (FAILED(SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, Buffer))) { return std::wstring(); } }
return std::wstring(Buffer);}
BOOL Settings_GetShouldWriteDump(void){ CRegKey key; if (key.Open(HKEY_CURRENT_USER, L"SOFTWARE\\ReactOS\\Crash Reporter", KEY_READ) != ERROR_SUCCESS) { return FALSE; }
DWORD Value; if (key.QueryDWORDValue(L"Minidump", Value) != ERROR_SUCCESS) { return FALSE; }
return (Value != 0);}
HRESULT WriteMinidump(LPCWSTR LogFilePath, DumpData& data){ HRESULT hr = S_OK;
WCHAR DumpFilePath[MAX_PATH] = L""; StringCchCopyW(DumpFilePath, _countof(DumpFilePath), LogFilePath); PathRemoveExtensionW(DumpFilePath); PathAddExtensionW(DumpFilePath, L".dmp");
HANDLE hDumpFile = CreateFileW(DumpFilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hDumpFile == INVALID_HANDLE_VALUE) { return HRESULT_FROM_WIN32(GetLastError()); }
ThreadData& Thread = data.Threads[data.ThreadID]; Thread.Update(); PCONTEXT ContextPointer = &Thread.Context;
MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo = {0}; EXCEPTION_POINTERS ExceptionPointers = {0}; ExceptionPointers.ExceptionRecord = &data.ExceptionInfo.ExceptionRecord; ExceptionPointers.ContextRecord = ContextPointer;
DumpExceptionInfo.ThreadId = data.ThreadID; DumpExceptionInfo.ExceptionPointers = &ExceptionPointers; DumpExceptionInfo.ClientPointers = FALSE;
BOOL DumpSucceeded = MiniDumpWriteDump(data.ProcessHandle, data.ProcessID, hDumpFile, MiniDumpNormal, &DumpExceptionInfo, NULL, NULL); if (!DumpSucceeded) { // According to MSDN, this value is already an HRESULT, so don't convert it again. hr = GetLastError(); }
CloseHandle(hDumpFile); return hr;}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR cmdLine, INT){ int argc; WCHAR **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
DWORD pid = 0; WCHAR Filename[50]; FILE* output = NULL; SYSTEMTIME st; DumpData data;
for (int n = 0; n < argc; ++n) { WCHAR* arg = argv[n];
if (!wcscmp(arg, L"-i")) { /* FIXME: Installs as the postmortem debugger. */ } else if (!wcscmp(arg, L"-g")) { } else if (!wcscmp(arg, L"-p")) { if (n + 1 < argc) { pid = wcstoul(argv[n+1], NULL, 10); n++; } } else if (!wcscmp(arg, L"-e")) { if (n + 1 < argc) { data.Event = (HANDLE)(ULONG_PTR)_wcstoui64(argv[n+1], NULL, 10); n++; } } else if (!wcscmp(arg, L"-?")) { MessageBoxA(NULL, szUsage, "ReactOS Crash Reporter", MB_OK); return abort(output, 0); } else if (!wcscmp(arg, L"/?")) { xfprintf(stdout, "%s\n", szUsage); return abort(stdout, 0); } }
if (!pid) { MessageBoxA(NULL, szUsage, "ReactOS Crash Reporter", MB_OK); return abort(stdout, 0); }
GetLocalTime(&st);
std::wstring OutputPath = Settings_GetOutputPath(); BOOL HasPath = (OutputPath.size() != 0);
if (!PathIsDirectoryW(OutputPath.c_str())) { int res = SHCreateDirectoryExW(NULL, OutputPath.c_str(), NULL); if (res != ERROR_SUCCESS && res != ERROR_ALREADY_EXISTS) { xfprintf(stdout, "Could not create output directory, not writing dump\n"); MessageBoxA(NULL, "Could not create directory to write crash report.", "ReactOS Crash Reporter", MB_ICONERROR | MB_OK); return abort(stdout, 0); } }
if (HasPath && SUCCEEDED(StringCchPrintfW(Filename, _countof(Filename), L"Appcrash_%d-%02d-%02d_%02d-%02d-%02d.txt", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond))) { OutputPath += L"\\"; OutputPath += Filename; output = _wfopen(OutputPath.c_str(), L"wb"); } if (!output) output = stdout;
if (!DebugActiveProcess(pid)) return abort(output, -2);
/* We should not kill it? */ DebugSetProcessKillOnExit(FALSE);
DEBUG_EVENT evt; if (!WaitForDebugEvent(&evt, 30000)) return abort(output, -3);
assert(evt.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT);
while (UpdateFromEvent(evt, data)) { ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, DBG_CONTINUE);
if (!WaitForDebugEvent(&evt, 30000)) return abort(output, -4); }
PrintBugreport(output, data); if (Settings_GetShouldWriteDump() && HasPath) { WriteMinidump(OutputPath.c_str(), data); }
TerminateProcess(data.ProcessHandle, data.ExceptionInfo.ExceptionRecord.ExceptionCode);
CStringW FormattedMessage; FormattedMessage.Format(IDS_USER_ALERT_MESSAGE, data.ProcessName.c_str(), OutputPath.c_str()); CStringW DialogTitle; DialogTitle.LoadString(hInstance, IDS_APP_TITLE);
MessageBoxW(NULL, FormattedMessage.GetString(), DialogTitle.GetString(), MB_OK);
return abort(output, 0);}