Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -431,13 +431,34 @@ ExceptionResult result = ExceptionResult::SendToApplication; switch (record.GetExceptionCode()) { case DWORD(STATUS_SINGLE_STEP): - case STATUS_WX86_SINGLE_STEP: - StopThread(record.GetThreadID(), StopReason::eStopReasonTrace); + case STATUS_WX86_SINGLE_STEP: { + uint32_t wp_id = LLDB_INVALID_INDEX32; + if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) { + NativeRegisterContextWindows ®_ctx = thread->GetRegisterContext(); + Status error = + reg_ctx.GetWatchpointHitIndex(wp_id, record.GetExceptionAddress()); + if (error.Fail()) + LLDB_LOG(log, + "received error while checking for watchpoint hits, pid = " + "{0}, error = {1}", + thread->GetID(), error); + if (wp_id != LLDB_INVALID_INDEX32) { + addr_t wp_addr = reg_ctx.GetWatchpointAddress(wp_id); + addr_t wp_hit_addr = reg_ctx.GetWatchpointHitAddress(wp_id); + std::string desc = + formatv("{0} {1} {2}", wp_addr, wp_id, wp_hit_addr).str(); + StopThread(record.GetThreadID(), StopReason::eStopReasonWatchpoint, + desc); + } + } + if (wp_id == LLDB_INVALID_INDEX32) + StopThread(record.GetThreadID(), StopReason::eStopReasonTrace); + SetState(eStateStopped, true); // Continue the debugger. return ExceptionResult::MaskException; - + } case DWORD(STATUS_BREAKPOINT): case STATUS_WX86_BREAKPOINT: if (FindSoftwareBreakpoint(record.GetExceptionAddress())) { @@ -513,8 +534,16 @@ void NativeProcessWindows::OnCreateThread(const HostThread &new_thread) { llvm::sys::ScopedLock lock(m_mutex); - m_threads.push_back( - std::make_unique(*this, new_thread)); + + auto thread = std::make_unique(*this, new_thread); + thread->GetRegisterContext().ClearAllHardwareWatchpoints(); + for (const auto &pair : GetWatchpointMap()) { + const NativeWatchpoint &wp = pair.second; + thread->SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, + wp.m_hardware); + } + + m_threads.push_back(std::move(thread)); } void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id, Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h @@ -49,10 +49,6 @@ Status ClearAllHardwareWatchpoints() override; - Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, - uint32_t watch_flags, - uint32_t wp_index); - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) override; @@ -64,8 +60,15 @@ Status GPRRead(const uint32_t reg, RegisterValue ®_value); Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + Status DRRead(const uint32_t reg, RegisterValue ®_value); + Status DRWrite(const uint32_t reg, const RegisterValue ®_value); + private: + Status ApplyHardwareBreakpoint(uint32_t wp_index, lldb::addr_t addr, + size_t size, uint32_t flags); + bool IsGPR(uint32_t reg_index) const; + bool IsDR(uint32_t reg_index) const; }; } // namespace lldb_private Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp @@ -56,12 +56,14 @@ return new RegisterContextWindows_i386(target_arch); } -static Status GetWoW64ThreadContextHelper(lldb::thread_t thread_handle, - PWOW64_CONTEXT context_ptr) { +static Status +GetWoW64ThreadContextHelper(lldb::thread_t thread_handle, + PWOW64_CONTEXT context_ptr, + const DWORD control_flag = kWoW64ContextFlags) { Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_REGISTERS); Status error; memset(context_ptr, 0, sizeof(::WOW64_CONTEXT)); - context_ptr->ContextFlags = kWoW64ContextFlags; + context_ptr->ContextFlags = control_flag; if (!::Wow64GetThreadContext(thread_handle, context_ptr)) { error.SetError(GetLastError(), eErrorTypeWin32); LLDB_LOG(log, "{0} Wow64GetThreadContext failed with error {1}", @@ -93,6 +95,10 @@ return (reg_index >= k_first_gpr_i386 && reg_index < k_first_alias_i386); } +bool NativeRegisterContextWindows_WoW64::IsDR(uint32_t reg_index) const { + return (reg_index >= lldb_dr0_i386 && reg_index <= lldb_dr7_i386); +} + uint32_t NativeRegisterContextWindows_WoW64::GetRegisterSetCount() const { return k_num_register_sets; } @@ -228,6 +234,82 @@ return SetWoW64ThreadContextHelper(thread_handle, &tls_context); } +Status NativeRegisterContextWindows_WoW64::DRRead(const uint32_t reg, + RegisterValue ®_value) { + ::WOW64_CONTEXT tls_context; + DWORD context_flag = CONTEXT_DEBUG_REGISTERS; + Status error = GetWoW64ThreadContextHelper(GetThreadHandle(), &tls_context, + context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_dr0_i386: + reg_value.SetUInt32(tls_context.Dr0); + break; + case lldb_dr1_i386: + reg_value.SetUInt32(tls_context.Dr1); + break; + case lldb_dr2_i386: + reg_value.SetUInt32(tls_context.Dr2); + break; + case lldb_dr3_i386: + reg_value.SetUInt32(tls_context.Dr3); + break; + case lldb_dr4_i386: + return Status("register DR4 is obsolete"); + case lldb_dr5_i386: + return Status("register DR5 is obsolete"); + case lldb_dr6_i386: + reg_value.SetUInt32(tls_context.Dr6); + break; + case lldb_dr7_i386: + reg_value.SetUInt32(tls_context.Dr7); + break; + } + + return {}; +} + +Status +NativeRegisterContextWindows_WoW64::DRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::WOW64_CONTEXT tls_context; + DWORD context_flag = CONTEXT_DEBUG_REGISTERS; + auto thread_handle = GetThreadHandle(); + Status error = + GetWoW64ThreadContextHelper(thread_handle, &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_dr0_i386: + tls_context.Dr0 = reg_value.GetAsUInt32(); + break; + case lldb_dr1_i386: + tls_context.Dr1 = reg_value.GetAsUInt32(); + break; + case lldb_dr2_i386: + tls_context.Dr2 = reg_value.GetAsUInt32(); + break; + case lldb_dr3_i386: + tls_context.Dr3 = reg_value.GetAsUInt32(); + break; + case lldb_dr4_i386: + return Status("register DR4 is obsolete"); + case lldb_dr5_i386: + return Status("register DR5 is obsolete"); + case lldb_dr6_i386: + tls_context.Dr6 = reg_value.GetAsUInt32(); + break; + case lldb_dr7_i386: + tls_context.Dr7 = reg_value.GetAsUInt32(); + break; + } + + return SetWoW64ThreadContextHelper(thread_handle, &tls_context); +} + Status NativeRegisterContextWindows_WoW64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { @@ -250,6 +332,9 @@ if (IsGPR(reg)) return GPRRead(reg, reg_value); + if (IsDR(reg)) + return DRRead(reg, reg_value); + return Status("unimplemented"); } @@ -275,6 +360,9 @@ if (IsGPR(reg)) return GPRWrite(reg, reg_value); + if (IsDR(reg)) + return DRWrite(reg, reg_value); + return Status("unimplemented"); } @@ -317,46 +405,198 @@ Status NativeRegisterContextWindows_WoW64::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { - return Status("unimplemented"); + is_hit = false; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("watchpoint index out of range"); + + RegisterValue reg_value; + Status error = DRRead(lldb_dr6_i386, reg_value); + if (error.Fail()) + return error; + + is_hit = reg_value.GetAsUInt32() & (1 << wp_index); + + return {}; } Status NativeRegisterContextWindows_WoW64::GetWatchpointHitIndex( uint32_t &wp_index, lldb::addr_t trap_addr) { - return Status("unimplemented"); + wp_index = LLDB_INVALID_INDEX32; + + for (uint32_t i = 0; i < NumSupportedHardwareWatchpoints(); i++) { + bool is_hit; + Status error = IsWatchpointHit(i, is_hit); + if (error.Fail()) + return error; + + if (is_hit) { + wp_index = i; + return {}; + } + } + + return {}; } Status NativeRegisterContextWindows_WoW64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) { - return Status("unimplemented"); -} + is_vacant = false; -Status NativeRegisterContextWindows_WoW64::SetHardwareWatchpointWithIndex( - lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { - return Status("unimplemented"); + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + Status error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return error; + + is_vacant = !(reg_value.GetAsUInt32() & (1 << (2 * wp_index))); + + return error; } bool NativeRegisterContextWindows_WoW64::ClearHardwareWatchpoint( uint32_t wp_index) { - return false; + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of + // the debug status register (DR6) + + RegisterValue reg_value; + Status error = DRRead(lldb_dr6_i386, reg_value); + if (error.Fail()) + return false; + + uint32_t bit_mask = 1 << wp_index; + uint32_t status_bits = reg_value.GetAsUInt32() & ~bit_mask; + error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits)); + if (error.Fail()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19}, + // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register + // (DR7) + + error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return false; + + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask; + return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)).Success(); } Status NativeRegisterContextWindows_WoW64::ClearAllHardwareWatchpoints() { - return Status("unimplemented"); + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + + Status error = DRRead(lldb_dr6_i386, reg_value); + if (error.Fail()) + return error; + + uint32_t status_bits = reg_value.GetAsUInt32() & ~0xF; + error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits)); + if (error.Fail()) + return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + + error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return error; + + uint32_t control_bits = reg_value.GetAsUInt32() & ~0xFFFF00FF; + return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)); } uint32_t NativeRegisterContextWindows_WoW64::SetHardwareWatchpoint( lldb::addr_t addr, size_t size, uint32_t watch_flags) { + switch (size) { + case 1: + case 2: + case 4: + break; + default: + return LLDB_INVALID_INDEX32; + } + + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return LLDB_INVALID_INDEX32; + + for (uint32_t wp_index = 0; wp_index < NumSupportedHardwareWatchpoints(); + ++wp_index) { + bool is_vacant; + if (IsWatchpointVacant(wp_index, is_vacant).Fail()) + return LLDB_INVALID_INDEX32; + + if (is_vacant) { + if (!ClearHardwareWatchpoint(wp_index)) + return LLDB_INVALID_INDEX32; + + if (ApplyHardwareBreakpoint(wp_index, addr, size, watch_flags).Fail()) + return LLDB_INVALID_INDEX32; + + return wp_index; + } + } return LLDB_INVALID_INDEX32; } +Status NativeRegisterContextWindows_WoW64::ApplyHardwareBreakpoint( + uint32_t wp_index, lldb::addr_t addr, size_t size, uint32_t flags) { + RegisterValue reg_value; + auto error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return error; + + // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 + uint32_t enable_bit = 1 << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint32_t rw_bits = flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint32_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint32_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask; + control_bits |= enable_bit | rw_bits | size_bits; + + error = DRWrite(lldb_dr7_i386, RegisterValue(control_bits)); + if (error.Fail()) + return error; + + error = DRWrite(lldb_dr0_i386 + wp_index, RegisterValue(addr)); + if (error.Fail()) + return error; + + return {}; +} + lldb::addr_t NativeRegisterContextWindows_WoW64::GetWatchpointAddress(uint32_t wp_index) { - return LLDB_INVALID_ADDRESS; + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + RegisterValue reg_value; + if (DRRead(lldb_dr0_i386 + wp_index, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + + return reg_value.GetAsUInt32(); } uint32_t NativeRegisterContextWindows_WoW64::NumSupportedHardwareWatchpoints() { - // Not implemented - return 0; + return 4; } #endif // defined(__x86_64__) || defined(_M_X64) Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h @@ -49,10 +49,6 @@ Status ClearAllHardwareWatchpoints() override; - Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, - uint32_t watch_flags, - uint32_t wp_index); - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) override; @@ -64,8 +60,15 @@ Status GPRRead(const uint32_t reg, RegisterValue ®_value); Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); + Status DRRead(const uint32_t reg, RegisterValue ®_value); + Status DRWrite(const uint32_t reg, const RegisterValue ®_value); + private: + Status ApplyHardwareBreakpoint(uint32_t wp_index, lldb::addr_t addr, + size_t size, uint32_t flags); + bool IsGPR(uint32_t reg_index) const; + bool IsDR(uint32_t reg_index) const; }; } // namespace lldb_private Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp @@ -87,7 +87,7 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows( const ArchSpec &target_arch, NativeThreadProtocol &native_thread) { return std::make_unique(target_arch, - native_thread); + native_thread); } NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386( @@ -99,6 +99,10 @@ return (reg_index < k_first_alias_i386); } +bool NativeRegisterContextWindows_i386::IsDR(uint32_t reg_index) const { + return (reg_index >= lldb_dr0_i386 && reg_index <= lldb_dr7_i386); +} + uint32_t NativeRegisterContextWindows_i386::GetRegisterSetCount() const { return k_num_register_sets; } @@ -238,6 +242,82 @@ return SetThreadContextHelper(thread_handle, &tls_context); } +Status NativeRegisterContextWindows_i386::DRRead(const uint32_t reg, + RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_DEBUG_REGISTERS; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_dr0_i386: + reg_value.SetUInt32(tls_context.Dr0); + break; + case lldb_dr1_i386: + reg_value.SetUInt32(tls_context.Dr1); + break; + case lldb_dr2_i386: + reg_value.SetUInt32(tls_context.Dr2); + break; + case lldb_dr3_i386: + reg_value.SetUInt32(tls_context.Dr3); + break; + case lldb_dr4_i386: + return Status("register DR4 is obsolete"); + case lldb_dr5_i386: + return Status("register DR5 is obsolete"); + case lldb_dr6_i386: + reg_value.SetUInt32(tls_context.Dr6); + break; + case lldb_dr7_i386: + reg_value.SetUInt32(tls_context.Dr7); + break; + } + + return {}; +} + +Status +NativeRegisterContextWindows_i386::DRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_DEBUG_REGISTERS; + auto thread_handle = GetThreadHandle(); + Status error = + GetThreadContextHelper(thread_handle, &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_dr0_i386: + tls_context.Dr0 = reg_value.GetAsUInt32(); + break; + case lldb_dr1_i386: + tls_context.Dr1 = reg_value.GetAsUInt32(); + break; + case lldb_dr2_i386: + tls_context.Dr2 = reg_value.GetAsUInt32(); + break; + case lldb_dr3_i386: + tls_context.Dr3 = reg_value.GetAsUInt32(); + break; + case lldb_dr4_i386: + return Status("register DR4 is obsolete"); + case lldb_dr5_i386: + return Status("register DR5 is obsolete"); + case lldb_dr6_i386: + tls_context.Dr6 = reg_value.GetAsUInt32(); + break; + case lldb_dr7_i386: + tls_context.Dr7 = reg_value.GetAsUInt32(); + break; + } + + return SetThreadContextHelper(thread_handle, &tls_context); +} + Status NativeRegisterContextWindows_i386::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { @@ -261,6 +341,9 @@ if (IsGPR(reg)) return GPRRead(reg, reg_value); + if (IsDR(reg)) + return DRRead(reg, reg_value); + return Status("unimplemented"); } @@ -286,6 +369,9 @@ if (IsGPR(reg)) return GPRWrite(reg, reg_value); + if (IsDR(reg)) + return DRWrite(reg, reg_value); + return Status("unimplemented"); } @@ -329,46 +415,198 @@ Status NativeRegisterContextWindows_i386::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { - return Status("unimplemented"); + is_hit = false; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("watchpoint index out of range"); + + RegisterValue reg_value; + Status error = DRRead(lldb_dr6_i386, reg_value); + if (error.Fail()) + return error; + + is_hit = reg_value.GetAsUInt32() & (1 << wp_index); + + return {}; } Status NativeRegisterContextWindows_i386::GetWatchpointHitIndex( uint32_t &wp_index, lldb::addr_t trap_addr) { - return Status("unimplemented"); + wp_index = LLDB_INVALID_INDEX32; + + for (uint32_t i = 0; i < NumSupportedHardwareWatchpoints(); i++) { + bool is_hit; + Status error = IsWatchpointHit(i, is_hit); + if (error.Fail()) + return error; + + if (is_hit) { + wp_index = i; + return {}; + } + } + + return {}; } Status NativeRegisterContextWindows_i386::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) { - return Status("unimplemented"); -} + is_vacant = false; -Status NativeRegisterContextWindows_i386::SetHardwareWatchpointWithIndex( - lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { - return Status("unimplemented"); + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + Status error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return error; + + is_vacant = !(reg_value.GetAsUInt32() & (1 << (2 * wp_index))); + + return error; } bool NativeRegisterContextWindows_i386::ClearHardwareWatchpoint( uint32_t wp_index) { - return false; + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of + // the debug status register (DR6) + + RegisterValue reg_value; + Status error = DRRead(lldb_dr6_i386, reg_value); + if (error.Fail()) + return false; + + uint32_t bit_mask = 1 << wp_index; + uint32_t status_bits = reg_value.GetAsUInt32() & ~bit_mask; + error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits)); + if (error.Fail()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19}, + // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register + // (DR7) + + error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return false; + + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask; + return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)).Success(); } Status NativeRegisterContextWindows_i386::ClearAllHardwareWatchpoints() { - return Status("unimplemented"); + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + + Status error = DRRead(lldb_dr6_i386, reg_value); + if (error.Fail()) + return error; + + uint32_t status_bits = reg_value.GetAsUInt32() & ~0xF; + error = DRWrite(lldb_dr6_i386, RegisterValue(status_bits)); + if (error.Fail()) + return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + + error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return error; + + uint32_t control_bits = reg_value.GetAsUInt32() & ~0xFFFF00FF; + return DRWrite(lldb_dr7_i386, RegisterValue(control_bits)); } uint32_t NativeRegisterContextWindows_i386::SetHardwareWatchpoint( lldb::addr_t addr, size_t size, uint32_t watch_flags) { + switch (size) { + case 1: + case 2: + case 4: + break; + default: + return LLDB_INVALID_INDEX32; + } + + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return LLDB_INVALID_INDEX32; + + for (uint32_t wp_index = 0; wp_index < NumSupportedHardwareWatchpoints(); + ++wp_index) { + bool is_vacant; + if (IsWatchpointVacant(wp_index, is_vacant).Fail()) + return LLDB_INVALID_INDEX32; + + if (is_vacant) { + if (!ClearHardwareWatchpoint(wp_index)) + return LLDB_INVALID_INDEX32; + + if (ApplyHardwareBreakpoint(wp_index, addr, size, watch_flags).Fail()) + return LLDB_INVALID_INDEX32; + + return wp_index; + } + } return LLDB_INVALID_INDEX32; } +Status NativeRegisterContextWindows_i386::ApplyHardwareBreakpoint( + uint32_t wp_index, lldb::addr_t addr, size_t size, uint32_t flags) { + RegisterValue reg_value; + auto error = DRRead(lldb_dr7_i386, reg_value); + if (error.Fail()) + return error; + + // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 + uint32_t enable_bit = 1 << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint32_t rw_bits = flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint32_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint32_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint32_t control_bits = reg_value.GetAsUInt32() & ~bit_mask; + control_bits |= enable_bit | rw_bits | size_bits; + + error = DRWrite(lldb_dr7_i386, RegisterValue(control_bits)); + if (error.Fail()) + return error; + + error = DRWrite(lldb_dr0_i386 + wp_index, RegisterValue(addr)); + if (error.Fail()) + return error; + + return {}; +} + lldb::addr_t NativeRegisterContextWindows_i386::GetWatchpointAddress(uint32_t wp_index) { - return LLDB_INVALID_ADDRESS; + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + RegisterValue reg_value; + if (DRRead(lldb_dr0_i386 + wp_index, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + + return reg_value.GetAsUInt32(); } uint32_t NativeRegisterContextWindows_i386::NumSupportedHardwareWatchpoints() { - // Not implemented - return 0; + return 4; } #endif Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h @@ -50,10 +50,6 @@ Status ClearAllHardwareWatchpoints() override; - Status SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, - uint32_t watch_flags, - uint32_t wp_index); - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags) override; @@ -63,17 +59,21 @@ protected: Status GPRRead(const uint32_t reg, RegisterValue ®_value); - Status GPRWrite(const uint32_t reg, const RegisterValue ®_value); Status FPRRead(const uint32_t reg, RegisterValue ®_value); - Status FPRWrite(const uint32_t reg, const RegisterValue ®_value); + Status DRRead(const uint32_t reg, RegisterValue ®_value); + Status DRWrite(const uint32_t reg, const RegisterValue ®_value); + private: - bool IsGPR(uint32_t reg_index) const; + Status ApplyHardwareBreakpoint(uint32_t wp_index, lldb::addr_t addr, + size_t size, uint32_t flags); + bool IsGPR(uint32_t reg_index) const; bool IsFPR(uint32_t reg_index) const; + bool IsDR(uint32_t reg_index) const; }; } // namespace lldb_private Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp @@ -101,11 +101,11 @@ // Register context for a WoW64 application. if (target_arch.GetAddressByteSize() == 4) return std::make_unique(target_arch, - native_thread); + native_thread); // Register context for a native 64-bit application. return std::make_unique(target_arch, - native_thread); + native_thread); } NativeRegisterContextWindows_x86_64::NativeRegisterContextWindows_x86_64( @@ -121,6 +121,10 @@ return (reg_index >= lldb_xmm0_x86_64 && reg_index <= k_last_fpr_x86_64); } +bool NativeRegisterContextWindows_x86_64::IsDR(uint32_t reg_index) const { + return (reg_index >= lldb_dr0_x86_64 && reg_index <= lldb_dr7_x86_64); +} + uint32_t NativeRegisterContextWindows_x86_64::GetRegisterSetCount() const { return k_num_register_sets; } @@ -436,6 +440,82 @@ return SetThreadContextHelper(thread_handle, &tls_context); } +Status NativeRegisterContextWindows_x86_64::DRRead(const uint32_t reg, + RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_DEBUG_REGISTERS; + Status error = + GetThreadContextHelper(GetThreadHandle(), &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_dr0_x86_64: + reg_value.SetUInt64(tls_context.Dr0); + break; + case lldb_dr1_x86_64: + reg_value.SetUInt64(tls_context.Dr1); + break; + case lldb_dr2_x86_64: + reg_value.SetUInt64(tls_context.Dr2); + break; + case lldb_dr3_x86_64: + reg_value.SetUInt64(tls_context.Dr3); + break; + case lldb_dr4_x86_64: + return Status("register DR4 is obsolete"); + case lldb_dr5_x86_64: + return Status("register DR5 is obsolete"); + case lldb_dr6_x86_64: + reg_value.SetUInt64(tls_context.Dr6); + break; + case lldb_dr7_x86_64: + reg_value.SetUInt64(tls_context.Dr7); + break; + } + + return {}; +} + +Status +NativeRegisterContextWindows_x86_64::DRWrite(const uint32_t reg, + const RegisterValue ®_value) { + ::CONTEXT tls_context; + DWORD context_flag = CONTEXT_DEBUG_REGISTERS; + auto thread_handle = GetThreadHandle(); + Status error = + GetThreadContextHelper(thread_handle, &tls_context, context_flag); + if (error.Fail()) + return error; + + switch (reg) { + case lldb_dr0_x86_64: + tls_context.Dr0 = reg_value.GetAsUInt64(); + break; + case lldb_dr1_x86_64: + tls_context.Dr1 = reg_value.GetAsUInt64(); + break; + case lldb_dr2_x86_64: + tls_context.Dr2 = reg_value.GetAsUInt64(); + break; + case lldb_dr3_x86_64: + tls_context.Dr3 = reg_value.GetAsUInt64(); + break; + case lldb_dr4_x86_64: + return Status("register DR4 is obsolete"); + case lldb_dr5_x86_64: + return Status("register DR5 is obsolete"); + case lldb_dr6_x86_64: + tls_context.Dr6 = reg_value.GetAsUInt64(); + break; + case lldb_dr7_x86_64: + tls_context.Dr7 = reg_value.GetAsUInt64(); + break; + } + + return SetThreadContextHelper(thread_handle, &tls_context); +} + Status NativeRegisterContextWindows_x86_64::ReadRegister(const RegisterInfo *reg_info, RegisterValue ®_value) { @@ -461,6 +541,9 @@ if (IsFPR(reg)) return FPRRead(reg, reg_value); + if (IsDR(reg)) + return DRRead(reg, reg_value); + return Status("unimplemented"); } @@ -489,6 +572,9 @@ if (IsFPR(reg)) return FPRWrite(reg, reg_value); + if (IsDR(reg)) + return DRWrite(reg, reg_value); + return Status("unimplemented"); } @@ -532,48 +618,201 @@ Status NativeRegisterContextWindows_x86_64::IsWatchpointHit(uint32_t wp_index, bool &is_hit) { - return Status("unimplemented"); + is_hit = false; + + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("watchpoint index out of range"); + + RegisterValue reg_value; + Status error = DRRead(lldb_dr6_x86_64, reg_value); + if (error.Fail()) + return error; + + is_hit = reg_value.GetAsUInt64() & (1ULL << wp_index); + + return {}; } Status NativeRegisterContextWindows_x86_64::GetWatchpointHitIndex( uint32_t &wp_index, lldb::addr_t trap_addr) { - return Status("unimplemented"); + wp_index = LLDB_INVALID_INDEX32; + + for (uint32_t i = 0; i < NumSupportedHardwareWatchpoints(); i++) { + bool is_hit; + Status error = IsWatchpointHit(i, is_hit); + if (error.Fail()) + return error; + + if (is_hit) { + wp_index = i; + return {}; + } + } + + return {}; } Status NativeRegisterContextWindows_x86_64::IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) { - return Status("unimplemented"); -} + is_vacant = false; -Status NativeRegisterContextWindows_x86_64::SetHardwareWatchpointWithIndex( - lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) { - return Status("unimplemented"); + if (wp_index >= NumSupportedHardwareWatchpoints()) + return Status("Watchpoint index out of range"); + + RegisterValue reg_value; + Status error = DRRead(lldb_dr7_x86_64, reg_value); + if (error.Fail()) + return error; + + is_vacant = !(reg_value.GetAsUInt64() & (1ULL << (2 * wp_index))); + + return error; } bool NativeRegisterContextWindows_x86_64::ClearHardwareWatchpoint( uint32_t wp_index) { - return false; + if (wp_index >= NumSupportedHardwareWatchpoints()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits 0, 1, 2, or 3 of + // the debug status register (DR6) + + RegisterValue reg_value; + Status error = DRRead(lldb_dr6_x86_64, reg_value); + if (error.Fail()) + return false; + + uint64_t bit_mask = 1ULL << wp_index; + uint64_t status_bits = reg_value.GetAsUInt64() & ~bit_mask; + error = DRWrite(lldb_dr6_x86_64, RegisterValue(status_bits)); + if (error.Fail()) + return false; + + // for watchpoints 0, 1, 2, or 3, respectively, clear bits {0-1,16-19}, + // {2-3,20-23}, {4-5,24-27}, or {6-7,28-31} of the debug control register + // (DR7) + + error = DRRead(lldb_dr7_x86_64, reg_value); + if (error.Fail()) + return false; + + bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + return DRWrite(lldb_dr7_x86_64, RegisterValue(control_bits)).Success(); } Status NativeRegisterContextWindows_x86_64::ClearAllHardwareWatchpoints() { - return Status("unimplemented"); + RegisterValue reg_value; + + // clear bits {0-4} of the debug status register (DR6) + + Status error = DRRead(lldb_dr6_x86_64, reg_value); + if (error.Fail()) + return error; + + uint64_t status_bits = reg_value.GetAsUInt64() & ~0xFULL; + error = DRWrite(lldb_dr6_x86_64, RegisterValue(status_bits)); + if (error.Fail()) + return error; + + // clear bits {0-7,16-31} of the debug control register (DR7) + + error = DRRead(lldb_dr7_x86_64, reg_value); + if (error.Fail()) + return error; + + uint64_t control_bits = reg_value.GetAsUInt64() & ~0xFFFF00FFULL; + return DRWrite(lldb_dr7_x86_64, RegisterValue(control_bits)); } uint32_t NativeRegisterContextWindows_x86_64::SetHardwareWatchpoint( lldb::addr_t addr, size_t size, uint32_t watch_flags) { + switch (size) { + case 1: + case 2: + case 4: + case 8: + break; + default: + return LLDB_INVALID_INDEX32; + } + + if (watch_flags == 0x2) + watch_flags = 0x3; + + if (watch_flags != 0x1 && watch_flags != 0x3) + return LLDB_INVALID_INDEX32; + + for (uint32_t wp_index = 0; wp_index < NumSupportedHardwareWatchpoints(); + ++wp_index) { + bool is_vacant; + if (IsWatchpointVacant(wp_index, is_vacant).Fail()) + return LLDB_INVALID_INDEX32; + + if (is_vacant) { + if (!ClearHardwareWatchpoint(wp_index)) + return LLDB_INVALID_INDEX32; + + if (ApplyHardwareBreakpoint(wp_index, addr, size, watch_flags).Fail()) + return LLDB_INVALID_INDEX32; + + return wp_index; + } + } return LLDB_INVALID_INDEX32; } +Status NativeRegisterContextWindows_x86_64::ApplyHardwareBreakpoint( + uint32_t wp_index, lldb::addr_t addr, size_t size, uint32_t flags) { + RegisterValue reg_value; + auto error = DRRead(lldb_dr7_x86_64, reg_value); + if (error.Fail()) + return error; + + // for watchpoints 0, 1, 2, or 3, respectively, set bits 1, 3, 5, or 7 + uint64_t enable_bit = 1ULL << (2 * wp_index); + + // set bits 16-17, 20-21, 24-25, or 28-29 + // with 0b01 for write, and 0b11 for read/write + uint64_t rw_bits = flags << (16 + 4 * wp_index); + + // set bits 18-19, 22-23, 26-27, or 30-31 + // with 0b00, 0b01, 0b10, or 0b11 + // for 1, 2, 8 (if supported), or 4 bytes, respectively + uint64_t size_bits = (size == 8 ? 0x2 : size - 1) << (18 + 4 * wp_index); + + uint64_t bit_mask = (0x3 << (2 * wp_index)) | (0xF << (16 + 4 * wp_index)); + + uint64_t control_bits = reg_value.GetAsUInt64() & ~bit_mask; + control_bits |= enable_bit | rw_bits | size_bits; + + error = DRWrite(lldb_dr7_x86_64, RegisterValue(control_bits)); + if (error.Fail()) + return error; + + error = DRWrite(lldb_dr0_x86_64 + wp_index, RegisterValue(addr)); + if (error.Fail()) + return error; + + return {}; +} + lldb::addr_t NativeRegisterContextWindows_x86_64::GetWatchpointAddress(uint32_t wp_index) { - return LLDB_INVALID_ADDRESS; + if (wp_index >= NumSupportedHardwareWatchpoints()) + return LLDB_INVALID_ADDRESS; + + RegisterValue reg_value; + if (DRRead(lldb_dr0_x86_64 + wp_index, reg_value).Fail()) + return LLDB_INVALID_ADDRESS; + + return reg_value.GetAsUInt64(); } uint32_t NativeRegisterContextWindows_x86_64::NumSupportedHardwareWatchpoints() { - // Not implemented - return 0; + return 4; } #endif // defined(__x86_64__) || defined(_M_X64) Index: lldb/trunk/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp +++ lldb/trunk/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp @@ -132,11 +132,30 @@ Status NativeThreadWindows::SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, bool hardware) { - return Status("unimplemented."); + if (!hardware) + return Status("not implemented"); + if (m_state == eStateLaunching) + return Status(); + Status error = RemoveWatchpoint(addr); + if (error.Fail()) + return error; + uint32_t wp_index = + m_reg_context_up->SetHardwareWatchpoint(addr, size, watch_flags); + if (wp_index == LLDB_INVALID_INDEX32) + return Status("Setting hardware watchpoint failed."); + m_watchpoint_index_map.insert({addr, wp_index}); + return Status(); } Status NativeThreadWindows::RemoveWatchpoint(lldb::addr_t addr) { - return Status("unimplemented"); + auto wp = m_watchpoint_index_map.find(addr); + if (wp == m_watchpoint_index_map.end()) + return Status(); + uint32_t wp_index = wp->second; + m_watchpoint_index_map.erase(wp); + if (m_reg_context_up->ClearHardwareWatchpoint(wp_index)) + return Status(); + return Status("Clearing hardware watchpoint failed."); } Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr,