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
26using namespace llvm;
27using namespace llvm::msf;
28using namespace llvm::pdb;
29
30namespace {
31bool 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
51using namespace llvm;
52
53LinePrinter::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
70void LinePrinter::Indent(uint32_t Amount) {
71  if (Amount == 0)
72    Amount = IndentSpaces;
73  CurrentIndent += Amount;
74}
75
76void LinePrinter::Unindent(uint32_t Amount) {
77  if (Amount == 0)
78    Amount = IndentSpaces;
79  CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
80}
81
82void LinePrinter::NewLine() {
83  OS << "\n";
84  OS.indent(CurrentIndent);
85}
86
87void LinePrinter::print(const Twine &T) { OS << T; }
88
89void LinePrinter::printLine(const Twine &T) {
90  NewLine();
91  OS << T;
92}
93
94bool 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
102void 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
115void 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
129namespace {
130struct 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
138static 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
165static 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
174void 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
212void 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
246void 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
270bool 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
278bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
279  return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
280}
281
282bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
283  return IsItemExcluded(CompilandName, IncludeCompilandFilters,
284                        ExcludeCompilandFilters);
285}
286
287WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
288    : OS(P.OS), UseColor(P.hasColor()) {
289  if (UseColor)
290    applyColor(C);
291}
292
293WithColor::~WithColor() {
294  if (UseColor)
295    OS.resetColor();
296}
297
298void 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