mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
[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
This commit is contained in:
parent
59aa6b9e74
commit
280d7a9775
8 changed files with 749 additions and 0 deletions
|
@ -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)
|
||||
|
|
18
base/applications/drwtsn32/CMakeLists.txt
Normal file
18
base/applications/drwtsn32/CMakeLists.txt
Normal file
|
@ -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)
|
158
base/applications/drwtsn32/drwtsn32.cpp
Normal file
158
base/applications/drwtsn32/drwtsn32.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
68
base/applications/drwtsn32/drwtsn32.h
Normal file
68
base/applications/drwtsn32/drwtsn32.h
Normal file
|
@ -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);
|
||||
|
222
base/applications/drwtsn32/main.cpp
Normal file
222
base/applications/drwtsn32/main.cpp
Normal file
|
@ -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);
|
||||
}
|
28
base/applications/drwtsn32/precomp.h
Normal file
28
base/applications/drwtsn32/precomp.h
Normal file
|
@ -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_
|
139
base/applications/drwtsn32/stacktrace.cpp
Normal file
139
base/applications/drwtsn32/stacktrace.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
115
base/applications/drwtsn32/sysinfo.cpp
Normal file
115
base/applications/drwtsn32/sysinfo.cpp
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue