From 2665e747d5866480f2f704d5a72fe016d258f200 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Tue, 25 Feb 2025 13:11:33 +0000 Subject: [PATCH] [lldb]HostInfoMacOSX] Search CommandLineTools directory when looking up SDK paths `GetSDKRoot` uses `xcrun` to find an SDK root path for a given SDK version string. But if the SDK doesn't exist in the Xcode installations, but instead lives in the `CommandLineTools`, `xcrun` will fail to find it. Negative searches for an SDK path cost a lot (a few seconds) each time `xcrun` is invoked. We do cache negative results in `find_cached_path` inside LLDB, but we would still pay the price on every new debug session the first time we evaluate an expression. This doesn't only cause a noticable delay in running the expression, but also generates following error: ``` error: Error while searching for Xcode SDK: timed out waiting for shell command to complete (int) $0 = 42 ``` To avoid this `xcrun` penalty, we search `CommandLineTools` for a matching SDK ourselves, and only if we don't find it, do we fall back to calling `xcrun`. rdar://113619904 rdar://113619723 --- lldb/include/lldb/Host/FileSystem.h | 5 +- .../Host/macosx/objcxx/HostInfoMacOSX.mm | 60 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/Host/FileSystem.h b/lldb/include/lldb/Host/FileSystem.h index 640f3846e448c..4128d7b012041 100644 --- a/lldb/include/lldb/Host/FileSystem.h +++ b/lldb/include/lldb/Host/FileSystem.h @@ -183,8 +183,9 @@ class FileSystem { eEnumerateDirectoryResultQuit }; - typedef EnumerateDirectoryResult (*EnumerateDirectoryCallbackType)( - void *baton, llvm::sys::fs::file_type file_type, llvm::StringRef); + typedef std::function + EnumerateDirectoryCallbackType; typedef std::function diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm index 6e924fdc684cf..a94fd3b57f9d6 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -15,11 +15,14 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/Timer.h" +#include "clang/Basic/DarwinSDKInfo.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/VersionTuple.h" #include "llvm/Support/raw_ostream.h" // C++ Includes @@ -569,10 +572,52 @@ static bool ResolveAndVerifyCandidateSupportDir(FileSpec &path) { cache.insert({key, {error, true}}); return llvm::createStringError(llvm::inconvertibleErrorCode(), error); } + + if (path_or_err->empty()) + return llvm::createStringError("Empty path determined for '%s'", + key.data()); + auto it_new = cache.insert({key, {*path_or_err, false}}); return it_new.first->second.str; } +static llvm::Expected +GetCommandLineToolsSDKRoot(llvm::VersionTuple version) { + std::string clt_root_dir; + FileSystem::Instance().EnumerateDirectory( + "/Library/Developer/CommandLineTools/SDKs/", /*find_directories=*/true, + /*find_files=*/false, /*find_other=*/false, + [&](void *baton, llvm::sys::fs::file_type file_type, + llvm::StringRef name) { + assert(file_type == llvm::sys::fs::file_type::directory_file); + + if (!name.ends_with(".sdk")) + return FileSystem::eEnumerateDirectoryResultNext; + + llvm::Expected> sdk_info = + clang::parseDarwinSDKInfo( + *FileSystem::Instance().GetVirtualFileSystem(), name); + if (!sdk_info) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), sdk_info.takeError(), + "Error while parsing {1}: {0}", name); + return FileSystem::eEnumerateDirectoryResultNext; + } + + if (!*sdk_info) + return FileSystem::eEnumerateDirectoryResultNext; + + if (version == (*sdk_info)->getVersion()) { + clt_root_dir = name; + return FileSystem::eEnumerateDirectoryResultQuit; + } + + return FileSystem::eEnumerateDirectoryResultNext; + }, + /*baton=*/nullptr); + + return clt_root_dir; +} + llvm::Expected HostInfoMacOSX::GetSDKRoot(SDKOptions options) { static llvm::StringMap g_sdk_path; static std::mutex g_sdk_path_mutex; @@ -581,6 +626,21 @@ static bool ResolveAndVerifyCandidateSupportDir(FileSpec &path) { "XcodeSDK not specified"); XcodeSDK sdk = *options.XcodeSDKSelection; auto key = sdk.GetString(); + + // xcrun doesn't search SDKs in the CommandLineTools (CLT) directory. So if + // a program was compiled against a CLT SDK, but that SDK wasn't present in + // any of the Xcode installations, then xcrun would fail to find the SDK + // (which is expensive). To avoid this we first try to find the specified SDK + // in the CLT directory. + auto clt_root_dir = find_cached_path(g_sdk_path, g_sdk_path_mutex, key, [&] { + return GetCommandLineToolsSDKRoot(sdk.GetVersion()); + }); + + if (clt_root_dir) + return clt_root_dir; + else + llvm::consumeError(clt_root_dir.takeError()); + return find_cached_path(g_sdk_path, g_sdk_path_mutex, key, [&](){ return GetXcodeSDK(sdk); });