Home | History | Annotate | Line # | Download | only in llvm-pdbutil
      1 //===- LinePrinter.cpp ------------------------------------------*- C++ -*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 
      9 #include "LinePrinter.h"
     10 
     11 #include "llvm-pdbutil.h"
     12 
     13 #include "llvm/ADT/STLExtras.h"
     14 #include "llvm/DebugInfo/MSF/MSFCommon.h"
     15 #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
     16 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
     17 #include "llvm/DebugInfo/PDB/UDTLayout.h"
     18 #include "llvm/Support/BinaryStreamReader.h"
     19 #include "llvm/Support/Format.h"
     20 #include "llvm/Support/FormatAdapters.h"
     21 #include "llvm/Support/FormatVariadic.h"
     22 #include "llvm/Support/Regex.h"
     23 
     24 #include <algorithm>
     25 
     26 using namespace llvm;
     27 using namespace llvm::msf;
     28 using namespace llvm::pdb;
     29 
     30 namespace {
     31 bool IsItemExcluded(llvm::StringRef Item,
     32                     std::list<llvm::Regex> &IncludeFilters,
     33                     std::list<llvm::Regex> &ExcludeFilters) {
     34   if (Item.empty())
     35     return false;
     36 
     37   auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); };
     38 
     39   // Include takes priority over exclude.  If the user specified include
     40   // filters, and none of them include this item, them item is gone.
     41   if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred))
     42     return true;
     43 
     44   if (any_of(ExcludeFilters, match_pred))
     45     return true;
     46 
     47   return false;
     48 }
     49 }
     50 
     51 using namespace llvm;
     52 
     53 LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream)
     54     : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) {
     55   SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(),
     56              opts::pretty::ExcludeTypes.end());
     57   SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(),
     58              opts::pretty::ExcludeSymbols.end());
     59   SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(),
     60              opts::pretty::ExcludeCompilands.end());
     61 
     62   SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(),
     63              opts::pretty::IncludeTypes.end());
     64   SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(),
     65              opts::pretty::IncludeSymbols.end());
     66   SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(),
     67              opts::pretty::IncludeCompilands.end());
     68 }
     69 
     70 void LinePrinter::Indent(uint32_t Amount) {
     71   if (Amount == 0)
     72     Amount = IndentSpaces;
     73   CurrentIndent += Amount;
     74 }
     75 
     76 void LinePrinter::Unindent(uint32_t Amount) {
     77   if (Amount == 0)
     78     Amount = IndentSpaces;
     79   CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
     80 }
     81 
     82 void LinePrinter::NewLine() {
     83   OS << "\n";
     84   OS.indent(CurrentIndent);
     85 }
     86 
     87 void LinePrinter::print(const Twine &T) { OS << T; }
     88 
     89 void LinePrinter::printLine(const Twine &T) {
     90   NewLine();
     91   OS << T;
     92 }
     93 
     94 bool LinePrinter::IsClassExcluded(const ClassLayout &Class) {
     95   if (IsTypeExcluded(Class.getName(), Class.getSize()))
     96     return true;
     97   if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold)
     98     return true;
     99   return false;
    100 }
    101 
    102 void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
    103                                uint32_t StartOffset) {
    104   NewLine();
    105   OS << Label << " (";
    106   if (!Data.empty()) {
    107     OS << "\n";
    108     OS << format_bytes_with_ascii(Data, StartOffset, 32, 4,
    109                                   CurrentIndent + IndentSpaces, true);
    110     NewLine();
    111   }
    112   OS << ")";
    113 }
    114 
    115 void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
    116                                uint64_t Base, uint32_t StartOffset) {
    117   NewLine();
    118   OS << Label << " (";
    119   if (!Data.empty()) {
    120     OS << "\n";
    121     Base += StartOffset;
    122     OS << format_bytes_with_ascii(Data, Base, 32, 4,
    123                                   CurrentIndent + IndentSpaces, true);
    124     NewLine();
    125   }
    126   OS << ")";
    127 }
    128 
    129 namespace {
    130 struct Run {
    131   Run() = default;
    132   explicit Run(uint32_t Block) : Block(Block) {}
    133   uint32_t Block = 0;
    134   uint32_t ByteLen = 0;
    135 };
    136 } // namespace
    137 
    138 static std::vector<Run> computeBlockRuns(uint32_t BlockSize,
    139                                          const msf::MSFStreamLayout &Layout) {
    140   std::vector<Run> Runs;
    141   if (Layout.Length == 0)
    142     return Runs;
    143 
    144   ArrayRef<support::ulittle32_t> Blocks = Layout.Blocks;
    145   assert(!Blocks.empty());
    146   uint32_t StreamBytesRemaining = Layout.Length;
    147   uint32_t CurrentBlock = Blocks[0];
    148   Runs.emplace_back(CurrentBlock);
    149   while (!Blocks.empty()) {
    150     Run *CurrentRun = &Runs.back();
    151     uint32_t NextBlock = Blocks.front();
    152     if (NextBlock < CurrentBlock || (NextBlock - CurrentBlock > 1)) {
    153       Runs.emplace_back(NextBlock);
    154       CurrentRun = &Runs.back();
    155     }
    156     uint32_t Used = std::min(BlockSize, StreamBytesRemaining);
    157     CurrentRun->ByteLen += Used;
    158     StreamBytesRemaining -= Used;
    159     CurrentBlock = NextBlock;
    160     Blocks = Blocks.drop_front();
    161   }
    162   return Runs;
    163 }
    164 
    165 static std::pair<Run, uint32_t> findRun(uint32_t Offset, ArrayRef<Run> Runs) {
    166   for (const auto &R : Runs) {
    167     if (Offset < R.ByteLen)
    168       return std::make_pair(R, Offset);
    169     Offset -= R.ByteLen;
    170   }
    171   llvm_unreachable("Invalid offset!");
    172 }
    173 
    174 void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File,
    175                                       uint32_t StreamIdx,
    176                                       StringRef StreamPurpose, uint32_t Offset,
    177                                       uint32_t Size) {
    178   if (StreamIdx >= File.getNumStreams()) {
    179     formatLine("Stream {0}: Not present", StreamIdx);
    180     return;
    181   }
    182   if (Size + Offset > File.getStreamByteSize(StreamIdx)) {
    183     formatLine(
    184         "Stream {0}: Invalid offset and size, range out of stream bounds",
    185         StreamIdx);
    186     return;
    187   }
    188 
    189   auto S = File.createIndexedStream(StreamIdx);
    190   if (!S) {
    191     NewLine();
    192     formatLine("Stream {0}: Not present", StreamIdx);
    193     return;
    194   }
    195 
    196   uint32_t End =
    197       (Size == 0) ? S->getLength() : std::min(Offset + Size, S->getLength());
    198   Size = End - Offset;
    199 
    200   formatLine("Stream {0}: {1} (dumping {2:N} / {3:N} bytes)", StreamIdx,
    201              StreamPurpose, Size, S->getLength());
    202   AutoIndent Indent(*this);
    203   BinaryStreamRef Slice(*S);
    204   BinarySubstreamRef Substream;
    205   Substream.Offset = Offset;
    206   Substream.StreamData = Slice.drop_front(Offset).keep_front(Size);
    207 
    208   auto Layout = File.getStreamLayout(StreamIdx);
    209   formatMsfStreamData(Label, File, Layout, Substream);
    210 }
    211 
    212 void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File,
    213                                       const msf::MSFStreamLayout &Stream,
    214                                       BinarySubstreamRef Substream) {
    215   BinaryStreamReader Reader(Substream.StreamData);
    216 
    217   auto Runs = computeBlockRuns(File.getBlockSize(), Stream);
    218 
    219   NewLine();
    220   OS << Label << " (";
    221   while (Reader.bytesRemaining() > 0) {
    222     OS << "\n";
    223 
    224     Run FoundRun;
    225     uint32_t RunOffset;
    226     std::tie(FoundRun, RunOffset) = findRun(Substream.Offset, Runs);
    227     assert(FoundRun.ByteLen >= RunOffset);
    228     uint32_t Len = FoundRun.ByteLen - RunOffset;
    229     Len = std::min(Len, Reader.bytesRemaining());
    230     uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset;
    231     ArrayRef<uint8_t> Data;
    232     consumeError(Reader.readBytes(Data, Len));
    233     OS << format_bytes_with_ascii(Data, Base, 32, 4,
    234                                   CurrentIndent + IndentSpaces, true);
    235     if (Reader.bytesRemaining() > 0) {
    236       NewLine();
    237       OS << formatv("  {0}",
    238                     fmt_align("<discontinuity>", AlignStyle::Center, 114, '-'));
    239     }
    240     Substream.Offset += Len;
    241   }
    242   NewLine();
    243   OS << ")";
    244 }
    245 
    246 void LinePrinter::formatMsfStreamBlocks(
    247     PDBFile &File, const msf::MSFStreamLayout &StreamLayout) {
    248   auto Blocks = makeArrayRef(StreamLayout.Blocks);
    249   uint32_t L = StreamLayout.Length;
    250 
    251   while (L > 0) {
    252     NewLine();
    253     assert(!Blocks.empty());
    254     OS << formatv("Block {0} (\n", uint32_t(Blocks.front()));
    255     uint32_t UsedBytes = std::min(L, File.getBlockSize());
    256     ArrayRef<uint8_t> BlockData =
    257         cantFail(File.getBlockData(Blocks.front(), File.getBlockSize()));
    258     uint64_t BaseOffset = Blocks.front();
    259     BaseOffset *= File.getBlockSize();
    260     OS << format_bytes_with_ascii(BlockData, BaseOffset, 32, 4,
    261                                   CurrentIndent + IndentSpaces, true);
    262     NewLine();
    263     OS << ")";
    264     NewLine();
    265     L -= UsedBytes;
    266     Blocks = Blocks.drop_front();
    267   }
    268 }
    269 
    270 bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) {
    271   if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters))
    272     return true;
    273   if (Size < opts::pretty::SizeThreshold)
    274     return true;
    275   return false;
    276 }
    277 
    278 bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
    279   return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
    280 }
    281 
    282 bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
    283   return IsItemExcluded(CompilandName, IncludeCompilandFilters,
    284                         ExcludeCompilandFilters);
    285 }
    286 
    287 WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
    288     : OS(P.OS), UseColor(P.hasColor()) {
    289   if (UseColor)
    290     applyColor(C);
    291 }
    292 
    293 WithColor::~WithColor() {
    294   if (UseColor)
    295     OS.resetColor();
    296 }
    297 
    298 void WithColor::applyColor(PDB_ColorItem C) {
    299   switch (C) {
    300   case PDB_ColorItem::None:
    301     OS.resetColor();
    302     return;
    303   case PDB_ColorItem::Comment:
    304     OS.changeColor(raw_ostream::GREEN, false);
    305     return;
    306   case PDB_ColorItem::Address:
    307     OS.changeColor(raw_ostream::YELLOW, /*bold=*/true);
    308     return;
    309   case PDB_ColorItem::Keyword:
    310     OS.changeColor(raw_ostream::MAGENTA, true);
    311     return;
    312   case PDB_ColorItem::Register:
    313   case PDB_ColorItem::Offset:
    314     OS.changeColor(raw_ostream::YELLOW, false);
    315     return;
    316   case PDB_ColorItem::Type:
    317     OS.changeColor(raw_ostream::CYAN, true);
    318     return;
    319   case PDB_ColorItem::Identifier:
    320     OS.changeColor(raw_ostream::CYAN, false);
    321     return;
    322   case PDB_ColorItem::Path:
    323     OS.changeColor(raw_ostream::CYAN, false);
    324     return;
    325   case PDB_ColorItem::Padding:
    326   case PDB_ColorItem::SectionHeader:
    327     OS.changeColor(raw_ostream::RED, true);
    328     return;
    329   case PDB_ColorItem::LiteralValue:
    330     OS.changeColor(raw_ostream::GREEN, true);
    331     return;
    332   }
    333 }
    334