Skip to content

Commit 0d413e5

Browse files
Merge pull request #12025 from adrian-prantl/166576110
[LLDB] Support importing modules from llvmcas:// URLs
2 parents a24a64c + 572034f commit 0d413e5

File tree

15 files changed

+568
-156
lines changed

15 files changed

+568
-156
lines changed

lldb/include/lldb/Core/ModuleList.h

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@
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

27+
// BEGIN CAS
28+
#include "llvm/CAS/CASConfiguration.h"
29+
// END CAS
30+
2831
#include <functional>
2932
#include <list>
3033
#include <mutex>
@@ -33,6 +36,14 @@
3336
#include <cstddef>
3437
#include <cstdint>
3538

39+
namespace llvm {
40+
namespace cas {
41+
class CASConfiguration;
42+
class ObjectStore;
43+
class ActionCache;
44+
} // namespace cas
45+
} // namespace llvm
46+
3647
namespace lldb_private {
3748
class ConstString;
3849
class FileSpecList;
@@ -106,6 +117,7 @@ class ModuleListProperties : public Properties {
106117
// START CAS
107118
/// Get CASPath set via properties.
108119
FileSpec GetCASOnDiskPath() const;
120+
bool SetCASOnDiskPath(const FileSpec &);
109121

110122
/// Get CASPluginPath set via properties.
111123
FileSpec GetCASPluginPath() const;
@@ -538,10 +550,14 @@ class ModuleList {
538550

539551
// START CAS
540552

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);
553+
struct CAS {
554+
llvm::cas::CASConfiguration configuration;
555+
std::shared_ptr<llvm::cas::ObjectStore> object_store;
556+
std::shared_ptr<llvm::cas::ActionCache> action_cache;
557+
};
558+
/// Search for a CAS configuration file near this module. This
559+
/// operation does a lot of file system stat calls.
560+
static llvm::Expected<CAS> GetOrCreateCAS(const lldb::ModuleSP &module_sp);
545561

546562
/// Gets the shared module from CAS. It works the same as `GetSharedModule`
547563
/// but the lookup is done inside the CAS.
@@ -550,11 +566,9 @@ class ModuleList {
550566
/// true if module is successfully loaded, false if module is not found
551567
/// in the CAS, error if there are any errors happened during the loading
552568
/// 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);
569+
static llvm::Expected<bool>
570+
GetSharedModuleFromCAS(llvm::StringRef cas_id, const lldb::ModuleSP &nearby,
571+
ModuleSpec &module_spec, lldb::ModuleSP &module_sp);
558572
// END CAS
559573

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

lldb/source/Core/ModuleList.cpp

Lines changed: 187 additions & 48 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,17 @@ 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+
if (auto config = it->second) {
1294+
m_cas_cache.erase(config);
1295+
m_module_configs.erase(it);
1296+
}
1297+
return IterationAction::Continue;
1298+
});
1299+
// END CAS
12811300
m_list.Remove(to_remove);
12821301
}
12831302
// Break when fixed-point is reached.
@@ -1292,14 +1311,28 @@ class SharedModuleList {
12921311
/// filename, for fast module lookups by name.
12931312
llvm::DenseMap<ConstString, llvm::SmallVector<ModuleSP, 1>> m_name_to_modules;
12941313

1314+
// BEGIN CAS
1315+
public:
1316+
/// Each module may have a CAS config associated with it.
1317+
/// Often many modules share the same CAS.
1318+
llvm::DenseMap<const Module *, llvm::cas::CASConfiguration> m_module_configs;
1319+
1320+
/// Each CAS config has a CAS associated with it.
1321+
llvm::DenseMap<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,21 @@ static SharedModuleList &GetSharedModuleList() {
13221355
return GetSharedModuleListInfo().module_list;
13231356
}
13241357

1325-
std::optional<llvm::cas::CASConfiguration>
1326-
ModuleList::GetCASConfiguration(FileSpec CandidateConfigSearchPath) {
1358+
static 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+
return {props.GetCASOnDiskPath().GetPath(),
1365+
props.GetCASPluginPath().GetPath(), props.GetCASPluginOptions()};
1366+
}
13391367
auto search_config = llvm::cas::CASConfiguration::createFromSearchConfigFile(
13401368
CandidateConfigSearchPath.GetPath());
13411369
if (search_config)
13421370
return search_config->second;
13431371

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;
1372+
return {};
13641373
}
13651374

