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,190 @@ +# RUN: yaml2obj --docnum=1 %s -o - \ +# RUN: | llvm-dwarfdump --show-sources - \ +# RUN: | FileCheck --check-prefix=CU-NAME-CHECK %s + +# CU-NAME-CHECK: name.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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: name.c + +# RUN: yaml2obj --docnum=2 %s -o - \ +# RUN: | llvm-dwarfdump --show-sources - \ +# RUN: | FileCheck --allow-empty --check-prefix=CU-COMP-DIR-CHECK %s + +# CU-COMP-DIR-CHECK-NOT: /comp/dir + +--- !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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /comp/dir + +# RUN: yaml2obj --docnum=3 %s -o - \ +# RUN: | llvm-dwarfdump --show-sources - \ +# RUN: | FileCheck --check-prefix=CU-COMP-DIR-REL-NAME-CHECK %s + +# CU-COMP-DIR-REL-NAME-CHECK: /comp/dir/name.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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: name.c + - CStr: /comp/dir + +# RUN: yaml2obj --docnum=4 %s -o - \ +# RUN: | llvm-dwarfdump --show-sources - \ +# RUN: | FileCheck --check-prefix=CU-COMP-DIR-ABS-NAME-CHECK %s + +# CU-COMP-DIR-ABS-NAME-CHECK: /abs/name.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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /abs/name.c + - CStr: comp/dir + +# RUN: yaml2obj --docnum=5 %s -o - \ +# RUN: | llvm-dwarfdump --show-sources - \ +# RUN: | FileCheck --check-prefix=LINE-TABLE-CHECK %s + +# LINE-TABLE-CHECK: /comp/dir/name.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/dir] + Files: + - Name: name.c + DirIdx: 1 + ModTime: 0 + Length: 0 + +# RUN: yaml2obj --docnum=6 %s -o - \ +# RUN: | llvm-dwarfdump --show-sources - \ +# RUN: | FileCheck --check-prefix=CU-LINE-TABLE-CHECK %s + +# CU-LINE-TABLE-CHECK: /comp/dir/name.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 + debug_info: + - Version: 4 + Entries: + - AbbrCode: 1 + Values: + - CStr: /comp + - Value: 0 + debug_line: + - Version: 4 + MinInstLength: 1 + MaxOpsPerInst: 1 + DefaultIsStmt: 1 + LineBase: 0 + LineRange: 0 + OpcodeBase: 1 + IncludeDirs: [dir] + Files: + - Name: name.c + DirIdx: 1 + ModTime: 0 + Length: 0 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,89 @@ return true; } +// Collect all absolute sources referenced from the given line table, scoped to +// the given CU compilation directory. If the CU directory is unknown, it may be +// empty. If a full absolute path cannot be determined, then they are excluded. +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); + // If the CU compilation directory information is unknown, this may not have + // produced an absolute path. + if (sys::path::is_absolute(Path, sys::path::Style::posix) || + sys::path::is_absolute(Path, sys::path::Style::windows)) + 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; + + for (const auto &CU : DICtx.compile_units()) { + // 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 lien table are + // relative. + const auto *LT = DICtx.getLineTableForUnit(CU.get()); + StringRef CompDir = CU->getCompilationDir(); + if (LT) + Result &= collectLineTableSources(*LT, CompDir, Sources); + + // Collect paths from the filename and compilation directory of the CU + // itself. This information isn't included in the line table in DWARF v4 and + // ealier. + const char *Name = CU->getUnitDIE().getShortName(); + if (!Name) + continue; + SmallString<64> AbsName; + if (!CompDir.empty() && sys::path::is_relative(Name)) + AbsName = CompDir; + sys::path::append(AbsName, Name); + Sources.push_back(std::string(AbsName)); + } + + // Walk the line table and extract out any referenced absolute paths. This + // includes line information for non CU sections (e.g., macros), as well as + // handling if the line information is present, but CUs aren't (allowed in + // DWARF v5). + 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); + // The CU compilation directory isn't known, so the line table include + // directory must be absolute for any paths to be extracted. + 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 (const auto &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 +767,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());