//===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "BytesOutputStyle.h" #include "FormatUtil.h" #include "StreamUtil.h" #include "llvm-pdbutil.h" #include "llvm/DebugInfo/CodeView/Formatters.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/MSF/MSFCommon.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/RawError.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" using namespace llvm; using namespace llvm::codeview; using namespace llvm::msf; using namespace llvm::pdb; namespace { struct StreamSpec { uint32_t SI = 0; uint32_t Begin = 0; uint32_t Size = 0; }; } // namespace static Expected parseStreamSpec(StringRef Str) { StreamSpec Result; if (Str.consumeInteger(0, Result.SI)) return make_error(raw_error_code::invalid_format, "Invalid Stream Specification"); if (Str.consume_front(":")) { if (Str.consumeInteger(0, Result.Begin)) return make_error(raw_error_code::invalid_format, "Invalid Stream Specification"); } if (Str.consume_front("@")) { if (Str.consumeInteger(0, Result.Size)) return make_error(raw_error_code::invalid_format, "Invalid Stream Specification"); } if (!Str.empty()) return make_error(raw_error_code::invalid_format, "Invalid Stream Specification"); return Result; } static SmallVector parseStreamSpecs(LinePrinter &P) { SmallVector Result; for (auto &Str : opts::bytes::DumpStreamData) { auto ESS = parseStreamSpec(Str); if (!ESS) { P.formatLine("Error parsing stream spec {0}: {1}", Str, toString(ESS.takeError())); continue; } Result.push_back(*ESS); } return Result; } static void printHeader(LinePrinter &P, const Twine &S) { P.NewLine(); P.formatLine("{0,=60}", S); P.formatLine("{0}", fmt_repeat('=', 60)); } BytesOutputStyle::BytesOutputStyle(PDBFile &File) : File(File), P(2, false, outs()) {} Error BytesOutputStyle::dump() { if (opts::bytes::DumpBlockRange.hasValue()) { auto &R = *opts::bytes::DumpBlockRange; uint32_t Max = R.Max.getValueOr(R.Min); if (Max < R.Min) return make_error( "Invalid block range specified. Max < Min", inconvertibleErrorCode()); if (Max >= File.getBlockCount()) return make_error( "Invalid block range specified. Requested block out of bounds", inconvertibleErrorCode()); dumpBlockRanges(R.Min, Max); P.NewLine(); } if (opts::bytes::DumpByteRange.hasValue()) { auto &R = *opts::bytes::DumpByteRange; uint32_t Max = R.Max.getValueOr(File.getFileSize()); if (Max < R.Min) return make_error("Invalid byte range specified. Max < Min", inconvertibleErrorCode()); if (Max >= File.getFileSize()) return make_error( "Invalid byte range specified. Requested byte larger than file size", inconvertibleErrorCode()); dumpByteRanges(R.Min, Max); P.NewLine(); } if (opts::bytes::Fpm) { dumpFpm(); P.NewLine(); } if (!opts::bytes::DumpStreamData.empty()) { dumpStreamBytes(); P.NewLine(); } if (opts::bytes::NameMap) { dumpNameMap(); P.NewLine(); } if (opts::bytes::SectionContributions) { dumpSectionContributions(); P.NewLine(); } if (opts::bytes::SectionMap) { dumpSectionMap(); P.NewLine(); } if (opts::bytes::ModuleInfos) { dumpModuleInfos(); P.NewLine(); } if (opts::bytes::FileInfo) { dumpFileInfo(); P.NewLine(); } if (opts::bytes::TypeServerMap) { dumpTypeServerMap(); P.NewLine(); } if (opts::bytes::ECData) { dumpECData(); P.NewLine(); } if (!opts::bytes::TypeIndex.empty()) { dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex); P.NewLine(); } if (!opts::bytes::IdIndex.empty()) { dumpTypeIndex(StreamIPI, opts::bytes::IdIndex); P.NewLine(); } if (opts::bytes::ModuleSyms) { dumpModuleSyms(); P.NewLine(); } if (opts::bytes::ModuleC11) { dumpModuleC11(); P.NewLine(); } if (opts::bytes::ModuleC13) { dumpModuleC13(); P.NewLine(); } return Error::success(); } void BytesOutputStyle::dumpNameMap() { printHeader(P, "Named Stream Map"); AutoIndent Indent(P); auto &InfoS = Err(File.getPDBInfoStream()); BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer(); auto Layout = File.getStreamLayout(StreamPDB); P.formatMsfStreamData("Named Stream Map", File, Layout, NS); } void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) { printHeader(P, "MSF Blocks"); AutoIndent Indent(P); for (uint32_t I = Min; I <= Max; ++I) { uint64_t Base = I; Base *= File.getBlockSize(); auto ExpectedData = File.getBlockData(I, File.getBlockSize()); if (!ExpectedData) { P.formatLine("Could not get block {0}. Reason = {1}", I, toString(ExpectedData.takeError())); continue; } std::string Label = formatv("Block {0}", I).str(); P.formatBinary(Label, *ExpectedData, Base, 0); } } void BytesOutputStyle::dumpSectionContributions() { printHeader(P, "Section Contributions"); AutoIndent Indent(P); auto &DbiS = Err(File.getPDBDbiStream()); BinarySubstreamRef NS = DbiS.getSectionContributionData(); auto Layout = File.getStreamLayout(StreamDBI); P.formatMsfStreamData("Section Contributions", File, Layout, NS); } void BytesOutputStyle::dumpSectionMap() { printHeader(P, "Section Map"); AutoIndent Indent(P); auto &DbiS = Err(File.getPDBDbiStream()); BinarySubstreamRef NS = DbiS.getSecMapSubstreamData(); auto Layout = File.getStreamLayout(StreamDBI); P.formatMsfStreamData("Section Map", File, Layout, NS); } void BytesOutputStyle::dumpModuleInfos() { printHeader(P, "Module Infos"); AutoIndent Indent(P); auto &DbiS = Err(File.getPDBDbiStream()); BinarySubstreamRef NS = DbiS.getModiSubstreamData(); auto Layout = File.getStreamLayout(StreamDBI); P.formatMsfStreamData("Module Infos", File, Layout, NS); } void BytesOutputStyle::dumpFileInfo() { printHeader(P, "File Info"); AutoIndent Indent(P); auto &DbiS = Err(File.getPDBDbiStream()); BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData(); auto Layout = File.getStreamLayout(StreamDBI); P.formatMsfStreamData("File Info", File, Layout, NS); } void BytesOutputStyle::dumpTypeServerMap() { printHeader(P, "Type Server Map"); AutoIndent Indent(P); auto &DbiS = Err(File.getPDBDbiStream()); BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData(); auto Layout = File.getStreamLayout(StreamDBI); P.formatMsfStreamData("Type Server Map", File, Layout, NS); } void BytesOutputStyle::dumpECData() { printHeader(P, "Edit and Continue Data"); AutoIndent Indent(P); auto &DbiS = Err(File.getPDBDbiStream()); BinarySubstreamRef NS = DbiS.getECSubstreamData(); auto Layout = File.getStreamLayout(StreamDBI); P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS); } void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx, ArrayRef Indices) { assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); assert(!Indices.empty()); bool IsTpi = (StreamIdx == StreamTPI); StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records"; printHeader(P, Label); auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream()); AutoIndent Indent(P); auto Substream = Stream.getTypeRecordsSubstream(); auto &Types = Err(initializeTypes(StreamIdx)); auto Layout = File.getStreamLayout(StreamIdx); for (const auto &Id : Indices) { TypeIndex TI(Id); if (TI.toArrayIndex() >= Types.capacity()) { P.formatLine("Error: TypeIndex {0} does not exist", TI); continue; } auto Type = Types.getType(TI); uint32_t Offset = Types.getOffsetOfType(TI); auto OneType = Substream.slice(Offset, Type.length()); P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType); } } template static void iterateOneModule(PDBFile &File, LinePrinter &P, const DbiModuleList &Modules, uint32_t I, uint32_t Digits, uint32_t IndentLevel, CallbackT Callback) { if (I >= Modules.getModuleCount()) { P.formatLine("Mod {0:4} | Invalid module index ", fmt_align(I, AlignStyle::Right, std::max(Digits, 4U))); return; } auto Modi = Modules.getModuleDescriptor(I); P.formatLine("Mod {0:4} | `{1}`: ", fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)), Modi.getModuleName()); uint16_t ModiStream = Modi.getModuleStreamIndex(); AutoIndent Indent2(P, IndentLevel); if (ModiStream == kInvalidStreamIndex) return; auto ModStreamData = MappedBlockStream::createIndexedStream( File.getMsfLayout(), File.getMsfBuffer(), ModiStream, File.getAllocator()); ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData)); if (auto EC = ModStream.reload()) { P.formatLine("Could not parse debug information."); return; } auto Layout = File.getStreamLayout(ModiStream); Callback(I, ModStream, Layout); } template static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, CallbackT Callback) { 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(); if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) { iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel, Callback); } else { uint32_t Count = Modules.getModuleCount(); uint32_t Digits = NumDigits(Count); for (uint32_t I = 0; I < Count; ++I) { iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback); } } } void BytesOutputStyle::dumpModuleSyms() { printHeader(P, "Module Symbols"); AutoIndent Indent(P); iterateModules(File, P, 2, [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, const MSFStreamLayout &Layout) { auto Symbols = Stream.getSymbolsSubstream(); P.formatMsfStreamData("Symbols", File, Layout, Symbols); }); } void BytesOutputStyle::dumpModuleC11() { printHeader(P, "C11 Debug Chunks"); AutoIndent Indent(P); iterateModules(File, P, 2, [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, const MSFStreamLayout &Layout) { auto Chunks = Stream.getC11LinesSubstream(); P.formatMsfStreamData("C11 Debug Chunks", File, Layout, Chunks); }); } void BytesOutputStyle::dumpModuleC13() { printHeader(P, "Debug Chunks"); AutoIndent Indent(P); iterateModules( File, P, 2, [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, const MSFStreamLayout &Layout) { auto Chunks = Stream.getC13LinesSubstream(); if (opts::bytes::SplitChunks) { for (const auto &SS : Stream.subsections()) { BinarySubstreamRef ThisChunk; std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength()); P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout, ThisChunk); } } else { P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks); } }); } void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) { printHeader(P, "MSF Bytes"); AutoIndent Indent(P); BinaryStreamReader Reader(File.getMsfBuffer()); ArrayRef Data; consumeError(Reader.skip(Min)); uint32_t Size = Max - Min + 1; auto EC = Reader.readBytes(Data, Size); assert(!EC); consumeError(std::move(EC)); P.formatBinary("Bytes", Data, Min); } Expected BytesOutputStyle::initializeTypes(uint32_t StreamIdx) { auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes; if (TypeCollection) return *TypeCollection; auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() : File.getPDBIpiStream(); if (!Tpi) return Tpi.takeError(); auto &Types = Tpi->typeArray(); uint32_t Count = Tpi->getNumTypeRecords(); auto Offsets = Tpi->getTypeIndexOffsets(); TypeCollection = llvm::make_unique(Types, Count, Offsets); return *TypeCollection; } void BytesOutputStyle::dumpFpm() { printHeader(P, "Free Page Map"); msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout(); P.formatMsfStreamBlocks(File, FpmLayout); } void BytesOutputStyle::dumpStreamBytes() { if (StreamPurposes.empty()) discoverStreamPurposes(File, StreamPurposes); printHeader(P, "Stream Data"); ExitOnError Err("Unexpected error reading stream data"); auto Specs = parseStreamSpecs(P); for (const auto &Spec : Specs) { AutoIndent Indent(P); if (Spec.SI >= StreamPurposes.size()) { P.formatLine("Stream {0}: Not present", Spec.SI); continue; } P.formatMsfStreamData("Data", File, Spec.SI, StreamPurposes[Spec.SI].getShortName(), Spec.Begin, Spec.Size); } }