[Lldb-commits] [lldb] [NFC][lldb][windows] break down NativeProcessWindows::OnDebugException (PR #200832)

via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 1 07:20:08 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Charles Zablit (charles-zablit)

<details>
<summary>Changes</summary>

This patch breaks down `NativeProcessWindows::OnDebugException` into 3 different handlers (one for each exception) for readability.

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


2 Files Affected:

- (modified) lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp (+132-126) 
- (modified) lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h (+9) 


``````````diff
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index 26fc31fb27cf6..47be9e5322112 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -471,152 +471,158 @@ void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) {
 }
 
 ExceptionResult
-NativeProcessWindows::OnDebugException(bool first_chance,
-                                       const ExceptionRecord &record) {
+NativeProcessWindows::HandleSingleStepException(const ExceptionRecord &record) {
   Log *log = GetLog(WindowsLog::Exception);
-  llvm::sys::ScopedLock lock(m_mutex);
+  uint32_t wp_id = LLDB_INVALID_INDEX32;
+#ifndef __aarch64__
+  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);
+    }
+  }
+#endif
+  if (wp_id == LLDB_INVALID_INDEX32)
+    StopThread(record.GetThreadID(), StopReason::eStopReasonTrace);
 
-  // Let the debugger establish the internal status.
-  ProcessDebugger::OnDebugException(first_chance, record);
+  SetState(eStateStopped, true);
+  return ExceptionResult::MaskException;
+}
 
