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