https://github.com/ionuthristodorescu created https://github.com/llvm/llvm-project/pull/197051
Added a public lldb::SBFileSpecList lldb::SBModule::GetSeparateDebugInfoFiles LLDB API for .dwp/.dwo discovery (wrapping around the existing const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() LLDB private API); returns a SBFileSpecList with either the .dwp file or .dwo (if no .dwp) or empty if no split debug info. >From f91836f7fb4d2a895fad60f91cd719893c7fbccc Mon Sep 17 00:00:00 2001 From: Ionut Hristodorescu <[email protected]> Date: Mon, 11 May 2026 15:45:21 -0700 Subject: [PATCH] Added a public lldb::SBModuleSpecList lldb::SBModule::GetSeparateDebugInfoFiles LLDB API for .dwp/.dwo discovery (wrapping around the existing const std::shared_ptr<SymbolFileDWARFDwo> &SymbolFileDWARF::GetDwpSymbolFile() LLDB private API); returns a SBFileSpecList with either the .dwp file or .dwo (if no .dwp) or empty if no split debug info. --- lldb/include/lldb/API/SBModule.h | 23 +++++++++ lldb/include/lldb/API/SBModuleSpec.h | 2 + lldb/include/lldb/Symbol/SymbolFile.h | 22 +++++++++ lldb/source/API/SBModule.cpp | 16 +++++++ lldb/source/API/SBModuleSpec.cpp | 2 + .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 41 ++++++++++++++++ .../SymbolFile/DWARF/SymbolFileDWARF.h | 2 + .../DWARF/SymbolFileDWARFDebugMap.cpp | 26 ++++++++++ .../DWARF/SymbolFileDWARFDebugMap.h | 2 + .../get-separate-debug-info-files-darwin.cpp | 38 +++++++++++++++ .../DWARF/get-separate-debug-info-files.cpp | 48 +++++++++++++++++++ 11 files changed, 222 insertions(+) create mode 100644 lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files-darwin.cpp create mode 100644 lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files.cpp diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h index 4009ca1461e51..b5106b0ede3c5 100644 --- a/lldb/include/lldb/API/SBModule.h +++ b/lldb/include/lldb/API/SBModule.h @@ -290,6 +290,29 @@ class LLDB_API SBModule { lldb::SBAddress GetObjectFileHeaderAddress() const; lldb::SBAddress GetObjectFileEntryPointAddress() const; + /// Get the separate debug info files for this module. + /// + /// Returns a list of file paths for the separate debug info files + /// associated with this module. Separate debug info files are + /// considered any files that are referenced from debug info but + /// aren't the actual object file that the symbol file parses. + /// + /// If this module uses split DWARF it will return a DWARF package + /// (.dwp) if it exists, otherwise it will return a list of all + /// .dwo files. + /// + /// If this module uses DWARF in .o files (Darwin), it will return + /// a list of all .o files if there is no dSYM file. If a dSYM file + /// is present, no specifications will be returned since the debug + /// info is self-contained in the dSYM bundle. + /// + /// An empty list will be returned if there are no separate debug + /// info files for this module. + /// + /// \return + /// A list of module specifications for the separate debug info files. + lldb::SBModuleSpecList GetSeparateDebugInfoFiles(); + /// Get the number of global modules. static uint32_t GetNumberAllocatedModules(); diff --git a/lldb/include/lldb/API/SBModuleSpec.h b/lldb/include/lldb/API/SBModuleSpec.h index 0e7f0f3489596..8f26dea4e2a4d 100644 --- a/lldb/include/lldb/API/SBModuleSpec.h +++ b/lldb/include/lldb/API/SBModuleSpec.h @@ -135,6 +135,8 @@ class SBModuleSpecList { bool GetDescription(lldb::SBStream &description); private: + friend class SBModule; + lldb_private::ModuleSpecList &ref(); std::unique_ptr<lldb_private::ModuleSpecList> m_opaque_up; }; diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 6b53ba8b6acce..8c0095185aad3 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -135,6 +135,28 @@ class SymbolFile : public PluginInterface { /// It will be true for most implementations except SymbolFileOnDemand. virtual bool GetLoadDebugInfoEnabled() { return true; } + /// Get the separate debug info files for this module. + /// + /// Returns a list of module specs for the separate debug info files + /// associated with this module. Separate debug info files are + /// considered any files that are referenced from debug info but + /// aren't the actual object file that the symbol file parses. + /// + /// If this module uses split DWARF it will return a DWARF package + /// (.dwp) if it exists, otherwise it will return a list of all + /// .dwo files. + /// + /// If this module uses DWARF in .o files (Darwin), it will return + /// a list of all .o files if there is no dSYM file. If a dSYM file + /// is present, no specifications will be returned since the debug + /// info is self-contained in the dSYM bundle. + /// + /// An empty list will be returned if there are no separate debug + /// info files for this module. + virtual ModuleSpecList GetSeparateDebugInfoModuleSpecs() { + return {}; + } + /// Specify debug info should be loaded. /// /// It will be no-op for most implementations except SymbolFileOnDemand. diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp index ea0cb2ae356b8..8483d9fd5bdd4 100644 --- a/lldb/source/API/SBModule.cpp +++ b/lldb/source/API/SBModule.cpp @@ -666,6 +666,22 @@ lldb::SBAddress SBModule::GetObjectFileEntryPointAddress() const { return sb_addr; } +lldb::SBModuleSpecList SBModule::GetSeparateDebugInfoFiles() { + LLDB_INSTRUMENT_VA(this); + + SBModuleSpecList sb_mspec_list; + ModuleSP module_sp(GetSP()); + if (!module_sp) + return sb_mspec_list; + + SymbolFile *sym_file = module_sp->GetSymbolFile(); + if (!sym_file) + return sb_mspec_list; + + sb_mspec_list.ref() = sym_file->GetSeparateDebugInfoModuleSpecs(); + return sb_mspec_list; +} + uint32_t SBModule::GetNumberAllocatedModules() { LLDB_INSTRUMENT(); diff --git a/lldb/source/API/SBModuleSpec.cpp b/lldb/source/API/SBModuleSpec.cpp index 7b59538f70161..6249302757de2 100644 --- a/lldb/source/API/SBModuleSpec.cpp +++ b/lldb/source/API/SBModuleSpec.cpp @@ -269,3 +269,5 @@ bool SBModuleSpecList::GetDescription(lldb::SBStream &description) { m_opaque_up->Dump(description.ref()); return true; } + +ModuleSpecList &SBModuleSpecList::ref() { return *m_opaque_up; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index aca36b45d69ca..4c8d8f8a87273 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -4560,6 +4560,47 @@ StatsDuration::Duration SymbolFileDWARF::GetDebugInfoIndexTime() { return {}; } +ModuleSpecList SymbolFileDWARF::GetSeparateDebugInfoModuleSpecs() { + ModuleSpecList specs; + + // Check if a .dwp file exists using LLDB's built-in DWP discovery. + if (const auto &dwp_sp = GetDwpSymbolFile()) { + if (ObjectFile *dwp_obj = dwp_sp->GetObjectFile()) { + specs.Append(ModuleSpec(dwp_obj->GetFileSpec())); + return specs; + } + } + + // No DWP — collect individual DWO file paths from the skeleton CUs. + DWARFDebugInfo &info = DebugInfo(); + const size_t num_cus = info.GetNumUnits(); + for (size_t cu_idx = 0; cu_idx < num_cus; cu_idx++) { + DWARFUnit *unit = info.GetUnitAtIndex(cu_idx); + DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(unit); + if (!dwarf_cu || !dwarf_cu->GetDWOId().has_value()) + continue; + + const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly(); + if (!die) + continue; + + const char *dwo_name = GetDWOName(*dwarf_cu, *die.GetDIE()); + if (!dwo_name) + continue; + + FileSpec dwo_file(dwo_name); + if (!dwo_file.IsAbsolute()) { + const char *comp_dir = + die.GetDIE()->GetAttributeValueAsString(dwarf_cu, + DW_AT_comp_dir, nullptr); + if (comp_dir) + dwo_file.PrependPathComponent(comp_dir); + } + specs.Append(ModuleSpec(dwo_file)); + } + return specs; +} + void SymbolFileDWARF::ResetStatistics() { m_parse_time.reset(); if (m_index) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 09dc8da9b7260..142297a6f512c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -321,6 +321,8 @@ class SymbolFileDWARF : public SymbolFileCommon { StatsDuration &GetDebugInfoParseTimeRef() { return m_parse_time; } + ModuleSpecList GetSeparateDebugInfoModuleSpecs() override; + void ResetStatistics() override; virtual lldb::offset_t diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index eb80b7ed45d7b..2db17557070cb 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Progress.h" #include "lldb/Core/Section.h" @@ -1327,6 +1328,31 @@ bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo( return true; } +ModuleSpecList SymbolFileDWARFDebugMap::GetSeparateDebugInfoModuleSpecs() { + ModuleSpecList specs; + const uint32_t cu_count = GetNumCompileUnits(); + for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) { + const auto &info = m_compile_unit_infos[cu_idx]; + if (!info.oso_path) + continue; + + ModuleSpec spec; + FileSpec oso_file; + ConstString oso_object; + if (ObjectFile::SplitArchivePathWithObject( + info.oso_path.GetStringRef(), oso_file, oso_object, + /*must_exist=*/false)) { + spec.GetFileSpec() = oso_file; + spec.GetObjectName() = oso_object; + } else { + spec.GetFileSpec() = FileSpec(info.oso_path.GetStringRef()); + } + spec.GetObjectModificationTime() = info.oso_mod_time; + specs.Append(spec); + } + return specs; +} + lldb::CompUnitSP SymbolFileDWARFDebugMap::GetCompileUnit(SymbolFileDWARF *oso_dwarf, DWARFCompileUnit &dwarf_cu) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index 74b97f610f29c..dcd685edbf78e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -136,6 +136,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon { bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only, bool load_all_debug_info = false) override; + ModuleSpecList GetSeparateDebugInfoModuleSpecs() override; + // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } diff --git a/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files-darwin.cpp b/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files-darwin.cpp new file mode 100644 index 0000000000000..4558bd5425e15 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files-darwin.cpp @@ -0,0 +1,38 @@ +// REQUIRES: system-darwin + +// Test SBModule::GetSeparateDebugInfoFiles() for Darwin scenarios: +// 1. DWARF in .o files (no dSYM) — returns .o file paths +// 2. dSYM present — returns empty (debug info is self-contained) + +struct A { + int x = 47; +}; +A a; +int main() {} + +// ============================================================================ +// TEST 1: DWARF in .o files (no dSYM) — returns .o paths +// ============================================================================ +// Compile to .o with debug info, then link without generating a dSYM. +// RUN: %clang_host -g -c %s -o %t.main.o +// RUN: %clang_host %t.main.o -o %t.oso -Wl,-no_uuid +// RUN: %lldb -b \ +// RUN: -o "script m = lldb.target.modules[0]; files = m.GetSeparateDebugInfoFiles(); print('OSO_COUNT=' + str(files.GetSize())); [print('OSO_FILE=' + files.GetSpecAtIndex(i).GetFileSpec().fullpath) for i in range(files.GetSize())]" \ +// RUN: %t.oso 2>&1 | FileCheck %s --check-prefix=OSO +// +// With DWARF in .o files, should list the .o file. +// OSO: OSO_COUNT=1 +// OSO: OSO_FILE={{.*}}.main.o + +// ============================================================================ +// TEST 2: dSYM present — returns empty list +// ============================================================================ +// Build with debug info and generate a dSYM bundle. +// RUN: %clang_host -g %s -o %t.dsym_exe +// RUN: dsymutil %t.dsym_exe +// RUN: %lldb -b \ +// RUN: -o "script m = lldb.target.modules[0]; files = m.GetSeparateDebugInfoFiles(); print('DSYM_COUNT=' + str(files.GetSize()))" \ +// RUN: %t.dsym_exe 2>&1 | FileCheck %s --check-prefix=DSYM +// +// With a dSYM, debug info is self-contained — no separate files. +// DSYM: DSYM_COUNT=0 diff --git a/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files.cpp b/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files.cpp new file mode 100644 index 0000000000000..a816f3cdd46fa --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files.cpp @@ -0,0 +1,48 @@ +// REQUIRES: system-linux + +// Test SBModule::GetSeparateDebugInfoFiles() for three scenarios: +// 1. No split DWARF — returns empty list +// 2. Split DWARF with .dwo files — returns DWO file paths +// 3. Split DWARF with .dwp file — returns DWP file path + +struct A { + int x = 47; +}; +A a; +int main() {} + +// ============================================================================ +// TEST 1: No split DWARF — GetSeparateDebugInfoFiles returns empty +// ============================================================================ +// RUN: %clang_host -g -c %s -o %t.nosplit.o +// RUN: %clang_host %t.nosplit.o -o %t.nosplit +// RUN: %lldb -b \ +// RUN: -o "script m = lldb.target.modules[0]; files = m.GetSeparateDebugInfoFiles(); print('NOSPLIT_COUNT=' + str(files.GetSize()))" \ +// RUN: %t.nosplit 2>&1 | FileCheck %s --check-prefix=NOSPLIT +// +// NOSPLIT: NOSPLIT_COUNT=0 + +// ============================================================================ +// TEST 2: Split DWARF with .dwo files — returns DWO paths +// ============================================================================ +// RUN: %clang_host -gsplit-dwarf -gdwarf-5 -c %s -o %t.dwo.o +// RUN: %clang_host %t.dwo.o -o %t.dwo +// RUN: rm -f %t.dwo.dwp +// RUN: %lldb -b \ +// RUN: -o "script m = lldb.target.modules[0]; files = m.GetSeparateDebugInfoFiles(); print('DWO_COUNT=' + str(files.GetSize())); [print('DWO_FILE=' + files.GetSpecAtIndex(i).GetFileSpec().fullpath) for i in range(files.GetSize())]" \ +// RUN: %t.dwo 2>&1 | FileCheck %s --check-prefix=DWO +// +// DWO: DWO_COUNT=1 +// DWO: DWO_FILE={{.*}}.dwo + +// ============================================================================ +// TEST 3: Split DWARF with .dwp file — returns DWP path +// ============================================================================ +// RUN: llvm-dwp %t.dwo.dwo -o %t.dwo.dwp +// RUN: rm %t.dwo.dwo +// RUN: %lldb -b \ +// RUN: -o "script m = lldb.target.modules[0]; files = m.GetSeparateDebugInfoFiles(); print('DWP_COUNT=' + str(files.GetSize())); [print('DWP_FILE=' + files.GetSpecAtIndex(i).GetFileSpec().fullpath) for i in range(files.GetSize())]" \ +// RUN: %t.dwo 2>&1 | FileCheck %s --check-prefix=DWP +// +// DWP: DWP_COUNT=1 +// DWP: DWP_FILE={{.*}}.dwp _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
