aleksandr.urakov updated this revision to Diff 221671.
aleksandr.urakov added a comment.

Update the patch due to Pavel's request.


Repository:
  rLLDB LLDB

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D67222/new/

https://reviews.llvm.org/D67222

Files:
  lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
  
lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp
  
lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h
  
lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp
  lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h
  
lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp
  
lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h
  lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp

Index: lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
+++ lldb/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,
Index: lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.h
+++ lldb/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 &reg_value);
-
   Status GPRWrite(const uint32_t reg, const RegisterValue &reg_value);
 
   Status FPRRead(const uint32_t reg, RegisterValue &reg_value);
-
   Status FPRWrite(const uint32_t reg, const RegisterValue &reg_value);
 
+  Status DRRead(const uint32_t reg, RegisterValue &reg_value);
+  Status DRWrite(const uint32_t reg, const RegisterValue &reg_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/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp
+++ lldb/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<NativeRegisterContextWindows_WoW64>(target_arch,
-                                                                 native_thread);
+                                                                native_thread);
 
   // Register context for a native 64-bit application.
   return std::make_unique<NativeRegisterContextWindows_x86_64>(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 &reg_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 &reg_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 &reg_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/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.h
+++ lldb/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 &reg_value);
   Status GPRWrite(const uint32_t reg, const RegisterValue &reg_value);
 
+  Status DRRead(const uint32_t reg, RegisterValue &reg_value);
+  Status DRWrite(const uint32_t reg, const RegisterValue &reg_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/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp
+++ lldb/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<NativeRegisterContextWindows_i386>(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 &reg_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 &reg_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 &reg_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/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.h
+++ lldb/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 &reg_value);
   Status GPRWrite(const uint32_t reg, const RegisterValue &reg_value);
 
+  Status DRRead(const uint32_t reg, RegisterValue &reg_value);
+  Status DRWrite(const uint32_t reg, const RegisterValue &reg_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/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp
+++ lldb/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 &reg_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 &reg_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 &reg_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/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
===================================================================
--- lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ lldb/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 &reg_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<NativeThreadWindows>(*this, new_thread));
+
+  auto thread = std::make_unique<NativeThreadWindows>(*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,
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to