[Lldb-commits] [lldb] [lldb][API test] TestRunLocker is flakey, fix (PR #203429)
Jason Molenda via lldb-commits
lldb-commits at lists.llvm.org
Tue Aug 4 14:06:32 PDT 2026
https://github.com/jasonmolenda updated https://github.com/llvm/llvm-project/pull/203429
>From 2de45d30df5267d3e3f7b162aefc73968b265f24 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmolenda at apple.com>
Date: Thu, 11 Jun 2026 16:45:42 -0700
Subject: [PATCH] [lldb][API test] TestRunLocker is flakey, fix
TestRunLocker is intended to confirm that certain SB API commands
behave correctly while the inferior process is running. It has two
modes - one where it launches the process stopped, and then resumes
it, and another where it simply launches it. Both are intended to
wait until the process is eStateRunning, and then do the test
commands including a HandleCommand that refers to `lldb.target`.
This test is flakey on Linux and macOS because `lldb.target` comes
back as a None type very early in process setup. We weren't actually
running the "launch to the first instruction, then resume the process"
codepath that this test was written to have - I fixed that last week.
But we still see flakey behavior.
I thought the flakiness might be the private stops we are doing as
we're notified about binaries being loaded in the process, stepping
past the breakpoint in the dynamic loader, and then resume. I tested
this theory by changing the inferior program to be a tight infinite
loop, then putting a breakpoint with a very high skip count, so lldb
was forced to do many private stop/resumes. I changed the test case
to launch to main(), then start hitting these infinite private
stop/resumes and doing the test commands. The test commands work fine
in this case.
It's something unique about the early process startup that is
triggering the flakey failures. On the one hand, there is something
wrong here, where `lldb.target` will return None when we are doing
commands while executing very early in startup. But that wasn't
what this test was intended to exercise, and most importantly, it
doesn't work consistently so hammering on this in a test case is
just making our CI less reliable.
I removed the "launch to first instruction, then resume" codepath
and have TestRunLocker run to a breakpoint on main() - where we've
done all our process setup and binary loading - then resume the
process and test our commands while the inferior is in eStateRunning.
It doens't fail any more on my macOS desktop.
I also removed a Skip for aarch64 linux which said it was flakey.
I expect that's the same flakiness that's hitting all the other CI
too.
---
.../python_api/run_locker/TestRunLocker.py | 65 ++++++-------------
1 file changed, 19 insertions(+), 46 deletions(-)
diff --git a/lldb/test/API/python_api/run_locker/TestRunLocker.py b/lldb/test/API/python_api/run_locker/TestRunLocker.py
index 817530c77a429..595dc8f808763 100644
--- a/lldb/test/API/python_api/run_locker/TestRunLocker.py
+++ b/lldb/test/API/python_api/run_locker/TestRunLocker.py
@@ -14,43 +14,25 @@
class TestRunLocker(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- @expectedFailureAll(oslist=["windows"])
- # Is flaky on Linux AArch64 buildbot.
- @skipIf(oslist=["linux"], archs=["aarch64"])
- def test_run_locker(self):
- """Test that the run locker is set correctly when we launch"""
- self.build()
- self.runlocker_test(False)
-
- @expectedFailureAll(oslist=["windows"])
- # Is flaky on Linux AArch64 buildbot.
- @skipIf(oslist=["linux"], archs=["aarch64"])
- def test_run_locker_stop_at_entry(self):
- """Test that the run locker is set correctly when we launch"""
- self.build()
- self.runlocker_test(True)
-
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
self.main_source_file = lldb.SBFileSpec("main.c")
- def runlocker_test(self, stop_at_entry):
- """The code to stop at entry handles events slightly differently, so
- we test both versions of process launch."""
-
+ @expectedFailureAll(oslist=["windows"])
+ def test_run_locker(self):
+ """Test that the run locker is set correctly as we're running"""
+ self.build()
target = lldbutil.run_to_breakpoint_make_target(self)
-
launch_info = target.GetLaunchInfo()
- if stop_at_entry:
- flags = launch_info.GetLaunchFlags()
- launch_info.SetLaunchFlags(flags | lldb.eLaunchFlagStopAtEntry)
error = lldb.SBError()
# We are trying to do things when the process is running, so
# we have to run the debugger asynchronously.
self.dbg.SetAsync(True)
+ main_bp = target.BreakpointCreateByName("main")
+
listener = lldb.SBListener("test-run-lock-listener")
launch_info.SetListener(listener)
process = target.Launch(launch_info, error)
@@ -69,33 +51,24 @@ def runlocker_test(self, stop_at_entry):
)
state_type = lldb.SBProcess.GetStateFromEvent(event)
- # A stop_at_entry launch may have already stopped, it may
- # not be eStateRunning.
- if not stop_at_entry or state_type != lldb.eStateStopped:
+ # We may be in eStateStopped if we hit the breakpoint already.
+ if state_type != lldb.eStateStopped:
self.assertState(
state_type, lldb.eStateRunning, "Didn't get a running event"
)
-
- # We aren't checking the entry state, but just making sure
- # the running state is set properly if we continue in this state.
-
- if stop_at_entry:
- if state_type != lldb.eStateStopped:
- event_result = listener.WaitForEvent(10, event)
- self.assertTrue(
- event_result, "Timed out waiting for stop at entry stop"
- )
- state_type = lldb.SBProcess.GetStateFromEvent(event)
- self.assertState(state_type, eStateStopped, "Stop at entry stopped")
- process.Continue()
event_result = listener.WaitForEvent(10, event)
- self.assertTrue(event_result, "timed out waiting for Continue")
+ self.assertTrue(event_result, "timed out waiting for breakpoint stop")
state_type = lldb.SBProcess.GetStateFromEvent(event)
- self.assertState(
- state_type,
- lldb.eStateRunning,
- "Didn't get a running event after Continue",
- )
+
+ self.assertState(state_type, lldb.eStateStopped, "Stop at main stopped")
+ main_bp.SetEnabled(False)
+ process.Continue()
+
+ event_result = listener.WaitForEvent(10, event)
+ self.assertTrue(event_result, "timed out waiting for process resume")
+ state_type = lldb.SBProcess.GetStateFromEvent(event)
+
+ self.assertState(state_type, lldb.eStateRunning, "Continue after main() bp")
# Okay, now the process is running, make sure we can't do things
# you aren't supposed to do while running, and that we get some
More information about the lldb-commits
mailing list