From 4c23625357b0b6091bc97478a79d522a832c2b21 Mon Sep 17 00:00:00 2001 From: Kendal Harland <3987220+kendalharland@users.noreply.github.com> Date: Mon, 8 Jul 2024 06:20:45 -0400 Subject: [PATCH] Fix flake in TestZerothFrame.py (#96685) This test is currently flaky on a local Windows amd64 build. The reason is that it relies on the order of `process.threads` but this order is nondeterministic: If we print lldb's inputs and outputs while running, we can see that the breakpoints are always being set correctly, and always being hit: ```sh runCmd: breakpoint set -f "main.c" -l 2 output: Breakpoint 1: where = a.out`func_inner + 1 at main.c:2:9, address = 0x0000000140001001 runCmd: breakpoint set -f "main.c" -l 7 output: Breakpoint 2: where = a.out`main + 17 at main.c:7:5, address = 0x0000000140001021 runCmd: run output: Process 52328 launched: 'C:\workspace\llvm-project\llvm\build\lldb-test-build.noindex\functionalities\unwind\zeroth_frame\TestZerothFrame.test_dwarf\a.out' (x86_64) Process 52328 stopped * thread #1, stop reason = breakpoint 1.1 frame #0: 0x00007ff68f6b1001 a.out`func_inner at main.c:2:9 1 void func_inner() { -> 2 int a = 1; // Set breakpoint 1 here ^ 3 } 4 5 int main() { 6 func_inner(); 7 return 0; // Set breakpoint 2 here ``` However, sometimes the backtrace printed in this test shows that the process is stopped inside NtWaitForWorkViaWorkerFactory from `ntdll.dll`: ```sh Backtrace at the first breakpoint: frame #0: 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 frame #1: 0x00007ffecc74585e ntdll.dll`RtlClearThreadWorkOnBehalfTicket + 862 frame #2: 0x00007ffecc3e257d kernel32.dll`BaseThreadInitThunk + 29 frame #3: 0x00007ffecc76af28 ntdll.dll`RtlUserThreadStart + 40 ``` When this happens, the test fails with an assertion error that the stopped thread's zeroth frame's current line number does not match the expected line number. This is because the test is looking at the wrong thread: `process.threads[0]`. If we print the list of threads each time the test is run, we notice that threads are sometimes in a different order, within `process.threads`: ```sh Thread 0: thread #4: tid = 0x9c38, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 1: thread #2: tid = 0xa950, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 2: thread #1: tid = 0xab18, 0x00007ff64bc81001 a.out`func_inner at main.c:2:9, stop reason = breakpoint 1.1 Thread 3: thread #3: tid = 0xc514, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 0: thread #3: tid = 0x018c, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 1: thread #1: tid = 0x85c8, 0x00007ff7130c1001 a.out`func_inner at main.c:2:9, stop reason = breakpoint 1.1 Thread 2: thread #2: tid = 0xf344, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 Thread 3: thread #4: tid = 0x6a50, 0x00007ffecc7b3bf4 ntdll.dll`NtWaitForWorkViaWorkerFactory + 20 ``` Use `self.thread()` to consistently select the correct thread, instead. Co-authored-by: kendal --- .../unwind/zeroth_frame/TestZerothFrame.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py b/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py index f4e883d314644a..d660844405e137 100644 --- a/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py +++ b/lldb/test/API/functionalities/unwind/zeroth_frame/TestZerothFrame.py @@ -40,28 +40,28 @@ def test(self): target = self.dbg.CreateTarget(exe) self.assertTrue(target, VALID_TARGET) - bp1_line = line_number("main.c", "// Set breakpoint 1 here") - bp2_line = line_number("main.c", "// Set breakpoint 2 here") - - lldbutil.run_break_set_by_file_and_line( - self, "main.c", bp1_line, num_expected_locations=1 + main_dot_c = lldb.SBFileSpec("main.c") + bp1 = target.BreakpointCreateBySourceRegex( + "// Set breakpoint 1 here", main_dot_c ) - lldbutil.run_break_set_by_file_and_line( - self, "main.c", bp2_line, num_expected_locations=1 + bp2 = target.BreakpointCreateBySourceRegex( + "// Set breakpoint 2 here", main_dot_c ) process = target.LaunchSimple(None, None, self.get_process_working_directory()) self.assertTrue(process, VALID_PROCESS) - thread = process.GetThreadAtIndex(0) + thread = self.thread() + if self.TraceOn(): print("Backtrace at the first breakpoint:") for f in thread.frames: print(f) + # Check that we have stopped at correct breakpoint. self.assertEqual( - process.GetThreadAtIndex(0).frame[0].GetLineEntry().GetLine(), - bp1_line, + thread.frame[0].GetLineEntry().GetLine(), + bp1.GetLocationAtIndex(0).GetAddress().GetLineEntry().GetLine(), "LLDB reported incorrect line number.", ) @@ -70,7 +70,6 @@ def test(self): # 'continue' command. process.Continue() - thread = process.GetThreadAtIndex(0) if self.TraceOn(): print("Backtrace at the second breakpoint:") for f in thread.frames: @@ -78,7 +77,7 @@ def test(self): # Check that we have stopped at the breakpoint self.assertEqual( thread.frame[0].GetLineEntry().GetLine(), - bp2_line, + bp2.GetLocationAtIndex(0).GetAddress().GetLineEntry().GetLine(), "LLDB reported incorrect line number.", ) # Double-check with GetPCAddress()