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