|  | //===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #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<StreamSpec> parseStreamSpec(StringRef Str) { | 
|  | StreamSpec Result; | 
|  | if (Str.consumeInteger(0, Result.SI)) | 
|  | return make_error<RawError>(raw_error_code::invalid_format, | 
|  | "Invalid Stream Specification"); | 
|  | if (Str.consume_front(":")) { | 
|  | if (Str.consumeInteger(0, Result.Begin)) | 
|  | return make_error<RawError>(raw_error_code::invalid_format, | 
|  | "Invalid Stream Specification"); | 
|  | } | 
|  | if (Str.consume_front("@")) { | 
|  | if (Str.consumeInteger(0, Result.Size)) | 
|  | return make_error<RawError>(raw_error_code::invalid_format, | 
|  | "Invalid Stream Specification"); | 
|  | } | 
|  |  | 
|  | if (!Str.empty()) | 
|  | return make_error<RawError>(raw_error_code::invalid_format, | 
|  | "Invalid Stream Specification"); | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) { | 
|  | SmallVector<StreamSpec, 2> 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<StringError>( | 
|  | "Invalid block range specified.  Max < Min", | 
|  | inconvertibleErrorCode()); | 
|  | if (Max >= File.getBlockCount()) | 
|  | return make_error<StringError>( | 
|  | "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<StringError>("Invalid byte range specified.  Max < Min", | 
|  | inconvertibleErrorCode()); | 
|  | if (Max >= File.getFileSize()) | 
|  | return make_error<StringError>( | 
|  | "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<uint32_t> 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 <typename CallbackT> | 
|  | 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 <typename CallbackT> | 
|  | 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<uint8_t> 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<codeview::LazyRandomTypeCollection &> | 
|  | 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<LazyRandomTypeCollection>(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); | 
|  | } | 
|  | } |