[lldb][macOS] Recognize new layouts for DeviceSupport directories (#188646)

When debugging a remote Darwin device (iOS, macOS, etc), lldb needs to
find a local copy of all the system libraries (the system's shared
cache) so we don't need to read them over gdb-remote serial protocol at
the start of every debug session.

Xcode etc normally creates these expanded shared caches in
~/Library/Developer/Xcode/<OS> DeviceSupport/<OS VER> (<OS
BUILD>)/Symbols

So when lldb sees a file like /usr/lib/libSystem.B.dylib, it may find a
copy at in
~/L/D/Xcode/iOS DeviceSupport/26.2
(23B87)/Symbols/usr/lib/libSystem.B.dylib

There may be multiple expanded shared caches in these DeviceSupport
directories, so we try to parse the "os version" and "os build" out of
the filepath name, and look in a directory that matches the target
device's OS Version as an optimization, to avoid opening the given file
in other DeviceSupport directories.

There is a new layout where the cpu arch exists as a subdirectory under
the "<OS VER> (<OS BUILD>)" directory. e.g.

~/L/D/Xcode/iOS DeviceSupport/26.2 (23B87)/arm64e/Symbols/...

and it is possible that we could have multiple arch names for a given
build -- for instance, an Apple Watch that can have either arm64e or
arm64_32 shared caches.

The existing code also had a very simplistic way of parsing "<OS VER>
(<OS BUILD>)" from the directory name that hasn't been kept up to date
with how the directory names have changed over the years. We have some
like "Watch4,2 10.0 (21R329)" or "10.0 (21R329) universal" which may not
parse with the older parser.

This PR looks for target arch subdirs under the given directories,
passes the os version/build version string separately because it may not
be the final component in the directory name any more, and updates the
directory parser that finds the version and build numbers.

rdar://171821410
This commit is contained in:
Jason Molenda 2026-03-30 15:01:33 -07:00 committed by GitHub
parent 74c4243225
commit 5b00cdf8e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 135 additions and 92 deletions

View File

@ -862,18 +862,30 @@ FileSpec PlatformDarwin::GetSDKDirectoryForModules(XcodeSDK::Type sdk_type) {
return FindSDKInXcodeForModules(sdk_type, sdks_spec);
}
// Discovering the correct version and build can help us
// identify the most likely SDK directory when looking for
// files.
//
// The directory name can be one of many formats, such as
// 10.0 (21R329) universal
// 17.0 (23A200) arm64e
// 17.0 (20A352)
// Watch4,2 10.0 (21R329)
std::tuple<llvm::VersionTuple, llvm::StringRef>
PlatformDarwin::ParseVersionBuildDir(llvm::StringRef dir) {
llvm::StringRef build;
llvm::StringRef version_str;
llvm::StringRef build_str;
std::tie(version_str, build_str) = dir.split(' ');
llvm::VersionTuple version;
if (!version.tryParse(version_str) ||
build_str.empty()) {
if (build_str.consume_front("(")) {
size_t pos = build_str.find(')');
build = build_str.slice(0, pos);
llvm::SmallVector<llvm::StringRef> parts;
dir.split(parts, ' ');
for (llvm::StringRef part : parts) {
// Look for an OS version number, eg "17.0"
if (isdigit(part[0]))
version.tryParse(part);
// Look for a build number, eg "(20A352)"
if (part.consume_front("(")) {
size_t pos = part.find(')');
build = part.slice(0, pos);
}
}

View File

@ -23,62 +23,82 @@ using namespace lldb_private;
PlatformDarwinDevice::~PlatformDarwinDevice() = default;
FileSystem::EnumerateDirectoryResult
PlatformDarwinDevice::GetContainedFilesIntoVectorOfStringsCallback(
PlatformDarwinDevice::GetContainedFilesIntoVectorOfFileSpecsCallback(
void *baton, llvm::sys::fs::file_type ft, llvm::StringRef path) {
((PlatformDarwinDevice::SDKDirectoryInfoCollection *)baton)
->push_back(PlatformDarwinDevice::SDKDirectoryInfo(FileSpec(path)));
((std::vector<FileSpec> *)baton)->push_back(FileSpec(path));
return FileSystem::eEnumerateDirectoryResultNext;
}
void PlatformDarwinDevice::AddSharedCacheDirectory(
llvm::StringRef dir, llvm::StringRef log_msg_descriptor) {
Log *log = GetLog(LLDBLog::Host);
const bool find_directories = true;
const bool find_files = false;
const bool find_other = false;
std::vector<FileSpec> shared_cache_expanded_directories;
FileSystem::Instance().EnumerateDirectory(
dir, find_directories, find_files, find_other,
GetContainedFilesIntoVectorOfFileSpecsCallback,
&shared_cache_expanded_directories);
/// shared_cache_expanded_directories will have the directories under \a dir.
/// Those that have a /Symbols/ subdir are shared cache dirs.
/// Those that have /<arch>/Symbols/ subdirs are shared cache dirs.
for (const FileSpec &sc_directory : shared_cache_expanded_directories) {
FileSpec sc_directory_symbols = sc_directory;
sc_directory_symbols.AppendPathComponent("Symbols");
if (FileSystem::Instance().Exists(sc_directory_symbols)) {
SDKDirectoryInfo thisdir(sc_directory,
sc_directory.GetFilename().GetStringRef());
m_sdk_directory_infos.push_back(thisdir);
LLDB_LOGF(log,
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
"added %s %s",
log_msg_descriptor.str().c_str(),
sc_directory.GetPath().c_str());
}
// See if we have arch subdirs under sc_directory, and if there is
// a Symbols subdir under those.
std::vector<FileSpec> subdirs;
FileSystem::Instance().EnumerateDirectory(
sc_directory.GetPath().c_str(), find_directories, find_files,
find_other, GetContainedFilesIntoVectorOfFileSpecsCallback, &subdirs);
for (const FileSpec &subdir : subdirs) {
FileSpec subdir_directory_symbols = subdir;
subdir_directory_symbols.AppendPathComponent("Symbols");
if (FileSystem::Instance().Exists(subdir_directory_symbols)) {
SDKDirectoryInfo thisdir(subdir,
sc_directory.GetFilename().GetStringRef());
m_sdk_directory_infos.push_back(thisdir);
LLDB_LOGF(log,
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
"added %s %s",
log_msg_descriptor.str().c_str(), subdir.GetPath().c_str());
}
}
}
}
bool PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded() {
Log *log = GetLog(LLDBLog::Host);
std::lock_guard<std::mutex> guard(m_sdk_dir_mutex);
if (m_sdk_directory_infos.empty()) {
// A --sysroot option was supplied - add it to our list of SDKs to check
if (!m_sdk_sysroot.empty()) {
FileSpec sdk_sysroot_fspec(m_sdk_sysroot.c_str());
FileSystem::Instance().Resolve(sdk_sysroot_fspec);
const SDKDirectoryInfo sdk_sysroot_directory_info(sdk_sysroot_fspec);
m_sdk_directory_infos.push_back(sdk_sysroot_directory_info);
LLDB_LOGF(log,
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded added "
"--sysroot SDK directory %s",
m_sdk_sysroot.c_str());
return true;
}
if (!m_sdk_sysroot.empty())
AddSharedCacheDirectory(m_sdk_sysroot.c_str(), "--sysroot SDK directory");
const char *device_support_dir = GetDeviceSupportDirectory();
LLDB_LOGF(log,
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded Got "
"DeviceSupport directory %s",
device_support_dir);
if (device_support_dir) {
const bool find_directories = true;
const bool find_files = false;
const bool find_other = false;
AddSharedCacheDirectory(device_support_dir, "builtin SDK directory");
SDKDirectoryInfoCollection builtin_sdk_directory_infos;
FileSystem::Instance().EnumerateDirectory(
m_device_support_directory, find_directories, find_files, find_other,
GetContainedFilesIntoVectorOfStringsCallback,
&builtin_sdk_directory_infos);
// Only add SDK directories that have symbols in them, some SDKs only
// contain developer disk images and no symbols, so they aren't useful to
// us.
FileSpec sdk_symbols_symlink_fspec;
for (const auto &sdk_directory_info : builtin_sdk_directory_infos) {
sdk_symbols_symlink_fspec = sdk_directory_info.directory;
sdk_symbols_symlink_fspec.AppendPathComponent("Symbols");
if (FileSystem::Instance().Exists(sdk_symbols_symlink_fspec)) {
m_sdk_directory_infos.push_back(sdk_directory_info);
LLDB_LOGF(log,
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
"added builtin SDK directory %s",
sdk_symbols_symlink_fspec.GetPath().c_str());
}
}
const uint32_t num_installed = m_sdk_directory_infos.size();
// "macOS DeviceSupport", "iOS DeviceSupport", etc.
llvm::StringRef dirname = GetDeviceSupportDirectoryName();
std::string local_sdk_cache_str = "~/Library/Developer/Xcode/";
local_sdk_cache_str += std::string(dirname);
@ -89,45 +109,14 @@ bool PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded() {
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
"searching %s for additional SDKs",
local_sdk_cache.GetPath().c_str());
char path[PATH_MAX];
if (local_sdk_cache.GetPath(path, sizeof(path))) {
FileSystem::Instance().EnumerateDirectory(
path, find_directories, find_files, find_other,
GetContainedFilesIntoVectorOfStringsCallback,
&m_sdk_directory_infos);
const uint32_t num_sdk_infos = m_sdk_directory_infos.size();
// First try for an exact match of major, minor and update
for (uint32_t i = num_installed; i < num_sdk_infos; ++i) {
m_sdk_directory_infos[i].user_cached = true;
LLDB_LOGF(log,
"PlatformDarwinDevice::"
"UpdateSDKDirectoryInfosIfNeeded "
"user SDK directory %s",
m_sdk_directory_infos[i].directory.GetPath().c_str());
}
}
AddSharedCacheDirectory(local_sdk_cache.GetPath().c_str(),
"system developer dir directory");
}
const char *addtional_platform_dirs = getenv("PLATFORM_SDK_DIRECTORY");
if (addtional_platform_dirs) {
SDKDirectoryInfoCollection env_var_sdk_directory_infos;
FileSystem::Instance().EnumerateDirectory(
addtional_platform_dirs, find_directories, find_files, find_other,
GetContainedFilesIntoVectorOfStringsCallback,
&env_var_sdk_directory_infos);
FileSpec sdk_symbols_symlink_fspec;
for (const auto &sdk_directory_info : env_var_sdk_directory_infos) {
sdk_symbols_symlink_fspec = sdk_directory_info.directory;
sdk_symbols_symlink_fspec.AppendPathComponent("Symbols");
if (FileSystem::Instance().Exists(sdk_symbols_symlink_fspec)) {
m_sdk_directory_infos.push_back(sdk_directory_info);
LLDB_LOGF(log,
"PlatformDarwinDevice::UpdateSDKDirectoryInfosIfNeeded "
"added env var SDK directory %s",
sdk_symbols_symlink_fspec.GetPath().c_str());
}
}
}
if (addtional_platform_dirs)
AddSharedCacheDirectory(addtional_platform_dirs,
"env var SDK directory");
}
}
return !m_sdk_directory_infos.empty();

View File

@ -30,24 +30,35 @@ protected:
lldb_private::Process *process);
struct SDKDirectoryInfo {
SDKDirectoryInfo(const FileSpec &sdk_dir_spec);
SDKDirectoryInfo(const FileSpec &sdk_dir_spec, llvm::StringRef dirname_str);
FileSpec directory;
ConstString build;
llvm::VersionTuple version;
bool user_cached;
};
typedef std::vector<SDKDirectoryInfo> SDKDirectoryInfoCollection;
/// Look for expanded shared cache directories under the given dir.
/// Expanded shared cache directories found under the given dir will
/// be added to \a m_sdk_directory_infos.
///
/// \param[in] dir
/// Directory to search under.
///
/// \param[in] log_msg_descriptor
/// Text to describe the origin of this directory, in logging.
void AddSharedCacheDirectory(llvm::StringRef dir,
llvm::StringRef log_msg_descriptor);
bool UpdateSDKDirectoryInfosIfNeeded();
const SDKDirectoryInfo *GetSDKDirectoryForLatestOSVersion();
const SDKDirectoryInfo *GetSDKDirectoryForCurrentOSVersion();
static FileSystem::EnumerateDirectoryResult
GetContainedFilesIntoVectorOfStringsCallback(void *baton,
llvm::sys::fs::file_type ft,
llvm::StringRef path);
GetContainedFilesIntoVectorOfFileSpecsCallback(void *baton,
llvm::sys::fs::file_type ft,
llvm::StringRef path);
const char *GetDeviceSupportDirectory();
const char *GetDeviceSupportDirectoryForOSVersion();

View File

@ -28,10 +28,12 @@
using namespace lldb;
using namespace lldb_private;
/// sdk_dir FileSpec may be .../17.0 (15A100)
/// or it may be .../17.0 (15A100)/arm64e
/// In both of these cases, dirname_str should be "17.0 (15A100)"
PlatformRemoteDarwinDevice::SDKDirectoryInfo::SDKDirectoryInfo(
const lldb_private::FileSpec &sdk_dir)
: directory(sdk_dir), build(), user_cached(false) {
llvm::StringRef dirname_str = sdk_dir.GetFilename().GetStringRef();
const lldb_private::FileSpec &sdk_dir, llvm::StringRef dirname_str)
: directory(sdk_dir), build() {
llvm::StringRef build_str;
std::tie(version, build_str) = ParseVersionBuildDir(dirname_str);
build.SetString(build_str);

View File

@ -51,4 +51,33 @@ TEST_F(PlatformMacOSXTest, TestGetSupportedArchitectures) {
arm64_ios_arch));
#endif
}
struct NameAndResult {
std::string dir;
llvm::VersionTuple version;
std::string build;
};
TEST_F(PlatformMacOSXTest, TestDeviceSupportDirectoryNames) {
PlatformMacOSX platform;
NameAndResult tests[] = {
{"10.0 (21R329) universal", llvm::VersionTuple(10, 0), "21R329"},
{"17.0 (23X1010104078) universal", llvm::VersionTuple(17, 0),
"23X1010104078"},
{"17.0 (23A200) arm64e", llvm::VersionTuple(17, 0), "23A200"},
{"17.0 (20A352)", llvm::VersionTuple(17, 0), "20A352"},
{"Watch4,2 10.0 (21R329)", llvm::VersionTuple(10, 0), "21R329"},
{"iPhone11,2 26.0 (23A276)", llvm::VersionTuple(26, 0), "23A276"},
{"iPhone13,2 17.0 (18C22)", llvm::VersionTuple(17, 0), "18C22"},
};
for (size_t i = 0; i < std::size(tests); i++) {
llvm::VersionTuple version;
llvm::StringRef build_str;
std::tie(version, build_str) = platform.ParseVersionBuildDir(tests[i].dir);
EXPECT_EQ(tests[i].version, version);
EXPECT_EQ(tests[i].build, build_str);
}
}
#endif