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 {
IsItemExcluded(llvm::StringRef Item,std::list<llvm::Regex> & IncludeFilters,std::list<llvm::Regex> & ExcludeFilters)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 
LinePrinter(int Indent,bool UseColor,llvm::raw_ostream & Stream)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 
Indent(uint32_t Amount)70 void LinePrinter::Indent(uint32_t Amount) {
71   if (Amount == 0)
72     Amount = IndentSpaces;
73   CurrentIndent += Amount;
74 }
75 
Unindent(uint32_t Amount)76 void LinePrinter::Unindent(uint32_t Amount) {
77   if (Amount == 0)
78     Amount = IndentSpaces;
79   CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
80 }
81 
NewLine()82 void LinePrinter::NewLine() {
83   OS << "\n";
84   OS.indent(CurrentIndent);
85 }
86 
print(const Twine & T)87 void LinePrinter::print(const Twine &T) { OS << T; }
88 
printLine(const Twine & T)89 void LinePrinter::printLine(const Twine &T) {
90   NewLine();
91   OS << T;
92 }
93 
IsClassExcluded(const ClassLayout & Class)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 
formatBinary(StringRef Label,ArrayRef<uint8_t> Data,uint32_t StartOffset)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 
formatBinary(StringRef Label,ArrayRef<uint8_t> Data,uint64_t Base,uint32_t StartOffset)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;
Run__anonfea2779d0311::Run132   explicit Run(uint32_t Block) : Block(Block) {}
133   uint32_t Block = 0;
134   uint32_t ByteLen = 0;
135 };
136 } // namespace
137 
computeBlockRuns(uint32_t BlockSize,const msf::MSFStreamLayout & Layout)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 
findRun(uint32_t Offset,ArrayRef<Run> Runs)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 
formatMsfStreamData(StringRef Label,PDBFile & File,uint32_t StreamIdx,StringRef StreamPurpose,uint32_t Offset,uint32_t Size)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 
formatMsfStreamData(StringRef Label,PDBFile & File,const msf::MSFStreamLayout & Stream,BinarySubstreamRef Substream)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 
formatMsfStreamBlocks(PDBFile & File,const msf::MSFStreamLayout & StreamLayout)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 
IsTypeExcluded(llvm::StringRef TypeName,uint32_t Size)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 
IsSymbolExcluded(llvm::StringRef SymbolName)278 bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
279   return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
280 }
281 
IsCompilandExcluded(llvm::StringRef CompilandName)282 bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
283   return IsItemExcluded(CompilandName, IncludeCompilandFilters,
284                         ExcludeCompilandFilters);
285 }
286 
WithColor(LinePrinter & P,PDB_ColorItem C)287 WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
288     : OS(P.OS), UseColor(P.hasColor()) {
289   if (UseColor)
290     applyColor(C);
291 }
292 
~WithColor()293 WithColor::~WithColor() {
294   if (UseColor)
295     OS.resetColor();
296 }
297 
applyColor(PDB_ColorItem C)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