Skip to content

Commit e6ae8c2

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

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, {});
@@ -1253,8 +1283,11 @@ class SharedModuleList {
12531283
struct SharedModuleListInfo {
12541284
SharedModuleList module_list;
12551285
ModuleListProperties module_list_properties;
1286+
std::shared_ptr<llvm::cas::ObjectStore> cas_object_store;
1287+
std::mutex shared_lock;
12561288
};
12571289
}
1290+
12581291
static SharedModuleListInfo &GetSharedModuleListInfo()
12591292
{
12601293
static SharedModuleListInfo *g_shared_module_list_info = nullptr;
@@ -1273,6 +1306,45 @@ static SharedModuleList &GetSharedModuleList() {
12731306
return GetSharedModuleListInfo().module_list;
12741307
}
12751308

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

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

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)