https://github.com/ionuthristodorescu updated https://github.com/llvm/llvm-project/pull/197051
>From 963e634b9a66cb9e6e16179bcd572aee5582e19d Mon Sep 17 00:00:00 2001 From: Ionut Hristodorescu <[email protected]> Date: Mon, 11 May 2026 15:45:21 -0700 Subject: [PATCH 1/4] 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..f3092223b3400 --- /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..794849beed732 --- /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 >From ee1f2f34436cefbdb0efcf1e037c5ac3afce87cf Mon Sep 17 00:00:00 2001 From: Ionut Hristodorescu <[email protected]> Date: Wed, 13 May 2026 17:46:12 -0700 Subject: [PATCH 2/4] Fixed git-clang-format lints + converted testcases to Python --- lldb/include/lldb/Symbol/SymbolFile.h | 4 +- .../Python/lldbsuite/test/make/Makefile.rules | 1 + lldb/source/API/SBModule.cpp | 2 +- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 5 +- .../DWARF/SymbolFileDWARFDebugMap.cpp | 6 +- .../separate-debug-info-files/Makefile | 3 + .../TestGetSeparateDebugInfoFiles.py | 172 ++++++++++++++++++ .../separate-debug-info-files/foo.c | 1 + .../separate-debug-info-files/main.c | 2 + .../get-separate-debug-info-files-darwin.cpp | 38 ---- .../DWARF/get-separate-debug-info-files.cpp | 48 ----- 11 files changed, 186 insertions(+), 96 deletions(-) create mode 100644 lldb/test/API/functionalities/separate-debug-info-files/Makefile create mode 100644 lldb/test/API/functionalities/separate-debug-info-files/TestGetSeparateDebugInfoFiles.py create mode 100644 lldb/test/API/functionalities/separate-debug-info-files/foo.c create mode 100644 lldb/test/API/functionalities/separate-debug-info-files/main.c delete mode 100644 lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files-darwin.cpp delete mode 100644 lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files.cpp diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 8c0095185aad3..4629c79ab6761 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -153,9 +153,7 @@ class SymbolFile : public PluginInterface { /// /// An empty list will be returned if there are no separate debug /// info files for this module. - virtual ModuleSpecList GetSeparateDebugInfoModuleSpecs() { - return {}; - } + virtual ModuleSpecList GetSeparateDebugInfoModuleSpecs() { return {}; } /// Specify debug info should be loaded. /// diff --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules index 677124b8738f7..5b365da3a6e48 100644 --- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules +++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules @@ -218,6 +218,7 @@ else MAKE_DWO := YES DWP_NAME = $(EXE).dwp DYLIB_DWP_NAME = $(DYLIB_NAME).dwp + DSYM = $(DWP_NAME) endif endif diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp index 8483d9fd5bdd4..67771c3a9ca32 100644 --- a/lldb/source/API/SBModule.cpp +++ b/lldb/source/API/SBModule.cpp @@ -677,7 +677,7 @@ lldb::SBModuleSpecList SBModule::GetSeparateDebugInfoFiles() { SymbolFile *sym_file = module_sp->GetSymbolFile(); if (!sym_file) return sb_mspec_list; - + sb_mspec_list.ref() = sym_file->GetSeparateDebugInfoModuleSpecs(); return sb_mspec_list; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 4c8d8f8a87273..a89b82e1d2b5f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -4590,9 +4590,8 @@ ModuleSpecList SymbolFileDWARF::GetSeparateDebugInfoModuleSpecs() { FileSpec dwo_file(dwo_name); if (!dwo_file.IsAbsolute()) { - const char *comp_dir = - die.GetDIE()->GetAttributeValueAsString(dwarf_cu, - DW_AT_comp_dir, nullptr); + const char *comp_dir = die.GetDIE()->GetAttributeValueAsString( + dwarf_cu, DW_AT_comp_dir, nullptr); if (comp_dir) dwo_file.PrependPathComponent(comp_dir); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 2db17557070cb..5afa521877ae6 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1339,9 +1339,9 @@ ModuleSpecList SymbolFileDWARFDebugMap::GetSeparateDebugInfoModuleSpecs() { ModuleSpec spec; FileSpec oso_file; ConstString oso_object; - if (ObjectFile::SplitArchivePathWithObject( - info.oso_path.GetStringRef(), oso_file, oso_object, - /*must_exist=*/false)) { + if (ObjectFile::SplitArchivePathWithObject(info.oso_path.GetStringRef(), + oso_file, oso_object, + /*must_exist=*/false)) { spec.GetFileSpec() = oso_file; spec.GetObjectName() = oso_object; } else { diff --git a/lldb/test/API/functionalities/separate-debug-info-files/Makefile b/lldb/test/API/functionalities/separate-debug-info-files/Makefile new file mode 100644 index 0000000000000..472e733aaadb2 --- /dev/null +++ b/lldb/test/API/functionalities/separate-debug-info-files/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c foo.c + +include Makefile.rules diff --git a/lldb/test/API/functionalities/separate-debug-info-files/TestGetSeparateDebugInfoFiles.py b/lldb/test/API/functionalities/separate-debug-info-files/TestGetSeparateDebugInfoFiles.py new file mode 100644 index 0000000000000..1be9852901c68 --- /dev/null +++ b/lldb/test/API/functionalities/separate-debug-info-files/TestGetSeparateDebugInfoFiles.py @@ -0,0 +1,172 @@ +""" +Test SBModule.GetSeparateDebugInfoFiles() API. + +Uses multiple source files (main.c, foo.c) to verify behavior with: + 1. No split DWARF (dwarf) -- returns empty list + 2. Split DWARF .dwo files (dwo) -- returns DWO file paths + 3. Split DWARF .dwp file (dwp) -- returns single DWP file path + 4. Darwin .o files, no dSYM (dwarf) -- returns .o file paths + 5. Darwin dSYM present (dsym) -- returns empty list +""" + +import os + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestGetSeparateDebugInfoFilesNoSplit(TestBase): + NO_DEBUG_INFO_TESTCASE = True + SHARED_BUILD_TESTCASE = False + + @skipIfWindows + def test_no_split_dwarf(self): + """No split DWARF -- GetSeparateDebugInfoFiles returns empty.""" + self.build(debug_info="dwarf") + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid()) + + module = target.GetModuleAtIndex(0) + self.assertTrue(module.IsValid()) + + files = module.GetSeparateDebugInfoFiles() + self.assertEqual( + files.GetSize(), 0, + f"Expected no separate debug info files, got {files.GetSize()}", + ) + + +class TestGetSeparateDebugInfoFilesDwo(TestBase): + NO_DEBUG_INFO_TESTCASE = True + SHARED_BUILD_TESTCASE = False + + @skipUnlessPlatform(["linux", "freebsd"]) + def test_split_dwarf_dwo(self): + """Split DWARF with .dwo files -- returns DWO file paths.""" + self.build(debug_info="dwo") + exe = self.getBuildArtifact("a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid()) + + module = target.GetModuleAtIndex(0) + self.assertTrue(module.IsValid()) + + files = module.GetSeparateDebugInfoFiles() + self.assertGreaterEqual( + files.GetSize(), 2, + "Expected at least 2 .dwo files (main + foo)", + ) + + dwo_paths = [] + for i in range(files.GetSize()): + path = files.GetSpecAtIndex(i).GetFileSpec().fullpath + self.assertTrue(path.endswith(".dwo"), f"Expected .dwo, got: {path}") + dwo_paths.append(path) + + basenames = [os.path.basename(p) for p in dwo_paths] + self.assertTrue( + any("main" in b for b in basenames), + f"Expected a .dwo for main, got: {basenames}", + ) + self.assertTrue( + any("foo" in b for b in basenames), + f"Expected a .dwo for foo, got: {basenames}", + ) + + for p in dwo_paths: + self.assertTrue(os.path.exists(p), f"DWO file should exist: {p}") + + +class TestGetSeparateDebugInfoFilesDwp(TestBase): + NO_DEBUG_INFO_TESTCASE = True + SHARED_BUILD_TESTCASE = False + + @skipUnlessPlatform(["linux", "freebsd"]) + def test_split_dwarf_dwp(self): + """Split DWARF with .dwp file -- returns single DWP path.""" + self.build(debug_info="dwp") + exe = self.getBuildArtifact("a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid()) + + module = target.GetModuleAtIndex(0) + self.assertTrue(module.IsValid()) + + files = module.GetSeparateDebugInfoFiles() + self.assertEqual( + files.GetSize(), 1, + f"Expected 1 .dwp entry, got {files.GetSize()}", + ) + path = files.GetSpecAtIndex(0).GetFileSpec().fullpath + self.assertTrue(path.endswith(".dwp"), f"Expected .dwp, got: {path}") + self.assertTrue(os.path.exists(path), f"DWP should exist: {path}") + + +class TestGetSeparateDebugInfoFilesDarwinOso(TestBase): + NO_DEBUG_INFO_TESTCASE = True + SHARED_BUILD_TESTCASE = False + + @skipUnlessPlatform(["darwin"]) + def test_darwin_oso(self): + """DWARF in .o files (no dSYM) -- returns .o file paths.""" + self.build(debug_info="dwarf") + exe = self.getBuildArtifact("a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid()) + + module = target.GetModuleAtIndex(0) + self.assertTrue(module.IsValid()) + + files = module.GetSeparateDebugInfoFiles() + self.assertGreaterEqual( + files.GetSize(), 2, + "Expected at least 2 .o files (main + foo)", + ) + + o_paths = [] + for i in range(files.GetSize()): + path = files.GetSpecAtIndex(i).GetFileSpec().fullpath + self.assertTrue(path.endswith(".o"), f"Expected .o, got: {path}") + o_paths.append(path) + + basenames = [os.path.basename(p) for p in o_paths] + self.assertTrue( + any("main" in b for b in basenames), + f"Expected a .o for main, got: {basenames}", + ) + self.assertTrue( + any("foo" in b for b in basenames), + f"Expected a .o for foo, got: {basenames}", + ) + + for p in o_paths: + self.assertTrue(os.path.exists(p), f".o file should exist: {p}") + + +class TestGetSeparateDebugInfoFilesDarwinDsym(TestBase): + NO_DEBUG_INFO_TESTCASE = True + SHARED_BUILD_TESTCASE = False + + @skipUnlessPlatform(["darwin"]) + def test_darwin_dsym(self): + """dSYM present -- returns empty list.""" + self.build(debug_info="dsym") + exe = self.getBuildArtifact("a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid()) + + module = target.GetModuleAtIndex(0) + self.assertTrue(module.IsValid()) + + files = module.GetSeparateDebugInfoFiles() + self.assertEqual( + files.GetSize(), 0, + f"Expected no separate debug info with dSYM, got {files.GetSize()}", + ) diff --git a/lldb/test/API/functionalities/separate-debug-info-files/foo.c b/lldb/test/API/functionalities/separate-debug-info-files/foo.c new file mode 100644 index 0000000000000..464a23056475d --- /dev/null +++ b/lldb/test/API/functionalities/separate-debug-info-files/foo.c @@ -0,0 +1 @@ +int foo(void) { return 42; } diff --git a/lldb/test/API/functionalities/separate-debug-info-files/main.c b/lldb/test/API/functionalities/separate-debug-info-files/main.c new file mode 100644 index 0000000000000..570ba161b34b3 --- /dev/null +++ b/lldb/test/API/functionalities/separate-debug-info-files/main.c @@ -0,0 +1,2 @@ +extern int foo(void); +int main(void) { return foo(); } 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 deleted file mode 100644 index f3092223b3400..0000000000000 --- a/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files-darwin.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// 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 deleted file mode 100644 index 794849beed732..0000000000000 --- a/lldb/test/Shell/SymbolFile/DWARF/get-separate-debug-info-files.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// 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 >From 0bb1c447362bdc155705b4f9c716ff8c08cf2dbf Mon Sep 17 00:00:00 2001 From: ionuthristodorescu <[email protected]> Date: Sun, 17 May 2026 18:23:01 -0700 Subject: [PATCH 3/4] Update lldb/include/lldb/API/SBModule.h Co-authored-by: Jonas Devlieghere <[email protected]> --- lldb/include/lldb/API/SBModule.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h index b5106b0ede3c5..dacd4bec2d2b9 100644 --- a/lldb/include/lldb/API/SBModule.h +++ b/lldb/include/lldb/API/SBModule.h @@ -308,9 +308,6 @@ class LLDB_API SBModule { /// /// 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. >From 21e28fcb046d8c4e1553a1e0e5a6a6a63326a60e Mon Sep 17 00:00:00 2001 From: ionuthristodorescu <[email protected]> Date: Sun, 17 May 2026 18:23:21 -0700 Subject: [PATCH 4/4] Update lldb/include/lldb/Symbol/SymbolFile.h Co-authored-by: Jonas Devlieghere <[email protected]> --- lldb/include/lldb/Symbol/SymbolFile.h | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index 4629c79ab6761..15576537e5c15 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -136,23 +136,7 @@ class SymbolFile : public PluginInterface { 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. + /// See SBModule::GetSeparateDebugInfoFiles for a detailled description. virtual ModuleSpecList GetSeparateDebugInfoModuleSpecs() { return {}; } /// Specify debug info should be loaded. _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
