llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: satyanarayana reddy janga (satyajanga) <details> <summary>Changes</summary> Depends on #<!-- -->218030. The standalone-line-table implementation is the second commit in this stack. ## Summary - recognize and parse standalone DWARF `.debug_line` sections when `.debug_info` is absent - create LLDB compile units directly from line-table prologues without creating fake DWARF units - build line tables and address ranges for address-to-source and source-to-address lookup - accept the recoverable four-byte DWARF v2 prologue padding present in the CUDA images while rejecting unusable tables ## Testing - added a one-object SBAPI test for `.debug_line` + `.symtab` without `.debug_info` - verified that test fails on unmodified LLDB (`GetNumCompileUnits()` returns 0) and passes with this stack - focused standalone-line shell test passed - `SymbolFileDWARFTests`: 56/56 passed - `SymbolTests`: 126/126 passed - rebuilt and tested the exact stack against current `upstream/main` - NVIDIA validation: the CUDA core resolves the selected frame to CUDA source line 184 and GPU address `0x7f4b397cbaf0` to `vecn.cuh:111` --- Patch is 29.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/217932.diff 8 Files Affected: - (modified) lldb/include/lldb/Symbol/SymbolFile.h (+5-2) - (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (+201-34) - (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h (+6) - (modified) lldb/source/Symbol/SymbolFile.cpp (+10-1) - (added) lldb/test/API/python_api/symbol-context/standalone-debug-line/TestStandaloneDebugLine.py (+63) - (added) lldb/test/API/python_api/symbol-context/standalone-debug-line/main.s (+14) - (added) lldb/test/Shell/SymbolFile/DWARF/x86/standalone-debug-line.s (+160) - (modified) lldb/unittests/Symbol/LineTableTest.cpp (+36-10) ``````````diff diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index ae6504c016d7b..0ec2e10cfeaa0 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -95,8 +95,11 @@ class SymbolFile : public PluginInterface { /// trying to figure out which symbol file plug-in will get used /// for a given object file. The plug-in that responds with the /// best mix of "SymbolFile::Abilities" bits set, will get chosen to - /// be the symbol file parser. This allows each plug-in to check for - /// sections that contain data a symbol file plug-in would need. For + /// be the symbol file parser. Plug-ins that provide detailed debug + /// information such as line tables, blocks, local variables, or types are + /// preferred over plug-ins that provide only information obtainable from an + /// object file's symbol table. This allows each plug-in to check for sections + /// that contain data a symbol file plug-in would need. For /// example the DWARF plug-in requires DWARF sections in a file that /// contain debug information. If the DWARF plug-in doesn't find /// these sections, it won't respond with many ability bits set, and diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 81cd4444161f7..080c158f325e2 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -308,6 +308,68 @@ static void ParseSupportFilesFromPrologue( } } +struct lldb_private::plugin::dwarf::StandaloneDWARFLineTableInfo { + std::vector<llvm::DWARFDebugLine::LineTable> line_tables; + llvm::once_flag aranges_once_flag; + DWARFDebugAranges aranges; + + const llvm::DWARFDebugLine::LineTable *GetLineTable(uint32_t cu_idx) const; + + const DWARFDebugAranges &GetAranges(lldb::addr_t first_code_address); +}; + +static std::unique_ptr<LineTable> +ConvertLineTable(CompileUnit &comp_unit, + const llvm::DWARFDebugLine::LineTable &line_table, + lldb::addr_t first_code_address) { + // FIXME: Rather than parsing the whole line table and then copying it over + // into LLDB, we should explore using a callback to populate the line table + // while we parse to reduce memory usage. + std::vector<LineTable::Sequence> sequences; + // The Sequences view contains only valid line sequences. Don't iterate over + // the Rows directly. + for (const llvm::DWARFDebugLine::Sequence &seq : line_table.Sequences) { + // Ignore line sequences that do not start after the first code address. + // All addresses generated in a sequence are incremental so we only need + // to check the first one of the sequence. + if (seq.LowPC < first_code_address) + continue; + LineTable::Sequence sequence; + for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) { + const llvm::DWARFDebugLine::Row &row = line_table.Rows[idx]; + LineTable::AppendLineEntryToSequence( + sequence, row.Address.Address, row.Line, row.Column, row.File, + row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin, + row.EndSequence); + } + sequences.push_back(std::move(sequence)); + } + + return std::make_unique<LineTable>(&comp_unit, std::move(sequences)); +} + +const llvm::DWARFDebugLine::LineTable * +StandaloneDWARFLineTableInfo::GetLineTable(uint32_t cu_idx) const { + if (cu_idx >= line_tables.size()) + return nullptr; + return &line_tables[cu_idx]; +} + +const DWARFDebugAranges & +StandaloneDWARFLineTableInfo::GetAranges(lldb::addr_t first_code_address) { + llvm::call_once(aranges_once_flag, [&] { + for (uint32_t cu_idx = 0; cu_idx < line_tables.size(); ++cu_idx) { + for (const llvm::DWARFDebugLine::Sequence &seq : + line_tables[cu_idx].Sequences) { + if (seq.isValid() && seq.LowPC >= first_code_address) + aranges.AppendRange(cu_idx, seq.LowPC, seq.HighPC); + } + } + aranges.Sort(/*minimize=*/true); + }); + return aranges; +} + void SymbolFileDWARF::Initialize() { LogChannelDWARF::Initialize(); PluginManager::RegisterPlugin(GetPluginNameStatic(), @@ -628,6 +690,11 @@ uint32_t SymbolFileDWARF::CalculateAbilities() { if (section) section_list = §ion->GetChildren(); + section = + section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true).get(); + if (section) + debug_line_file_size = section->GetFileSize(); + section = section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get(); if (section != nullptr) { @@ -651,11 +718,6 @@ uint32_t SymbolFileDWARF::CalculateAbilities() { return 0; } - section = - section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true) - .get(); - if (section) - debug_line_file_size = section->GetFileSize(); } else { llvm::StringRef symfile_dir = m_objfile_sp->GetFileSpec().GetDirectory(); if (symfile_dir.contains_insensitive(".dsym")) { @@ -684,12 +746,17 @@ uint32_t SymbolFileDWARF::CalculateAbilities() { return 0; } - if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) + if (debug_abbrev_file_size > 0 && debug_info_file_size > 0) { abilities |= CompileUnits | Functions | Blocks | GlobalVariables | LocalVariables | VariableTypes; - if (debug_line_file_size > 0) - abilities |= LineTables; + if (debug_line_file_size > 0) + abilities |= LineTables; + } else if (debug_line_file_size > 0 && + !GetStandaloneLineTableInfo().line_tables.empty()) + // A standalone line table still supplies enough information to create + // compile units, even though there are no DIEs backing them. + abilities |= CompileUnits | LineTables; } return abilities; } @@ -741,9 +808,59 @@ DWARFDebugInfo &SymbolFileDWARF::DebugInfo() { return *m_info; } +StandaloneDWARFLineTableInfo &SymbolFileDWARF::GetStandaloneLineTableInfo() { + llvm::call_once(m_standalone_line_table_once_flag, [&] { + auto info = std::make_unique<StandaloneDWARFLineTableInfo>(); + + // A DWARFUnit implies the presence of .debug_info. Standalone line tables + // deliberately bypass DWARFUnit creation and directly synthesize LLDB + // compile units instead. + if (m_context.getOrLoadDebugInfoData().GetByteSize() != 0) { + m_standalone_line_table_info = std::move(info); + return; + } + + llvm::DWARFDataExtractor line_data = + m_context.getOrLoadLineData().GetAsLLVMDWARF(); + if (line_data.getData().empty()) { + m_standalone_line_table_info = std::move(info); + return; + } + + llvm::DWARFContext &context = m_context.GetAsLLVM(); + llvm::DWARFDebugLine::SectionParser parser(line_data, context, + context.normal_units()); + Log *log = GetLog(DWARFLog::DebugInfo); + while (!parser.done()) { + bool valid = true; + auto recoverable = [&](llvm::Error error) { + LLDB_LOG_ERROR(log, std::move(error), + "SymbolFileDWARF failed to parse standalone line " + "table: {0}"); + }; + auto unrecoverable = [&](llvm::Error error) { + valid = false; + LLDB_LOG_ERROR(log, std::move(error), + "SymbolFileDWARF failed to parse standalone line " + "table: {0}"); + }; + llvm::DWARFDebugLine::LineTable line_table = + parser.parseNext(recoverable, unrecoverable); + if (valid && SupportedVersion(line_table.Prologue.getVersion()) && + !line_table.Prologue.FileNames.empty()) + info->line_tables.push_back(std::move(line_table)); + } + m_standalone_line_table_info = std::move(info); + }); + return *m_standalone_line_table_info; +} + DWARFCompileUnit *SymbolFileDWARF::GetDWARFCompileUnit(CompileUnit *comp_unit) { if (!comp_unit) return nullptr; + if (m_standalone_line_table_info && + comp_unit->GetUserData() == m_standalone_line_table_info.get()) + return nullptr; // The compile unit ID is the index of the DWARF unit. DWARFUnit *dwarf_cu = DebugInfo().GetUnitAtIndex(comp_unit->GetID()); @@ -888,6 +1005,12 @@ std::optional<uint32_t> SymbolFileDWARF::GetDWARFUnitIndex(uint32_t cu_idx) { } uint32_t SymbolFileDWARF::CalculateNumCompileUnits() { + if (!GetDebugMapSymfile()) { + StandaloneDWARFLineTableInfo &standalone = GetStandaloneLineTableInfo(); + if (!standalone.line_tables.empty()) + return standalone.line_tables.size(); + } + BuildCuTranslationTable(); return m_lldb_cu_to_dwarf_unit.empty() ? DebugInfo().GetNumUnits() : m_lldb_cu_to_dwarf_unit.size(); @@ -895,6 +1018,33 @@ uint32_t SymbolFileDWARF::CalculateNumCompileUnits() { CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) { ASSERT_MODULE_LOCK(this); + StandaloneDWARFLineTableInfo &info = GetStandaloneLineTableInfo(); + const llvm::DWARFDebugLine::LineTable *line_table = + GetDebugMapSymfile() ? nullptr : info.GetLineTable(cu_idx); + if (line_table) { + SupportFileList support_files; + ParseSupportFilesFromPrologue(support_files, m_objfile_sp->GetModule(), + line_table->Prologue, + m_objfile_sp->GetFileSpec().GetPathStyle()); + + // DWARF v5 guarantees that file index zero names the primary source file. + // Earlier versions are one-based and have no explicit primary-file field; + // use the first file in the table as the best available fallback. + const size_t primary_file_idx = + line_table->Prologue.getVersion() < 5 ? 1 : 0; + SupportFileNSP primary_file = + support_files.GetSupportFileAtIndex(primary_file_idx); + + ModuleSP module_sp = m_objfile_sp->GetModule(); + if (!module_sp) + return {}; + CompUnitSP cu_sp = std::make_shared<CompileUnit>( + module_sp, m_standalone_line_table_info.get(), primary_file, cu_idx, + eLanguageTypeUnknown, eLazyBoolNo, std::move(support_files)); + SetCompileUnitAtIndex(cu_idx, cu_sp); + return cu_sp; + } + if (std::optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) { if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>( DebugInfo().GetUnitAtIndex(*dwarf_idx))) @@ -1239,6 +1389,20 @@ bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) { if (comp_unit.GetLineTable() != nullptr) return true; + if (m_standalone_line_table_info && + comp_unit.GetUserData() == m_standalone_line_table_info.get()) { + const llvm::DWARFDebugLine::LineTable *line_table = + m_standalone_line_table_info->GetLineTable(comp_unit.GetID()); + if (!line_table) + return false; + if (m_first_code_address == LLDB_INVALID_ADDRESS) + InitializeFirstCodeAddress(); + comp_unit.SetLineTable( + ConvertLineTable(comp_unit, *line_table, m_first_code_address) + .release()); + return true; + } + DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit); if (!dwarf_cu) return false; @@ -1255,32 +1419,8 @@ bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) { if (!line_table) return false; - // FIXME: Rather than parsing the whole line table and then copying it over - // into LLDB, we should explore using a callback to populate the line table - // while we parse to reduce memory usage. - std::vector<LineTable::Sequence> sequences; - // The Sequences view contains only valid line sequences. Don't iterate over - // the Rows directly. - for (const llvm::DWARFDebugLine::Sequence &seq : line_table->Sequences) { - // Ignore line sequences that do not start after the first code address. - // All addresses generated in a sequence are incremental so we only need - // to check the first one of the sequence. Check the comment at the - // m_first_code_address declaration for more details on this. - if (seq.LowPC < m_first_code_address) - continue; - LineTable::Sequence sequence; - for (unsigned idx = seq.FirstRowIndex; idx < seq.LastRowIndex; ++idx) { - const llvm::DWARFDebugLine::Row &row = line_table->Rows[idx]; - LineTable::AppendLineEntryToSequence( - sequence, row.Address.Address, row.Line, row.Column, row.File, - row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin, - row.EndSequence); - } - sequences.push_back(std::move(sequence)); - } - std::unique_ptr<LineTable> line_table_up = - std::make_unique<LineTable>(&comp_unit, std::move(sequences)); + ConvertLineTable(comp_unit, *line_table, m_first_code_address); if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) { // We have an object file that has a line table with addresses that are not @@ -2151,6 +2291,33 @@ uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr, eSymbolContextLineEntry | eSymbolContextVariable)) { lldb::addr_t file_vm_addr = so_addr.GetFileAddress(); + StandaloneDWARFLineTableInfo &standalone = GetStandaloneLineTableInfo(); + if (!GetDebugMapSymfile() && !standalone.line_tables.empty()) { + if (m_first_code_address == LLDB_INVALID_ADDRESS) + InitializeFirstCodeAddress(); + const DWARFDebugAranges &aranges = + standalone.GetAranges(m_first_code_address); + const dw_offset_t cu_idx = aranges.FindAddress(file_vm_addr); + if (cu_idx == DW_INVALID_OFFSET) + return 0; + + CompUnitSP cu_sp = GetCompileUnitAtIndex(cu_idx); + sc.comp_unit = cu_sp.get(); + if (!sc.comp_unit) + return 0; + resolved |= eSymbolContextCompUnit; + + if (resolve_scope & eSymbolContextLineEntry) { + if (LineTable *line_table = sc.comp_unit->GetLineTable()) { + Address exe_so_addr(so_addr); + if (FixupAddress(exe_so_addr) && + line_table->FindLineEntryByAddress(exe_so_addr, sc.line_entry)) + resolved |= eSymbolContextLineEntry; + } + } + return resolved; + } + DWARFDebugInfo &debug_info = DebugInfo(); const DWARFDebugAranges &aranges = debug_info.GetCompileUnitAranges(); const dw_offset_t cu_offset = aranges.FindAddress(file_vm_addr); @@ -2251,7 +2418,7 @@ uint32_t SymbolFileDWARF::ResolveSymbolContext( if (resolve_scope & eSymbolContextCompUnit) { for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus; ++cu_idx) { - CompileUnit *dc_cu = ParseCompileUnitAtIndex(cu_idx).get(); + CompileUnit *dc_cu = GetCompileUnitAtIndex(cu_idx).get(); if (!dc_cu) continue; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 9879fc4fe922c..a1f539e0fd890 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -60,6 +60,7 @@ class DWARFTypeUnit; class SymbolFileDWARFDebugMap; class SymbolFileDWARFDwo; class SymbolFileDWARFDwp; +struct StandaloneDWARFLineTableInfo; #define DIE_IS_BEING_PARSED ((lldb_private::Type *)1) @@ -516,6 +517,8 @@ class SymbolFileDWARF : public SymbolFileCommon { void BuildCuTranslationTable(); std::optional<uint32_t> GetDWARFUnitIndex(uint32_t cu_idx); + StandaloneDWARFLineTableInfo &GetStandaloneLineTableInfo(); + void FindDwpSymbolFile(); const SupportFileList *GetTypeUnitSupportFiles(DWARFTypeUnit &tu); @@ -538,6 +541,9 @@ class SymbolFileDWARF : public SymbolFileCommon { llvm::once_flag m_info_once_flag; std::unique_ptr<DWARFDebugInfo> m_info; + llvm::once_flag m_standalone_line_table_once_flag; + std::unique_ptr<StandaloneDWARFLineTableInfo> m_standalone_line_table_info; + std::unique_ptr<llvm::DWARFDebugAbbrev> m_abbr; std::unique_ptr<GlobalVariableMap> m_global_aranges_up; diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp index 0ef139b1d453a..d993e780a04cd 100644 --- a/lldb/source/Symbol/SymbolFile.cpp +++ b/lldb/source/Symbol/SymbolFile.cpp @@ -23,6 +23,7 @@ #include "lldb/lldb-private.h" #include <future> +#include <utility> using namespace lldb_private; using namespace lldb; @@ -30,6 +31,13 @@ using namespace lldb; char SymbolFile::ID; char SymbolFileCommon::ID; +static std::pair<bool, uint32_t> GetSymbolFileRank(uint32_t abilities) { + constexpr uint32_t detailed_info = + SymbolFile::LineTables | SymbolFile::Blocks | SymbolFile::LocalVariables | + SymbolFile::VariableTypes; + return {static_cast<bool>(abilities & detailed_info), abilities}; +} + void SymbolFile::PreloadSymbols() { // No-op for most implementations. } @@ -65,7 +73,8 @@ SymbolFile *SymbolFile::FindPlugin(ObjectFileSP objfile_sp) { if (curr_symfile_up) { const uint32_t sym_file_abilities = curr_symfile_up->GetAbilities(); - if (sym_file_abilities > best_symfile_abilities) { + if (GetSymbolFileRank(sym_file_abilities) > + GetSymbolFileRank(best_symfile_abilities)) { best_symfile_abilities = sym_file_abilities; best_symfile_up.reset(curr_symfile_up.release()); // If any symbol file parser has all of the abilities, then we should diff --git a/lldb/test/API/python_api/symbol-context/standalone-debug-line/TestStandaloneDebugLine.py b/lldb/test/API/python_api/symbol-context/standalone-debug-line/TestStandaloneDebugLine.py new file mode 100644 index 0000000000000..13d6db338b019 --- /dev/null +++ b/lldb/test/API/python_api/symbol-context/standalone-debug-line/TestStandaloneDebugLine.py @@ -0,0 +1,63 @@ +import os + +import lldb +from lldbsuite.test import configuration +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * + + +class StandaloneDebugLineTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipUnlessPlatform(["linux"]) + @skipIfLLVMTargetMissing("X86") + def test(self): + object_path = self.getBuildArtifact("line.o") + self.runBuildCommand( + [ + os.path.join(configuration.llvm_tools_dir, "llvm-mc"), + "-triple=x86_64-pc-linux", + "-filetype=obj", + "-dwarf-version=2", + self.getSourcePath("main.s"), + "-o", + object_path, + ] + ) + + target = self.createTestTarget(object_path) + + # The regular symbol table advertises functions, while the standalone + # line table must make SymbolFileDWARF the preferred reader. + module = target.GetModuleAtIndex(0) + self.assertTrue(module.FindSection(".debug_line").IsValid()) + self.assertFalse(module.FindSection(".debug_info").IsValid()) + self.assertFalse(module.FindSection(".debug_abbrev").IsValid()) + self.assertTrue(module.FindSection(".symtab").IsValid()) + self.assertEqual(module.GetNumCompileUnits(), 1) + + # An address must resolve to its symbol, synthetic compile unit, and + # line entry. It must not manufacture a DWARF function. + symbol = module.FindSymbol("foo", lldb.eSymbolTypeCode) + self.assertTrue(symbol.IsValid()) + + address = symbol.GetStartAddress() + self.assertTrue(address.IsValid()) + self.assertEqual(address.GetSymbol().GetName(), "foo") + self.assertFalse(address.GetFunction().IsValid()) + + compile_unit = address.GetCompileUnit() + self.assertTrue(compile_unit.IsValid()) + self.assertEqual(compile_unit.GetFileSpec().GetFilename(), "standalone.c") + + line_entry = address.GetLineEntry() + self.assertTrue(line_entry.IsValid()) + self.assertEqual(line_entry.GetFileSpec().GetFilename(), "standalone.c") + self.assertEqual(line_entry.GetLine(), 42) + self.assertEqual(line_entry.GetColumn(), 7) + + # Source-to-address lookup must use the same standalone line table. + breakpoint = target.BreakpointCreateByLocation("standalone.c", 42) + self.assertEqual(breakpoint.GetNumLocations(), 1) + breakpoint_address = breakpoint.GetLocationAtIndex(0).GetAddress() + self.assertEqual(breakpoint_a... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/217932 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
