Skip to content

Commit d16d36c

Browse files
[lldb] Teach LLDB to load gmodules from CAS
Teach lldb to load clang modules when gmodule + clang caching is used.
1 parent 609c782 commit d16d36c

File tree

7 files changed

+307
-2
lines changed

7 files changed

+307
-2
lines changed

lldb/include/lldb/Core/ModuleList.h

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

2424
#include "llvm/ADT/DenseSet.h"
25+
#include "llvm/CAS/CASConfiguration.h"
2526
#include "llvm/Support/RWMutex.h"
2627

2728
#include <functional>
@@ -100,6 +101,17 @@ class ModuleListProperties : public Properties {
100101
bool GetSwiftEnableASTContext() const;
101102
// END SWIFT
102103

104+
// START CAS
105+
/// Get CASPath set via properties.
106+
FileSpec GetCASOnDiskPath() const;
107+
108+
/// Get CASPluginPath set via properties.
109+
FileSpec GetCASPluginPath() const;
110+
111+
/// Get CASPluginOptions set via properties.
112+
std::vector<std::pair<std::string, std::string>> GetCASPluginOptions() const;
113+
// END CAS
114+
103115
FileSpec GetClangModulesCachePath() const;
104116
bool SetClangModulesCachePath(const FileSpec &path);
105117
bool GetEnableExternalLookup() const;
@@ -522,6 +534,27 @@ class ModuleList {
522534
llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
523535
bool *did_create_ptr, bool always_create = false);
524536

537+
// START CAS
538+
539+
/// Get CAS configuration using global module properties or from candidate
540+
/// search path.
541+
static std::optional<llvm::cas::CASConfiguration>
542+
GetCASConfiguration(FileSpec CandidateConfigSearchPath);
543+
544+
/// Gets the shared module from CAS. It works the same as `GetSharedModule`
545+
/// but the lookup is done inside the CAS.
546+
///
547+
/// \return
548+
/// true if module is successfully loaded, false if module is not found
549+
/// in the CAS, error if there are any errors happened during the loading
550+
/// process.
551+
static llvm::Expected<bool> GetSharedModuleFromCAS(ConstString module_name,
552+
llvm::StringRef cas_id,
553+
FileSpec cu_path,
554+
ModuleSpec &module_spec,
555+
lldb::ModuleSP &module_sp);
556+
// END CAS
557+
525558
static bool RemoveSharedModule(lldb::ModuleSP &module_sp);
526559

527560
static void FindSharedModules(const ModuleSpec &module_spec,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Usage: clang-explicit-module-build.py [-cas-path CAS_PATH] -- CLANG_COMMAND
4+
5+
The script builds with clang explicit module. If `-cas-path` is used, clang
6+
explicit module build is going to enable compilation caching.
7+
"""
8+
9+
import argparse
10+
import json
11+
import os
12+
import subprocess
13+
import sys
14+
15+
16+
def main():
17+
argv = sys.argv[1:]
18+
if "--" not in argv:
19+
print("missing clang command")
20+
exit(1)
21+
dash_idx = argv.index("--")
22+
clang_args = argv[dash_idx + 1:]
23+
if len(clang_args) == 0:
24+
print("empty clang command")
25+
exit(1)
26+
27+
parser = argparse.ArgumentParser()
28+
parser.add_argument("-cas-path", required=False)
29+
args = parser.parse_args(argv[:dash_idx])
30+
31+
clang_exe = clang_args[0]
32+
clang_scan_deps = os.path.join(
33+
os.path.dirname(clang_exe), "clang-scan-deps")
34+
35+
scan_cmd = [clang_scan_deps]
36+
if args.cas_path is None:
37+
scan_cmd.extend(["-format", "experimental-full", "-o", "-", "--"])
38+
else:
39+
scan_cmd.extend(["-format", "experimental-include-tree-full",
40+
"-cas-path", args.cas_path, "-o", "-", "--"])
41+
scan_cmd.extend(clang_args)
42+
scan_result = json.loads(subprocess.check_output(scan_cmd))
43+
44+
# build module: assuming modules in reverse dependency order.
45+
for module in reversed(scan_result["modules"]):
46+
cmd = [clang_exe] + module["command-line"]
47+
print(*cmd)
48+
subprocess.check_call(cmd)
49+
50+
# build tu: assuming only one TU.
51+
tu_cmd = [clang_exe] + \
52+
scan_result["translation-units"][0]["commands"][0]["command-line"]
53+
print(*tu_cmd)
54+
subprocess.check_call(tu_cmd)
55+
56+
57+
if __name__ == "__main__":
58+
main()

lldb/source/Core/CoreProperties.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,19 @@ let Definition = "modulelist" in {
7474
DefaultTrue,
7575
Desc<"Enable instantiating Swift AST contexts.">;
7676
// END SWIFT
77+
// BEGIN CAS
78+
def CASOnDiskPath: Property<"cas-path", "FileSpec">,
79+
Global,
80+
DefaultStringValue<"">,
81+
Desc<"The path for CAS storage">;
82+
def CASPluginPath: Property<"cas-plugin-path", "FileSpec">,
83+
Global,
84+
DefaultStringValue<"">,
85+
Desc<"The path for CAS plugin dynamic library">;
86+
def CASPluginOptions: Property<"cas-plugin-options", "Array">,
87+
ElementType<"String">,
88+
Desc<"A list of options to be passed to CAS plugins">;
89+
// END CAS
7790
def SymLinkPaths: Property<"debug-info-symlink-paths", "FileSpecList">,
7891
Global,
7992
DefaultStringValue<"">,

lldb/source/Core/ModuleList.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,23 @@
2121
#include "lldb/Symbol/VariableList.h"
2222
#include "lldb/Utility/ArchSpec.h"
2323
#include "lldb/Utility/ConstString.h"
24+
#include "lldb/Utility/DataBufferLLVM.h"
2425
#include "lldb/Utility/FileSpecList.h"
2526
#include "lldb/Utility/LLDBLog.h"
2627
#include "lldb/Utility/Log.h"
2728
#include "lldb/Utility/UUID.h"
2829
#include "lldb/lldb-defines.h"
30+
#include "llvm/ADT/ScopeExit.h"
31+
#include "llvm/Support/FileUtilities.h"
2932

3033
#if defined(_WIN32)
3134
#include "lldb/Host/windows/PosixApi.h"
3235
#endif
3336

3437
#include "clang/Driver/Driver.h"
3538
#include "llvm/ADT/StringRef.h"
39+
#include "llvm/CAS/CASConfiguration.h"
40+
#include "llvm/CAS/ObjectStore.h"
3641
#include "llvm/Support/FileSystem.h"
3742
#include "llvm/Support/Threading.h"
3843
#include "llvm/Support/raw_ostream.h"
@@ -282,6 +287,32 @@ bool ModuleListProperties::GetSwiftEnableASTContext() const {
282287
}
283288
// END SWIFT
284289

290+
// START CAS
291+
FileSpec ModuleListProperties::GetCASOnDiskPath() const {
292+
const uint32_t idx = ePropertyCASOnDiskPath;
293+
return GetPropertyAtIndexAs<FileSpec>(idx, {});
294+
}
295+
296+
FileSpec ModuleListProperties::GetCASPluginPath() const {
297+
const uint32_t idx = ePropertyCASPluginPath;
298+
return GetPropertyAtIndexAs<FileSpec>(idx, {});
299+
}
300+
301+
std::vector<std::pair<std::string, std::string>>
302+
ModuleListProperties::GetCASPluginOptions() const {
303+
Args args;
304+
const uint32_t idx = ePropertyCASPluginOptions;
305+
m_collection_sp->GetPropertyAtIndexAsArgs(idx, args);
306+
std::vector<std::pair<std::string, std::string>> options;
307+
for (auto &arg : args) {
308+
llvm::StringRef opt = arg.c_str();
309+
auto splitted = opt.split("=");
310+
options.emplace_back(splitted.first.str(), splitted.second.str());
311+
}
312+
return options;
313+
}
314+
// END CAS
315+
285316
FileSpec ModuleListProperties::GetLLDBIndexCachePath() const {
286317
const uint32_t idx = ePropertyLLDBIndexCachePath;
287318
return GetPropertyAtIndexAs<FileSpec>(idx, {});
@@ -1253,8 +1284,11 @@ class SharedModuleList {
12531284
struct SharedModuleListInfo {
12541285
SharedModuleList module_list;
12551286
ModuleListProperties module_list_properties;
1287+
std::shared_ptr<llvm::cas::ObjectStore> cas_object_store;
1288+
std::mutex shared_lock;
12561289
};
12571290
}
1291+
12581292
static SharedModuleListInfo &GetSharedModuleListInfo()
12591293
{
12601294
static SharedModuleListInfo *g_shared_module_list_info = nullptr;
@@ -1273,6 +1307,47 @@ static SharedModuleList &GetSharedModuleList() {
12731307
return GetSharedModuleListInfo().module_list;
12741308
}
12751309

1310+
std::optional<llvm::cas::CASConfiguration>
1311+
ModuleList::GetCASConfiguration(FileSpec CandidateConfigSearchPath) {
1312+
// Config CAS from properties.
1313+
llvm::cas::CASConfiguration cas_config;
1314+
cas_config.CASPath =
1315+
ModuleList::GetGlobalModuleListProperties().GetCASOnDiskPath().GetPath();
1316+
cas_config.PluginPath =
1317+
ModuleList::GetGlobalModuleListProperties().GetCASPluginPath().GetPath();
1318+
cas_config.PluginOptions =
1319+
ModuleList::GetGlobalModuleListProperties().GetCASPluginOptions();
1320+
1321+
if (!cas_config.CASPath.empty())
1322+
return cas_config;
1323+
1324+
auto search_config = llvm::cas::CASConfiguration::createFromSearchConfigFile(
1325+
CandidateConfigSearchPath.GetPath());
1326+
if (search_config)
1327+
return search_config->second;
1328+
1329+
return std::nullopt;
1330+
}
1331+
1332+
static llvm::Expected<std::shared_ptr<llvm::cas::ObjectStore>>
1333+
GetOrCreateCASStorage(FileSpec CandidateConfigSearchPath) {
1334+
auto &shared_module_list = GetSharedModuleListInfo();
1335+
if (shared_module_list.cas_object_store)
1336+
return shared_module_list.cas_object_store;
1337+
1338+
auto config = ModuleList::GetCASConfiguration(CandidateConfigSearchPath);
1339+
if (!config)
1340+
return nullptr;
1341+
1342+
auto cas = config->createDatabases();
1343+
if (!cas)
1344+
return cas.takeError();
1345+
1346+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1347+
shared_module_list.cas_object_store = std::move(cas->first);
1348+
return shared_module_list.cas_object_store;
1349+
}
1350+
12761351
ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
12771352
return GetSharedModuleListInfo().module_list_properties;
12781353
}
@@ -1544,6 +1619,65 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
15441619
return error;
15451620
}
15461621

1622+
static llvm::Expected<bool> loadModuleFromCAS(ConstString module_name,
1623+
llvm::StringRef cas_id,
1624+
FileSpec cu_path,
1625+
ModuleSpec &module_spec) {
1626+
auto maybe_cas = GetOrCreateCASStorage(cu_path);
1627+
if (!maybe_cas)
1628+
return maybe_cas.takeError();
1629+
1630+
auto cas = std::move(*maybe_cas);
1631+
if (!cas) {
1632+
LLDB_LOG(GetLog(LLDBLog::Modules),
1633+
"skip loading module '{0}' from CAS: CAS is not available",
1634+
module_name);
1635+
return false;
1636+
}
1637+
1638+
auto id = cas->parseID(cas_id);
1639+
if (!id) {
1640+
LLDB_LOG(GetLog(LLDBLog::Modules), "'{0}' is not valid CASID: {1}", cas_id,
1641+
toString(id.takeError()));
1642+
return false;
1643+
}
1644+
1645+
auto module_proxy = cas->getProxy(*id);
1646+
if (!module_proxy)
1647+
return module_proxy.takeError();
1648+
1649+
auto file_buffer =
1650+
std::make_shared<DataBufferLLVM>(module_proxy->getMemoryBuffer());
1651+
1652+
// Swap out the module_spec with the one loaded via CAS.
1653+
ModuleSpec loaded(module_spec.GetFileSpec(), module_spec.GetUUID(),
1654+
std::move(file_buffer));
1655+
loaded.GetArchitecture() = module_spec.GetArchitecture();
1656+
module_spec = loaded;
1657+
1658+
LLDB_LOG(GetLog(LLDBLog::Modules), "loading module '{0}' using CASID '{1}'",
1659+
module_name, cas_id);
1660+
return true;
1661+
}
1662+
1663+
llvm::Expected<bool> ModuleList::GetSharedModuleFromCAS(
1664+
ConstString module_name, llvm::StringRef cas_id, FileSpec cu_path,
1665+
ModuleSpec &module_spec, lldb::ModuleSP &module_sp) {
1666+
auto loaded = loadModuleFromCAS(module_name, cas_id, cu_path, module_spec);
1667+
if (!loaded)
1668+
return loaded.takeError();
1669+
1670+
if (!*loaded)
1671+
return false;
1672+
1673+
auto status =
1674+
GetSharedModule(module_spec, module_sp, nullptr, nullptr, nullptr,
1675+
/*always_create=*/true);
1676+
if (status.Success())
1677+
return true;
1678+
return status.takeError();
1679+
}
1680+
15471681
bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) {
15481682
return GetSharedModuleList().Remove(module_sp);
15491683
}

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,6 +2004,23 @@ void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() {
20042004
continue;
20052005

20062006
ModuleSpec dwo_module_spec;
2007+
2008+
dwo_module_spec.GetArchitecture() =
2009+
m_objfile_sp->GetModule()->GetArchitecture();
2010+
2011+
// Try load from CAS, if loaded, continue to next one.
2012+
auto loaded = ModuleList::GetSharedModuleFromCAS(
2013+
const_name, dwo_path, GetObjectFile()->GetFileSpec(), dwo_module_spec,
2014+
module_sp);
2015+
if (!loaded)
2016+
GetObjectFile()->GetModule()->ReportWarning(
2017+
"Failed to load module '{0}' from CAS: {1}", const_name,
2018+
toString(loaded.takeError()));
2019+
2020+
// succeed, loaded next module.
2021+
if (*loaded)
2022+
continue;
2023+
20072024
dwo_module_spec.GetFileSpec().SetFile(dwo_path, FileSpec::Style::native);
20082025
if (dwo_module_spec.GetFileSpec().IsRelative()) {
20092026
const char *comp_dir =
@@ -2015,8 +2032,6 @@ void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() {
20152032
dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path);
20162033
}
20172034
}
2018-
dwo_module_spec.GetArchitecture() =
2019-
m_objfile_sp->GetModule()->GetArchitecture();
20202035

20212036
// When LLDB loads "external" modules it looks at the presence of
20222037
// DW_AT_dwo_name. However, when the already created module

lldb/test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ if(TARGET clang)
162162

163163
# TestFullLtoStepping depends on LTO, and only runs when the compiler is clang.
164164
add_lldb_test_dependency(LTO)
165+
# Clang explicit module build test requires clang-scan-deps
166+
add_lldb_test_dependency(clang-scan-deps)
165167

166168
if (TARGET libcxx OR ("libcxx" IN_LIST LLVM_ENABLE_RUNTIMES))
167169
set(LLDB_HAS_LIBCXX ON)

0 commit comments

Comments
 (0)