Skip to content

Commit 3231ef3

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 3231ef3

File tree

14 files changed

+540
-154
lines changed

14 files changed

+540
-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: 197 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_module_configs.find(m.get());
1292+
if (it != m_module_configs.end()) {
1293+
m_cas_cache.erase(it->second.get());
1294+
m_module_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,31 @@ 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_module_configs;
1319+
1320+
llvm::StringMap<std::weak_ptr<llvm::cas::CASConfiguration>> m_cas_configs;
1321+
1322+
/// Each CAS config has a CAS associated with it.
1323+
llvm::DenseMap<const llvm::cas::CASConfiguration *,
1324+
std::pair<std::shared_ptr<llvm::cas::ObjectStore>,
1325+
std::shared_ptr<llvm::cas::ActionCache>>>
1326+
m_cas_cache;
1327+
1328+
private:
1329+
// END CAS
1330+
12951331
/// The use count of a module held only by m_list and m_name_to_modules.
12961332
static constexpr long kUseCountSharedModuleListOrphaned = 2;
12971333
};
12981334

12991335
struct SharedModuleListInfo {
13001336
SharedModuleList module_list;
13011337
ModuleListProperties module_list_properties;
1302-
std::shared_ptr<llvm::cas::ObjectStore> cas_object_store;
13031338
std::mutex shared_lock;
13041339
};
13051340
}
@@ -1322,45 +1357,24 @@ static SharedModuleList &GetSharedModuleList() {
13221357
return GetSharedModuleListInfo().module_list;
13231358
}
13241359

1325-
std::optional<llvm::cas::CASConfiguration>
1326-
ModuleList::GetCASConfiguration(FileSpec CandidateConfigSearchPath) {
1360+
static std::shared_ptr<llvm::cas::CASConfiguration>
1361+
GetCASConfiguration(FileSpec CandidateConfigSearchPath) {
13271362
// 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-
1363+
auto &props = ModuleList::GetGlobalModuleListProperties();
1364+
auto path = props.GetCASOnDiskPath().GetPath();
1365+
if (!path.empty()) {
1366+
auto config = std::make_shared<llvm::cas::CASConfiguration>();
1367+
config->CASPath = props.GetCASOnDiskPath().GetPath();
1368+
config->PluginPath = props.GetCASPluginPath().GetPath();
1369+
config->PluginOptions = props.GetCASPluginOptions();
1370+
return config;
1371+
}
13391372
auto search_config = llvm::cas::CASConfiguration::createFromSearchConfigFile(
13401373
CandidateConfigSearchPath.GetPath());
13411374
if (search_config)
1342-
return search_config->second;
1375+
return std::make_shared<llvm::cas::CASConfiguration>(search_config->second);
13431376

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;
1377+
return {};
13641378
}
13651379

13661380
ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
@@ -1634,19 +1648,18 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
16341648
return error;
16351649
}
16361650

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);
1651+
static llvm::Expected<bool> loadModuleFromCASImpl(llvm::StringRef cas_id,
1652+
const lldb::ModuleSP &nearby,
1653+
ModuleSpec &module_spec) {
1654+
auto maybe_cas = ModuleList::GetOrCreateCAS(nearby);
16421655
if (!maybe_cas)
16431656
return maybe_cas.takeError();
16441657

1645-
auto cas = std::move(*maybe_cas);
1658+
auto cas = std::move(maybe_cas->object_store);
16461659
if (!cas) {
16471660
LLDB_LOG(GetLog(LLDBLog::Modules),
16481661
"skip loading module '{0}' from CAS: CAS is not available",
1649-
module_name);
1662+
cas_id);
16501663
return false;
16511664
}
16521665

@@ -1670,15 +1683,143 @@ static llvm::Expected<bool> loadModuleFromCAS(ConstString module_name,
16701683
loaded.GetArchitecture() = module_spec.GetArchitecture();
16711684
module_spec = loaded;
16721685

1673-
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module '{0}' using CASID '{1}'",
1674-
module_name, cas_id);
1686+
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module using CASID '{0}'",
1687+
cas_id);
16751688
return true;
16761689
}
16771690

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

@@ -1688,8 +1829,17 @@ llvm::Expected<bool> ModuleList::GetSharedModuleFromCAS(
16881829
auto status =
16891830
GetSharedModule(module_spec, module_sp, nullptr, nullptr, nullptr,
16901831
/*always_create=*/true);
1691-
if (status.Success())
1832+
if (status.Success()) {
1833+
if (module_sp) {
1834+
// Enter the new module into the config cache.
1835+
auto &shared_module_list = GetSharedModuleListInfo();
1836+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1837+
auto &module_configs = shared_module_list.module_list.m_module_configs;
1838+
auto config = module_configs.lookup(nearby.get());
1839+
module_configs.insert({module_sp.get(), config});
1840+
}
16921841
return true;
1842+
}
16931843
return status.takeError();
16941844
}
16951845

0 commit comments

Comments
 (0)