Index: experimental/windows_debugger/debugger/core/debuggee_thread.cc |
diff --git a/experimental/windows_debugger/debugger/core/debuggee_thread.cc b/experimental/windows_debugger/debugger/core/debuggee_thread.cc |
deleted file mode 100644 |
index 048df5986856cfbde4b0955f0d075ee93cb75923..0000000000000000000000000000000000000000 |
--- a/experimental/windows_debugger/debugger/core/debuggee_thread.cc |
+++ /dev/null |
@@ -1,416 +0,0 @@ |
-// Copyright (c) 2011 The Native Client Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
-#include "debugger/core/debuggee_thread.h" |
-#include <assert.h> |
-#include "debugger/core/debug_api.h" |
-#include "debugger/core/debug_breakpoint.h" |
-#include "debugger/core/debug_event.h" |
-#include "debugger/core/debug_logger.h" |
-#include "debugger/core/debuggee_iprocess.h" |
- |
-#pragma warning(disable : 4996) // Disable sscanf warning. |
- |
-namespace { |
-const size_t kMaxStringSize = 32 * 1024; |
-const char* kNexeUuid = |
- "{7AA7C9CF-89EC-4ed3-8DAD-6DC84302AB11} -v 1 -event NaClThreadStart"; |
-} |
- |
-namespace debug { |
-DebuggeeThread::DebuggeeThread(int id, |
- HANDLE handle, |
- IDebuggeeProcess* parent_process) |
- : id_(id), |
- handle_(handle), |
- parent_process_(*parent_process), |
- state_(kHalted), |
- exit_code_(0), |
- triggered_breakpoint_addr_(NULL), |
- is_nacl_app_thread_(false) { |
- assert(NULL != parent_process); |
-} |
- |
-DebugAPI& DebuggeeThread::debug_api() { |
- return parent_process().debug_api(); |
-} |
- |
-void DebuggeeThread::SetState(State new_state) { |
- DBG_LOG("TR03.01", |
- "msg='DebuggeeThread::SetState' thread_id=%d old_state=%s" |
- " new_state=%s", |
- id(), |
- GetStateName(state_), |
- GetStateName(new_state)); |
- state_ = new_state; |
-} |
- |
-bool DebuggeeThread::IsHalted() const { |
- return (kHalted == state_); |
-} |
- |
-void DebuggeeThread::OnOutputDebugString(DebugEvent* debug_event) { |
- DEBUG_EVENT de = debug_event->windows_debug_event(); |
- if (0 == debug_event->windows_debug_event().u.DebugString.fUnicode) { |
- size_t sz = de.u.DebugString.nDebugStringLength + 1; |
- size_t str_sz = min(kMaxStringSize, sz); |
- char* tmp = static_cast<char*>(malloc(str_sz)); |
- if (NULL != tmp) { |
- if (parent_process().ReadMemory( |
- de.u.DebugString.lpDebugStringData, |
- str_sz, |
- tmp)) { |
- tmp[str_sz - 1] = 0; |
- |
- DBG_LOG("TR03.02", "OutputDebugString=%s", tmp); |
- |
- if (strncmp(tmp, kNexeUuid, strlen(kNexeUuid)) == 0) { |
- /// This string is coming from sel_ldr. |
- is_nacl_app_thread_ = true; |
- // Here we are passing pointers from sel_ldr to the debugger. |
- // One might think that we can't use them because they are |
- // valid in sel_ldr address space only. This is not true. |
- // ::ReadProcessMemory can be used with these pointers. |
- // Note: these pointers are not untrusted NaCl, they are |
- // native windows flat pointers. |
- void* nexe_mem_base = NULL; |
- void* nexe_entry_point = NULL; |
- sscanf(tmp + strlen(kNexeUuid), // NOLINT |
- " -mb %p -ep %p", // %p because size is different on 32bit |
- &nexe_mem_base, // and 64-bit versions of windows. |
- &nexe_entry_point); |
- parent_process().set_nexe_mem_base(nexe_mem_base); |
- parent_process().set_nexe_entry_point(nexe_entry_point); |
- |
- debug_event->set_nacl_debug_event_code( |
- DebugEvent::kThreadIsAboutToStart); |
- DBG_LOG("TR03.03", |
- "NaClThreadStart mem_base=%p entry_point=%p thread_id=%d", |
- nexe_mem_base, |
- nexe_entry_point, |
- id()); |
- } |
- } |
- free(tmp); |
- } |
- } |
-} |
- |
-// Here's what we have to do when we hit our breakpoint: |
-// a) recover original code at breakpoint adddress. |
-// b) return IP to the beginning of instruction (simple decrement work). |
-void DebuggeeThread::OnBreakpoint(DebugEvent* debug_event) { |
- DEBUG_EVENT de = debug_event->windows_debug_event(); |
- void* ex_addr = de.u.Exception.ExceptionRecord.ExceptionAddress; |
- void* br_addr = ex_addr; |
- if (IsNaClAppThread()) |
- br_addr = parent_process().FromNexeToFlatAddress(ex_addr); |
- |
- Breakpoint* br = parent_process().GetBreakpoint(br_addr); |
- |
- DBG_LOG("TR03.04", |
- "msg='DebuggeeThread::OnBreakpoint' thread_id=%d ex_addr=%p" |
- " br_addr=0x%p our=%s", |
- id(), |
- ex_addr, |
- br_addr, |
- (NULL != br) ? "yes" : "no"); |
- |
- if (NULL != br) { |
- // it's our breakpoint. |
- triggered_breakpoint_addr_ = br->address(); |
- br->RecoverCodeAtBreakpoint(); |
- SetIP(reinterpret_cast<char*>(GetIP()) - 1); |
- } |
-} |
- |
-bool DebuggeeThread::Continue(ContinueOption option) { |
- if (!IsHalted()) { |
- DBG_LOG("WARN03.01", |
- "msg='DebuggeeThread::Continue(%s) called while thread was in an" |
- " incompatible state: %s' thread_id=%d", |
- GetContinueOptionName(option), |
- GetStateName(state_), |
- id()); |
- return false; |
- } |
- const DebugEvent& last_debug_event = parent_process().last_debug_event(); |
- int last_debug_event_id = |
- last_debug_event.windows_debug_event().dwDebugEventCode; |
- if ((EXIT_THREAD_DEBUG_EVENT == last_debug_event_id) || |
- (EXIT_PROCESS_DEBUG_EVENT == last_debug_event_id)) { |
- SetState(kDead); |
- return (TRUE == debug_api().ContinueDebugEvent(parent_process().id(), |
- id(), |
- DBG_CONTINUE)); |
- } |
- |
- if (NULL != triggered_breakpoint_addr_) { |
- void* flat_ip = GetIP(); |
- if (IsNaClAppThread()) |
- flat_ip = parent_process().FromNexeToFlatAddress(GetIP()); |
- |
- if (flat_ip == triggered_breakpoint_addr_) { |
- return ContinueFromBreakpoint(); |
- } else { |
- // Just in case user changed IP so that it's not pointing to |
- // triggered breakpoint, we need to: |
- // a) recover breakpoint |
- // b) make it not triggered |
- Breakpoint* br = |
- parent_process().GetBreakpoint(triggered_breakpoint_addr_); |
- if (NULL != br) |
- br->WriteBreakpointCode(); |
- triggered_breakpoint_addr_ = NULL; |
- } |
- } |
- if (kSingleStep == option) |
- EnableSingleStep(true); |
- |
- int flags = DBG_CONTINUE; |
- if (kContinueAndPassException == option) |
- flags = DBG_EXCEPTION_NOT_HANDLED; |
- |
- SetState(kRunning); |
- return (TRUE == debug_api().ContinueDebugEvent(parent_process().id(), |
- id(), |
- flags)); |
-} |
- |
-/// Implements first steps of 'Continue from breakpoint' algorithm, |
-/// described in |DebuggeeThread::OnDebugEvent| method. |
-bool DebuggeeThread::ContinueFromBreakpoint() { |
- SetState(kContinueFromBreakpoint); |
- EnableSingleStep(true); |
- return (TRUE == debug_api().ContinueDebugEvent(parent_process().id(), |
- id(), |
- DBG_CONTINUE)); |
-} |
- |
-void DebuggeeThread::OnSingleStep(DebugEvent* debug_event) { |
- if (kContinueFromBreakpoint == state_) { |
- if (NULL == triggered_breakpoint_addr_) { |
- DBG_LOG("ERR03.01", |
- "msg=OnSingleStepDueToContinueFromBreakpoint thread_id=%d" |
- " triggered_breakpoint_=NULL", |
- id()); |
- SetState(kHalted); |
- } else { |
- DBG_LOG("TR03.05", |
- "msg=OnSingleStepDueToContinueFromBreakpoint thread_id=%d" |
- " breakpoint_addr=0x%p", |
- id(), |
- triggered_breakpoint_addr_); |
- // We got here because thread was continuing from breakpoint. |
- Breakpoint* br = |
- parent_process().GetBreakpoint(triggered_breakpoint_addr_); |
- if (NULL != br) |
- br->WriteBreakpointCode(); |
- triggered_breakpoint_addr_ = NULL; |
- debug_api().ContinueDebugEvent(parent_process().id(), |
- id(), |
- DBG_CONTINUE); |
- SetState(kRunning); |
- } |
- } else { |
- SetState(kHalted); |
- } |
-} |
- |
-void DebuggeeThread::OnDebugEvent(DebugEvent* debug_event) { |
- DEBUG_EVENT de = debug_event->windows_debug_event(); |
- EnableSingleStep(false); |
- |
- // Thread expected 'SingleStep' exception due to 'continue from breakpoint' |
- // algorithm, but got something else. For example, it happens when breakpoint |
- // is set to instruction generating 'memory access violation' exception. |
- // |
- // Here's description of 'continue from breakpoint' algorithm: |
- // a) recover original code at breakpoint address |
- // b) enable single step mode of CPU |
- // c) call DebugAPI::ContinueDebugEvent |
- // d) receive SingleStep exception |
- // e) write breakpoint code at breakpoint address |
- // f) disable single step mode of CPU |
- // h) call DebugAPI::ContinueDebugEvent |
- // f) DebuggeeThread is now in 'kRunning' state. |
- // Note: we can get different exception on step (d), |
- // we have to do the following: |
- // 1) disable single step mode of CPU (it's done in first statement in |
- // |DebuggeeThread::OnDebugEvent| method). |
- // 2) write breakpoint code at breakpoint address, if IP points |
- // somewhere else. |
- if (!debug_event->IsSingleStep() && |
- (kContinueFromBreakpoint == state_) && |
- (NULL != triggered_breakpoint_addr_)) { |
- void* flat_ip = GetIP(); |
- if (IsNaClAppThread()) |
- flat_ip = parent_process().FromNexeToFlatAddress(flat_ip); |
- |
- if (flat_ip != triggered_breakpoint_addr_) { |
- Breakpoint* br = |
- parent_process().GetBreakpoint(triggered_breakpoint_addr_); |
- if (NULL != br) |
- br->WriteBreakpointCode(); |
- triggered_breakpoint_addr_ = NULL; |
- } |
- } |
- |
- // Now we can proceed with debug event. |
- switch (de.dwDebugEventCode) { |
- case OUTPUT_DEBUG_STRING_EVENT: { |
- OnOutputDebugString(debug_event); |
- break; |
- } |
- case EXIT_THREAD_DEBUG_EVENT: { |
- exit_code_ = de.u.ExitThread.dwExitCode; |
- break; |
- } |
- case EXIT_PROCESS_DEBUG_EVENT: { |
- exit_code_ = de.u.ExitProcess.dwExitCode; |
- break; |
- } |
- case EXCEPTION_DEBUG_EVENT: { |
- switch (debug_event->GetExceptionCode()) { |
- case EXCEPTION_BREAKPOINT: { |
- OnBreakpoint(debug_event); |
- break; |
- } |
- case EXCEPTION_SINGLE_STEP: { |
- OnSingleStep(debug_event); |
- // Thread halts on all other events unconditionally, |
- // but here |OnSingleStep| can decide to continue - |
- // in case it's due to 'continue from breakpoint'. |
- return; |
- } |
- } |
- } |
- } |
- // DebugeeThread halts on all debug events, with one exception - |
- // when it receives SingleStep while continue from breakpoint. |
- SetState(kHalted); |
-} |
- |
-void DebuggeeThread::EnableSingleStep(bool enable) { |
- DBG_LOG("TR03.06", |
- "msg='DebuggeeThread::EnableSingleStep(%s)' thread_id=%d", |
- enable ? "true" : "false", |
- id()); |
- |
- if (parent_process().IsWoW()) { |
- WOW64_CONTEXT context; |
- GetWowContext(&context); |
- if (enable) |
- context.EFlags |= 1 << 8; |
- else |
- context.EFlags &= ~(1 << 8); |
- SetWowContext(context); |
- } else { |
- CONTEXT context; |
- GetContext(&context); |
- if (enable) |
- context.EFlags |= 1 << 8; |
- else |
- context.EFlags &= ~(1 << 8); |
- SetContext(context); |
- } |
-} |
- |
-const char* DebuggeeThread::GetStateName(State state) { |
- switch (state) { |
- case kRunning: return "kRunning"; |
- case kHalted: return "kHalted"; |
- case kContinueFromBreakpoint: return "kContinueFromBreakpoint"; |
- case kDead: return "kDead"; |
- } |
- return "N/A"; |
-} |
- |
-const char* DebuggeeThread::GetContinueOptionName(ContinueOption option) { |
- switch (option) { |
- case kSingleStep: return "kSingleStep"; |
- case kContinue: return "kContinue"; |
- case kContinueAndPassException: return "kContinueAndPassException"; |
- } |
- return "N/A"; |
-} |
- |
-void DebuggeeThread::Kill() { |
- if (NULL != handle_) |
- debug_api().TerminateThread(handle_, 0); |
-} |
- |
-bool DebuggeeThread::GetContext(CONTEXT* context) { |
- if (!parent_process().IsHalted()) |
- return false; |
- |
- context->ContextFlags = CONTEXT_ALL; |
- return (debug_api().GetThreadContext(handle_, context) != FALSE); |
-} |
- |
-bool DebuggeeThread::SetContext(const CONTEXT& context) { |
- if (!parent_process().IsHalted()) |
- return false; |
- |
- CONTEXT context_copy = context; |
- return (debug_api().SetThreadContext(handle_, &context_copy) != FALSE); |
-} |
- |
-bool DebuggeeThread::GetWowContext(WOW64_CONTEXT* context) { |
- if (!parent_process().IsHalted()) |
- return false; |
- |
- context->ContextFlags = CONTEXT_ALL; |
- return (debug_api().Wow64GetThreadContext(handle_, context) != FALSE); |
-} |
- |
-bool DebuggeeThread::SetWowContext(const WOW64_CONTEXT& context) { |
- if (!parent_process().IsHalted()) |
- return false; |
- |
- return (debug_api().Wow64SetThreadContext(handle_, &context) != FALSE); |
-} |
- |
-void* DebuggeeThread::GetIP() { |
- if (!parent_process().IsHalted()) |
- return false; |
- |
- if (parent_process().IsWoW()) { |
- WOW64_CONTEXT context; |
- GetWowContext(&context); |
- return reinterpret_cast<void*>(context.Eip); |
- } else { |
- CONTEXT context; |
- GetContext(&context); |
-#ifdef _WIN64 |
- return reinterpret_cast<void*>(context.Rip); |
-#else |
- return reinterpret_cast<void*>(context.Eip); |
-#endif |
- } |
-} |
- |
-bool DebuggeeThread::SetIP(void* ip) { |
- if (!parent_process().IsHalted()) |
- return false; |
- |
- if (parent_process().IsWoW()) { |
- WOW64_CONTEXT context; |
- if (!GetWowContext(&context)) |
- return false; |
- context.Eip = reinterpret_cast<DWORD>(ip); |
- return SetWowContext(context); |
- } else { |
- CONTEXT ct; |
- if (!GetContext(&ct)) |
- return false; |
-#ifdef _WIN64 |
- ct.Rip = reinterpret_cast<DWORD64>(ip); |
-#else |
- ct.Eip = reinterpret_cast<DWORD>(ip); |
-#endif |
- return SetContext(ct); |
- } |
-} |
-} // namespace debug |
- |