blob: d1c1aab1dbedcdff1f84068bb8b5e8f526677603 [file]
"""
Test that a process launched through an aliased directory reports its modules
under that directory, so LLDB does not duplicate the modules it already has.
The target is created from a junction to the build directory. LLDB must report
the executable and the DLL next to it with the path the Windows loader
recorded, which keeps the junction, rather than with the resolved path, which
does not. Otherwise LLDB adds a second module for each file and the breakpoints
get a location in both.
"""
import os
import subprocess
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
@requireWindows
@skipIfRemote
class TestModuleAliasedPath(TestBase):
NO_DEBUG_INFO_TESTCASE = True
def make_junction(self, real_dir):
"""Create a junction to real_dir, skipping the test if the host can't
create one."""
alias_dir = real_dir + ".alias"
try:
if os.path.lexists(alias_dir):
os.rmdir(alias_dir)
subprocess.run(
'mklink /J "%s" "%s"' % (alias_dir, real_dir),
shell=True,
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
)
except (OSError, subprocess.CalledProcessError) as e:
detail = getattr(e, "stderr", None) or e
self.skipTest("could not create a junction: %s" % detail)
self.addTearDownHook(lambda: os.rmdir(alias_dir))
self.assertNotEqual(os.path.realpath(alias_dir), alias_dir)
return alias_dir
def test_launch_through_aliased_path(self):
self.build()
alias_dir = self.make_junction(self.getBuildDir())
target = self.dbg.CreateTarget(os.path.join(alias_dir, "a.out"))
self.assertTrue(target, VALID_TARGET)
main_bkpt = target.BreakpointCreateBySourceRegex(
"break main", lldb.SBFileSpec("main.c")
)
foo_bkpt = target.BreakpointCreateBySourceRegex(
"break here", lldb.SBFileSpec("foo.c")
)
self.assertEqual(main_bkpt.GetNumLocations(), 1)
process = target.LaunchSimple(None, None, self.get_process_working_directory())
self.assertState(process.GetState(), lldb.eStateStopped)
self.assertIsNotNone(
lldbutil.get_one_thread_stopped_at_breakpoint(process, main_bkpt)
)
lib_name = self.platformContext.getFullLibName("foo")
for name in ["a.out", lib_name]:
paths = [
m.GetFileSpec().fullpath
for m in target.module_iter()
if m.GetFileSpec().GetFilename() == name
]
self.assertEqual(len(paths), 1, "one module for %s: %s" % (name, paths))
self.assertTrue(
paths[0].lower().startswith(alias_dir.lower()),
"%s is reported under the junction" % paths[0],
)
for bkpt in [main_bkpt, foo_bkpt]:
self.assertEqual(bkpt.GetNumLocations(), 1)
self.assertEqual(bkpt.GetNumResolvedLocations(), 1)
process.Continue()
self.assertIsNotNone(
lldbutil.get_one_thread_stopped_at_breakpoint(process, foo_bkpt)
)