Skip to content

Commit 3675193

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

File tree

8 files changed

+282
-2
lines changed

8 files changed

+282
-2
lines changed

lldb/include/lldb/Core/ModuleList.h

Lines changed: 22 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,12 @@ class ModuleListProperties : public Properties {
100101
bool GetSwiftEnableASTContext() const;
101102
// END SWIFT
102103

104+
// START CAS
105+
FileSpec GetCASOnDiskPath() const;
106+
FileSpec GetCASPluginPath() const;
107+
std::vector<std::pair<std::string, std::string>> GetCASPluginOptions() const;
108+
// END CAS
109+
103110
FileSpec GetClangModulesCachePath() const;
104111
bool SetClangModulesCachePath(const FileSpec &path);
105112
bool GetEnableExternalLookup() const;
@@ -522,6 +529,21 @@ class ModuleList {
522529
llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
523530
bool *did_create_ptr, bool always_create = false);
524531

532+
// START CAS
533+
534+
/// Get CAS configuration using global module properties or from candidate
535+
/// search path.
536+
static std::optional<llvm::cas::CASConfiguration>
537+
GetCASConfiguration(FileSpec CandidateConfigSearchPath);
538+
539+
/// Gets the shared module from CAS. It works the same as `GetSharedModule` but
540+
/// the lookup is done inside the CAS.
541+
static Status GetSharedModuleFromCAS(ConstString module_name,
542+
llvm::StringRef cas_id, FileSpec cu_path,
543+
ModuleSpec &module_spec,
544+
lldb::ModuleSP &module_sp);
545+
// END CAS
546+
525547
static bool RemoveSharedModule(lldb::ModuleSP &module_sp);
526548

527549
static void FindSharedModules(const ModuleSpec &module_spec,

lldb/include/lldb/Core/ModuleSpec.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ class ModuleSpec {
126126

127127
lldb::DataBufferSP GetData() const { return m_data; }
128128

129+
void SetData(lldb::DataBufferSP data) { m_data = std::move(data); }
130+
129131
void Clear() {
130132
m_file.Clear();
131133
m_platform_file.Clear();
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: 124 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,55 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
15441619
return error;
15451620
}
15461621

1622+
static llvm::Error loadModuleFromCAS(ConstString module_name,
1623+
llvm::StringRef cas_id, FileSpec cu_path,
1624+
ModuleSpec &module_spec) {
1625+
auto maybe_cas = GetOrCreateCASStorage(cu_path);
1626+
if (!maybe_cas)
1627+
return maybe_cas.takeError();
1628+
1629+
auto cas = std::move(*maybe_cas);
1630+
if (!cas)
1631+
return llvm::createStringError("CAS is not available");
1632+
1633+
auto id = cas->parseID(cas_id);
1634+
if (!id)
1635+
return id.takeError();
1636+
1637+
auto module_proxy = cas->getProxy(*id);
1638+
if (!module_proxy)
1639+
return module_proxy.takeError();
1640+
1641+
auto file_buffer =
1642+
std::make_shared<DataBufferLLVM>(module_proxy->getMemoryBuffer());
1643+
module_spec.SetData(std::move(file_buffer));
1644+
1645+
Log *log = GetLog(LLDBLog::Modules);
1646+
if (log != nullptr)
1647+
LLDB_LOGF(log, "loading module '%s' using CASID '%s'",
1648+
module_name.AsCString(), cas_id.str().c_str());
1649+
1650+
return llvm::Error::success();
1651+
}
1652+
1653+
Status ModuleList::GetSharedModuleFromCAS(ConstString module_name,
1654+
llvm::StringRef cas_id,
1655+
FileSpec cu_path,
1656+
ModuleSpec &module_spec,
1657+
lldb::ModuleSP &module_sp) {
1658+
auto err = loadModuleFromCAS(module_name, cas_id, cu_path, module_spec);
1659+
if (err) {
1660+
auto error_str = toString(std::move(err));
1661+
LLDB_LOGF(GetLog(LLDBLog::Modules),
1662+
"skip loading module '%s' from CAS: %s",
1663+
module_name.AsCString(), error_str.c_str());
1664+
return Status::FromErrorString(error_str.c_str());
1665+
}
1666+
1667+
return GetSharedModule(module_spec, module_sp, nullptr, nullptr, nullptr,
1668+
/*always_create=*/true);
1669+
}
1670+
15471671
bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) {
15481672
return GetSharedModuleList().Remove(module_sp);
15491673
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,6 +2004,17 @@ 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+
if (ModuleList::GetSharedModuleFromCAS(const_name, dwo_path,
2013+
GetObjectFile()->GetFileSpec(),
2014+
dwo_module_spec, module_sp)
2015+
.Success())
2016+
continue;
2017+
20072018
dwo_module_spec.GetFileSpec().SetFile(dwo_path, FileSpec::Style::native);
20082019
if (dwo_module_spec.GetFileSpec().IsRelative()) {
20092020
const char *comp_dir =
@@ -2015,8 +2026,6 @@ void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() {
20152026
dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path);
20162027
}
20172028
}
2018-
dwo_module_spec.GetArchitecture() =
2019-
m_objfile_sp->GetModule()->GetArchitecture();
20202029

20212030
// When LLDB loads "external" modules it looks at the presence of
20222031
// 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)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# REQUIRES: system-darwin
2+
# RUN: rm -rf %t
3+
# RUN: split-file %s %t
4+
5+
# RUN: %python %S/../../../scripts/clang-explicit-module-build.py -cas-path %t/cas -- %clang_host -c -g -o %t/test.o %t/tu.c -fmodules -gmodules -fmodules-cache-path=%t/module-cache
6+
# RUN: %clang_host %t/test.o -o %t/main
7+
# RUN: %lldb %t/main -s %t/lldb.script 2>&1 | FileCheck %s --check-prefix=FAIL
8+
# RUN: sed "s|DIR|%/t|g" %t/cas-config.template > %t/.cas-config
9+
# RUN: %lldb %t/main -s %t/lldb.script 2>&1 | FileCheck %s
10+
11+
# FAIL: skip loading module 'Bottom' from CAS: CAS is not available
12+
# FAIL: Unable to locate module needed for external types.
13+
# CHECK: loading module 'Bottom' using CASID
14+
# CHECK: loading module 'Top' using CASID
15+
# CHECK: top = (x = 1)
16+
17+
//--- module.modulemap
18+
module Top { header "Top.h" export *}
19+
module Bottom { header "Bottom.h" export *}
20+
21+
//--- Top.h
22+
#pragma once
23+
struct Top { int x; };
24+
25+
//--- Bottom.h
26+
#pragma once
27+
#include "Top.h"
28+
struct Bottom { struct Top top; };
29+
30+
//--- tu.c
31+
#include "Bottom.h"
32+
33+
int main(void) {
34+
struct Bottom _bottom;
35+
_bottom.top.x = 1;
36+
37+
return 0; // BREAK HERE
38+
}
39+
40+
//--- cas-config.template
41+
{
42+
"CASPath": "DIR/cas"
43+
}
44+
45+
//--- lldb.script
46+
log enable lldb module
47+
b tu.c:7
48+
run
49+
expr _bottom
50+
quit

0 commit comments

Comments
 (0)