Skip to content

Commit

Permalink
Fix flake in TestZerothFrame.py (llvm#96685)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
kendalharland and kendal authored Jul 8, 2024
1 parent c2fe75f commit 4c23625
Showing 1 changed file with 11 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
)

Expand All @@ -70,15 +70,14 @@ 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:
print(f)
# 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()
Expand Down

0 comments on commit 4c23625

Please sign in to comment.