[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