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(String: 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(Range&: IncludeFilters, P: match_pred)) |
44 | return true; |
45 | |
46 | if (any_of(Range&: ExcludeFilters, P: 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(List&: ExcludeTypeFilters, Begin: Filters.ExcludeTypes.begin(), |
60 | End: Filters.ExcludeTypes.end()); |
61 | SetFilters(List&: ExcludeSymbolFilters, Begin: Filters.ExcludeSymbols.begin(), |
62 | End: Filters.ExcludeSymbols.end()); |
63 | SetFilters(List&: ExcludeCompilandFilters, Begin: Filters.ExcludeCompilands.begin(), |
64 | End: Filters.ExcludeCompilands.end()); |
65 | |
66 | SetFilters(List&: IncludeTypeFilters, Begin: Filters.IncludeTypes.begin(), |
67 | End: Filters.IncludeTypes.end()); |
68 | SetFilters(List&: IncludeSymbolFilters, Begin: Filters.IncludeSymbols.begin(), |
69 | End: Filters.IncludeSymbols.end()); |
70 | SetFilters(List&: IncludeCompilandFilters, Begin: Filters.IncludeCompilands.begin(), |
71 | End: 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>(a: 0, b: CurrentIndent - Amount); |
84 | } |
85 | |
86 | void LinePrinter::NewLine() { |
87 | OS << "\n" ; |
88 | OS.indent(NumSpaces: 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(TypeName: Class.getName(), Size: 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(Bytes: Data, FirstByteOffset: StartOffset, NumPerLine: 32, ByteGroupSize: 4, |
113 | IndentLevel: CurrentIndent + IndentSpaces, Upper: 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(Bytes: Data, FirstByteOffset: Base, NumPerLine: 32, ByteGroupSize: 4, |
127 | IndentLevel: CurrentIndent + IndentSpaces, Upper: 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(args&: 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(args&: NextBlock); |
158 | CurrentRun = &Runs.back(); |
159 | } |
160 | uint64_t Used = |
161 | std::min(a: static_cast<uint64_t>(BlockSize), b: 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(x: R, y&: 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(Fmt: "Stream {0}: Not present" , Items&: StreamIdx); |
185 | return; |
186 | } |
187 | if (Size + Offset > File.getStreamByteSize(StreamIndex: StreamIdx)) { |
188 | formatLine( |
189 | Fmt: "Stream {0}: Invalid offset and size, range out of stream bounds" , |
190 | Items&: StreamIdx); |
191 | return; |
192 | } |
193 | |
194 | auto S = File.createIndexedStream(SN: StreamIdx); |
195 | if (!S) { |
196 | NewLine(); |
197 | formatLine(Fmt: "Stream {0}: Not present" , Items&: StreamIdx); |
198 | return; |
199 | } |
200 | |
201 | uint64_t End = |
202 | (Size == 0) ? S->getLength() : std::min(a: Offset + Size, b: S->getLength()); |
203 | Size = End - Offset; |
204 | |
205 | formatLine(Fmt: "Stream {0}: {1} (dumping {2:N} / {3:N} bytes)" , Items&: StreamIdx, |
206 | Items&: StreamPurpose, Items&: Size, Items: S->getLength()); |
207 | AutoIndent Indent(*this); |
208 | BinaryStreamRef Slice(*S); |
209 | BinarySubstreamRef Substream; |
210 | Substream.Offset = Offset; |
211 | Substream.StreamData = Slice.drop_front(N: Offset).keep_front(N: Size); |
212 | |
213 | auto Layout = File.getStreamLayout(StreamIdx); |
214 | formatMsfStreamData(Label, File, Stream: 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(BlockSize: File.getBlockSize(), Layout: 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(args&: FoundRun, args&: RunOffset) = findRun(Offset: Substream.Offset, Runs); |
232 | assert(FoundRun.ByteLen >= RunOffset); |
233 | uint64_t Len = FoundRun.ByteLen - RunOffset; |
234 | Len = std::min(a: Len, b: Reader.bytesRemaining()); |
235 | uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset; |
236 | ArrayRef<uint8_t> Data; |
237 | consumeError(Err: Reader.readBytes(Buffer&: Data, Size: Len)); |
238 | OS << format_bytes_with_ascii(Bytes: Data, FirstByteOffset: Base, NumPerLine: 32, ByteGroupSize: 4, |
239 | IndentLevel: CurrentIndent + IndentSpaces, Upper: true); |
240 | if (Reader.bytesRemaining() > 0) { |
241 | NewLine(); |
242 | OS << formatv(Fmt: " {0}" , |
243 | Vals: fmt_align(Item: "<discontinuity>" , Where: AlignStyle::Center, Amount: 114, Fill: '-')); |
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(Fmt: "Block {0} (\n" , Vals: uint32_t(Blocks.front())); |
260 | uint64_t UsedBytes = |
261 | std::min(a: L, b: static_cast<uint64_t>(File.getBlockSize())); |
262 | ArrayRef<uint8_t> BlockData = |
263 | cantFail(ValOrErr: File.getBlockData(BlockIndex: Blocks.front(), NumBytes: File.getBlockSize())); |
264 | uint64_t BaseOffset = Blocks.front(); |
265 | BaseOffset *= File.getBlockSize(); |
266 | OS << format_bytes_with_ascii(Bytes: BlockData, FirstByteOffset: BaseOffset, NumPerLine: 32, ByteGroupSize: 4, |
267 | IndentLevel: CurrentIndent + IndentSpaces, Upper: 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(Item: TypeName, IncludeFilters&: IncludeTypeFilters, ExcludeFilters&: 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(Item: SymbolName, IncludeFilters&: IncludeSymbolFilters, ExcludeFilters&: ExcludeSymbolFilters); |
286 | } |
287 | |
288 | bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { |
289 | return IsItemExcluded(Item: CompilandName, IncludeFilters&: IncludeCompilandFilters, |
290 | ExcludeFilters&: 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(Color: raw_ostream::GREEN, Bold: false); |
311 | return; |
312 | case PDB_ColorItem::Address: |
313 | OS.changeColor(Color: raw_ostream::YELLOW, /*bold=*/Bold: true); |
314 | return; |
315 | case PDB_ColorItem::Keyword: |
316 | OS.changeColor(Color: raw_ostream::MAGENTA, Bold: true); |
317 | return; |
318 | case PDB_ColorItem::Register: |
319 | case PDB_ColorItem::Offset: |
320 | OS.changeColor(Color: raw_ostream::YELLOW, Bold: false); |
321 | return; |
322 | case PDB_ColorItem::Type: |
323 | OS.changeColor(Color: raw_ostream::CYAN, Bold: true); |
324 | return; |
325 | case PDB_ColorItem::Identifier: |
326 | OS.changeColor(Color: raw_ostream::CYAN, Bold: false); |
327 | return; |
328 | case PDB_ColorItem::Path: |
329 | OS.changeColor(Color: raw_ostream::CYAN, Bold: false); |
330 | return; |
331 | case PDB_ColorItem::Padding: |
332 | case PDB_ColorItem::SectionHeader: |
333 | OS.changeColor(Color: raw_ostream::RED, Bold: true); |
334 | return; |
335 | case PDB_ColorItem::LiteralValue: |
336 | OS.changeColor(Color: raw_ostream::GREEN, Bold: true); |
337 | return; |
338 | } |
339 | } |
340 | |