| //===- 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<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); |
| } |
| } |