Files
llvm-project/lldb/test/API/commands/platform/basic/TestPlatformCommand.py
Jason Molenda 481f248e08 [lldb] Get shared cache path from inferior, open (#180323)
Get the shared cache filepath and uuid that the inferior process is
using from debugserver, try to open that shared cache on the lldb host
mac and if the UUID matches, index all of the binaries in that shared
cache. When looking for binaries loaded in the process, get them from
the already-indexed shared cache.

Every time a binary is loaded, PlatformMacOSX may query the shared cache
filepath and uuid from the Process, and pass that to
HostInfo::GetSharedCacheImageInfo() if available (else fall back to the
old HostInfo::GetSharedCacheImageInfo method which only looks at lldb's
own shared cache), to get the file being requested.

ProcessGDBRemote caches the shared cache filepath and uuid from the
inferior, once it has a non-zero UUID. I added a lock for this ivar
specifically, so I don't have 20 threads all asking for the shared cache
information from debugserver and updating the cached answer. If we never
get back a non-zero UUID shared cache reply, we will re-query at every
library loaded notification. debugserver has been providing the shared
cache UUID since 2013, although I only added the shared cache filepath
field last November.

Note that a process will not report its shared cache filepath or uuid at
initial launch. As dyld gets a chance to execute a bit, it will start
returning binaries -- it will be available at the point when libraries
start loading. (it won't be available yet when the binary & dyld are the
only two binaries loaded in the process)

I tested this by disabling lldb's scan of its own shared cache
pre-execution -- only loading the system shared cache when the inferior
process reports that it is using that. I got 6-7 additional testsuite
failures running lldb like that, because no system binaries were loaded
before exeuction start, and the tests assumed they would be.

rdar://148939795

---------

Co-authored-by: Jonas Devlieghere <jonas@devlieghere.com>
2026-02-09 16:22:25 -08:00

109 lines
3.6 KiB
Python

"""
Test some lldb platform commands.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class PlatformCommandTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@no_debug_info_test
def test_help_platform(self):
self.runCmd("help platform")
@no_debug_info_test
def test_help_shell_alias(self):
self.expect(
"help shell",
substrs=[
"Run a shell command on the host.",
"shell <shell-command>",
"'shell' is an abbreviation",
],
)
# "platform shell" has options. The "shell" alias for it does not.
self.expect("help shell", substrs=["Command Options:"], matching=False)
@no_debug_info_test
def test_list(self):
self.expect("platform list", patterns=["^Available platforms:"])
@no_debug_info_test
def test_process_list(self):
self.expect("platform process list", substrs=["PID", "TRIPLE", "NAME"])
@no_debug_info_test
def test_process_info_with_no_arg(self):
"""This is expected to fail and to return a proper error message."""
self.expect(
"platform process info",
error=True,
substrs=["one or more process id(s) must be specified"],
)
@no_debug_info_test
def test_status(self):
self.expect(
"platform status",
substrs=[
"Platform",
"Triple",
"OS Version",
"Hostname",
"Kernel",
],
)
@expectedFailureAll(oslist=["windows"])
@no_debug_info_test
def test_shell(self):
"""Test that the platform shell command can invoke ls."""
triple = self.dbg.GetSelectedPlatform().GetTriple()
if re.match(".*-.*-windows", triple):
self.expect("platform shell dir c:\\", substrs=["Windows", "Program Files"])
self.expect("shell dir c:\\", substrs=["Windows", "Program Files"])
elif re.match(".*-.*-.*-android", triple):
self.expect("platform shell ls /", substrs=["cache", "dev", "system"])
self.expect("shell ls /", substrs=["cache", "dev", "system"])
else:
self.expect(
"platform shell ls /", substrs=["dev", "tmp", "usr"], ordered=False
)
self.expect("shell ls /", substrs=["dev", "tmp", "usr"], ordered=False)
@no_debug_info_test
def test_shell_builtin(self):
"""Test a shell built-in command (echo)"""
self.expect("platform shell echo hello lldb", substrs=["hello lldb"])
self.expect("shell echo hello lldb", substrs=["hello lldb"])
@no_debug_info_test
def test_shell_timeout(self):
"""Test a shell built-in command (sleep) that times out"""
self.skipTest("Alias with option not supported by the command interpreter.")
self.expect(
"platform shell -t 1 -- sleep 15",
error=True,
substrs=["error: timed out waiting for shell command to complete"],
)
self.expect(
"shell -t 1 -- sleep 3",
error=True,
substrs=["error: timed out waiting for shell command to complete"],
)
@no_debug_info_test
@skipIfRemote
def test_host_shell_interpreter(self):
"""Test the host platform shell with a different interpreter"""
self.build()
exe = self.getBuildArtifact("a.out")
self.expect(
"platform shell -h -s " + exe + " -- 'echo $0'",
substrs=["SUCCESS", "a.out"],
)