Skip to content

Conversation

@charles-zablit
Copy link
Contributor

@charles-zablit charles-zablit commented Dec 2, 2025

This patch converts the jit-loader_rtdyld_elf.test test from a Shell test to an API test.

This test is timing out in CI on Windows and the hang cannot be reproduced at desk. Converting it to an API test would allow us to instrument it better in order to trace the failure.

@llvmbot
Copy link
Member

llvmbot commented Dec 2, 2025

@llvm/pr-subscribers-platform-windows

@llvm/pr-subscribers-lldb

Author: Charles Zablit (charles-zablit)

Changes

This patch converts the jit-loader_rtdyld_elf.test test from a Shell test to an API test.

This test is timing out in CI on Windows and the hang cannot be reproduced at desk. Converting it to an API test would allow us to instrument it better in order to trace the failure.


Full diff: https://github.com/llvm/llvm-project/pull/170333.diff

4 Files Affected:

  • (added) lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/Makefile (+14)
  • (added) lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/TestJitBreakPoint.py (+53)
  • (renamed) lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/jitbp.cpp ()
  • (removed) lldb/test/Shell/Breakpoint/jit-loader_rtdyld_elf.test (-22)
diff --git a/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/Makefile b/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/Makefile
new file mode 100644
index 0000000000000..51a6bfc5fee67
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/Makefile
@@ -0,0 +1,14 @@
+CXX_SOURCES := jitbp.cpp
+
+include Makefile.rules
+
+# Build LLVM IR that lli will JIT.
+# Matches the commands from the shell test.
+jitbp.ll: jitbp.cpp
+	$(CXX) -g -S -emit-llvm --target=x86_64-unknown-unknown-elf \
+	      -o $@ $<
+
+all: jitbp.ll
+
+clean::
+	rm -f jitbp.ll
diff --git a/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/TestJitBreakPoint.py b/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/TestJitBreakPoint.py
new file mode 100644
index 0000000000000..1a4b16f03f474
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/TestJitBreakPoint.py
@@ -0,0 +1,53 @@
+"""
+Test that pending breakpoints resolve for JITted code with mcjit and rtdyld.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestJitBreakpoint(TestBase):
+    def setUp(self):
+        TestBase.setUp(self)
+        self.ll = self.getBuildArtifact("jitbp.ll")
+
+    @skipUnlessArch("x86_64")
+    @expectedFailureAll(oslist=["windows"])
+    def test_jit_breakpoints(self):
+        self.build()
+        self.do_test("--jit-kind=mcjit")
+        self.do_test("--jit-linker=rtdyld")
+
+    def do_test(self, jit_flag: str):
+        self.dbg.SetAsync(False)
+
+        self.dbg.HandleCommand("settings set plugin.jit-loader.gdb.enable on")
+
+        lli_path = lldbutil.which("lli")
+        self.assertIsNotNone(lli_path, "Could not find lli executable")
+        target = self.dbg.CreateTarget(lli_path)
+        self.assertTrue(target.IsValid())
+
+        bp = target.BreakpointCreateByName("jitbp")
+        self.assertTrue(bp.IsValid())
+        self.assertEqual(bp.GetNumLocations(), 0, "Expected a pending breakpoint")
+
+        launch_info = target.GetLaunchInfo()
+        launch_info.SetArguments([jit_flag, self.ll], True)
+
+        error = lldb.SBError()
+        process = target.Launch(launch_info, error)
+        self.assertTrue(process.IsValid())
+        self.assertTrue(error.Success(), error.GetCString())
+
+        self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+        thread = process.GetSelectedThread()
+        frame = thread.GetSelectedFrame()
+        self.assertIn("jitbp", frame.GetFunctionName())
+
+        self.assertGreaterEqual(
+            bp.GetNumLocations(), 1, "Breakpoint must be resolved after JIT loads code"
+        )
diff --git a/lldb/test/Shell/Breakpoint/Inputs/jitbp.cpp b/lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/jitbp.cpp
similarity index 100%
rename from lldb/test/Shell/Breakpoint/Inputs/jitbp.cpp
rename to lldb/test/API/functionalities/breakpoint/jit_loader_rtdyld_elf/jitbp.cpp
diff --git a/lldb/test/Shell/Breakpoint/jit-loader_rtdyld_elf.test b/lldb/test/Shell/Breakpoint/jit-loader_rtdyld_elf.test
deleted file mode 100644
index ae9402a519494..0000000000000
--- a/lldb/test/Shell/Breakpoint/jit-loader_rtdyld_elf.test
+++ /dev/null
@@ -1,22 +0,0 @@
-# REQUIRES: target-x86_64
-# XFAIL: system-windows
-
-# RuntimeDyld can be used to link and load emitted code for both, MCJIT and Orc.
-#
-# RUN: %clangxx -g -S -emit-llvm --target=x86_64-unknown-unknown-elf \
-# RUN:          -o %t.ll %p/Inputs/jitbp.cpp
-#
-# RUN: %lldb -b -o 'settings set plugin.jit-loader.gdb.enable on' -o 'b jitbp' \
-# RUN:          -o 'run --jit-kind=mcjit %t.ll' lli | FileCheck %s
-#
-# RUN: %lldb -b -o 'settings set plugin.jit-loader.gdb.enable on' -o 'b jitbp' \
-# RUN:          -o 'run --jit-linker=rtdyld %t.ll' lli | FileCheck %s
-
-# CHECK: Breakpoint 1: no locations (pending).
-# CHECK: (lldb) run {{.*}}
-# CHECK: Process {{.*}} launched: {{.*}}
-# CHECK: Process {{.*}} stopped
-# CHECK: JIT(0x{{.*}})`jitbp() at jitbp.cpp:1:15
-# CHECK: -> 1    int jitbp() { return 0; }
-# CHECK:                       ^
-# CHECK:    2    int main() { return jitbp(); }

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

🐧 Linux x64 Test Results

  • 33221 tests passed
  • 507 tests skipped

✅ The build succeeded and all tests passed.

@charles-zablit charles-zablit force-pushed the charles-zablit/lldb/convert-shell-test-to-api-test branch from 77c1872 to 29ee61c Compare December 3, 2025 13:06
@github-actions
Copy link

github-actions bot commented Dec 3, 2025

✅ With the latest revision this PR passed the Python code formatter.

@charles-zablit charles-zablit force-pushed the charles-zablit/lldb/convert-shell-test-to-api-test branch 2 times, most recently from c83a241 to 344dfcc Compare December 3, 2025 13:31
@charles-zablit charles-zablit force-pushed the charles-zablit/lldb/convert-shell-test-to-api-test branch from 344dfcc to 07ecfc8 Compare December 3, 2025 14:47
TestBase.setUp(self)
self.ll = self.getBuildArtifact("jitbp.ll")

@skipUnlessArch("x86_64")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll also need a decorator that checks that the compiler is clang (and not, e.g., gcc) since we use the -emit-llvm flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added @skipUnlessCompilerIsClang

class TestJitBreakpoint(TestBase):
def setUp(self):
TestBase.setUp(self)
self.ll = self.getBuildArtifact("jitbp.ll")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make this a local variable below, you can remove the entire setUp function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks 👍

from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *

import shutil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe sinking this into the function below makes test discovery marginally faster?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks 👍

error = lldb.SBError()
process = target.Launch(launch_info, error)
self.assertTrue(process.IsValid())
self.assertTrue(error.Success(), error.GetCString())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a good reason why we can't use any of the lldbutil helper functions that launch a program and check for breakpoints here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first, I tried to mimic the Shell test style. Now, I copied how the TestInlinedBreakpoints.py test is setup.


lldb_dir = os.path.dirname(lldbtest_config.lldbExec)
lli_path = shutil.which("lli", path=lldb_dir)
self.assertTrue(os.path.exists(lli_path), "lli not found")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to fail in out-of-tree builds?
Do we have a way to get the LLVM BINDIR here?

Copy link
Contributor Author

@charles-zablit charles-zablit Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have replaced the whole invocation with self.runCmd("target create lli", CURRENT_EXECUTABLE_SET). This is what we were doing in the shell version of the test.

I did not find a way to get the LLVM bindir.

self.assertTrue(clang_path, "built clang could not be found")
lli_path = os.path.join(os.path.dirname(clang_path), "lli")
self.assertTrue(lldbutil.is_exe(lli_path), f"'{lli_path}' is not an executable")
self.runCmd(f"target create {lli_path}", CURRENT_EXECUTABLE_SET)
Copy link
Collaborator

@jimingham jimingham Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lldbutil.run_to_source_breakpoint takes the path to the executable to run as a named argument, and a boolean for whether the pattern is found on launch, so you could replace from line 27 to line 46 with:

lldbutil.run_to_source_breakpoint(self, "int jitbp()", lldb.SBFileSpec("jitbt.cpp"), exe_name=lli_path, has_locations_before_run=False)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if your goal is to have this be in separate pieces in case you need to instrument it when it mysteriously fails on some bot, maybe doing the separate stages by hand in the test is okay too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that, as you said, we should keep the separate stages to instrument the test later on once it times out.

Copy link
Collaborator

@adrian-prantl adrian-prantl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants