|  | //===- LinePrinter.cpp ------------------------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "LinePrinter.h" | 
|  |  | 
|  | #include "llvm-pdbutil.h" | 
|  |  | 
|  | #include "llvm/ADT/STLExtras.h" | 
|  | #include "llvm/DebugInfo/MSF/MSFCommon.h" | 
|  | #include "llvm/DebugInfo/MSF/MSFStreamLayout.h" | 
|  | #include "llvm/DebugInfo/MSF/MappedBlockStream.h" | 
|  | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" | 
|  | #include "llvm/DebugInfo/PDB/UDTLayout.h" | 
|  | #include "llvm/Support/BinaryStreamReader.h" | 
|  | #include "llvm/Support/Format.h" | 
|  | #include "llvm/Support/FormatAdapters.h" | 
|  | #include "llvm/Support/FormatVariadic.h" | 
|  | #include "llvm/Support/Regex.h" | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace llvm::msf; | 
|  | using namespace llvm::pdb; | 
|  |  | 
|  | namespace { | 
|  | bool IsItemExcluded(llvm::StringRef Item, | 
|  | std::list<llvm::Regex> &IncludeFilters, | 
|  | std::list<llvm::Regex> &ExcludeFilters) { | 
|  | if (Item.empty()) | 
|  | return false; | 
|  |  | 
|  | auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); }; | 
|  |  | 
|  | // Include takes priority over exclude.  If the user specified include | 
|  | // filters, and none of them include this item, them item is gone. | 
|  | if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred)) | 
|  | return true; | 
|  |  | 
|  | if (any_of(ExcludeFilters, match_pred)) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream) | 
|  | : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) { | 
|  | SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(), | 
|  | opts::pretty::ExcludeTypes.end()); | 
|  | SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(), | 
|  | opts::pretty::ExcludeSymbols.end()); | 
|  | SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(), | 
|  | opts::pretty::ExcludeCompilands.end()); | 
|  |  | 
|  | SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(), | 
|  | opts::pretty::IncludeTypes.end()); | 
|  | SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(), | 
|  | opts::pretty::IncludeSymbols.end()); | 
|  | SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(), | 
|  | opts::pretty::IncludeCompilands.end()); | 
|  | } | 
|  |  | 
|  | void LinePrinter::Indent(uint32_t Amount) { | 
|  | if (Amount == 0) | 
|  | Amount = IndentSpaces; | 
|  | CurrentIndent += Amount; | 
|  | } | 
|  |  | 
|  | void LinePrinter::Unindent(uint32_t Amount) { | 
|  | if (Amount == 0) | 
|  | Amount = IndentSpaces; | 
|  | CurrentIndent = std::max<int>(0, CurrentIndent - Amount); | 
|  | } | 
|  |  | 
|  | void LinePrinter::NewLine() { | 
|  | OS << "\n"; | 
|  | OS.indent(CurrentIndent); | 
|  | } | 
|  |  | 
|  | void LinePrinter::print(const Twine &T) { OS << T; } | 
|  |  | 
|  | void LinePrinter::printLine(const Twine &T) { | 
|  | NewLine(); | 
|  | OS << T; | 
|  | } | 
|  |  | 
|  | bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { | 
|  | if (IsTypeExcluded(Class.getName(), Class.getSize())) | 
|  | return true; | 
|  | if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, | 
|  | uint32_t StartOffset) { | 
|  | NewLine(); | 
|  | OS << Label << " ("; | 
|  | if (!Data.empty()) { | 
|  | OS << "\n"; | 
|  | OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, | 
|  | CurrentIndent + IndentSpaces, true); | 
|  | NewLine(); | 
|  | } | 
|  | OS << ")"; | 
|  | } | 
|  |  | 
|  | void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, | 
|  | uint64_t Base, uint32_t StartOffset) { | 
|  | NewLine(); | 
|  | OS << Label << " ("; | 
|  | if (!Data.empty()) { | 
|  | OS << "\n"; | 
|  | Base += StartOffset; | 
|  | OS << format_bytes_with_ascii(Data, Base, 32, 4, | 
|  | CurrentIndent + IndentSpaces, true); | 
|  | NewLine(); | 
|  | } | 
|  | OS << ")"; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | struct Run { | 
|  | Run() = default; | 
|  | explicit Run(uint32_t Block) : Block(Block) {} | 
|  | uint32_t Block = 0; | 
|  | uint32_t ByteLen = 0; | 
|  | }; | 
|  | } // namespace | 
|  |  | 
|  | static std::vector<Run> computeBlockRuns(uint32_t BlockSize, | 
|  | const msf::MSFStreamLayout &Layout) { | 
|  | std::vector<Run> Runs; | 
|  | if (Layout.Length == 0) | 
|  | return Runs; | 
|  |  | 
|  | ArrayRef<support::ulittle32_t> Blocks = Layout.Blocks; | 
|  | assert(!Blocks.empty()); | 
|  | uint32_t StreamBytesRemaining = Layout.Length; | 
|  | uint32_t CurrentBlock = Blocks[0]; | 
|  | Runs.emplace_back(CurrentBlock); | 
|  | while (!Blocks.empty()) { | 
|  | Run *CurrentRun = &Runs.back(); | 
|  | uint32_t NextBlock = Blocks.front(); | 
|  | if (NextBlock < CurrentBlock || (NextBlock - CurrentBlock > 1)) { | 
|  | Runs.emplace_back(NextBlock); | 
|  | CurrentRun = &Runs.back(); | 
|  | } | 
|  | uint32_t Used = std::min(BlockSize, StreamBytesRemaining); | 
|  | CurrentRun->ByteLen += Used; | 
|  | StreamBytesRemaining -= Used; | 
|  | CurrentBlock = NextBlock; | 
|  | Blocks = Blocks.drop_front(); | 
|  | } | 
|  | return Runs; | 
|  | } | 
|  |  | 
|  | static std::pair<Run, uint32_t> findRun(uint32_t Offset, ArrayRef<Run> Runs) { | 
|  | for (const auto &R : Runs) { | 
|  | if (Offset < R.ByteLen) | 
|  | return std::make_pair(R, Offset); | 
|  | Offset -= R.ByteLen; | 
|  | } | 
|  | llvm_unreachable("Invalid offset!"); | 
|  | } | 
|  |  | 
|  | void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, | 
|  | uint32_t StreamIdx, | 
|  | StringRef StreamPurpose, uint32_t Offset, | 
|  | uint32_t Size) { | 
|  | if (StreamIdx >= File.getNumStreams()) { | 
|  | formatLine("Stream {0}: Not present", StreamIdx); | 
|  | return; | 
|  | } | 
|  | if (Size + Offset > File.getStreamByteSize(StreamIdx)) { | 
|  | formatLine( | 
|  | "Stream {0}: Invalid offset and size, range out of stream bounds", | 
|  | StreamIdx); | 
|  | return; | 
|  | } | 
|  |  | 
|  | auto S = MappedBlockStream::createIndexedStream( | 
|  | File.getMsfLayout(), File.getMsfBuffer(), StreamIdx, File.getAllocator()); | 
|  | if (!S) { | 
|  | NewLine(); | 
|  | formatLine("Stream {0}: Not present", StreamIdx); | 
|  | return; | 
|  | } | 
|  |  | 
|  | uint32_t End = | 
|  | (Size == 0) ? S->getLength() : std::min(Offset + Size, S->getLength()); | 
|  | Size = End - Offset; | 
|  |  | 
|  | formatLine("Stream {0}: {1} (dumping {2:N} / {3:N} bytes)", StreamIdx, | 
|  | StreamPurpose, Size, S->getLength()); | 
|  | AutoIndent Indent(*this); | 
|  | BinaryStreamRef Slice(*S); | 
|  | BinarySubstreamRef Substream; | 
|  | Substream.Offset = Offset; | 
|  | Substream.StreamData = Slice.drop_front(Offset).keep_front(Size); | 
|  |  | 
|  | auto Layout = File.getStreamLayout(StreamIdx); | 
|  | formatMsfStreamData(Label, File, Layout, Substream); | 
|  | } | 
|  |  | 
|  | void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, | 
|  | const msf::MSFStreamLayout &Stream, | 
|  | BinarySubstreamRef Substream) { | 
|  | BinaryStreamReader Reader(Substream.StreamData); | 
|  |  | 
|  | auto Runs = computeBlockRuns(File.getBlockSize(), Stream); | 
|  |  | 
|  | NewLine(); | 
|  | OS << Label << " ("; | 
|  | while (Reader.bytesRemaining() > 0) { | 
|  | OS << "\n"; | 
|  |  | 
|  | Run FoundRun; | 
|  | uint32_t RunOffset; | 
|  | std::tie(FoundRun, RunOffset) = findRun(Substream.Offset, Runs); | 
|  | assert(FoundRun.ByteLen >= RunOffset); | 
|  | uint32_t Len = FoundRun.ByteLen - RunOffset; | 
|  | Len = std::min(Len, Reader.bytesRemaining()); | 
|  | uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset; | 
|  | ArrayRef<uint8_t> Data; | 
|  | consumeError(Reader.readBytes(Data, Len)); | 
|  | OS << format_bytes_with_ascii(Data, Base, 32, 4, | 
|  | CurrentIndent + IndentSpaces, true); | 
|  | if (Reader.bytesRemaining() > 0) { | 
|  | NewLine(); | 
|  | OS << formatv("  {0}", | 
|  | fmt_align("<discontinuity>", AlignStyle::Center, 114, '-')); | 
|  | } | 
|  | Substream.Offset += Len; | 
|  | } | 
|  | NewLine(); | 
|  | OS << ")"; | 
|  | } | 
|  |  | 
|  | bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) { | 
|  | if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters)) | 
|  | return true; | 
|  | if (Size < opts::pretty::SizeThreshold) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { | 
|  | return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters); | 
|  | } | 
|  |  | 
|  | bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { | 
|  | return IsItemExcluded(CompilandName, IncludeCompilandFilters, | 
|  | ExcludeCompilandFilters); | 
|  | } | 
|  |  | 
|  | WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) | 
|  | : OS(P.OS), UseColor(P.hasColor()) { | 
|  | if (UseColor) | 
|  | applyColor(C); | 
|  | } | 
|  |  | 
|  | WithColor::~WithColor() { | 
|  | if (UseColor) | 
|  | OS.resetColor(); | 
|  | } | 
|  |  | 
|  | void WithColor::applyColor(PDB_ColorItem C) { | 
|  | switch (C) { | 
|  | case PDB_ColorItem::None: | 
|  | OS.resetColor(); | 
|  | return; | 
|  | case PDB_ColorItem::Comment: | 
|  | OS.changeColor(raw_ostream::GREEN, false); | 
|  | return; | 
|  | case PDB_ColorItem::Address: | 
|  | OS.changeColor(raw_ostream::YELLOW, /*bold=*/true); | 
|  | return; | 
|  | case PDB_ColorItem::Keyword: | 
|  | OS.changeColor(raw_ostream::MAGENTA, true); | 
|  | return; | 
|  | case PDB_ColorItem::Register: | 
|  | case PDB_ColorItem::Offset: | 
|  | OS.changeColor(raw_ostream::YELLOW, false); | 
|  | return; | 
|  | case PDB_ColorItem::Type: | 
|  | OS.changeColor(raw_ostream::CYAN, true); | 
|  | return; | 
|  | case PDB_ColorItem::Identifier: | 
|  | OS.changeColor(raw_ostream::CYAN, false); | 
|  | return; | 
|  | case PDB_ColorItem::Path: | 
|  | OS.changeColor(raw_ostream::CYAN, false); | 
|  | return; | 
|  | case PDB_ColorItem::Padding: | 
|  | case PDB_ColorItem::SectionHeader: | 
|  | OS.changeColor(raw_ostream::RED, true); | 
|  | return; | 
|  | case PDB_ColorItem::LiteralValue: | 
|  | OS.changeColor(raw_ostream::GREEN, true); | 
|  | return; | 
|  | } | 
|  | } |