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 {
IsItemExcluded(llvm::StringRef Item,std::list<llvm::Regex> & IncludeFilters,std::list<llvm::Regex> & ExcludeFilters)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
LinePrinter(int Indent,bool UseColor,llvm::raw_ostream & Stream,const FilterOptions & Filters)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
Indent(uint32_t Amount)75 void LinePrinter::Indent(uint32_t Amount) {
76 if (Amount == 0)
77 Amount = IndentSpaces;
78 CurrentIndent += Amount;
79 }
80
Unindent(uint32_t Amount)81 void LinePrinter::Unindent(uint32_t Amount) {
82 if (Amount == 0)
83 Amount = IndentSpaces;
84 CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
85 }
86
NewLine()87 void LinePrinter::NewLine() {
88 OS << "\n";
89 OS.indent(CurrentIndent);
90 }
91
print(const Twine & T)92 void LinePrinter::print(const Twine &T) { OS << T; }
93
printLine(const Twine & T)94 void LinePrinter::printLine(const Twine &T) {
95 NewLine();
96 OS << T;
97 }
98
IsClassExcluded(const ClassLayout & Class)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
formatBinary(StringRef Label,ArrayRef<uint8_t> Data,uint64_t StartOffset)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
formatBinary(StringRef Label,ArrayRef<uint8_t> Data,uint64_t Base,uint64_t StartOffset)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;
Run__anone95217990311::Run137 explicit Run(uint32_t Block) : Block(Block) {}
138 uint32_t Block = 0;
139 uint64_t ByteLen = 0;
140 };
141 } // namespace
142
computeBlockRuns(uint32_t BlockSize,const msf::MSFStreamLayout & Layout)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
findRun(uint64_t Offset,ArrayRef<Run> Runs)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
formatMsfStreamData(StringRef Label,PDBFile & File,uint32_t StreamIdx,StringRef StreamPurpose,uint64_t Offset,uint64_t Size)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
formatMsfStreamData(StringRef Label,PDBFile & File,const msf::MSFStreamLayout & Stream,BinarySubstreamRef Substream)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
formatMsfStreamBlocks(PDBFile & File,const msf::MSFStreamLayout & StreamLayout)252 void LinePrinter::formatMsfStreamBlocks(
253 PDBFile &File, const msf::MSFStreamLayout &StreamLayout) {
254 auto Blocks = ArrayRef(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
IsTypeExcluded(llvm::StringRef TypeName,uint64_t Size)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
IsSymbolExcluded(llvm::StringRef SymbolName)285 bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
286 return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
287 }
288
IsCompilandExcluded(llvm::StringRef CompilandName)289 bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
290 return IsItemExcluded(CompilandName, IncludeCompilandFilters,
291 ExcludeCompilandFilters);
292 }
293
WithColor(LinePrinter & P,PDB_ColorItem C)294 WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
295 : OS(P.OS), UseColor(P.hasColor()) {
296 if (UseColor)
297 applyColor(C);
298 }
299
~WithColor()300 WithColor::~WithColor() {
301 if (UseColor)
302 OS.resetColor();
303 }
304
applyColor(PDB_ColorItem C)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