Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1007)

Side by Side Diff: experimental/windows_debugger/debugger/core/debuggee_thread.cc

Issue 10928195: First round of dead file removal (Closed) Base URL: https://github.com/samclegg/nativeclient-sdk.git@master
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_thread.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 #include "debugger/core/debug_logger.h"
10 #include "debugger/core/debuggee_iprocess.h"
11
12 #pragma warning(disable : 4996) // Disable sscanf warning.
13
14 namespace {
15 const size_t kMaxStringSize = 32 * 1024;
16 const char* kNexeUuid =
17 "{7AA7C9CF-89EC-4ed3-8DAD-6DC84302AB11} -v 1 -event NaClThreadStart";
18 }
19
20 namespace debug {
21 DebuggeeThread::DebuggeeThread(int id,
22 HANDLE handle,
23 IDebuggeeProcess* parent_process)
24 : id_(id),
25 handle_(handle),
26 parent_process_(*parent_process),
27 state_(kHalted),
28 exit_code_(0),
29 triggered_breakpoint_addr_(NULL),
30 is_nacl_app_thread_(false) {
31 assert(NULL != parent_process);
32 }
33
34 DebugAPI& DebuggeeThread::debug_api() {
35 return parent_process().debug_api();
36 }
37
38 void DebuggeeThread::SetState(State new_state) {
39 DBG_LOG("TR03.01",
40 "msg='DebuggeeThread::SetState' thread_id=%d old_state=%s"
41 " new_state=%s",
42 id(),
43 GetStateName(state_),
44 GetStateName(new_state));
45 state_ = new_state;
46 }
47
48 bool DebuggeeThread::IsHalted() const {
49 return (kHalted == state_);
50 }
51
52 void DebuggeeThread::OnOutputDebugString(DebugEvent* debug_event) {
53 DEBUG_EVENT de = debug_event->windows_debug_event();
54 if (0 == debug_event->windows_debug_event().u.DebugString.fUnicode) {
55 size_t sz = de.u.DebugString.nDebugStringLength + 1;
56 size_t str_sz = min(kMaxStringSize, sz);
57 char* tmp = static_cast<char*>(malloc(str_sz));
58 if (NULL != tmp) {
59 if (parent_process().ReadMemory(
60 de.u.DebugString.lpDebugStringData,
61 str_sz,
62 tmp)) {
63 tmp[str_sz - 1] = 0;
64
65 DBG_LOG("TR03.02", "OutputDebugString=%s", tmp);
66
67 if (strncmp(tmp, kNexeUuid, strlen(kNexeUuid)) == 0) {
68 /// This string is coming from sel_ldr.
69 is_nacl_app_thread_ = true;
70 // Here we are passing pointers from sel_ldr to the debugger.
71 // One might think that we can't use them because they are
72 // valid in sel_ldr address space only. This is not true.
73 // ::ReadProcessMemory can be used with these pointers.
74 // Note: these pointers are not untrusted NaCl, they are
75 // native windows flat pointers.
76 void* nexe_mem_base = NULL;
77 void* nexe_entry_point = NULL;
78 sscanf(tmp + strlen(kNexeUuid), // NOLINT
79 " -mb %p -ep %p", // %p because size is different on 32bit
80 &nexe_mem_base, // and 64-bit versions of windows.
81 &nexe_entry_point);
82 parent_process().set_nexe_mem_base(nexe_mem_base);
83 parent_process().set_nexe_entry_point(nexe_entry_point);
84
85 debug_event->set_nacl_debug_event_code(
86 DebugEvent::kThreadIsAboutToStart);
87 DBG_LOG("TR03.03",
88 "NaClThreadStart mem_base=%p entry_point=%p thread_id=%d",
89 nexe_mem_base,
90 nexe_entry_point,
91 id());
92 }
93 }
94 free(tmp);
95 }
96 }
97 }
98
99 // Here's what we have to do when we hit our breakpoint:
100 // a) recover original code at breakpoint adddress.
101 // b) return IP to the beginning of instruction (simple decrement work).
102 void DebuggeeThread::OnBreakpoint(DebugEvent* debug_event) {
103 DEBUG_EVENT de = debug_event->windows_debug_event();
104 void* ex_addr = de.u.Exception.ExceptionRecord.ExceptionAddress;
105 void* br_addr = ex_addr;
106 if (IsNaClAppThread())
107 br_addr = parent_process().FromNexeToFlatAddress(ex_addr);
108
109 Breakpoint* br = parent_process().GetBreakpoint(br_addr);
110
111 DBG_LOG("TR03.04",
112 "msg='DebuggeeThread::OnBreakpoint' thread_id=%d ex_addr=%p"
113 " br_addr=0x%p our=%s",
114 id(),
115 ex_addr,
116 br_addr,
117 (NULL != br) ? "yes" : "no");
118
119 if (NULL != br) {
120 // it's our breakpoint.
121 triggered_breakpoint_addr_ = br->address();
122 br->RecoverCodeAtBreakpoint();
123 SetIP(reinterpret_cast<char*>(GetIP()) - 1);
124 }
125 }
126
127 bool DebuggeeThread::Continue(ContinueOption option) {
128 if (!IsHalted()) {
129 DBG_LOG("WARN03.01",
130 "msg='DebuggeeThread::Continue(%s) called while thread was in an"
131 " incompatible state: %s' thread_id=%d",
132 GetContinueOptionName(option),
133 GetStateName(state_),
134 id());
135 return false;
136 }
137 const DebugEvent& last_debug_event = parent_process().last_debug_event();
138 int last_debug_event_id =
139 last_debug_event.windows_debug_event().dwDebugEventCode;
140 if ((EXIT_THREAD_DEBUG_EVENT == last_debug_event_id) ||
141 (EXIT_PROCESS_DEBUG_EVENT == last_debug_event_id)) {
142 SetState(kDead);
143 return (TRUE == debug_api().ContinueDebugEvent(parent_process().id(),
144 id(),
145 DBG_CONTINUE));
146 }
147
148 if (NULL != triggered_breakpoint_addr_) {
149 void* flat_ip = GetIP();
150 if (IsNaClAppThread())
151 flat_ip = parent_process().FromNexeToFlatAddress(GetIP());
152
153 if (flat_ip == triggered_breakpoint_addr_) {
154 return ContinueFromBreakpoint();
155 } else {
156 // Just in case user changed IP so that it's not pointing to
157 // triggered breakpoint, we need to:
158 // a) recover breakpoint
159 // b) make it not triggered
160 Breakpoint* br =
161 parent_process().GetBreakpoint(triggered_breakpoint_addr_);
162 if (NULL != br)
163 br->WriteBreakpointCode();
164 triggered_breakpoint_addr_ = NULL;
165 }
166 }
167 if (kSingleStep == option)
168 EnableSingleStep(true);
169
170 int flags = DBG_CONTINUE;
171 if (kContinueAndPassException == option)
172 flags = DBG_EXCEPTION_NOT_HANDLED;
173
174 SetState(kRunning);
175 return (TRUE == debug_api().ContinueDebugEvent(parent_process().id(),
176 id(),
177 flags));
178 }
179
180 /// Implements first steps of 'Continue from breakpoint' algorithm,
181 /// described in |DebuggeeThread::OnDebugEvent| method.
182 bool DebuggeeThread::ContinueFromBreakpoint() {
183 SetState(kContinueFromBreakpoint);
184 EnableSingleStep(true);
185 return (TRUE == debug_api().ContinueDebugEvent(parent_process().id(),
186 id(),
187 DBG_CONTINUE));
188 }
189
190 void DebuggeeThread::OnSingleStep(DebugEvent* debug_event) {
191 if (kContinueFromBreakpoint == state_) {
192 if (NULL == triggered_breakpoint_addr_) {
193 DBG_LOG("ERR03.01",
194 "msg=OnSingleStepDueToContinueFromBreakpoint thread_id=%d"
195 " triggered_breakpoint_=NULL",
196 id());
197 SetState(kHalted);
198 } else {
199 DBG_LOG("TR03.05",
200 "msg=OnSingleStepDueToContinueFromBreakpoint thread_id=%d"
201 " breakpoint_addr=0x%p",
202 id(),
203 triggered_breakpoint_addr_);
204 // We got here because thread was continuing from breakpoint.
205 Breakpoint* br =
206 parent_process().GetBreakpoint(triggered_breakpoint_addr_);
207 if (NULL != br)
208 br->WriteBreakpointCode();
209 triggered_breakpoint_addr_ = NULL;
210 debug_api().ContinueDebugEvent(parent_process().id(),
211 id(),
212 DBG_CONTINUE);
213 SetState(kRunning);
214 }
215 } else {
216 SetState(kHalted);
217 }
218 }
219
220 void DebuggeeThread::OnDebugEvent(DebugEvent* debug_event) {
221 DEBUG_EVENT de = debug_event->windows_debug_event();
222 EnableSingleStep(false);
223
224 // Thread expected 'SingleStep' exception due to 'continue from breakpoint'
225 // algorithm, but got something else. For example, it happens when breakpoint
226 // is set to instruction generating 'memory access violation' exception.
227 //
228 // Here's description of 'continue from breakpoint' algorithm:
229 // a) recover original code at breakpoint address
230 // b) enable single step mode of CPU
231 // c) call DebugAPI::ContinueDebugEvent
232 // d) receive SingleStep exception
233 // e) write breakpoint code at breakpoint address
234 // f) disable single step mode of CPU
235 // h) call DebugAPI::ContinueDebugEvent
236 // f) DebuggeeThread is now in 'kRunning' state.
237 // Note: we can get different exception on step (d),
238 // we have to do the following:
239 // 1) disable single step mode of CPU (it's done in first statement in
240 // |DebuggeeThread::OnDebugEvent| method).
241 // 2) write breakpoint code at breakpoint address, if IP points
242 // somewhere else.
243 if (!debug_event->IsSingleStep() &&
244 (kContinueFromBreakpoint == state_) &&
245 (NULL != triggered_breakpoint_addr_)) {
246 void* flat_ip = GetIP();
247 if (IsNaClAppThread())
248 flat_ip = parent_process().FromNexeToFlatAddress(flat_ip);
249
250 if (flat_ip != triggered_breakpoint_addr_) {
251 Breakpoint* br =
252 parent_process().GetBreakpoint(triggered_breakpoint_addr_);
253 if (NULL != br)
254 br->WriteBreakpointCode();
255 triggered_breakpoint_addr_ = NULL;
256 }
257 }
258
259 // Now we can proceed with debug event.
260 switch (de.dwDebugEventCode) {
261 case OUTPUT_DEBUG_STRING_EVENT: {
262 OnOutputDebugString(debug_event);
263 break;
264 }
265 case EXIT_THREAD_DEBUG_EVENT: {
266 exit_code_ = de.u.ExitThread.dwExitCode;
267 break;
268 }
269 case EXIT_PROCESS_DEBUG_EVENT: {
270 exit_code_ = de.u.ExitProcess.dwExitCode;
271 break;
272 }
273 case EXCEPTION_DEBUG_EVENT: {
274 switch (debug_event->GetExceptionCode()) {
275 case EXCEPTION_BREAKPOINT: {
276 OnBreakpoint(debug_event);
277 break;
278 }
279 case EXCEPTION_SINGLE_STEP: {
280 OnSingleStep(debug_event);
281 // Thread halts on all other events unconditionally,
282 // but here |OnSingleStep| can decide to continue -
283 // in case it's due to 'continue from breakpoint'.
284 return;
285 }
286 }
287 }
288 }
289 // DebugeeThread halts on all debug events, with one exception -
290 // when it receives SingleStep while continue from breakpoint.
291 SetState(kHalted);
292 }
293
294 void DebuggeeThread::EnableSingleStep(bool enable) {
295 DBG_LOG("TR03.06",
296 "msg='DebuggeeThread::EnableSingleStep(%s)' thread_id=%d",
297 enable ? "true" : "false",
298 id());
299
300 if (parent_process().IsWoW()) {
301 WOW64_CONTEXT context;
302 GetWowContext(&context);
303 if (enable)
304 context.EFlags |= 1 << 8;
305 else
306 context.EFlags &= ~(1 << 8);
307 SetWowContext(context);
308 } else {
309 CONTEXT context;
310 GetContext(&context);
311 if (enable)
312 context.EFlags |= 1 << 8;
313 else
314 context.EFlags &= ~(1 << 8);
315 SetContext(context);
316 }
317 }
318
319 const char* DebuggeeThread::GetStateName(State state) {
320 switch (state) {
321 case kRunning: return "kRunning";
322 case kHalted: return "kHalted";
323 case kContinueFromBreakpoint: return "kContinueFromBreakpoint";
324 case kDead: return "kDead";
325 }
326 return "N/A";
327 }
328
329 const char* DebuggeeThread::GetContinueOptionName(ContinueOption option) {
330 switch (option) {
331 case kSingleStep: return "kSingleStep";
332 case kContinue: return "kContinue";
333 case kContinueAndPassException: return "kContinueAndPassException";
334 }
335 return "N/A";
336 }
337
338 void DebuggeeThread::Kill() {
339 if (NULL != handle_)
340 debug_api().TerminateThread(handle_, 0);
341 }
342
343 bool DebuggeeThread::GetContext(CONTEXT* context) {
344 if (!parent_process().IsHalted())
345 return false;
346
347 context->ContextFlags = CONTEXT_ALL;
348 return (debug_api().GetThreadContext(handle_, context) != FALSE);
349 }
350
351 bool DebuggeeThread::SetContext(const CONTEXT& context) {
352 if (!parent_process().IsHalted())
353 return false;
354
355 CONTEXT context_copy = context;
356 return (debug_api().SetThreadContext(handle_, &context_copy) != FALSE);
357 }
358
359 bool DebuggeeThread::GetWowContext(WOW64_CONTEXT* context) {
360 if (!parent_process().IsHalted())
361 return false;
362
363 context->ContextFlags = CONTEXT_ALL;
364 return (debug_api().Wow64GetThreadContext(handle_, context) != FALSE);
365 }
366
367 bool DebuggeeThread::SetWowContext(const WOW64_CONTEXT& context) {
368 if (!parent_process().IsHalted())
369 return false;
370
371 return (debug_api().Wow64SetThreadContext(handle_, &context) != FALSE);
372 }
373
374 void* DebuggeeThread::GetIP() {
375 if (!parent_process().IsHalted())
376 return false;
377
378 if (parent_process().IsWoW()) {
379 WOW64_CONTEXT context;
380 GetWowContext(&context);
381 return reinterpret_cast<void*>(context.Eip);
382 } else {
383 CONTEXT context;
384 GetContext(&context);
385 #ifdef _WIN64
386 return reinterpret_cast<void*>(context.Rip);
387 #else
388 return reinterpret_cast<void*>(context.Eip);
389 #endif
390 }
391 }
392
393 bool DebuggeeThread::SetIP(void* ip) {
394 if (!parent_process().IsHalted())
395 return false;
396
397 if (parent_process().IsWoW()) {
398 WOW64_CONTEXT context;
399 if (!GetWowContext(&context))
400 return false;
401 context.Eip = reinterpret_cast<DWORD>(ip);
402 return SetWowContext(context);
403 } else {
404 CONTEXT ct;
405 if (!GetContext(&ct))
406 return false;
407 #ifdef _WIN64
408 ct.Rip = reinterpret_cast<DWORD64>(ip);
409 #else
410 ct.Eip = reinterpret_cast<DWORD>(ip);
411 #endif
412 return SetContext(ct);
413 }
414 }
415 } // namespace debug
416
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698