OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 #include "debugger/core/debuggee_process.h" | |
5 #include <assert.h> | |
6 #include "debugger/core/debug_api.h" | |
7 #include "debugger/core/debug_breakpoint.h" | |
8 #include "debugger/core/debug_event.h" | |
9 | |
10 namespace debug { | |
11 DebuggeeProcess::DebuggeeProcess(int id, | |
12 HANDLE handle, | |
13 HANDLE file_handle, | |
14 DebugAPI* debug_api) | |
15 : id_(id), | |
16 handle_(handle), | |
17 file_handle_(file_handle), | |
18 state_(kRunning), | |
19 exit_code_(0), | |
20 debug_api_(*debug_api), | |
21 nexe_mem_base_(NULL), | |
22 nexe_entry_point_(NULL) { | |
23 } | |
24 | |
25 DebuggeeProcess::~DebuggeeProcess() { | |
26 DeleteThreads(); | |
27 if (NULL != file_handle_) { | |
28 debug_api().CloseHandle(file_handle_); | |
29 file_handle_ = NULL; | |
30 } | |
31 // Delete breakpoints. | |
32 std::map<void*, Breakpoint*>::const_iterator it = breakpoints_.begin(); | |
33 while (it != breakpoints_.end()) { | |
34 delete it->second; | |
35 ++it; | |
36 } | |
37 breakpoints_.clear(); | |
38 } | |
39 | |
40 /// Implementation relies on the fact that 32 bit debugger cannot run | |
41 /// 64 bit debuggee process (::CreateProcess with DEBUG_PROCESS fails). | |
42 /// Also, 32 bit debugger is not able to attach to 64 bit process. | |
43 /// | |
44 /// Debugger x debuggee matrix: | |
45 /// a) 32 x 32 -> _WIN64 not defined, not WoW, returns 32 | |
46 /// b) 32 x 64 -> ::CreateProcess fails, impossible to get here | |
47 /// c) 64 x 64 -> _WIN64 is defined, not WoW, returns 64 | |
48 /// d) 64 x 32 -> _WIN64 is defined, WoW, returns 32 | |
49 int DebuggeeProcess::GetWordSizeInBits() { | |
50 #ifndef _WIN64 | |
51 // Not 64-bit debugger, so must be a 32-bit debugger and debuggee. | |
52 return 32; | |
53 #else | |
54 if (IsWoW()) | |
55 return 32; | |
56 return 64; | |
57 #endif | |
58 } | |
59 | |
60 bool DebuggeeProcess::IsWoW() { | |
61 #ifndef _WIN64 | |
62 return false; | |
63 #else | |
64 BOOL is_wow = FALSE; | |
65 if (!debug_api().IsWoW64Process(handle_, &is_wow)) | |
66 return false; | |
67 return is_wow ? true : false; | |
68 #endif | |
69 } | |
70 | |
71 void* DebuggeeProcess::FromNexeToFlatAddress(void* addr) const { | |
72 #ifndef _WIN64 | |
73 addr = reinterpret_cast<char*>(addr) + | |
74 reinterpret_cast<size_t>(nexe_mem_base_); | |
75 #endif | |
76 return addr; | |
77 } | |
78 | |
79 bool DebuggeeProcess::Continue() { | |
80 return ContinueHaltedThread(DebuggeeThread::kContinue); | |
81 } | |
82 | |
83 bool DebuggeeProcess::ContinueAndPassExceptionToDebuggee() { | |
84 return ContinueHaltedThread(DebuggeeThread::kContinueAndPassException); | |
85 } | |
86 | |
87 bool DebuggeeProcess::SingleStep() { | |
88 return ContinueHaltedThread(DebuggeeThread::kSingleStep); | |
89 } | |
90 | |
91 bool DebuggeeProcess::Break() { | |
92 return (FALSE != debug_api().DebugBreakProcess(handle_)); | |
93 } | |
94 | |
95 bool DebuggeeProcess::Kill() { | |
96 std::deque<DebuggeeThread*>::const_iterator it = threads_.begin(); | |
97 while (it != threads_.end()) { | |
98 DebuggeeThread* thread = *it; | |
99 ++it; | |
100 if (NULL != thread) | |
101 thread->Kill(); | |
102 } | |
103 return Continue(); | |
104 } | |
105 | |
106 bool DebuggeeProcess::Detach() { | |
107 BOOL res = debug_api().DebugActiveProcessStop(id()); | |
108 DeleteThreads(); | |
109 state_ = kDead; | |
110 return (FALSE != res); | |
111 } | |
112 | |
113 DebuggeeThread* DebuggeeProcess::GetThread(int id) { | |
114 std::deque<DebuggeeThread*>::iterator it = threads_.begin(); | |
115 while (it != threads_.end()) { | |
116 DebuggeeThread* thread = *it; | |
117 if (thread->id() == id) | |
118 return thread; | |
119 ++it; | |
120 } | |
121 return NULL; | |
122 } | |
123 | |
124 DebuggeeThread* DebuggeeProcess::GetHaltedThread() { | |
125 std::deque<DebuggeeThread*>::const_iterator it = threads_.begin(); | |
126 while (it != threads_.end()) { | |
127 DebuggeeThread* thread = *it++; | |
128 if (thread->IsHalted()) | |
129 return thread; | |
130 } | |
131 return NULL; | |
132 } | |
133 | |
134 void DebuggeeProcess::GetThreadIds(std::deque<int>* threads) const { | |
135 if (NULL == threads) | |
136 return; | |
137 threads->clear(); | |
138 std::deque<DebuggeeThread*>::const_iterator it = threads_.begin(); | |
139 while (it != threads_.end()) { | |
140 DebuggeeThread* thread = *it++; | |
141 threads->push_back(thread->id()); | |
142 } | |
143 } | |
144 | |
145 bool DebuggeeProcess::ReadMemory(const void* addr, | |
146 size_t size, | |
147 void* destination) { | |
148 // There's no need to change memory protection, because debugger | |
149 // has full access to the debuggee memory. | |
150 // The function fails if the requested read operation crosses into an area | |
151 // of the process that is inaccessible. | |
152 if (!debug_api().ReadProcessMemory(handle_, addr, destination, size, NULL)) { | |
153 return false; | |
154 } | |
155 return true; | |
156 } | |
157 | |
158 bool DebuggeeProcess::WriteMemory(const void* addr, | |
159 size_t size, | |
160 const void* source) { | |
161 if (!IsHalted()) | |
162 return false; | |
163 // There's no need to change memory protection, because debugger | |
164 // has full access to the debuggee memory. | |
165 // The function fails if the requested write operation crosses into an area | |
166 // of the process that is inaccessible. | |
167 BOOL res = debug_api().WriteProcessMemory(handle_, | |
168 const_cast<void*>(addr), | |
169 source, | |
170 size, | |
171 NULL); | |
172 if (!res) { | |
173 return false; | |
174 } | |
175 // Flushes the instruction cache for the debuggee process. | |
176 // The CPU cannot detect the change, and may execute the old code it cached. | |
177 res = debug_api().FlushInstructionCache(handle_, addr, size); | |
178 if (!res) { | |
179 return false; | |
180 } | |
181 return true; | |
182 } | |
183 | |
184 bool DebuggeeProcess::SetBreakpoint(void* addr) { | |
185 if (!IsHalted()) | |
186 return false; | |
187 | |
188 if (NULL != GetBreakpoint(addr)) | |
189 return false; | |
190 | |
191 Breakpoint* br = new Breakpoint(addr, this); | |
192 if (br->Init()) { | |
193 breakpoints_[addr] = br; | |
194 return true; | |
195 } | |
196 delete br; | |
197 return false; | |
198 } | |
199 | |
200 Breakpoint* DebuggeeProcess::GetBreakpoint(void* addr) { | |
201 std::map<void*, Breakpoint*>::iterator it = breakpoints_.find(addr); | |
202 if (breakpoints_.end() == it) | |
203 return NULL; | |
204 return it->second; | |
205 } | |
206 | |
207 bool DebuggeeProcess::RemoveBreakpoint(void* addr) { | |
208 if (!IsHalted()) | |
209 return false; | |
210 | |
211 bool result = true; | |
212 std::map<void*, Breakpoint*>::iterator it = breakpoints_.find(addr); | |
213 if (breakpoints_.end() != it) { | |
214 Breakpoint* br = it->second; | |
215 if (!br->RecoverCodeAtBreakpoint()) | |
216 result = false; | |
217 delete br; | |
218 breakpoints_.erase(it); | |
219 } | |
220 return result; | |
221 } | |
222 | |
223 void DebuggeeProcess::GetBreakpoints(std::deque<Breakpoint*>* breakpoints) { | |
224 if (NULL == breakpoints) | |
225 return; | |
226 breakpoints->clear(); | |
227 std::map<void*, Breakpoint*>::const_iterator it = breakpoints_.begin(); | |
228 while (it != breakpoints_.end()) { | |
229 breakpoints->push_back(it->second); | |
230 ++it; | |
231 } | |
232 } | |
233 | |
234 void DebuggeeProcess::OnDebugEvent(DebugEvent* debug_event) { | |
235 last_debug_event_ = *debug_event; | |
236 DEBUG_EVENT wde = debug_event->windows_debug_event(); | |
237 | |
238 switch (wde.dwDebugEventCode) { | |
239 case CREATE_PROCESS_DEBUG_EVENT: { | |
240 AddThread( | |
241 wde.dwThreadId, | |
242 wde.u.CreateProcessInfo.hThread); | |
243 break; | |
244 } | |
245 case CREATE_THREAD_DEBUG_EVENT: { | |
246 AddThread( | |
247 wde.dwThreadId, | |
248 wde.u.CreateThread.hThread); | |
249 break; | |
250 } | |
251 case EXIT_PROCESS_DEBUG_EVENT: { | |
252 exit_code_ = wde.u.ExitProcess.dwExitCode; | |
253 break; | |
254 } | |
255 } | |
256 DebuggeeThread* thread = GetThread(wde.dwThreadId); | |
257 if (NULL != thread) { | |
258 thread->OnDebugEvent(debug_event); | |
259 if (thread->IsHalted()) | |
260 state_ = kHalted; | |
261 } else { | |
262 /// To prevent halting the process in case we lost the thread | |
263 /// object somehow. | |
264 debug_api().ContinueDebugEvent(id(), wde.dwThreadId, DBG_CONTINUE); | |
265 } | |
266 } | |
267 | |
268 bool DebuggeeProcess::ContinueHaltedThread( | |
269 DebuggeeThread::ContinueOption option) { | |
270 if (state_ != kHalted) | |
271 return false; | |
272 | |
273 DebuggeeThread* halted_thread = GetHaltedThread(); | |
274 assert(NULL != halted_thread); | |
275 | |
276 if (NULL != halted_thread) { | |
277 bool res = halted_thread->Continue(option); | |
278 if (halted_thread->state() == DebuggeeThread::kDead) | |
279 RemoveThread(halted_thread->id()); | |
280 | |
281 int last_debug_event_id = | |
282 last_debug_event_.windows_debug_event().dwDebugEventCode; | |
283 if (EXIT_PROCESS_DEBUG_EVENT == last_debug_event_id) | |
284 state_ = kDead; | |
285 else | |
286 state_ = kRunning; | |
287 return (TRUE == res); | |
288 } | |
289 return false; | |
290 } | |
291 | |
292 DebuggeeThread* DebuggeeProcess::AddThread(int id, HANDLE handle) { | |
293 DebuggeeThread* thread = GetThread(id); | |
294 if (NULL == thread) { | |
295 thread = new DebuggeeThread(id, handle, this); | |
296 threads_.push_back(thread); | |
297 } | |
298 return thread; | |
299 } | |
300 | |
301 void DebuggeeProcess::RemoveThread(int id) { | |
302 std::deque<DebuggeeThread*>::iterator it = threads_.begin(); | |
303 while (it != threads_.end()) { | |
304 DebuggeeThread* thread = *it; | |
305 if (thread->id() == id) { | |
306 threads_.erase(it); | |
307 delete thread; | |
308 break; | |
309 } | |
310 ++it; | |
311 } | |
312 } | |
313 | |
314 void DebuggeeProcess::DeleteThreads() { | |
315 std::deque<DebuggeeThread*>::const_iterator it = threads_.begin(); | |
316 while (it != threads_.end()) { | |
317 DebuggeeThread* thread = *it; | |
318 delete thread; | |
319 ++it; | |
320 } | |
321 threads_.clear(); | |
322 } | |
323 } // namespace debug | |
324 | |
OLD | NEW |