[Lldb-commits] [lldb] e8c8cbb - [lldb] Don't adopt in the ExecutionContext from auto-continue events (#191433)

via lldb-commits lldb-commits at lists.llvm.org
Fri Apr 17 10:05:12 PDT 2026


Author: Jonas Devlieghere
Date: 2026-04-17T10:05:08-07:00
New Revision: e8c8cbb06a5285b19a009af15d05d3f9fd09bfee

URL: https://github.com/llvm/llvm-project/commit/e8c8cbb06a5285b19a009af15d05d3f9fd09bfee
DIFF: https://github.com/llvm/llvm-project/commit/e8c8cbb06a5285b19a009af15d05d3f9fd09bfee.diff

LOG: [lldb] Don't adopt in the ExecutionContext from auto-continue events (#191433)

When a breakpoint auto-continues, the event handler receives a "stopped
but restarted" event. During the transition where we step over the
breakpoint (before continuing), the public state hasn't yet been set to
running. This caused the `DefaultEventHandler` to call
`ExecutionContextRef` with `adopt_selected=true`, which would fetch
stale thread/frame state and needlessly (and incorrectly) interrupt the
target to compute the execution context (used by the statusline). This
PR fixes that by not doing that.

Fixes #190956

Co-authored-by: Jim Ingham <jingham at apple.com>

Added: 
    lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/Makefile
    lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/TestBreakpointCommandAutoContinue.py
    lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/bpcmd.py
    lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/main.cpp

Modified: 
    lldb/source/Core/Debugger.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 9ea298d07804b..65519932eb317 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -2255,10 +2255,19 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
           uint32_t event_type = event_sp->GetType();
           llvm::StringRef broadcaster_class(broadcaster->GetBroadcasterClass());
           if (broadcaster_class == broadcaster_class_process) {
-            if (ProcessSP process_sp = HandleProcessEvent(event_sp))
+            if (ProcessSP process_sp = HandleProcessEvent(event_sp)) {
+              // Don't pass adopt_selected = true if this is a stop for an
+              // auto-continue event (e.g. an auto-continue breakpoint).  We
+              // would be fetching stale state, since the process resumed since
+              // this event, and we'd needlessly interrupt the target to do so.
+              const bool adopt_selected =
+                  process_sp->GetPrivateState() == eStateStopped &&
+                  !Process::ProcessEventData::GetRestartedFromEvent(
+                      event_sp.get());
               if (!RequiresFollowChildWorkaround(*process_sp))
-                exe_ctx_ref = ExecutionContextRef(process_sp.get(),
-                                                  /*adopt_selected=*/true);
+                exe_ctx_ref =
+                    ExecutionContextRef(process_sp.get(), adopt_selected);
+            }
           } else if (broadcaster_class == broadcaster_class_target) {
             if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
                     event_sp.get())) {

diff  --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/Makefile b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/Makefile
new file mode 100644
index 0000000000000..c46619c662348
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/Makefile
@@ -0,0 +1,4 @@
+CXX_SOURCES := main.cpp
+ENABLE_THREADS := YES
+
+include Makefile.rules

diff  --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/TestBreakpointCommandAutoContinue.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/TestBreakpointCommandAutoContinue.py
new file mode 100644
index 0000000000000..ebf75f8563256
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/TestBreakpointCommandAutoContinue.py
@@ -0,0 +1,40 @@
+"""
+Regression test for a bug in the default event handler (specifically when
+redrawing the statusline) that triggered when auto-continuing from a
+breakpoint.
+"""
+
+import os
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.lldbpexpect import PExpectTest
+
+
+ at skipIfAsan
+ at skipIfEditlineSupportMissing
+class BreakpointCommandAutoContinueTestCase(PExpectTest):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_breakpoint_command_auto_continue(self):
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        bpcmd = os.path.join(self.getSourceDir(), "bpcmd.py")
+
+        self.launch(executable=exe, timeout=60, dimensions=(25, 80))
+
+        self.expect("breakpoint set --name break_here", substrs=["Breakpoint 1"])
+        self.expect(
+            f"command script import {bpcmd}",
+        )
+        self.expect(
+            "breakpoint command add --python-function bpcmd.write_ok 1",
+        )
+
+        # Run the program. It should complete successfully (print PASSED).
+        # Without the fix, the debugger would interrupt the process when
+        # processing auto-continue events to fetch stale thread/frame state
+        # for the statusline, causing the memory writes to fail and the
+        # program to abort.
+        self.child.sendline("run")
+        self.child.expect("PASSED", timeout=30)

diff  --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/bpcmd.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/bpcmd.py
new file mode 100644
index 0000000000000..4205732630eac
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/bpcmd.py
@@ -0,0 +1,11 @@
+import lldb
+
+
+def write_ok(frame, bp_loc, internal_dict):
+    """Write "OK" into the buffer pointed to by 'buf' and auto-continue."""
+    buf = frame.FindVariable("buf")
+    addr = buf.GetValueAsUnsigned()
+    if addr != 0:
+        error = lldb.SBError()
+        frame.GetThread().GetProcess().WriteMemory(addr, b"OK", error)
+    return False

diff  --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/main.cpp b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/main.cpp
new file mode 100644
index 0000000000000..9657ab08b204f
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command_auto_continue/main.cpp
@@ -0,0 +1,35 @@
+#include <pthread.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstring>
+
+__attribute__((noinline)) void break_here(char *buf) {
+  // Memory barrier to keep the function body non-empty.
+  asm volatile("" ::: "memory");
+}
+
+void *test_thread(void *) {
+  char buf[1024];
+  for (int i = 0; i < 200; i++) {
+    memset(buf, 0, sizeof(buf));
+    break_here(buf);
+    if (memcmp(buf, "OK", 2) != 0) {
+      printf("FAILED at iteration %d\n", i);
+      _exit(1);
+    }
+    usleep(50);
+  }
+  return nullptr;
+}
+
+int main() {
+  pthread_t thread;
+  if (pthread_create(&thread, nullptr, test_thread, nullptr) != 0) {
+    perror("pthread_create");
+    return 1;
+  }
+  pthread_join(thread, nullptr);
+  printf("PASSED\n");
+  return 0;
+}


        


More information about the lldb-commits mailing list