Skip to content

Commit b06a198

Browse files
committed
[LLDB] Support importing modules from llvmcas:// URLs
This patch creates a wrapper LLDB Swft module loader that owns both an ESML (explicit Swift module loader) and a CAS explicit module loader, dispatches module discovery calls to both, and tries to find each module in the CAS first, before trying the ESML. rdar://166576110
1 parent 69757c8 commit b06a198

File tree

14 files changed

+526
-154
lines changed

14 files changed

+526
-154
lines changed

lldb/include/lldb/Core/ModuleList.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include "lldb/lldb-types.h"
2323

2424
#include "llvm/ADT/DenseSet.h"
25-
#include "llvm/CAS/CASConfiguration.h"
2625
#include "llvm/Support/RWMutex.h"
2726

2827
#include <functional>
@@ -33,6 +32,14 @@
3332
#include <cstddef>
3433
#include <cstdint>
3534

35+
namespace llvm {
36+
namespace cas {
37+
class CASConfiguration;
38+
class ObjectStore;
39+
class ActionCache;
40+
} // namespace cas
41+
} // namespace llvm
42+
3643
namespace lldb_private {
3744
class ConstString;
3845
class FileSpecList;
@@ -106,6 +113,7 @@ class ModuleListProperties : public Properties {
106113
// START CAS
107114
/// Get CASPath set via properties.
108115
FileSpec GetCASOnDiskPath() const;
116+
bool SetCASOnDiskPath(const FileSpec &);
109117

110118
/// Get CASPluginPath set via properties.
111119
FileSpec GetCASPluginPath() const;
@@ -538,10 +546,14 @@ class ModuleList {
538546

539547
// START CAS
540548

541-
/// Get CAS configuration using global module properties or from candidate
542-
/// search path.
543-
static std::optional<llvm::cas::CASConfiguration>
544-
GetCASConfiguration(FileSpec CandidateConfigSearchPath);
549+
struct CAS {
550+
std::shared_ptr<llvm::cas::CASConfiguration> configuration;
551+
std::shared_ptr<llvm::cas::ObjectStore> object_store;
552+
std::shared_ptr<llvm::cas::ActionCache> action_cache;
553+
};
554+
/// Search for a CAS configuration file near this module. This
555+
/// operation does a lot of file system stat calls.
556+
static llvm::Expected<CAS> GetOrCreateCAS(const lldb::ModuleSP &module_sp);
545557

546558
/// Gets the shared module from CAS. It works the same as `GetSharedModule`
547559
/// but the lookup is done inside the CAS.
@@ -550,11 +562,9 @@ class ModuleList {
550562
/// true if module is successfully loaded, false if module is not found
551563
/// in the CAS, error if there are any errors happened during the loading
552564
/// process.
553-
static llvm::Expected<bool> GetSharedModuleFromCAS(ConstString module_name,
554-
llvm::StringRef cas_id,
555-
FileSpec cu_path,
556-
ModuleSpec &module_spec,
557-
lldb::ModuleSP &module_sp);
565+
static llvm::Expected<bool>
566+
GetSharedModuleFromCAS(llvm::StringRef cas_id, const lldb::ModuleSP &nearby,
567+
ModuleSpec &module_spec, lldb::ModuleSP &module_sp);
558568
// END CAS
559569

560570
static bool RemoveSharedModule(lldb::ModuleSP &module_sp);

lldb/source/Core/ModuleList.cpp

Lines changed: 183 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "lldb/Interpreter/Property.h"
1818
#include "lldb/Symbol/ObjectFile.h"
1919
#include "lldb/Symbol/SymbolContext.h"
20+
#include "lldb/Symbol/SymbolFile.h"
2021
#include "lldb/Symbol/TypeList.h"
2122
#include "lldb/Symbol/VariableList.h"
2223
#include "lldb/Utility/ArchSpec.h"
@@ -27,7 +28,9 @@
2728
#include "lldb/Utility/Log.h"
2829
#include "lldb/Utility/UUID.h"
2930
#include "lldb/lldb-defines.h"
31+
#include "lldb/lldb-private-enumerations.h"
3032
#include "llvm/ADT/ScopeExit.h"
33+
#include "llvm/Support/Error.h"
3134
#include "llvm/Support/FileUtilities.h"
3235

3336
#if defined(_WIN32)
@@ -304,6 +307,11 @@ FileSpec ModuleListProperties::GetCASOnDiskPath() const {
304307
return GetPropertyAtIndexAs<FileSpec>(idx, {});
305308
}
306309

310+
bool ModuleListProperties::SetCASOnDiskPath(const FileSpec &path) {
311+
const uint32_t idx = ePropertyCASOnDiskPath;
312+
return SetPropertyAtIndex(idx, path);
313+
}
314+
307315
FileSpec ModuleListProperties::GetCASPluginPath() const {
308316
const uint32_t idx = ePropertyCASPluginPath;
309317
return GetPropertyAtIndexAs<FileSpec>(idx, {});
@@ -1278,6 +1286,16 @@ class SharedModuleList {
12781286
continue;
12791287
ModuleList to_remove = RemoveOrphansFromVector(vec);
12801288
remove_count += to_remove.GetSize();
1289+
// BEGIN CAS
1290+
to_remove.ForEach([&](auto &m) {
1291+
auto it = m_cas_configs.find(m.get());
1292+
if (it != m_cas_configs.end()) {
1293+
m_cas_cache.erase(it->second.get());
1294+
m_cas_configs.erase(it);
1295+
}
1296+
return IterationAction::Continue;
1297+
});
1298+
// END CAS
12811299
m_list.Remove(to_remove);
12821300
}
12831301
// Break when fixed-point is reached.
@@ -1292,14 +1310,29 @@ class SharedModuleList {
12921310
/// filename, for fast module lookups by name.
12931311
llvm::DenseMap<ConstString, llvm::SmallVector<ModuleSP, 1>> m_name_to_modules;
12941312

1313+
// BEGIN CAS
1314+
public:
1315+
/// Each module may have a CAS config associated with it.
1316+
/// Often many modules share the same CAS.
1317+
llvm::DenseMap<const Module *, std::shared_ptr<llvm::cas::CASConfiguration>>
1318+
m_cas_configs;
1319+
1320+
/// Each CAS config has a CAS associated with it.
1321+
llvm::DenseMap<const llvm::cas::CASConfiguration *,
1322+
std::pair<std::shared_ptr<llvm::cas::ObjectStore>,
1323+
std::shared_ptr<llvm::cas::ActionCache>>>
1324+
m_cas_cache;
1325+
1326+
private:
1327+
// END CAS
1328+
12951329
/// The use count of a module held only by m_list and m_name_to_modules.
12961330
static constexpr long kUseCountSharedModuleListOrphaned = 2;
12971331
};
12981332

12991333
struct SharedModuleListInfo {
13001334
SharedModuleList module_list;
13011335
ModuleListProperties module_list_properties;
1302-
std::shared_ptr<llvm::cas::ObjectStore> cas_object_store;
13031336
std::mutex shared_lock;
13041337
};
13051338
}
@@ -1322,45 +1355,24 @@ static SharedModuleList &GetSharedModuleList() {
13221355
return GetSharedModuleListInfo().module_list;
13231356
}
13241357

1325-
std::optional<llvm::cas::CASConfiguration>
1326-
ModuleList::GetCASConfiguration(FileSpec CandidateConfigSearchPath) {
1358+
static std::shared_ptr<llvm::cas::CASConfiguration>
1359+
GetCASConfiguration(FileSpec CandidateConfigSearchPath) {
13271360
// Config CAS from properties.
1328-
llvm::cas::CASConfiguration cas_config;
1329-
cas_config.CASPath =
1330-
ModuleList::GetGlobalModuleListProperties().GetCASOnDiskPath().GetPath();
1331-
cas_config.PluginPath =
1332-
ModuleList::GetGlobalModuleListProperties().GetCASPluginPath().GetPath();
1333-
cas_config.PluginOptions =
1334-
ModuleList::GetGlobalModuleListProperties().GetCASPluginOptions();
1335-
1336-
if (!cas_config.CASPath.empty())
1337-
return cas_config;
1338-
1361+
auto &props = ModuleList::GetGlobalModuleListProperties();
1362+
auto path = props.GetCASOnDiskPath().GetPath();
1363+
if (!path.empty()) {
1364+
auto config = std::make_shared<llvm::cas::CASConfiguration>();
1365+
config->CASPath = props.GetCASOnDiskPath().GetPath();
1366+
config->PluginPath = props.GetCASPluginPath().GetPath();
1367+
config->PluginOptions = props.GetCASPluginOptions();
1368+
return config;
1369+
}
13391370
auto search_config = llvm::cas::CASConfiguration::createFromSearchConfigFile(
13401371
CandidateConfigSearchPath.GetPath());
13411372
if (search_config)
1342-
return search_config->second;
1373+
return std::make_shared<llvm::cas::CASConfiguration>(search_config->second);
13431374

1344-
return std::nullopt;
1345-
}
1346-
1347-
static llvm::Expected<std::shared_ptr<llvm::cas::ObjectStore>>
1348-
GetOrCreateCASStorage(FileSpec CandidateConfigSearchPath) {
1349-
auto &shared_module_list = GetSharedModuleListInfo();
1350-
if (shared_module_list.cas_object_store)
1351-
return shared_module_list.cas_object_store;
1352-
1353-
auto config = ModuleList::GetCASConfiguration(CandidateConfigSearchPath);
1354-
if (!config)
1355-
return nullptr;
1356-
1357-
auto cas = config->createDatabases();
1358-
if (!cas)
1359-
return cas.takeError();
1360-
1361-
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1362-
shared_module_list.cas_object_store = std::move(cas->first);
1363-
return shared_module_list.cas_object_store;
1375+
return {};
13641376
}
13651377

13661378
ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
@@ -1634,19 +1646,18 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
16341646
return error;
16351647
}
16361648

1637-
static llvm::Expected<bool> loadModuleFromCAS(ConstString module_name,
1638-
llvm::StringRef cas_id,
1639-
FileSpec cu_path,
1640-
ModuleSpec &module_spec) {
1641-
auto maybe_cas = GetOrCreateCASStorage(cu_path);
1649+
static llvm::Expected<bool> loadModuleFromCASImpl(llvm::StringRef cas_id,
1650+
const lldb::ModuleSP &nearby,
1651+
ModuleSpec &module_spec) {
1652+
auto maybe_cas = ModuleList::GetOrCreateCAS(nearby);
16421653
if (!maybe_cas)
16431654
return maybe_cas.takeError();
16441655

1645-
auto cas = std::move(*maybe_cas);
1656+
auto cas = std::move(maybe_cas->object_store);
16461657
if (!cas) {
16471658
LLDB_LOG(GetLog(LLDBLog::Modules),
16481659
"skip loading module '{0}' from CAS: CAS is not available",
1649-
module_name);
1660+
cas_id);
16501661
return false;
16511662
}
16521663

@@ -1670,15 +1681,131 @@ static llvm::Expected<bool> loadModuleFromCAS(ConstString module_name,
16701681
loaded.GetArchitecture() = module_spec.GetArchitecture();
16711682
module_spec = loaded;
16721683

1673-
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module '{0}' using CASID '{1}'",
1674-
module_name, cas_id);
1684+
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module using CASID '{0}'",
1685+
cas_id);
16751686
return true;
16761687
}
16771688

1689+
/// Load the module referenced by \c cas_id from a CAS located
1690+
/// near \c nearby.
1691+
static llvm::Expected<bool> loadModuleFromCAS(llvm::StringRef cas_id,
1692+
const lldb::ModuleSP &nearby,
1693+
ModuleSpec &module_spec) {
1694+
static llvm::StringMap<bool> g_cache;
1695+
static std::recursive_mutex g_cache_lock;
1696+
std::scoped_lock<std::recursive_mutex> lock(g_cache_lock);
1697+
auto cached = g_cache.find(cas_id);
1698+
if (cached != g_cache.end())
1699+
return cached->second;
1700+
auto result = loadModuleFromCASImpl(cas_id, nearby, module_spec);
1701+
// Errors are only returned the first time.
1702+
g_cache.insert({cas_id, result ? *result : false});
1703+
return result;
1704+
}
1705+
1706+
static std::shared_ptr<llvm::cas::CASConfiguration>
1707+
FindCASConfiguration(const ModuleSP &module_sp) {
1708+
auto get_dir = [](FileSpec path) {
1709+
path.ClearFilename();
1710+
return path;
1711+
};
1712+
1713+
// Look near the binary / dSYM.
1714+
std::set<FileSpec> unique_paths;
1715+
std::shared_ptr<llvm::cas::CASConfiguration> cas_config =
1716+
GetCASConfiguration(module_sp->GetFileSpec());
1717+
1718+
if (!cas_config) {
1719+
// Look near the object files.
1720+
auto insert_module_path = [&](const ModuleSP &m) -> IterationAction {
1721+
if (m)
1722+
unique_paths.insert(get_dir(m->GetFileSpec()));
1723+
return IterationAction::Continue;
1724+
};
1725+
1726+
if (SymbolFile *sf = module_sp->GetSymbolFile()) {
1727+
sf->GetDebugInfoModules().ForEach(insert_module_path);
1728+
for (auto &path : unique_paths)
1729+
if ((cas_config = GetCASConfiguration(path)))
1730+
break;
1731+
}
1732+
}
1733+
if (!cas_config)
1734+
for (auto &path : unique_paths) {
1735+
llvm::StringRef parent = path.GetDirectory().GetStringRef();
1736+
while (!parent.empty() &&
1737+
llvm::sys::path::filename(parent) != "DerivedData")
1738+
parent = llvm::sys::path::parent_path(parent);
1739+
if (parent.empty())
1740+
continue;
1741+
llvm::SmallString<256> cas_path(parent);
1742+
llvm::sys::path::append(cas_path, "CompilationCache.noindex", "builtin");
1743+
FileSpec fs = FileSpec(cas_path);
1744+
ModuleList::GetGlobalModuleListProperties().SetCASOnDiskPath(fs);
1745+
cas_config = GetCASConfiguration(fs);
1746+
if (cas_config)
1747+
break;
1748+
}
1749+
return cas_config;
1750+
}
1751+
1752+
llvm::Expected<ModuleList::CAS>
1753+
ModuleList::GetOrCreateCAS(const ModuleSP &module_sp) {
1754+
if (!module_sp)
1755+
return llvm::createStringError("no lldb::Module available");
1756+
1757+
// Look in cache first.
1758+
auto &shared_module_list = GetSharedModuleListInfo();
1759+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1760+
auto &cas_configs = shared_module_list.module_list.m_cas_configs;
1761+
auto &cas_cache = shared_module_list.module_list.m_cas_cache;
1762+
1763+
std::shared_ptr<llvm::cas::CASConfiguration> cas_config;
1764+
{
1765+
auto cached_config = cas_configs.find(module_sp.get());
1766+
if (cached_config != cas_configs.end()) {
1767+
cas_config = cached_config->second;
1768+
if (!cas_config)
1769+
return llvm::createStringError("no CAS available (cached)");
1770+
}
1771+
}
1772+
1773+
if (!cas_config) {
1774+
cas_config = FindCASConfiguration(module_sp);
1775+
// Cache the config or lack thereof.
1776+
cas_configs.insert({module_sp.get(), cas_config});
1777+
}
1778+
1779+
if (!cas_config)
1780+
return llvm::createStringError("no CAS available");
1781+
1782+
// Look in the cache.
1783+
{
1784+
auto cached = cas_cache.find(cas_config.get());
1785+
if (cached != cas_cache.end()) {
1786+
if (!cached->second.first)
1787+
return llvm::createStringError(
1788+
"CAS config created, but CAS not available (cached)");
1789+
return ModuleList::CAS{cas_config, cached->second.first,
1790+
cached->second.second};
1791+
}
1792+
}
1793+
1794+
// Create the CAS.
1795+
auto cas = cas_config->createDatabases();
1796+
if (!cas) {
1797+
cas_cache.insert({cas_config.get(), {}});
1798+
return cas.takeError();
1799+
}
1800+
1801+
cas_cache.insert({cas_config.get(), {cas->first, cas->second}});
1802+
return ModuleList::CAS{cas_config, cas->first, cas->second};
1803+
}
1804+
16781805
llvm::Expected<bool> ModuleList::GetSharedModuleFromCAS(
1679-
ConstString module_name, llvm::StringRef cas_id, FileSpec cu_path,
1806+
llvm::StringRef cas_id, const lldb::ModuleSP &nearby,
16801807
ModuleSpec &module_spec, lldb::ModuleSP &module_sp) {
1681-
auto loaded = loadModuleFromCAS(module_name, cas_id, cu_path, module_spec);
1808+
auto loaded = loadModuleFromCAS(cas_id, nearby, module_spec);
16821809
if (!loaded)
16831810
return loaded.takeError();
16841811

@@ -1688,8 +1815,17 @@ llvm::Expected<bool> ModuleList::GetSharedModuleFromCAS(
16881815
auto status =
16891816
GetSharedModule(module_spec, module_sp, nullptr, nullptr, nullptr,
16901817
/*always_create=*/true);
1691-
if (status.Success())
1818+
if (status.Success()) {
1819+
if (module_sp) {
1820+
// Enter the new module into the config cache.
1821+
auto &shared_module_list = GetSharedModuleListInfo();
1822+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1823+
auto &cas_configs = shared_module_list.module_list.m_cas_configs;
1824+
auto config = cas_configs.lookup(nearby.get());
1825+
cas_configs.insert({module_sp.get(), config});
1826+
}
16921827
return true;
1828+
}
16931829
return status.takeError();
16941830
}
16951831

0 commit comments

Comments
 (0)