https://git.reactos.org/?p=reactos.git;a=commitdiff;h=280d7a97758b28d078124f...
commit 280d7a97758b28d078124f17eb61b5c9d4a30d2d Author: Mark Jansen mark.jansen@reactos.org AuthorDate: Sat Jan 6 11:47:54 2018 +0100
[DRWTSN32] Implement basic crash report functionality
On application crash, drwtsn32 will attach to the application and try to get a dump, consisting of: - List of loaded modules - List of loaded threads - Per thread, a stacktrace - Per thread, a small hexdump from the stack - Per thread, a dump of the most common registers
This dump is saved to the desktop, and the user is notified of the dump being dropped there.
CORE-14180 #145 --- base/applications/CMakeLists.txt | 1 + base/applications/drwtsn32/CMakeLists.txt | 18 +++ base/applications/drwtsn32/drwtsn32.cpp | 158 +++++++++++++++++++++ base/applications/drwtsn32/drwtsn32.h | 68 +++++++++ base/applications/drwtsn32/main.cpp | 222 ++++++++++++++++++++++++++++++ base/applications/drwtsn32/precomp.h | 28 ++++ base/applications/drwtsn32/stacktrace.cpp | 139 +++++++++++++++++++ base/applications/drwtsn32/sysinfo.cpp | 115 ++++++++++++++++ 8 files changed, 749 insertions(+)
diff --git a/base/applications/CMakeLists.txt b/base/applications/CMakeLists.txt index d25fa3e039..2af017dff5 100644 --- a/base/applications/CMakeLists.txt +++ b/base/applications/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(charmap) add_subdirectory(clipbrd) add_subdirectory(cmdutils) add_subdirectory(control) +add_subdirectory(drwtsn32) add_subdirectory(dxdiag) add_subdirectory(extrac32) add_subdirectory(findstr) diff --git a/base/applications/drwtsn32/CMakeLists.txt b/base/applications/drwtsn32/CMakeLists.txt new file mode 100644 index 0000000000..3a7e93f736 --- /dev/null +++ b/base/applications/drwtsn32/CMakeLists.txt @@ -0,0 +1,18 @@ + +PROJECT(drwtsn32) + +set_cpp(WITH_RUNTIME WITH_EXCEPTIONS WITH_STL) + +list(APPEND CPP_SOURCE + drwtsn32.cpp + main.cpp + stacktrace.cpp + sysinfo.cpp + drwtsn32.h + precomp.h) + +add_executable(drwtsn32 ${CPP_SOURCE}) +add_pch(drwtsn32 precomp.h CPP_SOURCE) +set_module_type(drwtsn32 win32gui) +add_importlibs(drwtsn32 dbghelp psapi advapi32 shell32 msvcrt user32 kernel32 ntdll) +add_cd_file(TARGET drwtsn32 DESTINATION reactos/system32 FOR all) diff --git a/base/applications/drwtsn32/drwtsn32.cpp b/base/applications/drwtsn32/drwtsn32.cpp new file mode 100644 index 0000000000..b667dd723c --- /dev/null +++ b/base/applications/drwtsn32/drwtsn32.cpp @@ -0,0 +1,158 @@ +/* + * PROJECT: Dr. Watson crash reporter + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Debug loop + * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" +#include <psapi.h> + +#define MS_VC_EXCEPTION_THREAD_NAME 0x406d1388 + +ModuleData::ModuleData(void* addr) +{ + BaseAddress = addr; + Size = 0; + Unloaded = false; +} + +void ModuleData::Update(HANDLE hProcess) +{ + MODULEINFO mi = {0}; + GetModuleInformation(hProcess, (HMODULE)BaseAddress, &mi, sizeof(mi)); + assert(BaseAddress == mi.lpBaseOfDll); + Size = mi.SizeOfImage; + + ModuleName.resize(MAX_PATH); + DWORD dwLen = GetModuleFileNameExA(hProcess, (HMODULE)BaseAddress, &ModuleName[0], ModuleName.size()); + ModuleName.resize(dwLen); +} + + +ThreadData::ThreadData(HANDLE handle) + : Handle(handle) +{ + memset(&Context, 0, sizeof(Context)); +} + +void ThreadData::Update() +{ + Context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS; + GetThreadContext(Handle, &Context); +} + +DumpData::DumpData() + :ProcessID(0) + ,ThreadID(0) + ,ProcessHandle(NULL) + ,Event(NULL) + ,FirstBPHit(false) +{ + memset(&ExceptionInfo, 0, sizeof(ExceptionInfo)); +} + + +bool UpdateFromEvent(DEBUG_EVENT& evt, DumpData& data) +{ + switch(evt.dwDebugEventCode) + { + case CREATE_PROCESS_DEBUG_EVENT: + { + data.ProcessPath.resize(MAX_PATH); + DWORD len = GetModuleFileNameExA(evt.u.CreateProcessInfo.hProcess, NULL, &data.ProcessPath[0], data.ProcessPath.size()); + if (len) + { + data.ProcessPath.resize(len); + std::string::size_type pos = data.ProcessPath.find_last_of("\/"); + if (pos != std::string::npos) + data.ProcessName = data.ProcessPath.substr(pos+1); + } + else + { + data.ProcessPath = "??"; + } + if (data.ProcessName.empty()) + data.ProcessName = data.ProcessPath; + + CloseHandle(evt.u.CreateProcessInfo.hFile); + data.ProcessID = evt.dwProcessId; + data.ProcessHandle = evt.u.CreateProcessInfo.hProcess; + data.Threads[evt.dwThreadId] = ThreadData(evt.u.CreateProcessInfo.hThread); + } + break; + case CREATE_THREAD_DEBUG_EVENT: + data.Threads[evt.dwThreadId] = ThreadData(evt.u.CreateThread.hThread); + break; + case EXIT_THREAD_DEBUG_EVENT: + { + ThreadMap::iterator it = data.Threads.find(evt.dwThreadId); + if (it != data.Threads.end()) + { + data.Threads.erase(it); + } + } + break; + case LOAD_DLL_DEBUG_EVENT: + CloseHandle(evt.u.LoadDll.hFile); + for (size_t n = 0; n < data.Modules.size(); ++n) + { + if (data.Modules[n].BaseAddress == evt.u.LoadDll.lpBaseOfDll) + { + data.Modules[n].Unloaded = false; + return true; + } + } + data.Modules.push_back(ModuleData(evt.u.LoadDll.lpBaseOfDll)); + break; + case UNLOAD_DLL_DEBUG_EVENT: + for (size_t n = 0; n < data.Modules.size(); ++n) + { + if (data.Modules[n].BaseAddress == evt.u.UnloadDll.lpBaseOfDll) + data.Modules[n].Unloaded = true; + } + break; + case OUTPUT_DEBUG_STRING_EVENT: // ignore + break; + case EXCEPTION_DEBUG_EVENT: + if (evt.u.Exception.dwFirstChance) + { + switch(evt.u.Exception.ExceptionRecord.ExceptionCode) + { + case EXCEPTION_BREAKPOINT: + if (!data.FirstBPHit) + { + data.FirstBPHit = true; + + if (data.Event) + { + SetEvent(data.Event); + CloseHandle(data.Event); + data.Event = NULL; + } + return true; + } + break; + case MS_VC_EXCEPTION_THREAD_NAME: + /* Thread name */ + return true; + case DBG_CONTROL_C: + case DBG_CONTROL_BREAK: + return true; + } + } + data.ExceptionInfo = evt.u.Exception; + data.ThreadID = evt.dwThreadId; + return false; + case EXIT_PROCESS_DEBUG_EVENT: + //assert(FALSE); + return false; + case RIP_EVENT: + //assert(FALSE); + return false; + default: + assert(false); + } + return true; +} + diff --git a/base/applications/drwtsn32/drwtsn32.h b/base/applications/drwtsn32/drwtsn32.h new file mode 100644 index 0000000000..4d161ffe2a --- /dev/null +++ b/base/applications/drwtsn32/drwtsn32.h @@ -0,0 +1,68 @@ +/* + * PROJECT: Dr. Watson crash reporter + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Project header + * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) + */ + +#pragma once + + +struct ModuleData +{ + std::string ModuleName; + void *BaseAddress; + DWORD Size; + bool Unloaded; + + + ModuleData(void* addr); + void Update(HANDLE hProcess); +}; + +struct ThreadData +{ + HANDLE Handle; + CONTEXT Context; + + ThreadData(HANDLE handle = NULL); + + void Update(); +}; + +typedef std::vector<ModuleData> ModuleList; +typedef std::map<DWORD, ThreadData> ThreadMap; + +class DumpData +{ +public: + std::string ProcessPath; + std::string ProcessName; + DWORD ProcessID; + DWORD ThreadID; + HANDLE ProcessHandle; + ModuleList Modules; + ThreadMap Threads; + EXCEPTION_DEBUG_INFO ExceptionInfo; + HANDLE Event; + bool FirstBPHit; + + DumpData(); +}; + +#define NEWLINE "\r\n" + +/* main.cpp */ +void xfprintf(FILE* stream, const char *fmt, ...); + +/* drwtsn32.cpp */ +bool UpdateFromEvent(DEBUG_EVENT& evt, DumpData& data); + +/* sysinfo.cpp */ +void PrintSystemInfo(FILE* output, DumpData& data); + +/* stacktrace.cpp */ +void BeginStackBacktrace(DumpData& data); +void PrintStackBacktrace(FILE* output, DumpData& data, ThreadData& thread); +void EndStackBacktrace(DumpData& data); + diff --git a/base/applications/drwtsn32/main.cpp b/base/applications/drwtsn32/main.cpp new file mode 100644 index 0000000000..2ac1e057e1 --- /dev/null +++ b/base/applications/drwtsn32/main.cpp @@ -0,0 +1,222 @@ +/* + * 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 <strsafe.h> +#include <tlhelp32.h> +#include <conio.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); +} + + + +static bool SortModules(const ModuleData& left, const ModuleData& right) +{ + return left.BaseAddress < right.BaseAddress; +} + + +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: %s" 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) %s" 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); + for (ThreadMap::iterator it = data.Threads.begin(); it != data.Threads.end(); ++it) + { + it->second.Update(); + + xfprintf(output, NEWLINE "State Dump for Thread Id 0x%x" NEWLINE NEWLINE, it->first); + const CONTEXT& ctx = it->second.Context; + if (ctx.ContextFlags & CONTEXT_INTEGER) + 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); + if (ctx.ContextFlags & CONTEXT_CONTROL) + xfprintf(output, "eip:%p esp:%p ebp:%p" NEWLINE, + ctx.Eip, ctx.Esp, ctx.Ebp); + if (ctx.ContextFlags & CONTEXT_DEBUG_REGISTERS) + 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); + + PrintStackBacktrace(output, data, it->second); + } + EndStackBacktrace(data); +} + + +int abort(FILE* output, int err) +{ + if (output != stdout) + fclose(output); + else + _getch(); + + return err; +} + +int main(int argc, char* argv[]) +{ + DWORD pid = 0; + char Buffer[MAX_PATH+55]; + char Filename[50]; + FILE* output = NULL; + SYSTEMTIME st; + DumpData data; + + + for (int n = 0; n < argc; ++n) + { + char* arg = argv[n]; + + if (!strcmp(arg, "-i")) + { + /* FIXME: Installs as the postmortem debugger. */ + } + else if (!strcmp(arg, "-g")) + { + } + else if (!strcmp(arg, "-p")) + { + if (n + 1 < argc) + { + pid = strtoul(argv[n+1], NULL, 10); + n++; + } + } + else if (!strcmp(arg, "-e")) + { + if (n + 1 < argc) + { + data.Event = (HANDLE)strtoul(argv[n+1], NULL, 10); + n++; + } + } + else if (!strcmp(arg, "-?")) + { + MessageBoxA(NULL, szUsage, "DrWtsn32", MB_OK); + return abort(output, 0); + } + else if (!strcmp(arg, "/?")) + { + xfprintf(stdout, "%s\n", szUsage); + return abort(stdout, 0); + } + } + + if (!pid) + { + MessageBoxA(NULL, szUsage, "DrWtsn32", MB_OK); + return abort(stdout, 0); + } + + GetLocalTime(&st); + + if (SHGetFolderPathA(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, Buffer) == S_OK && + SUCCEEDED(StringCchPrintfA(Filename, _countof(Filename), "Appcrash_%d-%02d-%02d_%02d-%02d-%02d.txt", + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond))) + { + StringCchCatA(Buffer, _countof(Buffer), "\"); + StringCchCatA(Buffer, _countof(Buffer), Filename); + output = fopen(Buffer, "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); + + TerminateProcess(data.ProcessHandle, data.ExceptionInfo.ExceptionRecord.ExceptionCode); + + std::string Message = "The application '"; + Message += data.ProcessName; + Message += "' has just crashed :(\n"; + Message += "Information about this crash is saved to:\n"; + Message += Filename; + Message += "\nThis file is stored on your desktop."; + MessageBoxA(NULL, Message.c_str(), "Sorry!", MB_OK); + + return abort(output, 0); +} diff --git a/base/applications/drwtsn32/precomp.h b/base/applications/drwtsn32/precomp.h new file mode 100644 index 0000000000..8e456c79a7 --- /dev/null +++ b/base/applications/drwtsn32/precomp.h @@ -0,0 +1,28 @@ +/* + * PROJECT: Dr. Watson crash reporter + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Precompiled Header + * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) + */ + +#ifndef _DRWTSN32_PRECOMP_H_ +#define _DRWTSN32_PRECOMP_H_ + +#include <ntstatus.h> +#define WIN32_NO_STATUS + +#include <windef.h> +#include <winbase.h> +#include <winver.h> + +#include <string> +#include <vector> +#include <map> +#include <stdio.h> +#include <assert.h> + +#include "drwtsn32.h" + +typedef LONG NTSTATUS; + +#endif // _DRWTSN32_PRECOMP_H_ diff --git a/base/applications/drwtsn32/stacktrace.cpp b/base/applications/drwtsn32/stacktrace.cpp new file mode 100644 index 0000000000..a867150ca4 --- /dev/null +++ b/base/applications/drwtsn32/stacktrace.cpp @@ -0,0 +1,139 @@ +/* + * PROJECT: Dr. Watson crash reporter + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Print a stacktrace + * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" +#include <dbghelp.h> + + +void BeginStackBacktrace(DumpData& data) +{ + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_UNDNAME | SYMOPT_AUTO_PUBLICS | SYMOPT_DEFERRED_LOADS; + SymSetOptions(symOptions); + SymInitialize(data.ProcessHandle, NULL, TRUE); +} + +void EndStackBacktrace(DumpData& data) +{ + SymCleanup(data.ProcessHandle); +} + +static char ToChar(UCHAR data) +{ + if (data < 0xa) + return '0' + data; + else if (data <= 0xf) + return 'a' + data - 0xa; + return '?'; +} + +void PrintStackBacktrace(FILE* output, DumpData& data, ThreadData& thread) +{ + DWORD MachineType; + STACKFRAME64 StackFrame = { { 0 } }; + +#ifdef _M_X64 + MachineType = IMAGE_FILE_MACHINE_AMD64; + StackFrame.AddrPC.Offset = thread.Context.Rip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = thread.Context.Rsp; + StackFrame.AddrStack.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = thread.Context.Rbp; + StackFrame.AddrFrame.Mode = AddrModeFlat; +#else + MachineType = IMAGE_FILE_MACHINE_I386; + StackFrame.AddrPC.Offset = thread.Context.Eip; + StackFrame.AddrPC.Mode = AddrModeFlat; + StackFrame.AddrStack.Offset = thread.Context.Esp; + StackFrame.AddrStack.Mode = AddrModeFlat; + StackFrame.AddrFrame.Offset = thread.Context.Ebp; + StackFrame.AddrFrame.Mode = AddrModeFlat; +#endif + + +#define STACKWALK_MAX_NAMELEN 512 + char buf[sizeof(SYMBOL_INFO) + STACKWALK_MAX_NAMELEN] = {0}; + SYMBOL_INFO* sym = (SYMBOL_INFO *)buf; + IMAGEHLP_MODULE64 Module = { 0 }; + sym->SizeOfStruct = sizeof(sym); + + /* FIXME: Disasm function! */ + + xfprintf(output, NEWLINE "*----> Stack Back Trace <----*" NEWLINE NEWLINE); + bool first = true; + ULONG_PTR LastFrame = StackFrame.AddrFrame.Offset - 8; + while(StackWalk64(MachineType, data.ProcessHandle, thread.Handle, &StackFrame, &thread.Context, + NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + { + if (!StackFrame.AddrPC.Offset) + break; + + if (LastFrame >= StackFrame.AddrFrame.Offset) + break; + + LastFrame = StackFrame.AddrFrame.Offset; + + if (first) + { + xfprintf(output, "FramePtr ReturnAd Param#1 Param#2 Param#3 Param#4 Function Name" NEWLINE); + first = false; + } + + Module.SizeOfStruct = sizeof(Module); + DWORD64 ModBase = SymGetModuleBase64(data.ProcessHandle, StackFrame.AddrPC.Offset); + if (!SymGetModuleInfo64(data.ProcessHandle, ModBase, &Module)) + strcpy(Module.ModuleName, "<nomod>"); + + memset(sym, '\0', sizeof(*sym) + STACKWALK_MAX_NAMELEN); + sym->SizeOfStruct = sizeof(*sym); + sym->MaxNameLen = STACKWALK_MAX_NAMELEN; + DWORD64 displacement; + + if (!SymFromAddr(data.ProcessHandle, StackFrame.AddrPC.Offset, &displacement, sym)) + strcpy(sym->Name, "<nosymbols>"); + + xfprintf(output, "%p %p %p %p %p %p %s!%s" NEWLINE, + (ULONG_PTR)StackFrame.AddrFrame.Offset, (ULONG_PTR)StackFrame.AddrPC.Offset, + (ULONG_PTR)StackFrame.Params[0], (ULONG_PTR)StackFrame.Params[1], + (ULONG_PTR)StackFrame.Params[2], (ULONG_PTR)StackFrame.Params[3], + Module.ModuleName, sym->Name); + } + + UCHAR stackData[0x10 * 10]; + DWORD dwSizeRead; + if (!ReadProcessMemory(data.ProcessHandle, (LPCVOID)thread.Context.Esp, stackData, sizeof(stackData), &dwSizeRead)) + return; + + xfprintf(output, NEWLINE "*----> Raw Stack Dump <----*" NEWLINE NEWLINE); + for (size_t n = 0; n < sizeof(stackData); n += 0x10) + { + char HexData1[] = "?? ?? ?? ?? ?? ?? ?? ??"; + char HexData2[] = "?? ?? ?? ?? ?? ?? ?? ??"; + char AsciiData1[] = "????????"; + char AsciiData2[] = "????????"; + + for (size_t j = 0; j < 8; ++j) + { + size_t idx = j + n; + if (idx < dwSizeRead) + { + HexData1[j * 3] = ToChar(stackData[idx] >> 4); + HexData1[j * 3 + 1] = ToChar(stackData[idx] & 0xf); + AsciiData1[j] = isprint(stackData[idx]) ? stackData[idx] : '.'; + } + idx += 8; + if (idx < dwSizeRead) + { + HexData2[j * 3] = ToChar(stackData[idx] >> 4); + HexData2[j * 3 + 1] = ToChar(stackData[idx] & 0xf); + AsciiData2[j] = isprint(stackData[idx]) ? stackData[idx] : '.'; + } + } + + xfprintf(output, "%p %s - %s %s%s" NEWLINE, thread.Context.Esp+n, HexData1, HexData2, AsciiData1, AsciiData2); + } +} diff --git a/base/applications/drwtsn32/sysinfo.cpp b/base/applications/drwtsn32/sysinfo.cpp new file mode 100644 index 0000000000..7cad0cab5a --- /dev/null +++ b/base/applications/drwtsn32/sysinfo.cpp @@ -0,0 +1,115 @@ +/* + * PROJECT: Dr. Watson crash reporter + * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) + * PURPOSE: Output system info + * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" +#include <winreg.h> +#include <reactos/buildno.h> + +static const char* Exception2Str(DWORD code) +{ + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_DATATYPE_MISALIGNMENT: return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_BREAKPOINT: return "EXCEPTION_BREAKPOINT"; + case EXCEPTION_SINGLE_STEP: return "EXCEPTION_SINGLE_STEP"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_FLT_DENORMAL_OPERAND: return "EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: return "EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: return "EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: return "EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: return "EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: return "EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: return "EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_PRIV_INSTRUCTION: return "EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: return "EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_STACK_OVERFLOW: return "EXCEPTION_STACK_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: return "EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_GUARD_PAGE: return "EXCEPTION_GUARD_PAGE"; + case EXCEPTION_INVALID_HANDLE: return "EXCEPTION_INVALID_HANDLE"; + } + + return "--"; +} + +static void ReadKey(HKEY hKey, const char* ValueName, char* Buffer, DWORD size) +{ + DWORD dwType; + LSTATUS ret = RegQueryValueExA(hKey, ValueName, NULL, &dwType, (LPBYTE)Buffer, &size); + if (ret != ERROR_SUCCESS || dwType != REG_SZ) + Buffer[0] = '\0'; +} + +void PrintSystemInfo(FILE* output, DumpData& data) +{ + SYSTEMTIME LocalTime; + GetLocalTime(&LocalTime); + xfprintf(output, NEWLINE "ReactOS " KERNEL_VERSION_STR " DrWtsn32" NEWLINE NEWLINE); + xfprintf(output, "Application exception occurred:" NEWLINE); + xfprintf(output, " App: %s (pid=%d, tid=0x%x)" NEWLINE, data.ProcessName.c_str(), data.ProcessID, data.ThreadID); + xfprintf(output, " When: %d/%d/%d @ %02d:%02d:%02d.%d" NEWLINE, + LocalTime.wDay, LocalTime.wMonth, LocalTime.wYear, + LocalTime.wHour, LocalTime.wMinute, LocalTime.wSecond, LocalTime.wMilliseconds); + DWORD ExceptionCode = data.ExceptionInfo.ExceptionRecord.ExceptionCode; + xfprintf(output, " Exception number: 0x%8x (%s)" NEWLINE, ExceptionCode, Exception2Str(ExceptionCode)); + + char Buffer[MAX_PATH]; + DWORD count = sizeof(Buffer); + xfprintf(output, NEWLINE "*----> System Information <----*" NEWLINE NEWLINE); + if (GetComputerNameA(Buffer, &count)) + xfprintf(output, " Computer Name: %s" NEWLINE, Buffer); + count = sizeof(Buffer); + if (GetUserNameA(Buffer, &count)) + xfprintf(output, " User Name: %s" NEWLINE, Buffer); + + + SYSTEM_INFO info; + GetSystemInfo(&info); + xfprintf(output, " Number of Processors: %d" NEWLINE, info.dwNumberOfProcessors); + + HKEY hKey; + LONG ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\DESCRIPTION\System\CentralProcessor\0", + 0, KEY_READ, &hKey); + if (ret == ERROR_SUCCESS) + { + DWORD dwType; + count = sizeof(Buffer); + ret = RegQueryValueExA(hKey, "Identifier", NULL, &dwType, (LPBYTE)Buffer, &count); + if (ret == ERROR_SUCCESS && dwType == REG_SZ) + { + Buffer[count] = '\0'; + xfprintf(output, " Processor Type: %s" NEWLINE, Buffer); + } + RegCloseKey(hKey); + } + + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion", 0, KEY_READ, &hKey); + if (ret == ERROR_SUCCESS) + { + char Version[50]; + ReadKey(hKey, "ProductName", Buffer, sizeof(Buffer)); + ReadKey(hKey, "CurrentVersion", Version, sizeof(Version)); + xfprintf(output, " %s Version: %s" NEWLINE, Buffer, Version); + ReadKey(hKey, "BuildLab", Buffer, sizeof(Buffer)); + xfprintf(output, " BuildLab: %s" NEWLINE, Buffer); + ReadKey(hKey, "CSDVersion", Buffer, sizeof(Buffer)); + if (Buffer[0]) + xfprintf(output, " Service Pack: %s" NEWLINE, Buffer); + ReadKey(hKey, "CurrentType", Buffer, sizeof(Buffer)); + xfprintf(output, " Current Type: %s" NEWLINE, Buffer); + ReadKey(hKey, "RegisteredOrganization", Buffer, sizeof(Buffer)); + xfprintf(output, " Registered Organization: %s" NEWLINE, Buffer); + ReadKey(hKey, "RegisteredOwner", Buffer, sizeof(Buffer)); + xfprintf(output, " Registered Owner: %s" NEWLINE, Buffer); + + RegCloseKey(hKey); + } +}