-  static bool initial_stop = false;
+ExceptionResult
+NativeProcessWindows::HandleBreakpointException(const ExceptionRecord &record) {
+  Log *log = GetLog(WindowsLog::Exception);
+  const auto exception_addr = record.GetExceptionAddress();
+  const auto thread_id = record.GetThreadID();
+
+  if (NativeThreadWindows *stop_thread = GetThreadByID(thread_id)) {
+    auto &reg_ctx = stop_thread->GetRegisterContext();
+
+    if (FindSoftwareBreakpoint(exception_addr)) {
+      LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
+               exception_addr);
+      StopThread(thread_id, StopReason::eStopReasonBreakpoint);
+      // The current PC is AFTER the BP opcode, on all architectures.
+      reg_ctx.SetPC(reg_ctx.GetPC() - GetSoftwareBreakpointPCOffset());
+      SetState(eStateStopped, true);
+      return ExceptionResult::MaskException;
+    }
 
-  switch (record.GetExceptionCode()) {
-  case DWORD(STATUS_SINGLE_STEP):
-  case STATUS_WX86_SINGLE_STEP: {
-#ifndef __aarch64__
-    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());
+    // This block of code will only be entered in case of a hardware
+    // watchpoint or breakpoint hit on AArch64. However, we only handle
+    // hardware watchpoints below as breakpoints are not yet supported.
+    const std::vector<ULONG_PTR> &args = record.GetExceptionArguments();
+    // Check that the ExceptionInformation array of EXCEPTION_RECORD
+    // contains at least two elements: the first is a read-write flag
+    // indicating the type of data access operation (read or write) while
+    // the second contains the virtual address of the accessed data.
+    if (args.size() >= 2) {
+      uint32_t hw_id = LLDB_INVALID_INDEX32;
+      Status error = reg_ctx.GetWatchpointHitIndex(hw_id, args[1]);
       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)
-#endif
-      StopThread(record.GetThreadID(), StopReason::eStopReasonTrace);
-
-    SetState(eStateStopped, true);
+                 thread_id, error);
 
-    // Continue the debugger.
-    return ExceptionResult::MaskException;
-  }
-  case DWORD(STATUS_BREAKPOINT):
-  case STATUS_WX86_BREAKPOINT: {
-    if (NativeThreadWindows *stop_thread =
-            GetThreadByID(record.GetThreadID())) {
-      auto &reg_ctx = stop_thread->GetRegisterContext();
-      const auto exception_addr = record.GetExceptionAddress();
-      const auto thread_id = record.GetThreadID();
-
-      if (FindSoftwareBreakpoint(exception_addr)) {
-        LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
-                 exception_addr);
-        StopThread(thread_id, StopReason::eStopReasonBreakpoint);
-        // The current PC is AFTER the BP opcode, on all architectures.
-        reg_ctx.SetPC(reg_ctx.GetPC() - GetSoftwareBreakpointPCOffset());
+      if (hw_id != LLDB_INVALID_INDEX32) {
+        std::string desc =
+            formatv("{0} {1} {2}", reg_ctx.GetWatchpointAddress(hw_id), hw_id,
+                    exception_addr)
+                .str();
+        StopThread(thread_id, StopReason::eStopReasonWatchpoint, desc);
         SetState(eStateStopped, true);
         return ExceptionResult::MaskException;
-      } else {
-        // This block of code will only be entered in case of a hardware
-        // watchpoint or breakpoint hit on AArch64. However, we only handle
-        // hardware watchpoints below as breakpoints are not yet supported.
-        const std::vector<ULONG_PTR> &args = record.GetExceptionArguments();
-        // Check that the ExceptionInformation array of EXCEPTION_RECORD
-        // contains at least two elements: the first is a read-write flag
-        // indicating the type of data access operation (read or write) while
-        // the second contains the virtual address of the accessed data.
-        if (args.size() >= 2) {
-          uint32_t hw_id = LLDB_INVALID_INDEX32;
-          Status error = reg_ctx.GetWatchpointHitIndex(hw_id, args[1]);
-          if (error.Fail())
-            LLDB_LOG(log,
-                     "received error while checking for watchpoint hits, pid = "
-                     "{0}, error = {1}",
-                     thread_id, error);
-
-          if (hw_id != LLDB_INVALID_INDEX32) {
-            std::string desc =
-                formatv("{0} {1} {2}", reg_ctx.GetWatchpointAddress(hw_id),
-                        hw_id, exception_addr)
-                    .str();
-            StopThread(thread_id, StopReason::eStopReasonWatchpoint, desc);
-            SetState(eStateStopped, true);
-            return ExceptionResult::MaskException;
-          }
-        }
       }
     }
-
-    if (!initial_stop) {
-      initial_stop = true;
-      LLDB_LOG(log,
-               "Hit loader breakpoint at address {0:x}, setting initial stop "
-               "event.",
-               record.GetExceptionAddress());
-
-      // We are required to report the reason for the first stop after
-      // launching or being attached.
-      if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID()))
-        SetStopReasonForThread(*thread, StopReason::eStopReasonBreakpoint);
-
-      // Do not notify the native delegate (e.g. llgs) since at this moment
-      // the program hasn't returned from Manager::Launch() and the delegate
-      // might not have an valid native process to operate on.
-      SetState(eStateStopped, false);
-
-      // Hit the initial stop. Continue the application.
-      return ExceptionResult::BreakInDebugger;
-    }
-
-    // Any remaining STATUS_BREAKPOINT is a breakpoint instruction in the
-    // program's own code (e.g. `__debugbreak()` or `__builtin_debugtrap()`).
-    // Stop the debugger and let the user decide what to do.
-    std::string desc =
-        formatv("Exception {0:x8} encountered at address {1:x8}",
-                record.GetExceptionCode(), record.GetExceptionAddress())
-            .str();
-    StopThread(record.GetThreadID(), StopReason::eStopReasonException,
-               std::move(desc));
-    SetState(eStateStopped, true);
-
-    return ExceptionResult::MaskException;
   }
