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