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