================
@@ -0,0 +1,125 @@
+"""
+Test that ProcessWindows keeps working when an OS plugin populates the
+user-facing thread list with virtual threads.
+"""
+
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbsuite.test.lldbutil as lldbutil
+
+# The tid the OS plugin in this directory reports for its virtual thread.
+OS_TID = 0x111111111
+
+
+@requireWindows
+@skipIfWindowsAndLLDBServer
+class TestWindowsOSPluginThreads(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.source = lldb.SBFileSpec("main.cpp")
+
+ def stop_and_load_os_plugin(self, stop_regex, args=None):
+ """Run to stop_regex, load the OS plugin, and return (target, process).
+
+ Asserts the premise the plugin sets up: the real thread the process
+ stopped on is gone from the user-facing list, replaced by a virtual
+ thread that is not the list's first entry.
+ """
+ self.build()
+ launch_info = None
+ if args:
+ launch_info = lldb.SBLaunchInfo(args)
+
launch_info.SetWorkingDirectory(self.get_process_working_directory())
+ target, process, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, stop_regex, self.source, launch_info=launch_info
+ )
+
+ # These paths only exist in the in-process plugin. LLDB_USE_LLDB_SERVER
+ # is not the only thing that can select lldb-server, so check what we
+ # actually got rather than trusting the decorator.
+ if process.GetPluginName() != "windows":
+ self.skipTest("test covers the in-process Windows process plugin")
+
+ # main is core 0; the worker thread keeps a second real thread around.
+ self.assertGreaterEqual(process.GetNumThreads(), 2)
+ real_tid = thread.GetThreadID()
+ self.assertEqual(process.GetThreadAtIndex(0).GetThreadID(), real_tid)
+
+ self.runCmd(
+ "settings set target.process.python-os-plugin-path '%s'"
+ % os.path.join(self.getSourceDir(), "operating_system.py")
+ )
+
+ os_thread = process.GetThreadByID(OS_TID)
+ self.assertTrue(os_thread.IsValid(), "the OS plugin thread showed up")
+ self.assertFalse(
+ process.GetThreadByID(real_tid).IsValid(),
+ "the real thread we stopped on is no longer user-visible",
+ )
+ self.assertNotEqual(
+ process.GetThreadAtIndex(0).GetThreadID(),
+ OS_TID,
+ "the virtual thread is not the first thread in the list",
+ )
+ return target, process
+
+ def test_breakpoint_on_backed_thread(self):
+ """A breakpoint hit on a real thread is reported on the virtual thread
+ standing in for it, not on whichever thread happens to be first."""
+ target, process = self.stop_and_load_os_plugin("// Break here")
+
+ breakpoint = target.BreakpointCreateBySourceRegex(
+ "// Second stop here", self.source
+ )
+ self.assertEqual(breakpoint.GetNumLocations(), 1)
+
+ # Resuming has to walk the real threads: the virtual thread has no OS
+ # thread to resume.
+ process.Continue()
+ self.assertState(process.GetState(), lldb.eStateStopped)
+
+ stopped = lldbutil.get_threads_stopped_at_breakpoint(process,
breakpoint)
+ self.assertEqual(len(stopped), 1, "exactly one thread hit the
breakpoint")
+ self.assertEqual(stopped[0].GetThreadID(), OS_TID)
+
+ def test_watchpoint_on_backed_thread(self):
+ """A watchpoint is programmed into the real threads' debug registers
and
+ its hit is reported on the virtual thread."""
+ target, process = self.stop_and_load_os_plugin("// Break here")
+
+ self.runCmd("watchpoint set variable g_watched")
+ self.assertEqual(target.GetNumWatchpoints(), 1)
+
+ process.Continue()
+ self.assertState(process.GetState(), lldb.eStateStopped)
+
+ thread = lldbutil.get_stopped_thread(process,
lldb.eStopReasonWatchpoint)
+ self.assertIsNotNone(thread, "stopped for the watchpoint")
+ self.assertEqual(thread.GetThreadID(), OS_TID)
+
+ self.runCmd("watchpoint delete 1")
+ process.Continue()
+ self.assertState(process.GetState(), lldb.eStateExited)
+
+ def test_detach_with_virtual_threads(self):
+ """Detaching resumes the real threads rather than the virtual one, so
the
+ inferior runs on afterwards."""
+ marker = self.getBuildArtifact("detached.marker")
+ if os.path.exists(marker):
+ os.remove(marker)
+
+ _, process = self.stop_and_load_os_plugin("// Break here",
args=[marker])
+
+ self.assertSuccess(process.Detach())
----------------
charles-zablit wrote:
Fixed, thanks
https://github.com/llvm/llvm-project/pull/215574
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits