850323317962e94a62159398462e8f8e90012d81
[reactos.git] / base / applications / drwtsn32 / main.cpp
1 /*
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)
6 */
7
8 #include "precomp.h"
9 #include <winuser.h>
10 #include <algorithm>
11 #include <shlobj.h>
12 #include <strsafe.h>
13 #include <tlhelp32.h>
14 #include <conio.h>
15
16
17 static const char szUsage[] = "Usage: DrWtsn32 [-i] [-g] [-p dddd] [-e dddd] [-?]\n"
18 " -i: Install DrWtsn32 as the postmortem debugger\n"
19 " -g: Ignored, Provided for compatibility with WinDbg and CDB.\n"
20 " -p dddd: Attach to process dddd.\n"
21 " -e dddd: Signal the event dddd.\n"
22 " -?: This help.\n";
23
24 extern "C"
25 NTSYSAPI ULONG NTAPI vDbgPrintEx(_In_ ULONG ComponentId, _In_ ULONG Level, _In_z_ PCCH Format, _In_ va_list ap);
26 #define DPFLTR_ERROR_LEVEL 0
27
28 void xfprintf(FILE* stream, const char *fmt, ...)
29 {
30 va_list ap;
31
32 va_start(ap, fmt);
33 vfprintf(stream, fmt, ap);
34 vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL, fmt, ap);
35 }
36
37
38
39 static bool SortModules(const ModuleData& left, const ModuleData& right)
40 {
41 return left.BaseAddress < right.BaseAddress;
42 }
43
44
45 void PrintBugreport(FILE* output, DumpData& data)
46 {
47 PrintSystemInfo(output, data);
48 xfprintf(output, NEWLINE "*----> Task List <----*" NEWLINE NEWLINE);
49 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
50 if (hSnap != INVALID_HANDLE_VALUE)
51 {
52 PROCESSENTRY32 pe;
53 pe.dwSize = sizeof(pe);
54 if (Process32First(hSnap, &pe))
55 {
56 do
57 {
58 xfprintf(output, "%5d: %s" NEWLINE, pe.th32ProcessID, pe.szExeFile);
59 } while (Process32Next(hSnap, &pe));
60 }
61 CloseHandle(hSnap);
62 }
63
64 xfprintf(output, NEWLINE "*----> Module List <----*" NEWLINE NEWLINE);
65 std::sort(data.Modules.begin(), data.Modules.end(), SortModules);
66
67 ModuleData mainModule(NULL);
68 mainModule.Update(data.ProcessHandle);
69 xfprintf(output, "(%p - %p) %s" NEWLINE,
70 mainModule.BaseAddress,
71 (PBYTE)mainModule.BaseAddress + mainModule.Size,
72 data.ProcessPath.c_str());
73
74 for (size_t n = 0; n < data.Modules.size(); ++n)
75 {
76 ModuleData& mod = data.Modules[n];
77 if (!mod.Unloaded)
78 {
79 mod.Update(data.ProcessHandle);
80 xfprintf(output, "(%p - %p) %s" NEWLINE,
81 mod.BaseAddress,
82 (PBYTE)mod.BaseAddress + mod.Size,
83 mod.ModuleName.c_str());
84 }
85 }
86
87 BeginStackBacktrace(data);
88 for (ThreadMap::iterator it = data.Threads.begin(); it != data.Threads.end(); ++it)
89 {
90 it->second.Update();
91
92 xfprintf(output, NEWLINE "State Dump for Thread Id 0x%x" NEWLINE NEWLINE, it->first);
93 const CONTEXT& ctx = it->second.Context;
94 if ((ctx.ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
95 {
96 #if defined(_M_IX86)
97 xfprintf(output, "eax:%p ebx:%p ecx:%p edx:%p esi:%p edi:%p" NEWLINE,
98 ctx.Eax, ctx.Ebx, ctx.Ecx, ctx.Edx, ctx.Esi, ctx.Edi);
99 #elif defined(_M_AMD64)
100 xfprintf(output, "rax:%p rbx:%p rcx:%p rdx:%p rsi:%p rdi:%p" NEWLINE,
101 ctx.Rax, ctx.Rbx, ctx.Rcx, ctx.Rdx, ctx.Rsi, ctx.Rdi);
102 xfprintf(output, "r8:%p r9:%p r10:%p r11:%p r12:%p r13:%p r14:%p r15:%p" NEWLINE,
103 ctx.R8, ctx.R9, ctx.R10, ctx.R11, ctx.R12, ctx.R13, ctx.R14, ctx.R15);
104 #else
105 #error Unknown architecture
106 #endif
107 }
108 if ((ctx.ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
109 {
110 #if defined(_M_IX86)
111 xfprintf(output, "eip:%p esp:%p ebp:%p" NEWLINE,
112 ctx.Eip, ctx.Esp, ctx.Ebp);
113 #elif defined(_M_AMD64)
114 xfprintf(output, "eip:%p esp:%p ebp:%p" NEWLINE,
115 ctx.Rip, ctx.Rsp, ctx.Rbp);
116 #else
117 #error Unknown architecture
118 #endif
119 }
120 if ((ctx.ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
121 {
122 #if defined(_M_IX86) || defined(_M_AMD64)
123 xfprintf(output, "dr0:%p dr1:%p dr2:%p dr3:%p dr6:%p dr7:%p" NEWLINE,
124 ctx.Dr0, ctx.Dr1, ctx.Dr2, ctx.Dr3, ctx.Dr6, ctx.Dr7);
125 #else
126 #error Unknown architecture
127 #endif
128 }
129
130 PrintStackBacktrace(output, data, it->second);
131 }
132 EndStackBacktrace(data);
133 }
134
135
136 int abort(FILE* output, int err)
137 {
138 if (output != stdout)
139 fclose(output);
140 else
141 _getch();
142
143 return err;
144 }
145
146 int main(int argc, char* argv[])
147 {
148 DWORD pid = 0;
149 char Buffer[MAX_PATH+55];
150 char Filename[50];
151 FILE* output = NULL;
152 SYSTEMTIME st;
153 DumpData data;
154
155
156 for (int n = 0; n < argc; ++n)
157 {
158 char* arg = argv[n];
159
160 if (!strcmp(arg, "-i"))
161 {
162 /* FIXME: Installs as the postmortem debugger. */
163 }
164 else if (!strcmp(arg, "-g"))
165 {
166 }
167 else if (!strcmp(arg, "-p"))
168 {
169 if (n + 1 < argc)
170 {
171 pid = strtoul(argv[n+1], NULL, 10);
172 n++;
173 }
174 }
175 else if (!strcmp(arg, "-e"))
176 {
177 if (n + 1 < argc)
178 {
179 data.Event = (HANDLE)strtoul(argv[n+1], NULL, 10);
180 n++;
181 }
182 }
183 else if (!strcmp(arg, "-?"))
184 {
185 MessageBoxA(NULL, szUsage, "DrWtsn32", MB_OK);
186 return abort(output, 0);
187 }
188 else if (!strcmp(arg, "/?"))
189 {
190 xfprintf(stdout, "%s\n", szUsage);
191 return abort(stdout, 0);
192 }
193 }
194
195 if (!pid)
196 {
197 MessageBoxA(NULL, szUsage, "DrWtsn32", MB_OK);
198 return abort(stdout, 0);
199 }
200
201 GetLocalTime(&st);
202
203 if (SHGetFolderPathA(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, Buffer) == S_OK &&
204 SUCCEEDED(StringCchPrintfA(Filename, _countof(Filename), "Appcrash_%d-%02d-%02d_%02d-%02d-%02d.txt",
205 st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond)))
206 {
207 StringCchCatA(Buffer, _countof(Buffer), "\\");
208 StringCchCatA(Buffer, _countof(Buffer), Filename);
209 output = fopen(Buffer, "wb");
210 }
211 if (!output)
212 output = stdout;
213
214
215 if (!DebugActiveProcess(pid))
216 return abort(output, -2);
217
218 /* We should not kill it? */
219 DebugSetProcessKillOnExit(FALSE);
220
221 DEBUG_EVENT evt;
222 if (!WaitForDebugEvent(&evt, 30000))
223 return abort(output, -3);
224
225 assert(evt.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT);
226
227 while (UpdateFromEvent(evt, data))
228 {
229 ContinueDebugEvent(evt.dwProcessId, evt.dwThreadId, DBG_CONTINUE);
230
231 if (!WaitForDebugEvent(&evt, 30000))
232 return abort(output, -4);
233 }
234
235 PrintBugreport(output, data);
236
237 TerminateProcess(data.ProcessHandle, data.ExceptionInfo.ExceptionRecord.ExceptionCode);
238
239 std::string Message = "The application '";
240 Message += data.ProcessName;
241 Message += "' has just crashed :(\n";
242 Message += "Information about this crash is saved to:\n";
243 Message += Filename;
244 Message += "\nThis file is stored on your desktop.";
245 MessageBoxA(NULL, Message.c_str(), "Sorry!", MB_OK);
246
247 return abort(output, 0);
248 }