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*2);
63         DWORD len = GetModuleFileNameExW(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(L"\\/");
68             if (pos != std::string::npos)
69                 data.ProcessName = data.ProcessPath.substr(pos+1);
70         }
71         else
72         {
73             data.ProcessPath = L"??";
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 
159