-  default: {
-    LLDB_LOG(log,
-             "Debugger thread reported exception {0:x} at address {1:x} "
-             "(first_chance={2})",
-             record.GetExceptionCode(), record.GetExceptionAddress(),
-             first_chance);
 
-    if (first_chance)
-      return ExceptionResult::SendToApplication;
+  if (!m_initial_stop_seen) {
+    m_initial_stop_seen = true;
+    LLDB_LOG(log,
+             "Hit loader breakpoint at address {0:x}, setting initial stop "
+             "event.",
+             exception_addr);
 
-    std::string desc;
-    llvm::raw_string_ostream desc_stream(desc);
-    desc_stream << "Exception "
-                << llvm::format_hex(record.GetExceptionCode(), 8)
-                << " encountered at address "
-                << llvm::format_hex(record.GetExceptionAddress(), 8);
-    StopThread(record.GetThreadID(), StopReason::eStopReasonException,
-               desc.c_str());
+    // We are required to report the reason for the first stop after
+    // launching or being attached.
+    if (NativeThreadWindows *thread = GetThreadByID(thread_id))
+      SetStopReasonForThread(*thread, StopReason::eStopReasonBreakpoint);
 
-    SetState(eStateStopped, true);
+    // Do not notify the native delegate (e.g. llgs) since at this moment
+    // the program hasn't returned from Manager::Launch() and the delegate
+    // might not have an valid native process to operate on.
+    SetState(eStateStopped, false);
 
+    // Hit the initial stop. Continue the application.
     return ExceptionResult::BreakInDebugger;
   }
+
+  // Any remaining STATUS_BREAKPOINT is a breakpoint instruction in the
+  // program's own code (e.g. `__debugbreak()` or `__builtin_debugtrap()`).
+  // Stop the debugger and let the user decide what to do.
+  std::string desc =
+      formatv("Exception {0:x8} encountered at address {1:x8}",
+              record.GetExceptionCode(), exception_addr)
+          .str();
+  StopThread(thread_id, StopReason::eStopReasonException, std::move(desc));
+  SetState(eStateStopped, true);
+  return ExceptionResult::MaskException;
+}
+
+ExceptionResult
+NativeProcessWindows::HandleGenericException(bool first_chance,
+                                             const ExceptionRecord &record) {
+  Log *log = GetLog(WindowsLog::Exception);
+  LLDB_LOG(log,
+           "Debugger thread reported exception {0:x} at address {1:x} "
+           "(first_chance={2})",
+           record.GetExceptionCode(), record.GetExceptionAddress(),
+           first_chance);
+
+  if (first_chance)
+    return ExceptionResult::SendToApplication;
+
+  std::string desc;
+  llvm::raw_string_ostream desc_stream(desc);
+  desc_stream << "Exception " << llvm::format_hex(record.GetExceptionCode(), 8)
+              << " encountered at address "
+              << llvm::format_hex(record.GetExceptionAddress(), 8);
+  StopThread(record.GetThreadID(), StopReason::eStopReasonException,
+             desc.c_str());
+
+  SetState(eStateStopped, true);
+  return ExceptionResult::BreakInDebugger;
+}
+
+ExceptionResult
+NativeProcessWindows::OnDebugException(bool first_chance,
+                                       const ExceptionRecord &record) {
+  llvm::sys::ScopedLock lock(m_mutex);
+
+  // Let the debugger establish the internal status.
+  ProcessDebugger::OnDebugException(first_chance, record);
+
+  switch (record.GetExceptionCode()) {
+  case DWORD(STATUS_SINGLE_STEP):
+  case STATUS_WX86_SINGLE_STEP:
+    return HandleSingleStepException(record);
+  case DWORD(STATUS_BREAKPOINT):
+  case STATUS_WX86_BREAKPOINT:
+    return HandleBreakpointException(record);
+  default:
+    return HandleGenericException(first_chance, record);
   }
 }
 
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index becb3dbe48188..d2c37d202f242 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -140,12 +140,21 @@ class NativeProcessWindows : public NativeProcessProtocol,
   NativeProcessWindows(lldb::pid_t pid, int terminal_fd,
                        NativeDelegate &delegate, llvm::Error &E);
 
+  ExceptionResult HandleSingleStepException(const ExceptionRecord &record);
+  ExceptionResult HandleBreakpointException(const ExceptionRecord &record);
+  ExceptionResult HandleGenericException(bool first_chance,
+                                         const ExceptionRecord &record);
+
   Status CacheLoadedModules();
   std::map<lldb_private::FileSpec, lldb::addr_t> m_loaded_modules;
 
   /// Set whenever an OS DLL load/unload event has been seen since the last stop
   /// reply.
   bool m_pending_library_events = true;
+
+  /// Whether we've seen the loader breakpoint that fires once per process at
+  /// launch / attach.
+  bool m_initial_stop_seen = false;
 };
 
 //------------------------------------------------------------------

``````````

</details>


https://github.com/llvm/llvm-project/pull/200832


More information about the lldb-commits mailing list