2 * PROJECT: Dr. Watson crash reporter
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Entrypoint / main print function
5 * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
23 static const char szUsage
[] = "Usage: DrWtsn32 [-i] [-g] [-p dddd] [-e dddd] [-?]\n"
24 " -i: Install DrWtsn32 as the postmortem debugger\n"
25 " -g: Ignored, Provided for compatibility with WinDbg and CDB.\n"
26 " -p dddd: Attach to process dddd.\n"
27 " -e dddd: Signal the event dddd.\n"
31 NTSYSAPI ULONG NTAPI
vDbgPrintEx(_In_ ULONG ComponentId
, _In_ ULONG Level
, _In_z_ PCCH Format
, _In_
va_list ap
);
32 #define DPFLTR_ERROR_LEVEL 0
34 void xfprintf(FILE* stream
, const char *fmt
, ...)
39 vfprintf(stream
, fmt
, ap
);
40 vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL
, fmt
, ap
);
46 static bool SortModules(const ModuleData
& left
, const ModuleData
& right
)
48 return left
.BaseAddress
< right
.BaseAddress
;
51 static void PrintThread(FILE* output
, DumpData
& data
, DWORD tid
, ThreadData
& thread
)
55 xfprintf(output
, NEWLINE
"State Dump for Thread Id 0x%x%s" NEWLINE NEWLINE
, tid
,
56 (tid
== data
.ThreadID
) ? " (CRASH)" : "");
58 const CONTEXT
& ctx
= thread
.Context
;
59 if ((ctx
.ContextFlags
& CONTEXT_INTEGER
) == CONTEXT_INTEGER
)
62 xfprintf(output
, "eax:%p ebx:%p ecx:%p edx:%p esi:%p edi:%p" NEWLINE
,
63 ctx
.Eax
, ctx
.Ebx
, ctx
.Ecx
, ctx
.Edx
, ctx
.Esi
, ctx
.Edi
);
64 #elif defined(_M_AMD64)
65 xfprintf(output
, "rax:%p rbx:%p rcx:%p rdx:%p rsi:%p rdi:%p" NEWLINE
,
66 ctx
.Rax
, ctx
.Rbx
, ctx
.Rcx
, ctx
.Rdx
, ctx
.Rsi
, ctx
.Rdi
);
67 xfprintf(output
, "r8:%p r9:%p r10:%p r11:%p r12:%p r13:%p r14:%p r15:%p" NEWLINE
,
68 ctx
.R8
, ctx
.R9
, ctx
.R10
, ctx
.R11
, ctx
.R12
, ctx
.R13
, ctx
.R14
, ctx
.R15
);
70 #error Unknown architecture
74 if ((ctx
.ContextFlags
& CONTEXT_CONTROL
) == CONTEXT_CONTROL
)
77 xfprintf(output
, "eip:%p esp:%p ebp:%p" NEWLINE
,
78 ctx
.Eip
, ctx
.Esp
, ctx
.Ebp
);
79 #elif defined(_M_AMD64)
80 xfprintf(output
, "rip:%p rsp:%p rbp:%p" NEWLINE
,
81 ctx
.Rip
, ctx
.Rsp
, ctx
.Rbp
);
83 #error Unknown architecture
87 if ((ctx
.ContextFlags
& CONTEXT_DEBUG_REGISTERS
) == CONTEXT_DEBUG_REGISTERS
)
89 #if defined(_M_IX86) || defined(_M_AMD64)
90 xfprintf(output
, "dr0:%p dr1:%p dr2:%p dr3:%p dr6:%p dr7:%p" NEWLINE
,
91 ctx
.Dr0
, ctx
.Dr1
, ctx
.Dr2
, ctx
.Dr3
, ctx
.Dr6
, ctx
.Dr7
);
93 #error Unknown architecture
97 PrintStackBacktrace(output
, data
, thread
);
100 void PrintBugreport(FILE* output
, DumpData
& data
)
102 PrintSystemInfo(output
, data
);
103 xfprintf(output
, NEWLINE
"*----> Task List <----*" NEWLINE NEWLINE
);
104 HANDLE hSnap
= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS
, 0);
105 if (hSnap
!= INVALID_HANDLE_VALUE
)
108 pe
.dwSize
= sizeof(pe
);
109 if (Process32First(hSnap
, &pe
))
113 xfprintf(output
, "%5d: %ls" NEWLINE
, pe
.th32ProcessID
, pe
.szExeFile
);
114 } while (Process32Next(hSnap
, &pe
));
119 xfprintf(output
, NEWLINE
"*----> Module List <----*" NEWLINE NEWLINE
);
120 std::sort(data
.Modules
.begin(), data
.Modules
.end(), SortModules
);
122 ModuleData
mainModule(NULL
);
123 mainModule
.Update(data
.ProcessHandle
);
124 xfprintf(output
, "(%p - %p) %ls" NEWLINE
,
125 mainModule
.BaseAddress
,
126 (PBYTE
)mainModule
.BaseAddress
+ mainModule
.Size
,
127 data
.ProcessPath
.c_str());
129 for (size_t n
= 0; n
< data
.Modules
.size(); ++n
)
131 ModuleData
& mod
= data
.Modules
[n
];
134 mod
.Update(data
.ProcessHandle
);
135 xfprintf(output
, "(%p - %p) %s" NEWLINE
,
137 (PBYTE
)mod
.BaseAddress
+ mod
.Size
,
138 mod
.ModuleName
.c_str());
142 BeginStackBacktrace(data
);
144 // First print the thread that crashed
145 ThreadMap::iterator crash
= data
.Threads
.find(data
.ThreadID
);
146 if (crash
!= data
.Threads
.end())
147 PrintThread(output
, data
, crash
->first
, crash
->second
);
149 // Print the other threads
150 for (ThreadMap::iterator it
= data
.Threads
.begin(); it
!= data
.Threads
.end(); ++it
)
152 if (it
->first
!= data
.ThreadID
)
153 PrintThread(output
, data
, it
->first
, it
->second
);
155 EndStackBacktrace(data
);
159 int abort(FILE* output
, int err
)
161 if (output
!= stdout
)
169 std::wstring
Settings_GetOutputPath(void)
171 WCHAR Buffer
[MAX_PATH
] = L
"";
172 ULONG BufferSize
= _countof(Buffer
);
173 BOOL UseDefaultPath
= FALSE
;
176 if (key
.Open(HKEY_CURRENT_USER
, L
"SOFTWARE\\ReactOS\\Crash Reporter", KEY_READ
) != ERROR_SUCCESS
)
178 UseDefaultPath
= TRUE
;
181 if (key
.QueryStringValue(L
"Dump Directory", Buffer
, &BufferSize
) != ERROR_SUCCESS
)
183 UseDefaultPath
= TRUE
;
188 if (FAILED(SHGetFolderPathW(NULL
, CSIDL_DESKTOP
, NULL
, SHGFP_TYPE_CURRENT
, Buffer
)))
190 return std::wstring();
194 return std::wstring(Buffer
);
197 BOOL
Settings_GetShouldWriteDump(void)
200 if (key
.Open(HKEY_CURRENT_USER
, L
"SOFTWARE\\ReactOS\\Crash Reporter", KEY_READ
) != ERROR_SUCCESS
)
206 if (key
.QueryDWORDValue(L
"Minidump", Value
) != ERROR_SUCCESS
)
214 HRESULT
WriteMinidump(LPCWSTR LogFilePath
, DumpData
& data
)
218 WCHAR DumpFilePath
[MAX_PATH
] = L
"";
219 StringCchCopyW(DumpFilePath
, _countof(DumpFilePath
), LogFilePath
);
220 PathRemoveExtensionW(DumpFilePath
);
221 PathAddExtensionW(DumpFilePath
, L
".dmp");
223 HANDLE hDumpFile
= CreateFileW(DumpFilePath
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
224 if (hDumpFile
== INVALID_HANDLE_VALUE
)
226 return HRESULT_FROM_WIN32(GetLastError());
229 ThreadData
& Thread
= data
.Threads
[data
.ThreadID
];
231 PCONTEXT ContextPointer
= &Thread
.Context
;
233 MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo
= {0};
234 EXCEPTION_POINTERS ExceptionPointers
= {0};
235 ExceptionPointers
.ExceptionRecord
= &data
.ExceptionInfo
.ExceptionRecord
;
236 ExceptionPointers
.ContextRecord
= ContextPointer
;
238 DumpExceptionInfo
.ThreadId
= data
.ThreadID
;
239 DumpExceptionInfo
.ExceptionPointers
= &ExceptionPointers
;
240 DumpExceptionInfo
.ClientPointers
= FALSE
;
242 BOOL DumpSucceeded
= MiniDumpWriteDump(data
.ProcessHandle
, data
.ProcessID
, hDumpFile
, MiniDumpNormal
, &DumpExceptionInfo
, NULL
, NULL
);
245 // According to MSDN, this value is already an HRESULT, so don't convert it again.
249 CloseHandle(hDumpFile
);
253 int WINAPI
wWinMain(HINSTANCE hInstance
, HINSTANCE
, LPWSTR cmdLine
, INT
)
256 WCHAR
**argv
= CommandLineToArgvW(cmdLine
, &argc
);
265 for (int n
= 0; n
< argc
; ++n
)
267 WCHAR
* arg
= argv
[n
];
269 if (!wcscmp(arg
, L
"-i"))
271 /* FIXME: Installs as the postmortem debugger. */
273 else if (!wcscmp(arg
, L
"-g"))
276 else if (!wcscmp(arg
, L
"-p"))
280 pid
= wcstoul(argv
[n
+1], NULL
, 10);
284 else if (!wcscmp(arg
, L
"-e"))
288 data
.Event
= (HANDLE
)wcstoul(argv
[n
+1], NULL
, 10);
292 else if (!wcscmp(arg
, L
"-?"))
294 MessageBoxA(NULL
, szUsage
, "ReactOS Crash Reporter", MB_OK
);
295 return abort(output
, 0);
297 else if (!wcscmp(arg
, L
"/?"))
299 xfprintf(stdout
, "%s\n", szUsage
);
300 return abort(stdout
, 0);
306 MessageBoxA(NULL
, szUsage
, "ReactOS Crash Reporter", MB_OK
);
307 return abort(stdout
, 0);
312 std::wstring OutputPath
= Settings_GetOutputPath();
313 BOOL HasPath
= (OutputPath
.size() != 0);
315 if (!PathIsDirectoryW(OutputPath
.c_str()))
317 int res
= SHCreateDirectoryExW(NULL
, OutputPath
.c_str(), NULL
);
318 if (res
!= ERROR_SUCCESS
&& res
!= ERROR_ALREADY_EXISTS
)
320 xfprintf(stdout
, "Could not create output directory, not writing dump\n");
321 MessageBoxA(NULL
, "Could not create directory to write crash report.", "ReactOS Crash Reporter", MB_ICONERROR
| MB_OK
);
322 return abort(stdout
, 0);
327 SUCCEEDED(StringCchPrintfW(Filename
, _countof(Filename
), L
"Appcrash_%d-%02d-%02d_%02d-%02d-%02d.txt",
328 st
.wYear
, st
.wMonth
, st
.wDay
, st
.wHour
, st
.wMinute
, st
.wSecond
)))
331 OutputPath
+= Filename
;
332 output
= _wfopen(OutputPath
.c_str(), L
"wb");
338 if (!DebugActiveProcess(pid
))
339 return abort(output
, -2);
341 /* We should not kill it? */
342 DebugSetProcessKillOnExit(FALSE
);
345 if (!WaitForDebugEvent(&evt
, 30000))
346 return abort(output
, -3);
348 assert(evt
.dwDebugEventCode
== CREATE_PROCESS_DEBUG_EVENT
);
350 while (UpdateFromEvent(evt
, data
))
352 ContinueDebugEvent(evt
.dwProcessId
, evt
.dwThreadId
, DBG_CONTINUE
);
354 if (!WaitForDebugEvent(&evt
, 30000))
355 return abort(output
, -4);
358 PrintBugreport(output
, data
);
359 if (Settings_GetShouldWriteDump() && HasPath
)
361 WriteMinidump(OutputPath
.c_str(), data
);
364 TerminateProcess(data
.ProcessHandle
, data
.ExceptionInfo
.ExceptionRecord
.ExceptionCode
);
366 CStringW FormattedMessage
;
367 FormattedMessage
.Format(IDS_USER_ALERT_MESSAGE
, data
.ProcessName
.c_str(), OutputPath
.c_str());
368 CStringW DialogTitle
;
369 DialogTitle
.LoadString(hInstance
, IDS_APP_TITLE
);
371 MessageBoxW(NULL
, FormattedMessage
.GetString(), DialogTitle
.GetString(), MB_OK
);
373 return abort(output
, 0);