llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Adrian Prantl (adrian-prantl) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/75880.diff 10 Files Affected: - (modified) lldb/include/lldb/Utility/FileSpecList.h (+40-6) - (modified) lldb/source/Core/ModuleList.cpp (+5-3) - (modified) lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp (+6-6) - (modified) lldb/source/Plugins/ExpressionParser/Clang/CppModuleConfiguration.cpp (+3-3) - (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (+46) - (modified) lldb/source/Utility/FileSpecList.cpp (+11-8) - (added) lldb/test/API/functionalities/inline-sourcefile/Makefile (+11) - (added) lldb/test/API/functionalities/inline-sourcefile/TestInlineSourceFiles.py (+17) - (added) lldb/test/API/functionalities/inline-sourcefile/inline.ll (+39) - (added) lldb/test/API/functionalities/inline-sourcefile/main.c (+8) ``````````diff 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 (g...@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 (g...@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; +} `````````` </details> https://github.com/llvm/llvm-project/pull/75880 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits