diff --git a/include/llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h b/include/llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h index c9b062717ba..7484af66310 100644 --- a/include/llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h +++ b/include/llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h @@ -19,7 +19,7 @@ namespace llvm { namespace codeview { -class DebugInlineeLinesSubsectionsRef; +class DebugInlineeLinesSubsectionRef; class DebugChecksumsSubsection; enum class InlineeLinesSignature : uint32_t { diff --git a/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h b/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h index 4fe5d450e2e..708b317164f 100644 --- a/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h +++ b/include/llvm/DebugInfo/CodeView/StringsAndChecksums.h @@ -36,6 +36,8 @@ public: StringsAndChecksumsRef(const DebugStringTableSubsectionRef &Strings, const DebugChecksumsSubsectionRef &Checksums); + void setChecksums(const DebugChecksumsSubsectionRef &CS); + template void initialize(T &&FragmentRange) { for (const DebugSubsectionRecord &R : FragmentRange) { if (Strings && Checksums) diff --git a/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h b/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h index 1e46f0032b1..c744696ae25 100644 --- a/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h +++ b/include/llvm/DebugInfo/PDB/Native/ModuleDebugStream.h @@ -31,7 +31,7 @@ class ModuleDebugStreamRef { public: ModuleDebugStreamRef(const DbiModuleDescriptor &Module, std::unique_ptr Stream); - ModuleDebugStreamRef(ModuleDebugStreamRef &&Other); + ModuleDebugStreamRef(ModuleDebugStreamRef &&Other) = default; ~ModuleDebugStreamRef(); Error reload(); @@ -45,6 +45,8 @@ public: return SymbolsSubstream; } + ModuleDebugStreamRef &operator=(ModuleDebugStreamRef &&Other) = default; + llvm::iterator_range subsections() const; bool hasDebugSubsections() const; @@ -59,7 +61,7 @@ private: uint32_t Signature; - std::unique_ptr Stream; + std::shared_ptr Stream; codeview::CVSymbolArray SymbolsSubstream; BinaryStreamRef C11LinesSubstream; diff --git a/include/llvm/DebugInfo/PDB/Native/PDBFile.h b/include/llvm/DebugInfo/PDB/Native/PDBFile.h index 3bed67141c5..4d3c569c3cd 100644 --- a/include/llvm/DebugInfo/PDB/Native/PDBFile.h +++ b/include/llvm/DebugInfo/PDB/Native/PDBFile.h @@ -108,6 +108,8 @@ public: bool hasPDBTpiStream() const; bool hasPDBStringTable(); + uint32_t getPointerSize(); + private: Expected> safelyCreateIndexedStream(const msf::MSFLayout &Layout, diff --git a/lib/DebugInfo/CodeView/StringsAndChecksums.cpp b/lib/DebugInfo/CodeView/StringsAndChecksums.cpp index ccd9aba45ea..928bf8c94f7 100644 --- a/lib/DebugInfo/CodeView/StringsAndChecksums.cpp +++ b/lib/DebugInfo/CodeView/StringsAndChecksums.cpp @@ -36,6 +36,13 @@ void StringsAndChecksumsRef::initializeStrings( Strings = OwnedStrings.get(); } +void StringsAndChecksumsRef::setChecksums( + const DebugChecksumsSubsectionRef &CS) { + OwnedChecksums = llvm::make_unique(); + *OwnedChecksums = CS; + Checksums = OwnedChecksums.get(); +} + void StringsAndChecksumsRef::initializeChecksums( const DebugSubsectionRecord &FCR) { assert(FCR.kind() == DebugSubsectionKind::FileChecksums); diff --git a/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp b/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp index 1ddad45df34..4186f2eb6ba 100644 --- a/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp +++ b/lib/DebugInfo/PDB/Native/ModuleDebugStream.cpp @@ -30,15 +30,6 @@ ModuleDebugStreamRef::ModuleDebugStreamRef( std::unique_ptr Stream) : Mod(Module), Stream(std::move(Stream)) {} -ModuleDebugStreamRef::ModuleDebugStreamRef(ModuleDebugStreamRef &&Other) - : Mod(Other.Mod), Signature(Other.Signature), - Stream(std::move(Other.Stream)), - SymbolsSubstream(std::move(Other.SymbolsSubstream)), - C11LinesSubstream(std::move(Other.C11LinesSubstream)), - C13LinesSubstream(std::move(Other.C13LinesSubstream)), - GlobalRefsSubstream(std::move(Other.GlobalRefsSubstream)), - Subsections(std::move(Other.Subsections)) {} - ModuleDebugStreamRef::~ModuleDebugStreamRef() = default; Error ModuleDebugStreamRef::reload() { diff --git a/lib/DebugInfo/PDB/Native/PDBFile.cpp b/lib/DebugInfo/PDB/Native/PDBFile.cpp index 1254e23c73e..a9597cdf4c4 100644 --- a/lib/DebugInfo/PDB/Native/PDBFile.cpp +++ b/lib/DebugInfo/PDB/Native/PDBFile.cpp @@ -363,6 +363,16 @@ Expected PDBFile::getStringTable() { return *Strings; } +uint32_t PDBFile::getPointerSize() { + auto DbiS = getPDBDbiStream(); + if (!DbiS) + return 0; + PDB_Machine Machine = DbiS->getMachineType(); + if (Machine == PDB_Machine::Amd64) + return 8; + return 4; +} + bool PDBFile::hasPDBDbiStream() const { return StreamDBI < getNumStreams(); } bool PDBFile::hasPDBGlobalsStream() { diff --git a/test/DebugInfo/PDB/pdbdump-headers.test b/test/DebugInfo/PDB/pdbdump-headers.test index a5acdf33d4d..fa9a25108fa 100644 --- a/test/DebugInfo/PDB/pdbdump-headers.test +++ b/test/DebugInfo/PDB/pdbdump-headers.test @@ -58,6 +58,17 @@ ALL-NEXT: ============================================================ ALL-NEXT: Mod 0000 | `d:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj`: ALL-NEXT: - (MD5: A0A5BD0D3ECD93FC29D19DE826FBF4BC) d:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp ALL-NEXT: Mod 0001 | `* Linker *`: +ALL: Lines +ALL-NEXT: ============================================================ +ALL-NEXT: Mod 0000 | `d:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj`: +ALL-NEXT: d:\src\llvm\test\debuginfo\pdb\inputs\empty.cpp (MD5: A0A5BD0D3ECD93FC29D19DE826FBF4BC) +ALL-NEXT: 0001:00000010-0000001A, line/addr entries = 3 +ALL-NEXT: 5 00000010 6 00000013 7 00000018 +ALL: Mod 0001 | `* Linker *`: +ALL: Inlinee Lines +ALL-NEXT: ============================================================ +ALL-NEXT: Mod 0000 | `d:\src\llvm\test\DebugInfo\PDB\Inputs\empty.obj`: +ALL-NEXT: Mod 0001 | `* Linker *`: ALL: Types (TPI Stream) ALL-NEXT: ============================================================ ALL-NEXT: Showing 75 records diff --git a/tools/llvm-pdbutil/RawOutputStyle.cpp b/tools/llvm-pdbutil/RawOutputStyle.cpp index 470043c44df..719e204d964 100644 --- a/tools/llvm-pdbutil/RawOutputStyle.cpp +++ b/tools/llvm-pdbutil/RawOutputStyle.cpp @@ -108,6 +108,16 @@ Error RawOutputStyle::dump() { return EC; } + if (opts::raw::DumpLines) { + if (auto EC = dumpLines()) + return EC; + } + + if (opts::raw::DumpInlineeLines) { + if (auto EC = dumpInlineeLines()) + return EC; + } + if (opts::raw::DumpTypes || opts::raw::DumpTypeExtras) { if (auto EC = dumpTpiStream(StreamTPI)) return EC; @@ -318,36 +328,6 @@ static Expected getModuleDebugStream(PDBFile &File, return std::move(ModS); } -static StringMap loadChecksums(PDBFile &File, - uint32_t ModuleIndex) { - StringMap Result; - auto MDS = getModuleDebugStream(File, ModuleIndex); - if (!MDS) { - consumeError(MDS.takeError()); - return Result; - } - - auto CS = MDS->findChecksumsSubsection(); - if (!CS) { - consumeError(CS.takeError()); - return Result; - } - - auto Strings = File.getStringTable(); - if (!Strings) { - consumeError(Strings.takeError()); - return Result; - } - - for (const auto &Entry : *CS) { - auto S = Strings->getStringForID(Entry.FileNameOffset); - if (!S) - continue; - Result[*S] = Entry; - } - return Result; -} - static std::string formatChecksumKind(FileChecksumKind Kind) { switch (Kind) { RETURN_CASE(FileChecksumKind, None, "None"); @@ -358,6 +338,120 @@ static std::string formatChecksumKind(FileChecksumKind Kind) { return formatUnknownEnum(Kind); } +template +static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, + Callback Fn) { + AutoIndent Indent(P); + if (!File.hasPDBDbiStream()) { + P.formatLine("DBI Stream not present"); + return; + } + + ExitOnError Err("Unexpected error processing modules"); + + auto &Stream = Err(File.getPDBDbiStream()); + + const DbiModuleList &Modules = Stream.modules(); + uint32_t Count = Modules.getModuleCount(); + uint32_t Digits = NumDigits(Count); + for (uint32_t I = 0; I < Count; ++I) { + auto Modi = Modules.getModuleDescriptor(I); + P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), + Modi.getModuleName()); + + AutoIndent Indent2(P, IndentLevel); + Fn(I, Modi); + } +} + +namespace { +class StringsAndChecksumsPrinter { + const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { + ExitOnError Err("Unexpected error processing modules"); + return Err(File.getStringTable()).getStringTable(); + } + + template + void formatInternal(LinePrinter &Printer, bool Append, + Args &&... args) const { + if (Append) + Printer.format(std::forward(args)...); + else + Printer.formatLine(std::forward(args)...); + } + +public: + StringsAndChecksumsPrinter(PDBFile &File, uint32_t Modi) + : Records(extractStringTable(File)) { + auto MDS = getModuleDebugStream(File, Modi); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + DebugStream = llvm::make_unique(std::move(*MDS)); + Records.initialize(MDS->subsections()); + if (Records.hasChecksums()) { + for (const auto &Entry : Records.checksums()) { + auto S = Records.strings().getString(Entry.FileNameOffset); + if (!S) + continue; + ChecksumsByFile[*S] = Entry; + } + } + } + + Expected getNameFromStringTable(uint32_t Offset) const { + return Records.strings().getString(Offset); + } + + void formatFromFileName(LinePrinter &Printer, StringRef File, + bool Append = false) const { + auto FC = ChecksumsByFile.find(File); + if (FC == ChecksumsByFile.end()) { + formatInternal(Printer, Append, "- (no checksum) {0}", File); + return; + } + + formatInternal(Printer, Append, "- ({0}: {1}) {2}", + formatChecksumKind(FC->getValue().Kind), + toHex(FC->getValue().Checksum), File); + } + + void formatFromChecksumsOffset(LinePrinter &Printer, uint32_t Offset, + bool Append = false) const { + if (!Records.hasChecksums()) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + return; + } + + auto Iter = Records.checksums().getArray().at(Offset); + if (Iter == Records.checksums().getArray().end()) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + return; + } + + uint32_t FO = Iter->FileNameOffset; + auto ExpectedFile = getNameFromStringTable(FO); + if (!ExpectedFile) { + formatInternal(Printer, Append, "(unknown file name offset {0})", Offset); + consumeError(ExpectedFile.takeError()); + return; + } + if (Iter->Kind == FileChecksumKind::None) { + formatInternal(Printer, Append, "{0} (no checksum)", *ExpectedFile); + } else { + formatInternal(Printer, Append, "{0} ({1}: {2})", *ExpectedFile, + formatChecksumKind(Iter->Kind), toHex(Iter->Checksum)); + } + } + + std::unique_ptr DebugStream; + StringsAndChecksumsRef Records; + StringMap ChecksumsByFile; +}; +} // namespace + Error RawOutputStyle::dumpModules() { printHeader(P, "Modules"); @@ -389,34 +483,143 @@ Error RawOutputStyle::dumpModules() { Error RawOutputStyle::dumpModuleFiles() { printHeader(P, "Files"); - AutoIndent Indent(P); - if (!File.hasPDBDbiStream()) { - P.formatLine("DBI Stream not present"); - return Error::success(); - } - ExitOnError Err("Unexpected error processing modules"); - auto &Stream = Err(File.getPDBDbiStream()); + iterateModules(File, P, 11, + [this, &Err](uint32_t I, const DbiModuleDescriptor &Modi) { + auto &Stream = Err(File.getPDBDbiStream()); + StringsAndChecksumsPrinter Strings(File, I); - const DbiModuleList &Modules = Stream.modules(); - uint32_t Count = Modules.getModuleCount(); - uint32_t Digits = NumDigits(Count); - for (uint32_t I = 0; I < Count; ++I) { - auto Modi = Modules.getModuleDescriptor(I); - P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, Digits), - Modi.getModuleName()); - StringMap CS = loadChecksums(File, I); - for (const auto &F : Modules.source_files(I)) { - auto FC = CS.find(F); - if (FC == CS.end()) - P.formatLine(" - (no checksum) {0}", F); + const DbiModuleList &Modules = Stream.modules(); + for (const auto &F : Modules.source_files(I)) { + Strings.formatFromFileName(P, F); + } + }); + return Error::success(); +} + +static void typesetLinesAndColumns(PDBFile &File, LinePrinter &P, + uint32_t Start, const LineColumnEntry &E) { + const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number + uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; + + // Let's try to keep it under 100 characters + constexpr uint32_t kMaxRowLength = 100; + // At least 3 spaces between columns. + uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); + uint32_t ItemsLeft = E.LineNumbers.size(); + auto LineIter = E.LineNumbers.begin(); + while (ItemsLeft != 0) { + uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); + for (uint32_t I = 0; I < RowColumns; ++I) { + LineInfo Line(LineIter->Flags); + std::string LineStr; + if (Line.isAlwaysStepInto()) + LineStr = "ASI"; + else if (Line.isNeverStepInto()) + LineStr = "NSI"; else - P.formatLine(" - ({0}: {1}) {2}", - formatChecksumKind(FC->getValue().Kind), - toHex(FC->getValue().Checksum), F); + LineStr = utostr(Line.getStartLine()); + char Statement = Line.isStatement() ? ' ' : '!'; + P.format("{0} {1:X-} {2} ", + fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), + fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), + Statement); + ++LineIter; + --ItemsLeft; } + P.NewLine(); } +} + +Error RawOutputStyle::dumpLines() { + printHeader(P, "Lines"); + ExitOnError Err("Unexpected error processing modules"); + + iterateModules( + File, P, 4, [this](uint32_t I, const DbiModuleDescriptor &Modi) { + + auto MDS = getModuleDebugStream(File, I); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + StringsAndChecksumsPrinter Strings(File, I); + + uint32_t LastNameIndex = UINT32_MAX; + for (const auto &SS : MDS->subsections()) { + if (SS.kind() != DebugSubsectionKind::Lines) + continue; + + DebugLinesSubsectionRef Lines; + BinaryStreamReader Reader(SS.getRecordData()); + if (auto EC = Lines.initialize(Reader)) { + P.formatLine("Line information not present"); + continue; + } + + uint16_t Segment = Lines.header()->RelocSegment; + uint32_t Begin = Lines.header()->RelocOffset; + uint32_t End = Begin + Lines.header()->CodeSize; + for (const auto &Block : Lines) { + if (Block.NameIndex != LastNameIndex) { + Strings.formatFromChecksumsOffset(P, Block.NameIndex); + LastNameIndex = Block.NameIndex; + } + + AutoIndent Indent(P, 2); + P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); + uint32_t Count = Block.LineNumbers.size(); + if (Lines.hasColumnInfo()) + P.format("line/column/addr entries = {0}", Count); + else + P.format("line/addr entries = {0}", Count); + + P.NewLine(); + typesetLinesAndColumns(File, P, Begin, Block); + } + } + }); + + return Error::success(); +} + +Error RawOutputStyle::dumpInlineeLines() { + printHeader(P, "Inlinee Lines"); + ExitOnError Err("Unexpected error processing modules"); + + iterateModules( + File, P, 2, [this](uint32_t I, const DbiModuleDescriptor &Modi) { + + auto MDS = getModuleDebugStream(File, I); + if (!MDS) { + consumeError(MDS.takeError()); + return; + } + + StringsAndChecksumsPrinter Strings(File, I); + + for (const auto &SS : MDS->subsections()) { + if (SS.kind() != DebugSubsectionKind::InlineeLines) + continue; + + DebugInlineeLinesSubsectionRef Lines; + BinaryStreamReader Reader(SS.getRecordData()); + if (auto EC = Lines.initialize(Reader)) { + continue; + } + P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", + "Source File"); + for (const auto &Entry : Lines) { + P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, + fmtle(Entry.Header->SourceLineNum)); + Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); + } + P.NewLine(); + } + }); + return Error::success(); } diff --git a/tools/llvm-pdbutil/RawOutputStyle.h b/tools/llvm-pdbutil/RawOutputStyle.h index 66069410b71..e9405fc2a92 100644 --- a/tools/llvm-pdbutil/RawOutputStyle.h +++ b/tools/llvm-pdbutil/RawOutputStyle.h @@ -42,6 +42,8 @@ private: Error dumpBlockRanges(); Error dumpStreamBytes(); Error dumpStringTable(); + Error dumpLines(); + Error dumpInlineeLines(); Error dumpTpiStream(uint32_t StreamIdx); Error dumpModules(); Error dumpModuleFiles(); diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index 9dcad07a10c..cbda8e6dbf5 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -334,7 +334,15 @@ cl::opt DumpModules("modules", cl::desc("dump compiland information"), cl::cat(FileOptions), cl::sub(RawSubcommand)); cl::opt DumpModuleFiles( "files", - cl::desc("for each module dumped, dump the contributing source files"), + cl::desc("Dump the source files that contribute to each module's."), + cl::cat(FileOptions), cl::sub(RawSubcommand)); +cl::opt DumpLines( + "l", + cl::desc("dump source file/line information (DEBUG_S_LINES subsection)"), + cl::cat(FileOptions), cl::sub(RawSubcommand)); +cl::opt DumpInlineeLines( + "il", + cl::desc("dump inlinee line information (DEBUG_S_INLINEELINES subsection)"), cl::cat(FileOptions), cl::sub(RawSubcommand)); // MISCELLANEOUS OPTIONS @@ -893,6 +901,8 @@ int main(int argc_, const char *argv_[]) { if (opts::RawSubcommand) { if (opts::raw::RawAll) { + opts::raw::DumpLines = true; + opts::raw::DumpInlineeLines = true; opts::raw::DumpIds = true; opts::raw::DumpPublics = true; opts::raw::DumpSectionContribs = true; diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h index f38ec2d5a0a..422b9293981 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/tools/llvm-pdbutil/llvm-pdbutil.h @@ -102,6 +102,9 @@ extern llvm::cl::opt DumpSummary; extern llvm::cl::opt DumpStreams; extern llvm::Optional DumpBlockRange; extern llvm::cl::list DumpStreamData; + +extern llvm::cl::opt DumpLines; +extern llvm::cl::opt DumpInlineeLines; extern llvm::cl::opt DumpStringTable; extern llvm::cl::opt DumpTypes; extern llvm::cl::opt DumpTypeData;