reactos/base/applications/drwtsn32/main.cpp
Mark Jansen 280d7a9775
[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
2018-01-06 11:47:54 +01:00

223 lines
6.4 KiB
C++

/*
* 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);
}