Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lldb][Windows] WoA HW Watchpoint support in LLDB #108072

Merged
merged 7 commits into from
Jan 31, 2025

Conversation

omjavaid
Copy link
Contributor

@omjavaid omjavaid commented Sep 10, 2024

This PR adds support for hardware watchpoints in LLDB for AArch64 Windows
targets.

Windows does not provide an API to query the number of available hardware
watchpoints supported by underlying hardware platform. Therefore, current
implementation supports only a single hardware watchpoint, which has been
verified on Windows 11 using Microsoft SQ2 and Snapdragon Elite X hardware.

LLDB test suite ninja check-lldb still fails watchpoint-related tests. However,
tests that do not require more than a single watchpoint pass successfully
when run individually.

Known Issues (Just for documentation):

  1. Number of Supported Hardware Breakpoints/Watchpoints: Windows does not provide the exact number of supported hardware breakpoints or watchpoints. The current implementation guesses this number based on testing how many can be successfully applied on the platform. winnt.h defines ARM64_MAX_WATCHPOINTS = 2 and ARM64_MAX_BREAKPOINTS = 8 however actual number differs.

  2. Initial Stop Behavior: Hardware breakpoints or watchpoints set on the initial stop do not trigger, even though they are correctly written to the Windows context. They only trigger if set after the main program has started. This issue causes the test suite to fail when it attempts to set hardware breakpoints on the main function before running, as these do not get triggered.

@llvmbot
Copy link
Member

llvmbot commented Sep 10, 2024

@llvm/pr-subscribers-lldb

Author: Omair Javaid (omjavaid)

Changes

This pull request adds support for hardware breakpoints and watchpoints in LLDB on Windows on ARM.

Known Issues:

  1. Number of Supported Hardware Breakpoints/Watchpoints: Windows does not provide the exact number of supported hardware breakpoints or watchpoints. The current implementation guesses this number based on testing how many can be successfully applied on the platform. winnt.h defines ARM64_MAX_WATCHPOINTS = 2 and ARM64_MAX_BREAKPOINTS = 8 however actual number differs.

  2. Initial Stop Behavior: Hardware breakpoints or watchpoints set on the initial stop do not trigger, even though they are correctly written to the Windows context. They only trigger if set after the main program has started. This issue causes the test suite to fail when it attempts to set hardware breakpoints on the main function before running, as these do not get triggered.

Testing:

  1. Tested setting 1 hardware watchpoint and 6 hardware breakpoints on snapdragon elite x hardware.
  2. The LLDB test suite currently fails related tests due to the initial stop behavior issue described above.

Full diff: https://github.com/llvm/llvm-project/pull/108072.diff

10 Files Affected:

  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp (+38-14)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp (-3)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h (+1-4)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp (+1-1)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp (+1-1)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp (+46-40)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h (+8-23)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp (+1-1)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp (+1-1)
  • (modified) lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp (+32)
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index 24c9aa6b32659d..886df987dc84b6 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -491,24 +491,47 @@ NativeProcessWindows::OnDebugException(bool first_chance,
     return ExceptionResult::MaskException;
   }
   case DWORD(STATUS_BREAKPOINT):