13661375
ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
@@ -1634,19 +1643,18 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
16341643
return error;
16351644
}
16361645

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);
1646+
static llvm::Expected<bool> loadModuleFromCASImpl(llvm::StringRef cas_id,
1647+
const lldb::ModuleSP &nearby,
1648+
ModuleSpec &module_spec) {
1649+
auto maybe_cas = ModuleList::GetOrCreateCAS(nearby);
16421650
if (!maybe_cas)
16431651
return maybe_cas.takeError();
16441652

1645-
auto cas = std::move(*maybe_cas);
1653+
auto cas = std::move(maybe_cas->object_store);
16461654
if (!cas) {
16471655
LLDB_LOG(GetLog(LLDBLog::Modules),
16481656
"skip loading module '{0}' from CAS: CAS is not available",
1649-
module_name);
1657+
cas_id);
16501658
return false;
16511659
}
16521660

@@ -1665,20 +1673,142 @@ static llvm::Expected<bool> loadModuleFromCAS(ConstString module_name,
16651673
std::make_shared<DataBufferLLVM>(module_proxy->getMemoryBuffer());
16661674

16671675
// Swap out the module_spec with the one loaded via CAS.
1668-
ModuleSpec loaded(module_spec.GetFileSpec(), module_spec.GetUUID(),
1669-
std::move(file_buffer));
1676+
FileSpec cas_spec;
1677+
cas_spec.SetDirectory(ConstString(maybe_cas->configuration.CASPath));
1678+
cas_spec.SetFilename(ConstString(cas_id));
1679+
ModuleSpec loaded(cas_spec, module_spec.GetUUID(), std::move(file_buffer));
16701680
loaded.GetArchitecture() = module_spec.GetArchitecture();
16711681
module_spec = loaded;
16721682

1673-
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module '{0}' using CASID '{1}'",
1674-
module_name, cas_id);
1683+
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module using CASID '{0}'",
1684+
cas_id);
16751685
return true;
16761686
}
16771687

