diff --git a/llvm/docs/CommandGuide/llvm-dwarfdump.rst b/llvm/docs/CommandGuide/llvm-dwarfdump.rst --- a/llvm/docs/CommandGuide/llvm-dwarfdump.rst +++ b/llvm/docs/CommandGuide/llvm-dwarfdump.rst @@ -115,6 +115,11 @@ as machine-readable single-line JSON output. The output format is described in the section below (:ref:`stats-format`). +.. option:: --show-sources + + Print all source files mentioned in the debug information. Absolute + paths are given whenever possible. + .. option:: --summarize-types Abbreviate the description of type unit entries. diff --git a/llvm/test/tools/llvm-dwarfdump/X86/sources.test b/llvm/test/tools/llvm-dwarfdump/X86/sources.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/sources.test @@ -0,0 +1,420 @@ +# RUN: yaml2obj --docnum=1 %s -o %t.name.o +# RUN: llvm-dwarfdump --show-sources %t.name.o > %t.name.dwarfdump +# RUN: FileCheck --check-prefix=CU-NAME \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.name.dwarfdump %s + +# CU-NAME: first.c +# CU-NAME: second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: first.c + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: second.c + +# RUN: yaml2obj --docnum=2 %s -o %t.comp-dir.o +# RUN: llvm-dwarfdump --show-sources %t.comp-dir.o > %t.comp-dir.dwarfdump 2> %t.comp-dir.err +# RUN: count 0 < %t.comp-dir.dwarfdump +# RUN: FileCheck --check-prefix=CU-COMP-DIR \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.comp-dir.err %s + +# CU-COMP-DIR: warning: {{.*}}: missing name for compilation unit. +# CU-COMP-DIR: warning: {{.*}}: missing name for compilation unit. + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /comp/first + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /comp/second + +# RUN: yaml2obj --docnum=3 %s -o %t.comp-dir-rel-name.o +# RUN: llvm-dwarfdump --show-sources %t.comp-dir-rel-name.o > %t.comp-dir-rel-name.dwarfdump +# RUN: FileCheck --check-prefix=CU-COMP-DIR-REL-NAME \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.comp-dir-rel-name.dwarfdump %s + +# CU-COMP-DIR-REL-NAME: /comp/first/first.c +# CU-COMP-DIR-REL-NAME: /comp/second/second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: first.c + - CStr: /comp/first + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: second.c + - CStr: /comp/second + +# RUN: yaml2obj --docnum=4 %s -o %t.comp-dir-abs-name-posix.o +# RUN: llvm-dwarfdump --show-sources %t.comp-dir-abs-name-posix.o > %t.comp-dir-abs-name-posix.dwarfdump +# RUN: FileCheck --check-prefix=CU-COMP-DIR-ABS-NAME-POSIX \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.comp-dir-abs-name-posix.dwarfdump %s + +# CU-COMP-DIR-ABS-NAME-POSIX: /abs/first.c +# CU-COMP-DIR-ABS-NAME-POSIX: /abs/second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /abs/first.c + - CStr: /comp/dir + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /abs/second.c + - CStr: /comp/dir + +# RUN: yaml2obj --docnum=5 %s -o %t.comp-dir-abs-name-windows.o +# RUN: llvm-dwarfdump --show-sources %t.comp-dir-abs-name-windows.o > %t.comp-dir-abs-name-windows.dwarfdump +# RUN: FileCheck --check-prefix=CU-COMP-DIR-ABS-NAME-WINDOWS \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.comp-dir-abs-name-windows.dwarfdump %s + +# CU-COMP-DIR-ABS-NAME-WINDOWS: C:\abs\first.c +# CU-COMP-DIR-ABS-NAME-WINDOWS: C:\abs\second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_string + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: C:\abs\first.c + - CStr: C:\comp\dir + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: C:\abs\second.c + - CStr: C:\comp\dir + +# RUN: yaml2obj --docnum=6 %s -o %t.line-table-abs.o +# RUN: llvm-dwarfdump --show-sources %t.line-table-abs.o > %t.line-table-abs.dwarfdump +# RUN: FileCheck --check-prefix=LINE-TABLE-ABS \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.line-table-abs.dwarfdump %s + +# LINE-TABLE-ABS: /comp/first/first.c +# LINE-TABLE-ABS: /comp/second/second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_line: + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + IncludeDirs: [/comp/first] + Files: + - Name: first.c + DirIdx: 1 + ModTime: 0 + Length: 0 + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + IncludeDirs: [/comp/second] + Files: + - Name: second.c + DirIdx: 1 + ModTime: 0 + Length: 0 + +# RUN: yaml2obj --docnum=7 %s -o %t.line-table-rel.o +# RUN: llvm-dwarfdump --show-sources %t.line-table-rel.o > %t.line-table-rel.dwarfdump +# RUN: FileCheck --check-prefix=LINE-TABLE-REL \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.line-table-rel.dwarfdump %s + +# LINE-TABLE-REL: first.c +# LINE-TABLE-REL: second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_line: + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + Files: + - Name: first.c + DirIdx: 0 + ModTime: 0 + Length: 0 + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + Files: + - Name: second.c + DirIdx: 0 + ModTime: 0 + Length: 0 + +# RUN: yaml2obj --docnum=8 %s -o %t.cu-line-table.o +# RUN: llvm-dwarfdump --show-sources %t.cu-line-table.o > %t.cu-line-table.dwarfdump +# RUN: FileCheck --check-prefix=CU-LINE-TABLE \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.cu-line-table.dwarfdump %s + +# CU-LINE-TABLE: /first/first/first.c +# CU-LINE-TABLE: /second/second/second.c + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_comp_dir + Form: DW_FORM_string + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /first + - Value: 0 + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /second + - Value: 0x23 + debug_line: + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + IncludeDirs: [first] + Files: + - Name: first.c + DirIdx: 1 + ModTime: 0 + Length: 0 + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + IncludeDirs: [second] + Files: + - Name: second.c + DirIdx: 1 + ModTime: 0 + Length: 0 + +# RUN: llvm-dwarfdump --show-sources %t.line-table-rel.o %t.cu-line-table.o > %t.multiple-files.dwarfdump +# RUN: FileCheck --check-prefix=MULTIPLE-FILES \ +# RUN: --match-full-lines --implicit-check-not={{.}} --input-file %t.multiple-files.dwarfdump %s + +# MULTIPLE-FILES: first.c +# MULTIPLE-FILES: second.c +# MULTIPLE-FILES: /first/first/first.c +# MULTIPLE-FILES: /second/second/second.c + +# RUN: yaml2obj --docnum=9 %s -o %t.no-filenames.o +# RUN: llvm-dwarfdump --show-sources %t.no-filenames.o > %t.no-filenames.dwarfdump +# RUN: count 0 < %t.no-filenames.dwarfdump + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +DWARF: + debug_line: + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + IncludeDirs: [] + +# RUN: llvm-mc -triple x86_64-pc-linux %S/Inputs/debug_line_malformed.s -filetype=obj -o %t-malformed.o +# RUN: not llvm-dwarfdump --show-sources %t-malformed.o diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp --- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -26,6 +26,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Regex.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" @@ -247,6 +248,10 @@ cl::desc("Show the sizes of all debug sections, " "expressed in bytes."), cat(DwarfDumpCategory)); +static cl::opt + ShowSources("show-sources", + cl::desc("Show the sources across all compilation units."), + cat(DwarfDumpCategory)); static opt Verify("verify", desc("Verify the DWARF debug info."), cat(DwarfDumpCategory)); static opt Quiet("quiet", desc("Use with -verify to not emit to STDOUT."), @@ -466,6 +471,87 @@ return true; } +// Collect all sources referenced from the given line table, scoped to the given +// CU compilation directory. +static bool collectLineTableSources(const DWARFDebugLine::LineTable <, + StringRef CompDir, + std::vector &Sources) { + bool Result = true; + llvm::Optional LastIndex = LT.getLastValidFileIndex(); + for (uint64_t I = LT.hasFileAtIndex(0) ? 0 : 1, + E = LastIndex ? *LastIndex + 1 : 0; + I < E; ++I) { + std::string Path; + Result &= LT.getFileNameByIndex( + I, CompDir, DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + Path); + Sources.push_back(std::move(Path)); + } + return Result; +} + +static bool collectObjectSources(ObjectFile &Obj, DWARFContext &DICtx, + const Twine &Filename, raw_ostream &OS) { + bool Result = true; + std::vector Sources; + + bool HasCompileUnits = false; + for (const auto &CU : DICtx.compile_units()) { + HasCompileUnits = true; + // Extract paths from the line table for this CU. This allows combining the + // compilation directory with the line information, in case both the include + // directory and file names in the line table are relative. + const DWARFDebugLine::LineTable *LT = DICtx.getLineTableForUnit(CU.get()); + StringRef CompDir = CU->getCompilationDir(); + if (LT) { + Result &= collectLineTableSources(*LT, CompDir, Sources); + } else { + // Since there's no line table for this CU, collect the name from the CU + // itself. + const char *Name = CU->getUnitDIE().getShortName(); + if (!Name) { + WithColor::warning() + << Filename << ": missing name for compilation unit.\n"; + continue; + } + SmallString<64> AbsName; + if (sys::path::is_relative(Name, sys::path::Style::posix) && + sys::path::is_relative(Name, sys::path::Style::windows)) + AbsName = CompDir; + sys::path::append(AbsName, Name); + Sources.push_back(std::string(AbsName)); + } + } + + if (!HasCompileUnits) { + // Since there's no compile units available, walk the line tables and + // extract out any referenced paths. + DWARFDataExtractor LineData(DICtx.getDWARFObj(), + DICtx.getDWARFObj().getLineSection(), + DICtx.isLittleEndian(), 0); + DWARFDebugLine::SectionParser Parser(LineData, DICtx, DICtx.normal_units()); + while (!Parser.done()) { + const auto RecoverableErrorHandler = [&](Error Err) { + Result = false; + WithColor::defaultErrorHandler(std::move(Err)); + }; + void (*UnrecoverableErrorHandler)(Error Err) = error; + + DWARFDebugLine::LineTable LT = + Parser.parseNext(RecoverableErrorHandler, UnrecoverableErrorHandler); + Result &= collectLineTableSources(LT, /*CompDir=*/"", Sources); + } + } + + // Dedup and order the sources. + llvm::sort(Sources.begin(), Sources.end()); + Sources.erase(std::unique(Sources.begin(), Sources.end()), Sources.end()); + + for (StringRef Name : Sources) + OS << Name << "\n"; + return Result; +} + static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, const Twine &Filename, raw_ostream &OS) { logAllUnhandledErrors(DICtx.loadRegisterInfo(Obj), errs(), @@ -679,6 +765,9 @@ } else if (ShowSectionSizes) { for (auto Object : Objects) Success &= handleFile(Object, collectObjectSectionSizes, OutputFile.os()); + } else if (ShowSources) { + for (auto Object : Objects) + Success &= handleFile(Object, collectObjectSources, OutputFile.os()); } else { for (auto Object : Objects) Success &= handleFile(Object, dumpObjectFile, OutputFile.os());