-  case STATUS_WX86_BREAKPOINT:
-    if (FindSoftwareBreakpoint(record.GetExceptionAddress())) {
-      LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
-               record.GetExceptionAddress());
-
-      StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint);
-
-      if (NativeThreadWindows *stop_thread =
-              GetThreadByID(record.GetThreadID())) {
-        auto &register_context = stop_thread->GetRegisterContext();
+  case STATUS_WX86_BREAKPOINT: {
+    bool breakpoint_hit = false;
+    NativeThreadWindows *stop_thread = GetThreadByID(record.GetThreadID());
+
+    if (stop_thread) {
+      uint32_t hw_id = LLDB_INVALID_INDEX32;
+      auto &reg_ctx = stop_thread->GetRegisterContext();
+      reg_ctx.GetHardwareBreakHitIndex(hw_id, record.GetExceptionAddress());
+      if (hw_id != LLDB_INVALID_INDEX32) {
+        breakpoint_hit = true;
+        LLDB_LOG(log, "Hit hardware breakpoint at address {0:x}.",
+                 record.GetExceptionAddress());
+      } else if (FindSoftwareBreakpoint(record.GetExceptionAddress())) {
+        breakpoint_hit = true;
+        LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
+                 record.GetExceptionAddress());
         uint32_t breakpoint_size = GetSoftwareBreakpointPCOffset();
         // The current PC is AFTER the BP opcode, on all architectures.
-        uint64_t pc = register_context.GetPC() - breakpoint_size;
-        register_context.SetPC(pc);
+        uint64_t pc = reg_ctx.GetPC() - breakpoint_size;
+        reg_ctx.SetPC(pc);
       }
 
-      SetState(eStateStopped, true);
-      return ExceptionResult::MaskException;
+      if (breakpoint_hit) {
+        StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint);
+        SetState(eStateStopped, true);
+        return ExceptionResult::MaskException;
+      } else {
+        const std::vector<ULONG_PTR> &args = record.GetExceptionArguments();
+        if (args.size() >= 2) {
+          reg_ctx.GetWatchpointHitIndex(hw_id, args[1]);
+          if (hw_id != LLDB_INVALID_INDEX32) {
+            addr_t wp_pc = record.GetExceptionAddress();
+            std::string desc =
+                formatv("{0} {1} {2}", args[1], hw_id, wp_pc).str();
+            StopThread(record.GetThreadID(), StopReason::eStopReasonWatchpoint,
+                       desc);
+            SetState(eStateStopped, true);
+            return ExceptionResult::MaskException;
+          }
+        }
+      } 
     }
 
     if (!initial_stop) {
@@ -531,6 +554,7 @@ NativeProcessWindows::OnDebugException(bool first_chance,
       // Hit the initial stop. Continue the application.
       return ExceptionResult::BreakInDebugger;
     }
+  }
 
     [[fallthrough]];
   default:
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp
index 9128363eaa577a..95be1183abb759 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.cpp
@@ -18,9 +18,6 @@
 using namespace lldb;
 using namespace lldb_private;
 
