[Lldb-commits] [lldb] [DRAFT] Add support for inline DWARF source files. (PR #75880)
Adrian Prantl via lldb-commits
lldb-commits at lists.llvm.org
Mon Dec 18 17:02:57 PST 2023
https://github.com/adrian-prantl created https://github.com/llvm/llvm-project/pull/75880
LLVM supports DWARF 5 linetable extension to store source files inline in DWARF. This is particularly useful for compiler-generated source code. This implementation tries to materialize them as temporary files lazily, so SBAPI clients don't need to be aware of them.
>From 638bd4e7e939ef28bf23ae2cfd1723797040b0a5 Mon Sep 17 00:00:00 2001
From: Adrian Prantl <aprantl at apple.com>
Date: Mon, 18 Dec 2023 15:59:00 -0800
Subject: [PATCH] Add support for inline DWARF source files.
LLVM supports DWARF 5 linetable extension to store source files inline
in DWARF. This is particularly useful for compiler-generated source
code. This implementation tries to materialize them as temporary files
lazily, so SBAPI clients don't need to be aware of them.
---
lldb/include/lldb/Utility/FileSpecList.h | 46 ++++++++++++++++---
lldb/source/Core/ModuleList.cpp | 8 ++--
.../Clang/ClangUserExpression.cpp | 12 ++---
.../Clang/CppModuleConfiguration.cpp | 6 +--
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 46 +++++++++++++++++++
lldb/source/Utility/FileSpecList.cpp | 19 ++++----
.../inline-sourcefile/Makefile | 11 +++++
.../TestInlineSourceFiles.py | 17 +++++++
.../inline-sourcefile/inline.ll | 39 ++++++++++++++++
.../functionalities/inline-sourcefile/main.c | 8 ++++
10 files changed, 186 insertions(+), 26 deletions(-)
create mode 100644 lldb/test/API/functionalities/inline-sourcefile/Makefile
create mode 100644 lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py
create mode 100644 lldb/test/API/functionalities/inline-sourcefile/inline.ll
create mode 100644 lldb/test/API/functionalities/inline-sourcefile/main.c
diff --git a/lldb/include/lldb/Utility/FileSpecList.h b/lldb/include/lldb/Utility/FileSpecList.h
index 77587aa917916b..078fd7208e7b5f 100644
--- a/lldb/include/lldb/Utility/FileSpecList.h
+++ b/lldb/include/lldb/Utility/FileSpecList.h
@@ -17,13 +17,40 @@
namespace lldb_private {
class Stream;
+/// Represents a source file whose contents is known (for example
+/// because it can be reconstructed from debug info), but that
+/// hasn't been written to a local disk yet.
+struct LazyFileSpec {
+ virtual ~LazyFileSpec() {}
+ virtual const FileSpec &Materialize() = 0;
+};
+
+/// Wraps either a FileSpec that represents a local file or a
+/// LazyFileSpec that could be materialized into a local file.
+class FileSpecHolder {
+ FileSpec m_file_spec;
+ std::shared_ptr<LazyFileSpec> m_lazy;
+public:
+ FileSpecHolder(const FileSpec &spec, std::shared_ptr<LazyFileSpec> lazy = {})
+ : m_file_spec(spec), m_lazy(lazy) {}
+ FileSpecHolder(const FileSpecHolder &other) = default;
+ FileSpecHolder(FileSpecHolder &&other) = default;
+ FileSpecHolder &operator=(const FileSpecHolder &other) = default;
+ const FileSpec &GetSpecOnly() const { return m_file_spec; };
+ const FileSpec &Materialize() const {
+ if (m_lazy)
+ return m_lazy->Materialize();
+ return m_file_spec;
+ }
+};
+
/// \class FileSpecList FileSpecList.h "lldb/Utility/FileSpecList.h"
/// A file collection class.
///
/// A class that contains a mutable list of FileSpec objects.
class FileSpecList {
public:
- typedef std::vector<FileSpec> collection;
+ typedef std::vector<FileSpecHolder> collection;
typedef collection::const_iterator const_iterator;
/// Default constructor.
@@ -38,7 +65,10 @@ class FileSpecList {
FileSpecList(FileSpecList &&rhs) = default;
/// Initialize this object from a vector of FileSpecs
- FileSpecList(std::vector<FileSpec> &&rhs) : m_files(std::move(rhs)) {}
+ FileSpecList(std::vector<FileSpec> &&rhs) {
+ for (auto &fs : rhs)
+ m_files.emplace_back(fs);
+ }
/// Destructor.
~FileSpecList();
@@ -83,7 +113,11 @@ class FileSpecList {
/// \param[in] args
/// Arguments to create the FileSpec
template <class... Args> void EmplaceBack(Args &&...args) {
- m_files.emplace_back(std::forward<Args>(args)...);
+ m_files.emplace_back(FileSpec(std::forward<Args>(args)...));
+ }
+
+ void Append(FileSpecHolder &&fsh) {
+ m_files.push_back(std::move(fsh));
}
/// Clears the file list.
@@ -175,10 +209,10 @@ class FileSpecList {
bool Insert(size_t idx, const FileSpec &file) {
if (idx < m_files.size()) {
- m_files.insert(m_files.begin() + idx, file);
+ m_files.insert(m_files.begin() + idx, FileSpecHolder(file));
return true;
} else if (idx == m_files.size()) {
- m_files.push_back(file);
+ m_files.push_back(FileSpecHolder(file));
return true;
}
return false;
@@ -186,7 +220,7 @@ class FileSpecList {
bool Replace(size_t idx, const FileSpec &file) {
if (idx < m_files.size()) {
- m_files[idx] = file;
+ m_files[idx] = FileSpecHolder(file);
return true;
}
return false;
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index aa89c93c8d0521..3b6c3ea899caf7 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -164,11 +164,13 @@ void ModuleListProperties::UpdateSymlinkMappings() {
llvm::sys::ScopedWriter lock(m_symlink_paths_mutex);
const bool notify = false;
m_symlink_paths.Clear(notify);
- for (FileSpec symlink : list) {
+ for (auto symlink : list) {
FileSpec resolved;
- Status status = FileSystem::Instance().Readlink(symlink, resolved);
+ Status status =
+ FileSystem::Instance().Readlink(symlink.Materialize(), resolved);
if (status.Success())
- m_symlink_paths.Append(symlink.GetPath(), resolved.GetPath(), notify);
+ m_symlink_paths.Append(symlink.Materialize().GetPath(),
+ resolved.GetPath(), notify);
}
}
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index 68bdd96e8adb03..ec03a38752c2fe 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -488,8 +488,8 @@ CppModuleConfiguration GetModuleConfig(lldb::LanguageType language,
// Build a list of files we need to analyze to build the configuration.
FileSpecList files;
- for (const FileSpec &f : sc.comp_unit->GetSupportFiles())
- files.AppendIfUnique(f);
+ for (auto &f : sc.comp_unit->GetSupportFiles())
+ files.AppendIfUnique(f.Materialize());
// We also need to look at external modules in the case of -gmodules as they
// contain the support files for libc++ and the C library.
llvm::DenseSet<SymbolFile *> visited_symbol_files;
@@ -498,8 +498,8 @@ CppModuleConfiguration GetModuleConfig(lldb::LanguageType language,
for (std::size_t i = 0; i < module.GetNumCompileUnits(); ++i) {
const FileSpecList &support_files =
module.GetCompileUnitAtIndex(i)->GetSupportFiles();
- for (const FileSpec &f : support_files) {
- files.AppendIfUnique(f);
+ for (auto &f : support_files) {
+ files.AppendIfUnique(f.Materialize());
}
}
return false;
@@ -508,9 +508,9 @@ CppModuleConfiguration GetModuleConfig(lldb::LanguageType language,
LLDB_LOG(log, "[C++ module config] Found {0} support files to analyze",
files.GetSize());
if (log && log->GetVerbose()) {
- for (const FileSpec &f : files)
+ for (auto &f : files)
LLDB_LOGV(log, "[C++ module config] Analyzing support file: {0}",
- f.GetPath());
+ f.Materialize().GetPath());
}
// Try to create a configuration from the files. If there is no valid
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp b/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
index 847dab6592b886..0ea5b1581c8e06 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp
@@ -134,9 +134,9 @@ bool CppModuleConfiguration::hasValidConfig() {
CppModuleConfiguration::CppModuleConfiguration(
const FileSpecList &support_files, const llvm::Triple &triple) {
// Analyze all files we were given to build the configuration.
- bool error = !llvm::all_of(support_files,
- std::bind(&CppModuleConfiguration::analyzeFile,
- this, std::placeholders::_1, triple));
+ bool error = !llvm::all_of(support_files, [&](auto &file) {
+ return CppModuleConfiguration::analyzeFile(file.Materialize(), triple);
+ });
// If we have a valid configuration at this point, set the
// include directories and module list that should be used.
if (!error && hasValidConfig()) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 7eddc5074eff12..f9faa85056b1a0 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -10,6 +10,7 @@
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Threading.h"
@@ -235,6 +236,51 @@ ParseSupportFilesFromPrologue(const lldb::ModuleSP &module,
for (size_t idx = first_file_idx; idx <= last_file_idx; ++idx) {
std::string remapped_file;
if (auto file_path = GetFileByIndex(prologue, idx, compile_dir, style)) {
+ auto entry = prologue.getFileNameEntry(idx);
+ auto source = entry.Source.getAsCString();
+ if (!source)
+ consumeError(source.takeError());
+ else {
+ llvm::StringRef source_ref(*source);
+ if (!source_ref.empty()) {
+ /// Wrap a path for an in-DWARF source file. Lazily write it
+ /// to disk when Materialize() is called.
+ struct LazyDWARFFile : public LazyFileSpec {
+ LazyDWARFFile(std::string file_name, llvm::StringRef source,
+ FileSpec::Style style)
+ : file_name(file_name), source(source), style(style) {}
+ std::string file_name;
+ FileSpec tmp_file;
+ llvm::StringRef source;
+ std::unique_ptr<llvm::FileRemover> remover;
+ FileSpec::Style style;
+
+ const FileSpec &Materialize() override {
+ if (tmp_file)
+ return tmp_file;
+ llvm::SmallString<0> name;
+ int fd;
+ auto ec = llvm::sys::fs::createTemporaryFile(
+ "", llvm::sys::path::filename(file_name, style), fd, name);
+ if (ec || fd <= 0) {
+ LLDB_LOG(GetLog(DWARFLog::DebugInfo),
+ "Could not create temporary file");
+ return tmp_file;
+ }
+ remover = std::make_unique<llvm::FileRemover>(name);
+ NativeFile file(fd, File::eOpenOptionWriteOnly, true);
+ size_t num_bytes = source.size();
+ file.Write(source.data(), num_bytes);
+ tmp_file.SetPath(name);
+ return tmp_file;
+ }
+ };
+ support_files.Append(FileSpecHolder(
+ FileSpec(*file_path),
+ std::make_shared<LazyDWARFFile>(*file_path, *source, style)));
+ continue;
+ }
+ }
if (auto remapped = module->RemapSourceFile(llvm::StringRef(*file_path)))
remapped_file = *remapped;
else
diff --git a/lldb/source/Utility/FileSpecList.cpp b/lldb/source/Utility/FileSpecList.cpp
index d5369ac4bbe516..749fdcc6431117 100644
--- a/lldb/source/Utility/FileSpecList.cpp
+++ b/lldb/source/Utility/FileSpecList.cpp
@@ -30,7 +30,9 @@ void FileSpecList::Append(const FileSpec &file_spec) {
// a copy of "file_spec".
bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) {
collection::iterator end = m_files.end();
- if (find(m_files.begin(), end, file_spec) == end) {
+ if (find_if(m_files.begin(), end, [&](auto &holder) {
+ return holder.GetSpecOnly() == file_spec;
+ }) == end) {
m_files.push_back(file_spec);
return true;
}
@@ -44,7 +46,7 @@ void FileSpecList::Clear() { m_files.clear(); }
void FileSpecList::Dump(Stream *s, const char *separator_cstr) const {
collection::const_iterator pos, end = m_files.end();
for (pos = m_files.begin(); pos != end; ++pos) {
- pos->Dump(s->AsRawOstream());
+ pos->GetSpecOnly().Dump(s->AsRawOstream());
if (separator_cstr && ((pos + 1) != end))
s->PutCString(separator_cstr);
}
@@ -64,13 +66,14 @@ size_t FileSpecList::FindFileIndex(size_t start_idx, const FileSpec &file_spec,
bool compare_filename_only = file_spec.GetDirectory().IsEmpty();
for (size_t idx = start_idx; idx < num_files; ++idx) {
+ auto f = m_files[idx].GetSpecOnly();
if (compare_filename_only) {
if (ConstString::Equals(
- m_files[idx].GetFilename(), file_spec.GetFilename(),
- file_spec.IsCaseSensitive() || m_files[idx].IsCaseSensitive()))
+ f.GetFilename(), file_spec.GetFilename(),
+ file_spec.IsCaseSensitive() || f.IsCaseSensitive()))
return idx;
} else {
- if (FileSpec::Equal(m_files[idx], file_spec, full))
+ if (FileSpec::Equal(f, file_spec, full))
return idx;
}
}
@@ -92,7 +95,7 @@ size_t FileSpecList::FindCompatibleIndex(size_t start_idx,
const bool full = !file_spec.GetDirectory().IsEmpty();
for (size_t idx = start_idx; idx < num_files; ++idx) {
- const FileSpec &curr_file = m_files[idx];
+ const FileSpec &curr_file = m_files[idx].GetSpecOnly();
// Always start by matching the filename first
if (!curr_file.FileEquals(file_spec))
@@ -135,7 +138,7 @@ size_t FileSpecList::FindCompatibleIndex(size_t start_idx,
// an empty FileSpec object will be returned.
const FileSpec &FileSpecList::GetFileSpecAtIndex(size_t idx) const {
if (idx < m_files.size())
- return m_files[idx];
+ return m_files[idx].Materialize();
static FileSpec g_empty_file_spec;
return g_empty_file_spec;
}
@@ -148,7 +151,7 @@ size_t FileSpecList::MemorySize() const {
size_t mem_size = sizeof(FileSpecList);
collection::const_iterator pos, end = m_files.end();
for (pos = m_files.begin(); pos != end; ++pos) {
- mem_size += pos->MemorySize();
+ mem_size += pos->GetSpecOnly().MemorySize();
}
return mem_size;
diff --git a/lldb/test/API/functionalities/inline-sourcefile/Makefile b/lldb/test/API/functionalities/inline-sourcefile/Makefile
new file mode 100644
index 00000000000000..adb29d3a88e26c
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/Makefile
@@ -0,0 +1,11 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -gdwarf-5
+
+include Makefile.rules
+
+OBJECTS += inline.o
+
+$(EXE): main.c inline.o
+
+%.o: %.ll
+ $(CC) $< -c -o $@
diff --git a/lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py b/lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py
new file mode 100644
index 00000000000000..6daf970b895b0f
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py
@@ -0,0 +1,17 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbplatform
+from lldbsuite.test import lldbutil
+
+
+class InlineSourceFilesTestCase(TestBase):
+ @skipIf(compiler="gcc")
+ @skipIf(compiler="clang", compiler_version=["<", "18.0"])
+ def test(self):
+ """Test DWARF inline source files."""
+ self.build()
+ #target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+ # self, 'break here', lldb.SBFileSpec('inlined.c'))
+ target, process, thread, bkpt = lldbutil.run_to_name_breakpoint(
+ self, 'f')
diff --git a/lldb/test/API/functionalities/inline-sourcefile/inline.ll b/lldb/test/API/functionalities/inline-sourcefile/inline.ll
new file mode 100644
index 00000000000000..56194e45b81387
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/inline.ll
@@ -0,0 +1,39 @@
+; ModuleID = '/tmp/t.c'
+source_filename = "/tmp/t.c"
+target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
+
+; Function Attrs: noinline nounwind optnone ssp uwtable(sync)
+define void @f() #0 !dbg !9 {
+entry:
+ call void @stop(), !dbg !13
+ ret void, !dbg !14
+}
+
+declare void @stop(...) #1
+
+attributes #0 = { noinline nounwind optnone ssp uwtable(sync) }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
+!llvm.ident = !{!8}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 18.0.0git (git at github.com:llvm/llvm-project.git 29ee66f4a0967e43a035f147c960743c7b640f2f)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+!1 = !DIFile(filename: "/INLINE/inlined.c", directory: "/Volumes/Data/llvm-project", checksumkind: CSK_MD5, checksum: "3183154a5cb31debe9a8e27ca500bc3c")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"uwtable", i32 1}
+!7 = !{i32 7, !"frame-pointer", i32 1}
+!8 = !{!"clang version 18.0.0git (git at github.com:llvm/llvm-project.git 29ee66f4a0967e43a035f147c960743c7b640f2f)"}
+!9 = distinct !DISubprogram(name: "f", scope: !10, file: !10, line: 2, type: !11, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DIFile(filename: "/INLINE/inlined.c", directory: "", source: "void stop();
+void f() {
+ // This is inline source code.
+ stop(); // break here
+}
+")
+!11 = !DISubroutineType(types: !12)
+!12 = !{null}
+!13 = !DILocation(line: 4, column: 3, scope: !9)
+!14 = !DILocation(line: 5, column: 1, scope: !9)
diff --git a/lldb/test/API/functionalities/inline-sourcefile/main.c b/lldb/test/API/functionalities/inline-sourcefile/main.c
new file mode 100644
index 00000000000000..29a22d956ad510
--- /dev/null
+++ b/lldb/test/API/functionalities/inline-sourcefile/main.c
@@ -0,0 +1,8 @@
+void f();
+void stop() {}
+
+int main(int argc, char const *argv[])
+{
+ f();
+ return 0;
+}
More information about the lldb-commits
mailing list