1688+
/// Load the module referenced by \c cas_id from a CAS located
1689+
/// near \c nearby.
1690+
static llvm::Expected<bool> loadModuleFromCAS(llvm::StringRef cas_id,
1691+
const lldb::ModuleSP &nearby,
1692+
ModuleSpec &module_spec) {
1693+
static llvm::StringMap<bool> g_cache;
1694+
static std::recursive_mutex g_cache_lock;
1695+
std::scoped_lock<std::recursive_mutex> lock(g_cache_lock);
1696+
auto cached = g_cache.find(cas_id);
1697+
if (cached != g_cache.end())
1698+
return cached->second;
1699+
auto result = loadModuleFromCASImpl(cas_id, nearby, module_spec);
1700+
// Errors are only returned the first time.
1701+
g_cache.insert({cas_id, result ? *result : false});
1702+
return result;
1703+
}
1704+
1705+
static llvm::cas::CASConfiguration
1706+
FindCASConfiguration(const ModuleSP &module_sp) {
1707+
auto get_dir = [](FileSpec path) {
1708+
path.ClearFilename();
1709+
return path;
1710+
};
1711+
1712+
// Look near the binary / dSYM.
1713+
std::set<FileSpec> unique_paths;
1714+
llvm::cas::CASConfiguration cas_config =
1715+
GetCASConfiguration(module_sp->GetFileSpec());
1716+
1717+
if (!cas_config) {
1718+
// Look near the object files.
1719+
if (SymbolFile *sf = module_sp->GetSymbolFile()) {
1720+
sf->GetDebugInfoModules().ForEach([&](const ModuleSP &m) {
1721+
if (m)
1722+
unique_paths.insert(get_dir(m->GetFileSpec()));
1723+
return IterationAction::Continue;
1724+
});
1725+
for (auto &path : unique_paths)
1726+
if ((cas_config = GetCASConfiguration(path)))
1727+
break;
1728+
}
1729+
}
1730+
// TODO: Remove this block once the build system consistently
1731+
// generates .cas-config files.
1732+
if (!cas_config) {
1733+
unique_paths.insert(get_dir(module_sp->GetFileSpec()));
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+
}
1750+
return cas_config;
1751+
}
1752+
1753+
llvm::Expected<ModuleList::CAS>
1754+
ModuleList::GetOrCreateCAS(const ModuleSP &module_sp) {
1755+
if (!module_sp)
1756+
return llvm::createStringError("no lldb::Module available");
1757+
1758+
// Look in cache first.
1759+
auto &shared_module_list = GetSharedModuleListInfo();
1760+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1761+
auto &module_configs = shared_module_list.module_list.m_module_configs;
1762+
auto &cas_cache = shared_module_list.module_list.m_cas_cache;
1763+
1764+
llvm::cas::CASConfiguration cas_config;
1765+
{
1766+
auto cached_config = module_configs.find(module_sp.get());
1767+
if (cached_config != module_configs.end()) {
1768+
cas_config = cached_config->second;
1769+
if (!cas_config)
1770+
return llvm::createStringError("no CAS available (cached)");
1771+
}
1772+
}
1773+
1774+
if (!cas_config) {
1775+
cas_config = FindCASConfiguration(module_sp);
1776+
// Cache the config or lack thereof.
1777+
module_configs.insert({module_sp.get(), cas_config});
1778+
}
1779+
1780+
if (!cas_config)
1781+
return llvm::createStringError("no CAS available");
1782+
1783+
// Look in the cache.
1784+
{
1785+
auto cached = cas_cache.find(cas_config);
1786+
if (cached != cas_cache.end()) {
1787+
if (!cached->second.first)
1788+
return llvm::createStringError(
1789+
"CAS config created, but CAS not available (cached)");
1790+
return ModuleList::CAS{cas_config, cached->second.first,
1791+
cached->second.second};
1792+
}
1793+
}
1794+
1795+
// Create the CAS.
1796+
auto cas = cas_config.createDatabases();
1797+
if (!cas) {
1798+
cas_cache.insert({cas_config, {}});
1799+
return cas.takeError();
1800+
}
1801+
1802+
LLDB_LOG(GetLog(LLDBLog::Modules | LLDBLog::Symbols),
1803+
"Initialized CAS at {0}", cas_config.CASPath);
1804+
cas_cache.insert({cas_config, {cas->first, cas->second}});
1805+
return ModuleList::CAS{cas_config, cas->first, cas->second};
1806+
}
1807+
16781808
llvm::Expected<bool> ModuleList::GetSharedModuleFromCAS(
1679-
ConstString module_name, llvm::StringRef cas_id, FileSpec cu_path,
1809+
llvm::StringRef cas_id, const lldb::ModuleSP &nearby,
16801810
ModuleSpec &module_spec, lldb::ModuleSP &module_sp) {
1681-
auto loaded = loadModuleFromCAS(module_name, cas_id, cu_path, module_spec);
1811+
auto loaded = loadModuleFromCAS(cas_id, nearby, module_spec);
16821812
if (!loaded)
16831813
return loaded.takeError();
16841814

@@ -1688,8 +1818,17 @@ llvm::Expected<bool> ModuleList::GetSharedModuleFromCAS(
16881818
auto status =
16891819
GetSharedModule(module_spec, module_sp, nullptr, nullptr, nullptr,
16901820
/*always_create=*/true);
1691-
if (status.Success())
1821+
if (status.Success()) {
1822+
if (module_sp) {
1823+
// Enter the new module into the config cache.
1824+
auto &shared_module_list = GetSharedModuleListInfo();
1825+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1826+
auto &module_configs = shared_module_list.module_list.m_module_configs;
1827+
auto config = module_configs.lookup(nearby.get());
1828+
module_configs.insert({module_sp.get(), config});
1829+
}
16921830
return true;
1831+
}
16931832
return status.takeError();
16941833
}
16951834

0 commit comments

Comments
 (0)