-NativeRegisterContextWindows::NativeRegisterContextWindows(
-    NativeThreadProtocol &thread, RegisterInfoInterface *reg_info_interface_p)
-    : NativeRegisterContextRegisterInfo(thread, reg_info_interface_p) {}
 
 lldb::thread_t NativeRegisterContextWindows::GetThreadHandle() const {
   auto wthread = static_cast<NativeThreadWindows *>(&m_thread);
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h
index 841b8547f3e904..66ced71d27d69a 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows.h
@@ -17,11 +17,8 @@ namespace lldb_private {
 
 class NativeThreadWindows;
 
-class NativeRegisterContextWindows : public NativeRegisterContextRegisterInfo {
+class NativeRegisterContextWindows : public virtual NativeRegisterContextRegisterInfo {
 public:
-  NativeRegisterContextWindows(
-      NativeThreadProtocol &native_thread,
-      RegisterInfoInterface *reg_info_interface_p);
 
   static std::unique_ptr<NativeRegisterContextWindows>
   CreateHostNativeRegisterContextWindows(const ArchSpec &target_arch,
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp
index a9642d1c5e48da..1ba89fbf3215bb 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_WoW64.cpp
@@ -88,7 +88,7 @@ static Status SetWoW64ThreadContextHelper(lldb::thread_t thread_handle,
 
 NativeRegisterContextWindows_WoW64::NativeRegisterContextWindows_WoW64(
     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
-    : NativeRegisterContextWindows(native_thread,
+    : NativeRegisterContextRegisterInfo(native_thread,
                                    CreateRegisterInfoInterface(target_arch)) {}
 
 bool NativeRegisterContextWindows_WoW64::IsGPR(uint32_t reg_index) const {
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
index 4209fdf3c7109a..470da1afc8222e 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm.cpp
@@ -128,7 +128,7 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
 
 NativeRegisterContextWindows_arm::NativeRegisterContextWindows_arm(
     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
-    : NativeRegisterContextWindows(native_thread,
+    : NativeRegisterContextRegisterInfo(native_thread,
                                    CreateRegisterInfoInterface(target_arch)) {}
 
 bool NativeRegisterContextWindows_arm::IsGPR(uint32_t reg_index) const {
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp
index 080a9140e36a64..ae09ebffa5508c 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.cpp
@@ -10,7 +10,6 @@
 
 #include "NativeRegisterContextWindows_arm64.h"
 #include "NativeThreadWindows.h"
-#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
 #include "ProcessWindowsLog.h"
 #include "lldb/Host/HostInfo.h"
 #include "lldb/Host/HostThread.h"
@@ -143,8 +142,14 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
 
 NativeRegisterContextWindows_arm64::NativeRegisterContextWindows_arm64(
     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
-    : NativeRegisterContextWindows(native_thread,
-                                   CreateRegisterInfoInterface(target_arch)) {}
+    : NativeRegisterContextRegisterInfo(native_thread,
+                                        CreateRegisterInfoInterface(target_arch)) {
+  // Currently, there is no API to query the maximum supported hardware
+  // breakpoints and watchpoints on Windows. The values set below are based
+  // on tests conducted on Windows 11 with Snapdragon Elite X hardware.
+  m_max_hwp_supported = 1;
+  m_max_hbp_supported = 6;
+}
 
 bool NativeRegisterContextWindows_arm64::IsGPR(uint32_t reg_index) const {
   return (reg_index >= k_first_gpr_arm64 && reg_index <= k_last_gpr_arm64);
@@ -709,48 +714,49 @@ Status NativeRegisterContextWindows_arm64::WriteAllRegisterValues(
   return SetThreadContextHelper(GetThreadHandle(), &tls_context);
 }
 
-Status NativeRegisterContextWindows_arm64::IsWatchpointHit(uint32_t wp_index,
-                                                           bool &is_hit) {
-  return Status::FromErrorString("unimplemented");
-}
-
-Status NativeRegisterContextWindows_arm64::GetWatchpointHitIndex(
-    uint32_t &wp_index, lldb::addr_t trap_addr) {
-  return Status::FromErrorString("unimplemented");
-}
-
-Status NativeRegisterContextWindows_arm64::IsWatchpointVacant(uint32_t wp_index,
-                                                              bool &is_vacant) {
-  return Status::FromErrorString("unimplemented");
-}
-
-Status NativeRegisterContextWindows_arm64::SetHardwareWatchpointWithIndex(
-    lldb::addr_t addr, size_t size, uint32_t watch_flags, uint32_t wp_index) {
-  return Status::FromErrorString("unimplemented");
-}
-
-bool NativeRegisterContextWindows_arm64::ClearHardwareWatchpoint(
-    uint32_t wp_index) {
-  return false;
-}
+llvm::Error NativeRegisterContextWindows_arm64::ReadHardwareDebugInfo() {
+  ::CONTEXT tls_context;
+  Status error =
+      GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_DEBUG_REGISTERS);
+  if (error.Fail())
+    return error.ToError();
 
-Status NativeRegisterContextWindows_arm64::ClearAllHardwareWatchpoints() {
-  return Status::FromErrorString("unimplemented");
-}
+  for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
+    m_hwp_regs[i].address = tls_context.Wvr[i];
+    m_hwp_regs[i].control = tls_context.Wcr[i];
+  }
 
-uint32_t NativeRegisterContextWindows_arm64::SetHardwareWatchpoint(
-    lldb::addr_t addr, size_t size, uint32_t watch_flags) {
-  return LLDB_INVALID_INDEX32;
+  for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
+    m_hbp_regs[i].address = tls_context.Bvr[i];
+    m_hbp_regs[i].control = tls_context.Bcr[i];
+  }
+  return llvm::Error::success();
 }
 
-lldb::addr_t
-NativeRegisterContextWindows_arm64::GetWatchpointAddress(uint32_t wp_index) {
-  return LLDB_INVALID_ADDRESS;
-}
+llvm::Error
+NativeRegisterContextWindows_arm64::WriteHardwareDebugRegs(DREGType hwbType) {
+  ::CONTEXT tls_context;
+  Status error =
+      GetThreadContextHelper(GetThreadHandle(), &tls_context, CONTEXT_DEBUG_REGISTERS);
+  if (error.Fail())
+    return error.ToError();
+
+  switch (hwbType) {
+  case eDREGTypeWATCH:
+    for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
+      tls_context.Wvr[i] = m_hwp_regs[i].address;
+      tls_context.Wcr[i] = m_hwp_regs[i].control;
+    }
+    break;
+  case eDREGTypeBREAK:
+    for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
+      tls_context.Bvr[i] = m_hbp_regs[i].address;
+      tls_context.Bcr[i] = m_hbp_regs[i].control;
+    }
+    break;
+  }
 
-uint32_t NativeRegisterContextWindows_arm64::NumSupportedHardwareWatchpoints() {
-  // Not implemented
-  return 0;
+  return SetThreadContextHelper(GetThreadHandle(), &tls_context).ToError();
 }
 
 #endif // defined(__aarch64__) || defined(_M_ARM64)
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h
index 88afc1e7b18a2f..164d46143df950 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_arm64.h
@@ -11,6 +11,8 @@
 #define liblldb_NativeRegisterContextWindows_arm64_h_
 
 #include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
 
 #include "NativeRegisterContextWindows.h"
 
@@ -18,7 +20,8 @@ namespace lldb_private {
 
 class NativeThreadWindows;
 
-class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
+class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows,
+public NativeRegisterContextDBReg_arm64 {
 public:
   NativeRegisterContextWindows_arm64(const ArchSpec &target_arch,
                                      NativeThreadProtocol &native_thread);
@@ -37,28 +40,6 @@ class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
 
   Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
 
-  Status IsWatchpointHit(uint32_t wp_index, bool &is_hit) override;
-
-  Status GetWatchpointHitIndex(uint32_t &wp_index,
-                               lldb::addr_t trap_addr) override;
-
-  Status IsWatchpointVacant(uint32_t wp_index, bool &is_vacant) override;
-
-  bool ClearHardwareWatchpoint(uint32_t wp_index) override;
-
-  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;
-
-  lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
-
-  uint32_t NumSupportedHardwareWatchpoints() override;
-
 protected:
   Status GPRRead(const uint32_t reg, RegisterValue &reg_value);
 
@@ -72,6 +53,10 @@ class NativeRegisterContextWindows_arm64 : public NativeRegisterContextWindows {
   bool IsGPR(uint32_t reg_index) const;
 
   bool IsFPR(uint32_t reg_index) const;
+
+  llvm::Error ReadHardwareDebugInfo() override;
+
+  llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
 };
 
 } // namespace lldb_private
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp
index 53df9866793946..71e3616d1af9c9 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_i386.cpp
@@ -92,7 +92,7 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
 
 NativeRegisterContextWindows_i386::NativeRegisterContextWindows_i386(
     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
-    : NativeRegisterContextWindows(native_thread,
+    : NativeRegisterContextRegisterInfo(native_thread,
                                    CreateRegisterInfoInterface(target_arch)) {}
 
 bool NativeRegisterContextWindows_i386::IsGPR(uint32_t reg_index) const {
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp
index 4c59273b845aaa..3709390d953d19 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeRegisterContextWindows_x86_64.cpp
@@ -110,7 +110,7 @@ NativeRegisterContextWindows::CreateHostNativeRegisterContextWindows(
 
 NativeRegisterContextWindows_x86_64::NativeRegisterContextWindows_x86_64(
     const ArchSpec &target_arch, NativeThreadProtocol &native_thread)
-    : NativeRegisterContextWindows(native_thread,
+    : NativeRegisterContextRegisterInfo(native_thread,
                                    CreateRegisterInfoInterface(target_arch)) {}
 
 bool NativeRegisterContextWindows_x86_64::IsGPR(uint32_t reg_index) const {
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
index 8ad59cd1ece887..cd5cd4541082fb 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
@@ -178,9 +178,41 @@ Status NativeThreadWindows::RemoveWatchpoint(lldb::addr_t addr) {
 
 Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr,
                                                   size_t size) {
+#if defined(__aarch64__) || defined(_M_ARM64)
+  if (m_state == eStateLaunching)
+    return Status();
+
+  Status error = RemoveHardwareBreakpoint(addr);
+  if (error.Fail())
+    return error;
+
+  uint32_t bp_index = m_reg_context_up->SetHardwareBreakpoint(addr, size);
+
+  if (bp_index == LLDB_INVALID_INDEX32)
+    return Status::FromErrorString("Setting hardware breakpoint failed.");
+
+  m_hw_breakpoint_index_map.insert({addr, bp_index});
+  
+  return Status();
+#else
   return Status::FromErrorString("unimplemented.");
+#endif
 }
 
 Status NativeThreadWindows::RemoveHardwareBreakpoint(lldb::addr_t addr) {
+#if defined(__aarch64__) || defined(_M_ARM64)
+  auto bp = m_hw_breakpoint_index_map.find(addr);
+  if (bp == m_hw_breakpoint_index_map.end())
+    return Status();
+
+  uint32_t bp_index = bp->second;
+  if (m_reg_context_up->ClearHardwareBreakpoint(bp_index)) {
+    m_hw_breakpoint_index_map.erase(bp);
+    return Status();
+  }
+
+  return Status::FromErrorString("Clearing hardware breakpoint failed.");
+#else
   return Status::FromErrorString("unimplemented.");
+#endif
 }

Copy link

github-actions bot commented Sep 10, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@jasonmolenda
Copy link
Collaborator

i skimmed the patch, I didn't see anything that concerns me. The fact that we can't query the number of hardware watchpoints/breakpoints means that this code will misbehave on other platforms where the numbers are different - we might have four hardware watchpoints, but you'll be limited to only setting one. The hardware might only have 4 hardware breakpoints, so this code will let you set two additional ones which are ignored. But I don't know what approach would be better here, given what Windows apparently provides.

I'll probably need to touch this new code a tiny bit once it's merged, for #105594 but there's no reason for Omair to do anything about that right now, I'm not ready to land my patches yet.

Copy link
Collaborator

@labath labath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems ok to me.

If I had to guess, I'd say that the initial stop problem is due to us stopping the process "too early" (i.e., in a state where it's not yet fully initialized and ready to accept debugger commands). Maybe there's a different way to stop the process at the first instruction?

@DavidSpickett DavidSpickett changed the title WoA HW Break and Watchpoint support in LLDB [lldb][Windows] WoA HW Break and Watchpoint support in LLDB Sep 11, 2024
@DavidSpickett
Copy link
Collaborator

Remind me, does lldb support hardware breakpoints for x86 right now either? From the content of this PR, I assume it does not.

(which is fine, I'm just trying to be clear what our starting point is)

@DavidSpickett
Copy link
Collaborator

Relates to #80665

@omjavaid
Copy link
Contributor Author

omjavaid commented Sep 11, 2024

Remind me, does lldb support hardware breakpoints for x86 right now either? From the content of this PR, I assume it does not.

(which is fine, I'm just trying to be clear what our starting point is)

yes x86_64 windows does not support hardware breakpoints. However it does support hardware watchpoints.

@omjavaid
Copy link
Contributor Author

Seems ok to me.

If I had to guess, I'd say that the initial stop problem is due to us stopping the process "too early" (i.e., in a state where it's not yet fully initialized and ready to accept debugger commands). Maybe there's a different way to stop the process at the first instruction?

Yes I think there might be a Windows API launch flag that can let us do early detection of hardware breakpoints and watchpoints. But so far after lots of experimentation I have not been able to find the right set.

@DavidSpickett
Copy link
Collaborator

Yes I think there might be a Windows API launch flag that can let us do early detection of hardware breakpoints and watchpoints. But so far after lots of experimentation I have not been able to find the right set.

Is it possible this is a bug in Windows itself? Though it seems unlikely given that Visual Studio supports WoA.

I'm not sure about merging this until we know more about that part, at least whether it's a bug or not. Perhaps we (Linaro) can get some information from Microsoft on this?

@omjavaid
Copy link
Contributor Author

Yes I think there might be a Windows API launch flag that can let us do early detection of hardware breakpoints and watchpoints. But so far after lots of experimentation I have not been able to find the right set.

Is it possible this is a bug in Windows itself? Though it seems unlikely given that Visual Studio supports WoA.

I'm not sure about merging this until we know more about that part, at least whether it's a bug or not. Perhaps we (Linaro) can get some information from Microsoft on this?

So further update from my offline (out of LLDB) testing with windows API and setting hardware breakpoints suggest that we might be doing something different on LLDB side during launch or while setting hardware breakpoints/watchpoints that stops us from hitting those exceptions.

In simple console based debugger I have played with Windows API and set hardware breakpoints and found a good configuration that works.

I will keep debugging this might get to fix it on LLDB side as well. And will talk to our friends at MS at some stage for any clarifications that might be needed.

@omjavaid omjavaid changed the title [lldb][Windows] WoA HW Break and Watchpoint support in LLDB [lldb][Windows] WoA HW Watchpoint support in LLDB Oct 9, 2024
@omjavaid
Copy link
Contributor Author

omjavaid commented Oct 9, 2024

@DavidSpickett @labath I have updated this PR and dropped hardware breakpoint support for the unresolved "set hw break on main" issue As x86_64 windows also does not support hardware breakpoints (probably for the same reason).

This PR now only implements HW watchpoint support.

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this X86 and ARM64 or just one?

Also this needs a release note, which is a good place to note the limitations as well.

@DavidSpickett
Copy link
Collaborator

Please update the PR description to reflect what the current code does, or if you want to leave the notes there, move them into an obvious section that you can cut out if/when this gets merged.

@omjavaid
Copy link
Contributor Author

omjavaid commented Jan 29, 2025

Please update the PR description to reflect what the current code does, or if you want to leave the notes there, move them into an obvious section that you can cut out if/when this gets merged.

I have updates the PR description the "Known Issues (Just for documentation):" part will be skipped in the final patch description.

@DavidSpickett
Copy link
Collaborator

I want to be 100% sure of the status of this issue:

Initial Stop Behavior: Hardware breakpoints or watchpoints set on the initial stop do not trigger, even though they are correctly written to the Windows context. They only trigger if set after the main program has started. This issue causes the test suite to fail when it attempts to set hardware breakpoints on the main function before running, as these do not get triggered.

This is a problem with watchpoints too, right?

I can see an argument that says code breakpoints are much more likely to be placed before the initial stop, so watchpoints are still useful even with this limitation and this PR should still be merged.

But please open an issue for that, describing the problem and as far as you know. why it might be occurring. It will give us something to "mark as duplicate" :)

@omjavaid
Copy link
Contributor Author

This is a problem with watchpoints too, right?

Yes this happens with watchpoints too on both AArch64 and x64 WIndows.

I can see an argument that says code breakpoints are much more likely to be placed before the initial stop, so watchpoints are still useful even with this limitation and this PR should still be merged.

Yes because LLDB requires a running process to set watchpoints however for breakpoints thats not the case. In most cases user sets breakpoint on main and then set watchpoints which masks this problem altogether.

But please open an issue for that, describing the problem and as far as you know. why it might be occurring. It will give us something to "mark as duplicate" :)

I have create #125054

@DavidSpickett
Copy link
Collaborator

In most cases user sets breakpoint on main and then set watchpoints which masks this problem altogether.

Agreed, this isn't ideal but it is an improvement from the current features.

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're hitting limitations in Windows itself, and 1 watchpoint is more useful than zero, so this LGTM.

To preempt a potential discussion: I think this should not be backported to 20. Unless you've got a very good reason for it. We don't have the most comprehensive testing on Windows and we already have a few caveats here. We might encounter more as time goes on and fixes may be complex.

But, by LLDB 21, maybe we will have ironed those out.

@DavidSpickett
Copy link
Collaborator

Also thanks for tackling this, I know I said once (possibly twice) that I'd do it but kept getting distracted. I know you put a bunch of research time into this too.

@omjavaid omjavaid merged commit 2bffa5b into llvm:main Jan 31, 2025
9 checks passed
@dzhidzhoev
Copy link
Member

@omjavaid

Apparently, these changes break lldb-remote-linux-win buildbot with the following error:

FAILED: tools/lldb/source/Plugins/Process/Windows/Common/CMakeFiles/lldbPluginProcessWindowsCommon.dir/NativeRegisterContextWindows_WoW64.cpp.obj 
ccache C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\MSVC\1441~1.341\bin\Hostx64\x64\cl.exe  /nologo /TP -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source\Plugins\Process\Windows\Common -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include -IC:\Python312\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source -D__OPTIMIZE__ /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Oi /bigobj /permissive- /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /O2 /Ob2  -MD   -wd4018 -wd4068 -wd4150 -wd4201 -wd4251 -wd4521 -wd4530 -wd4589  /EHs-c- /GR- -UNDEBUG -std:c++17 /showIncludes /Fotools\lldb\source\Plugins\Process\Windows\Common\CMakeFiles\lldbPluginProcessWindowsCommon.dir\NativeRegisterContextWindows_WoW64.cpp.obj /Fdtools\lldb\source\Plugins\Process\Windows\Common\CMakeFiles\lldbPluginProcessWindowsCommon.dir\lldbPluginProcessWindowsCommon.pdb /FS -c C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows_WoW64.cpp
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows_WoW64.cpp(92): error C2280: 'lldb_private::NativeRegisterContextWindows::NativeRegisterContextWindows(void)': attempting to reference a deleted function
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows.h(29): note: compiler has generated 'lldb_private::NativeRegisterContextWindows::NativeRegisterContextWindows' here
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows.h(29): note: 'lldb_private::NativeRegisterContextWindows::NativeRegisterContextWindows(void)': function was implicitly deleted because a base class 'lldb_private::NativeRegisterContextRegisterInfo' has either no appropriate default constructor or overload resolution was ambiguous
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h(18): note: see declaration of 'lldb_private::NativeRegisterContextRegisterInfo'
65.460 [198/9/5091]Building CXX object tools\lldb\source\Plugins\Process\Windows\Common\CMakeFiles\lldbPluginProcessWindowsCommon.dir\NativeRegisterContextWindows_x86_64.cpp.obj
FAILED: tools/lldb/source/Plugins/Process/Windows/Common/CMakeFiles/lldbPluginProcessWindowsCommon.dir/NativeRegisterContextWindows_x86_64.cpp.obj 
ccache C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\MSVC\1441~1.341\bin\Hostx64\x64\cl.exe  /nologo /TP -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source\Plugins\Process\Windows\Common -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include -IC:\Python312\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source -D__OPTIMIZE__ /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Oi /bigobj /permissive- /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /O2 /Ob2  -MD   -wd4018 -wd4068 -wd4150 -wd4201 -wd4251 -wd4521 -wd4530 -wd4589  /EHs-c- /GR- -UNDEBUG -std:c++17 /showIncludes /Fotools\lldb\source\Plugins\Process\Windows\Common\CMakeFiles\lldbPluginProcessWindowsCommon.dir\NativeRegisterContextWindows_x86_64.cpp.obj /Fdtools\lldb\source\Plugins\Process\Windows\Common\CMakeFiles\lldbPluginProcessWindowsCommon.dir\lldbPluginProcessWindowsCommon.pdb /FS -c C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows_x86_64.cpp
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows_x86_64.cpp(114): error C2280: 'lldb_private::NativeRegisterContextWindows::NativeRegisterContextWindows(void)': attempting to reference a deleted function
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows.h(29): note: compiler has generated 'lldb_private::NativeRegisterContextWindows::NativeRegisterContextWindows' here
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\Process\Windows\Common\NativeRegisterContextWindows.h(29): note: 'lldb_private::NativeRegisterContextWindows::NativeRegisterContextWindows(void)': function was implicitly deleted because a base class 'lldb_private::NativeRegisterContextRegisterInfo' has either no appropriate default constructor or overload resolution was ambiguous
C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h(18): note: see declaration of 'lldb_private::NativeRegisterContextRegisterInfo'

https://lab.llvm.org/buildbot/#/builders/197/builds/1447/steps/8/logs/stdio

https://lab.llvm.org/buildbot/#/builders/197/builds/1447

Could you take care of it?

@omjavaid
Copy link
Contributor Author

omjavaid commented Feb 3, 2025

omjavaid added a commit that referenced this pull request Feb 3, 2025
This patch fixes LLDB Windows build with MSVC compiler. MSVC deletes
the default constructor due to virtual inheritance rules. Explicitly
define the default constructor in NativeRegisterContextWindows to
ensure constructibility.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Feb 5, 2025
This PR adds support for hardware watchpoints in LLDB for AArch64
Windows targets.

Windows does not provide an API to query the number of available
hardware watchpoints supported by underlying hardware platform.
Therefore, current implementation supports only a single hardware
watchpoint, which has been verified on Windows 11 using Microsoft
SQ2 and Snapdragon Elite X hardware.

LLDB test suite ninja check-lldb still fails watchpoint-related tests.
However, tests that do not require more than a single watchpoint
pass successfully when run individually.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Feb 5, 2025
This PR adds support for hardware watchpoints in LLDB for AArch64
Windows targets.

Windows does not provide an API to query the number of available
hardware watchpoints supported by underlying hardware platform.
Therefore, current implementation supports only a single hardware
watchpoint, which has been verified on Windows 11 using Microsoft
SQ2 and Snapdragon Elite X hardware.

LLDB test suite ninja check-lldb still fails watchpoint-related tests.
However, tests that do not require more than a single watchpoint
pass successfully when run individually.
SquallATF pushed a commit to SquallATF/llvm-project that referenced this pull request Feb 7, 2025
This PR adds support for hardware watchpoints in LLDB for AArch64
Windows targets.

Windows does not provide an API to query the number of available
hardware watchpoints supported by underlying hardware platform.
Therefore, current implementation supports only a single hardware
watchpoint, which has been verified on Windows 11 using Microsoft
SQ2 and Snapdragon Elite X hardware.

LLDB test suite ninja check-lldb still fails watchpoint-related tests.
However, tests that do not require more than a single watchpoint
pass successfully when run individually.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants