Skip to content

Commit 0d3f967

Browse files
[lldb] Teach LLDB to load gmodules from CAS
Teach lldb to load clang modules when gmodule + clang caching is used.
1 parent 36a921b commit 0d3f967

File tree

7 files changed

+266
-2
lines changed

7 files changed

+266
-2
lines changed

lldb/include/lldb/Core/ModuleList.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ class ModuleListProperties : public Properties {
100100
bool GetSwiftEnableASTContext() const;
101101
// END SWIFT
102102

103+
// START CAS
104+
FileSpec GetCASOnDiskPath() const;
105+
FileSpec GetCASPluginPath() const;
106+
std::vector<std::pair<std::string, std::string>> GetCASPluginOptions() const;
107+
// END CAS
108+
103109
FileSpec GetClangModulesCachePath() const;
104110
bool SetClangModulesCachePath(const FileSpec &path);
105111
bool GetEnableExternalLookup() const;
@@ -522,6 +528,11 @@ class ModuleList {
522528
llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
523529
bool *did_create_ptr, bool always_create = false);
524530

531+
static Status GetSharedModuleFromCAS(ConstString module_name,
532+
const char *cas_id, FileSpec cu_path,
533+
ModuleSpec &module_spec,
534+
lldb::ModuleSP &module_sp);
535+
525536
static bool RemoveSharedModule(lldb::ModuleSP &module_sp);
526537

527538
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: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@
2626
#include "lldb/Utility/Log.h"
2727
#include "lldb/Utility/UUID.h"
2828
#include "lldb/lldb-defines.h"
29+
#include "llvm/ADT/ScopeExit.h"
30+
#include "llvm/Support/FileUtilities.h"
2931

3032
#if defined(_WIN32)
3133
#include "lldb/Host/windows/PosixApi.h"
3234
#endif
3335

3436
#include "clang/Driver/Driver.h"
3537
#include "llvm/ADT/StringRef.h"
38+
#include "llvm/CAS/CASConfiguration.h"
39+
#include "llvm/CAS/ObjectStore.h"
3640
#include "llvm/Support/FileSystem.h"
3741
#include "llvm/Support/Threading.h"
3842
#include "llvm/Support/raw_ostream.h"
@@ -282,6 +286,32 @@ bool ModuleListProperties::GetSwiftEnableASTContext() const {
282286
}
283287
// END SWIFT
284288

289+
// START CAS
290+
FileSpec ModuleListProperties::GetCASOnDiskPath() const {
291+
const uint32_t idx = ePropertyCASOnDiskPath;
292+
return GetPropertyAtIndexAs<FileSpec>(idx, {});
293+
}
294+
295+
FileSpec ModuleListProperties::GetCASPluginPath() const {
296+
const uint32_t idx = ePropertyCASPluginPath;
297+
return GetPropertyAtIndexAs<FileSpec>(idx, {});
298+
}
299+
300+
std::vector<std::pair<std::string, std::string>>
301+
ModuleListProperties::GetCASPluginOptions() const {
302+
Args args;
303+
const uint32_t idx = ePropertyCASPluginOptions;
304+
m_collection_sp->GetPropertyAtIndexAsArgs(idx, args);
305+
std::vector<std::pair<std::string, std::string>> options;
306+
for (auto &arg : args) {
307+
llvm::StringRef opt = arg.c_str();
308+
auto splitted = opt.split("=");
309+
options.emplace_back(splitted.first.str(), splitted.second.str());
310+
}
311+
return options;
312+
}
313+
// END CAS
314+
285315
FileSpec ModuleListProperties::GetLLDBIndexCachePath() const {
286316
const uint32_t idx = ePropertyLLDBIndexCachePath;
287317
return GetPropertyAtIndexAs<FileSpec>(idx, {});
@@ -1257,8 +1287,11 @@ class SharedModuleList {
12571287
struct SharedModuleListInfo {
12581288
SharedModuleList module_list;
12591289
ModuleListProperties module_list_properties;
1290+
std::shared_ptr<llvm::cas::ObjectStore> cas_object_store;
1291+
std::mutex shared_lock;
12601292
};
12611293
}
1294+
12621295
static SharedModuleListInfo &GetSharedModuleListInfo()
12631296
{
12641297
static SharedModuleListInfo *g_shared_module_list_info = nullptr;
@@ -1277,6 +1310,45 @@ static SharedModuleList &GetSharedModuleList() {
12771310
return GetSharedModuleListInfo().module_list;
12781311
}
12791312

1313+
static std::shared_ptr<llvm::cas::ObjectStore>
1314+
GetOrCreateCASStorage(FileSpec CandidateConfigSearchPath) {
1315+
auto &shared_module_list = GetSharedModuleListInfo();
1316+
if (shared_module_list.cas_object_store)
1317+
return shared_module_list.cas_object_store;
1318+
1319+
std::scoped_lock<std::mutex> lock(shared_module_list.shared_lock);
1320+
// Config CAS from properties.
1321+
llvm::cas::CASConfiguration cas_config;
1322+
cas_config.CASPath =
1323+
ModuleList::GetGlobalModuleListProperties().GetCASOnDiskPath().GetPath();
1324+
cas_config.PluginPath =
1325+
ModuleList::GetGlobalModuleListProperties().GetCASPluginPath().GetPath();
1326+
cas_config.PluginOptions =
1327+
ModuleList::GetGlobalModuleListProperties().GetCASPluginOptions();
1328+
1329+
if (!cas_config.CASPath.empty()) {
1330+
if (auto maybe_cas = cas_config.createDatabases()) {
1331+
shared_module_list.cas_object_store = std::move(maybe_cas->first);
1332+
return shared_module_list.cas_object_store;
1333+
} else
1334+
llvm::consumeError(maybe_cas.takeError());
1335+
}
1336+
1337+
// Try search from candidiate path.
1338+
auto search_config = llvm::cas::CASConfiguration::createFromSearchConfigFile(
1339+
CandidateConfigSearchPath.GetPath());
1340+
if (!search_config)
1341+
return nullptr;
1342+
1343+
if (auto maybe_cas = search_config->second.createDatabases()) {
1344+
shared_module_list.cas_object_store = std::move(maybe_cas->first);
1345+
return shared_module_list.cas_object_store;
1346+
} else
1347+
llvm::consumeError(maybe_cas.takeError());
1348+
1349+
return nullptr;
1350+
}
1351+
12801352
ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
12811353
return GetSharedModuleListInfo().module_list_properties;
12821354
}
@@ -1548,6 +1620,59 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
15481620
return error;
15491621
}
15501622

1623+
static llvm::Error loadModuleFromCAS(ConstString module_name,
1624+
const char *cas_id, FileSpec cu_path,
1625+
ModuleSpec &module_spec) {
1626+
auto cas = GetOrCreateCASStorage(cu_path);
1627+
if (!cas)
1628+
return llvm::createStringError("CAS is not available");
1629+
auto id = cas->parseID(cas_id);
1630+
if (!id)
1631+
return id.takeError();
1632+
1633+
auto module_proxy = cas->getProxy(*id);
1634+
if (!module_proxy)
1635+
return module_proxy.takeError();
1636+
1637+
// Create a temporary file that holds the module info.
1638+
llvm::SmallString<0> name;
1639+
int fd;
1640+
auto ec =
1641+
llvm::sys::fs::createTemporaryFile("lldb-module-cas", "pcm", fd, name);
1642+
if (ec || fd <= 0)
1643+
return llvm::createStringError(
1644+
"could not create temporary file for module");
1645+
1646+
NativeFile file(fd, File::eOpenOptionWriteOnly, true);
1647+
size_t num_bytes = module_proxy->getData().size();
1648+
file.Write(module_proxy->getData().data(), num_bytes);
1649+
1650+
Log *log = GetLog(LLDBLog::Modules);
1651+
if (log != nullptr)
1652+
LLDB_LOGF(log, "loading module '%s' using CASID '%s' from temp file: %s",
1653+
module_name.AsCString(), cas_id, name.c_str());
1654+
1655+
module_spec.GetFileSpec().SetFile(name, FileSpec::Style::native);
1656+
return llvm::Error::success();
1657+
}
1658+
1659+
Status ModuleList::GetSharedModuleFromCAS(ConstString module_name,
1660+
const char *cas_id, FileSpec cu_path,
1661+
ModuleSpec &module_spec,
1662+
lldb::ModuleSP &module_sp) {
1663+
auto err = loadModuleFromCAS(module_name, cas_id, cu_path, module_spec);
1664+
if (err)
1665+
return Status::FromErrorString(llvm::toString(std::move(err)).c_str());
1666+
1667+
// Remove the temporary file created from CAS content after module is loaded.
1668+
auto cleanup_module = llvm::make_scope_exit([&]() {
1669+
llvm::sys::fs::remove(
1670+
module_spec.GetFileSpec().GetPathAsConstString().GetStringRef());
1671+
});
1672+
1673+
return GetSharedModule(module_spec, module_sp, nullptr, nullptr, nullptr);
1674+
}
1675+
15511676
bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) {
15521677
return GetSharedModuleList().Remove(module_sp);
15531678
}

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: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# REQUIRES: system-darwin
2+
# RUN: rm -rf %t
3+
# RUN: split-file %s %t
4+
# RUN: sed "s|DIR|%/t|g" %t/cas-config.template > %t/.cas-config
5+
6+
# 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
7+
# RUN: %clang_host %t/test.o -o %t/main
8+
# RUN: %lldb %t/main -s %t/lldb.script 2>&1 | FileCheck %s
9+
# CHECK: loading module 'Bottom' using CASID
10+
# CHECK: loading module 'Top' using CASID
11+
# CHECK: top = (x = 1)
12+
13+
//--- module.modulemap
14+
module Top { header "Top.h" export *}
15+
module Bottom { header "Bottom.h" export *}
16+
17+
//--- Top.h
18+
#pragma once
19+
struct Top { int x; };
20+
21+
//--- Bottom.h
22+
#pragma once
23+
#include "Top.h"
24+
struct Bottom { struct Top top; };
25+
26+
//--- tu.c
27+
#include "Bottom.h"
28+
29+
int main(void) {
30+
struct Bottom _bottom;
31+
_bottom.top.x = 1;
32+
33+
return 0; // BREAK HERE
34+
}
35+
36+
//--- cas-config.template
37+
{
38+
"CASPath": "DIR/cas"
39+
}
40+
41+
//--- lldb.script
42+
log enable lldb module
43+
b tu.c:7
44+
run
45+
expr _bottom
46+
quit

0 commit comments

Comments
 (0)