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