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