[DRWTSN32] Implement basic crash report functionality
[reactos.git] / base / applications / drwtsn32 / drwtsn32.cpp
1 /*
2 * PROJECT: Dr. Watson crash reporter
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Debug loop
5 * COPYRIGHT: Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6 */
7
8 #include "precomp.h"
9 #include <psapi.h>
10
11 #define MS_VC_EXCEPTION_THREAD_NAME 0x406d1388
12
13 ModuleData::ModuleData(void* addr)
14 {
15 BaseAddress = addr;
16 Size = 0;
17 Unloaded = false;
18 }
19
20 void ModuleData::Update(HANDLE hProcess)
21 {
22 MODULEINFO mi = {0};
23 GetModuleInformation(hProcess, (HMODULE)BaseAddress, &mi, sizeof(mi));
24 assert(BaseAddress == mi.lpBaseOfDll);
25 Size = mi.SizeOfImage;
26
27 ModuleName.resize(MAX_PATH);
28 DWORD dwLen = GetModuleFileNameExA(hProcess, (HMODULE)BaseAddress, &ModuleName[0], ModuleName.size());
29 ModuleName.resize(dwLen);
30 }
31
32
33 ThreadData::ThreadData(HANDLE handle)
34 : Handle(handle)
35 {
36 memset(&Context, 0, sizeof(Context));
37 }
38
39 void ThreadData::Update()
40 {
41 Context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_DEBUG_REGISTERS;
42 GetThreadContext(Handle, &Context);
43 }
44
45 DumpData::DumpData()
46 :ProcessID(0)
47 ,ThreadID(0)
48 ,ProcessHandle(NULL)
49 ,Event(NULL)
50 ,FirstBPHit(false)
51 {
52 memset(&ExceptionInfo, 0, sizeof(ExceptionInfo));
53 }
54
55
56 bool UpdateFromEvent(DEBUG_EVENT& evt, DumpData& data)
57 {
58 switch(evt.dwDebugEventCode)
59 {
60 case CREATE_PROCESS_DEBUG_EVENT:
61 {
62 data.ProcessPath.resize(MAX_PATH);
63 DWORD len = GetModuleFileNameExA(evt.u.CreateProcessInfo.hProcess, NULL, &data.ProcessPath[0], data.ProcessPath.size());
64 if (len)
65 {
66 data.ProcessPath.resize(len);
67 std::string::size_type pos = data.ProcessPath.find_last_of("\\/");
68 if (pos != std::string::npos)
69 data.ProcessName = data.ProcessPath.substr(pos+1);
70 }
71 else
72 {
73 data.ProcessPath = "??";
74 }
75 if (data.ProcessName.empty())
76 data.ProcessName = data.ProcessPath;
77
78 CloseHandle(evt.u.CreateProcessInfo.hFile);
79 data.ProcessID = evt.dwProcessId;
80 data.ProcessHandle = evt.u.CreateProcessInfo.hProcess;
81 data.Threads[evt.dwThreadId] = ThreadData(evt.u.CreateProcessInfo.hThread);
82 }
83 break;
84 case CREATE_THREAD_DEBUG_EVENT:
85 data.Threads[evt.dwThreadId] = ThreadData(evt.u.CreateThread.hThread);
86 break;
87 case EXIT_THREAD_DEBUG_EVENT:
88 {
89 ThreadMap::iterator it = data.Threads.find(evt.dwThreadId);
90 if (it != data.Threads.end())
91 {
92 data.Threads.erase(it);
93 }
94 }
95 break;
96 case LOAD_DLL_DEBUG_EVENT:
97 CloseHandle(evt.u.LoadDll.hFile);
98 for (size_t n = 0; n < data.Modules.size(); ++n)
99 {
100 if (data.Modules[n].BaseAddress == evt.u.LoadDll.lpBaseOfDll)
101 {
102 data.Modules[n].Unloaded = false;
103 return true;
104 }
105 }
106 data.Modules.push_back(ModuleData(evt.u.LoadDll.lpBaseOfDll));
107 break;
108 case UNLOAD_DLL_DEBUG_EVENT:
109 for (size_t n = 0; n < data.Modules.size(); ++n)
110 {
111 if (data.Modules[n].BaseAddress == evt.u.UnloadDll.lpBaseOfDll)
112 data.Modules[n].Unloaded = true;
113 }
114 break;
115 case OUTPUT_DEBUG_STRING_EVENT: // ignore
116 break;
117 case EXCEPTION_DEBUG_EVENT:
118 if (evt.u.Exception.dwFirstChance)
119 {
120 switch(evt.u.Exception.ExceptionRecord.ExceptionCode)
121 {
122 case EXCEPTION_BREAKPOINT:
123 if (!data.FirstBPHit)
124 {
125 data.FirstBPHit = true;
126
127 if (data.Event)
128 {
129 SetEvent(data.Event);
130 CloseHandle(data.Event);
131 data.Event = NULL;
132 }
133 return true;
134 }
135 break;
136 case MS_VC_EXCEPTION_THREAD_NAME:
137 /* Thread name */
138 return true;
139 case DBG_CONTROL_C:
140 case DBG_CONTROL_BREAK:
141 return true;
142 }
143 }
144 data.ExceptionInfo = evt.u.Exception;
145 data.ThreadID = evt.dwThreadId;
146 return false;
147 case EXIT_PROCESS_DEBUG_EVENT:
148 //assert(FALSE);
149 return false;
150 case RIP_EVENT:
151 //assert(FALSE);
152 return false;
153 default:
154 assert(false);
155 }
156 return true;
157 }
158