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 { |
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(String: 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(Range&: IncludeFilters, P: match_pred)) |
45 | return true; |
46 | |
47 | if (any_of(Range&: ExcludeFilters, P: match_pred)) |
48 | return true; |
49 | |
50 | return false; |
51 | } |
52 | } // namespace |
53 | |
54 | using namespace llvm; |
55 | |
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(List&: ExcludeTypeFilters, Begin: Filters.ExcludeTypes.begin(), |
61 | End: Filters.ExcludeTypes.end()); |
62 | SetFilters(List&: ExcludeSymbolFilters, Begin: Filters.ExcludeSymbols.begin(), |
63 | End: Filters.ExcludeSymbols.end()); |
64 | SetFilters(List&: ExcludeCompilandFilters, Begin: Filters.ExcludeCompilands.begin(), |
65 | End: Filters.ExcludeCompilands.end()); |
66 | |
67 | SetFilters(List&: IncludeTypeFilters, Begin: Filters.IncludeTypes.begin(), |
68 | End: Filters.IncludeTypes.end()); |
69 | SetFilters(List&: IncludeSymbolFilters, Begin: Filters.IncludeSymbols.begin(), |
70 | End: Filters.IncludeSymbols.end()); |
71 | SetFilters(List&: IncludeCompilandFilters, Begin: Filters.IncludeCompilands.begin(), |
72 | End: Filters.IncludeCompilands.end()); |
73 | } |
74 | |
75 | void LinePrinter::Indent(uint32_t Amount) { |
76 | if (Amount == 0) |
77 | Amount = IndentSpaces; |
78 | CurrentIndent += Amount; |
79 | } |
80 | |
81 | void LinePrinter::Unindent(uint32_t Amount) { |
82 | if (Amount == 0) |
83 | Amount = IndentSpaces; |
84 | CurrentIndent = std::max<int>(a: 0, b: CurrentIndent - Amount); |
85 | } |
86 | |
87 | void LinePrinter::NewLine() { |
88 | OS << "\n" ; |
89 | OS.indent(NumSpaces: CurrentIndent); |
90 | } |
91 | |
92 | void LinePrinter::print(const Twine &T) { OS << T; } |
93 | |
94 | void LinePrinter::printLine(const Twine &T) { |
95 | NewLine(); |
96 | OS << T; |
97 | } |
98 | |
99 | bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { |
100 | if (IsTypeExcluded(TypeName: Class.getName(), Size: Class.getSize())) |
101 | return true; |
102 | if (Class.deepPaddingSize() < Filters.PaddingThreshold) |
103 | return true; |
104 | return false; |
105 | } |
106 | |
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(Bytes: Data, FirstByteOffset: StartOffset, NumPerLine: 32, ByteGroupSize: 4, |
114 | IndentLevel: CurrentIndent + IndentSpaces, Upper: true); |
115 | NewLine(); |
116 | } |
117 | OS << ")" ; |
118 | } |
119 | |
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(Bytes: Data, FirstByteOffset: Base, NumPerLine: 32, ByteGroupSize: 4, |
128 | IndentLevel: CurrentIndent + IndentSpaces, Upper: true); |
129 | NewLine(); |
130 | } |
131 | OS << ")" ; |
132 | } |
133 | |
134 | namespace { |
135 | struct Run { |
136 | Run() = default; |
137 | explicit Run(uint32_t Block) : Block(Block) {} |
138 | uint32_t Block = 0; |
139 | uint64_t ByteLen = 0; |
140 | }; |
141 | } // namespace |
142 | |
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(args&: 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(args&: NextBlock); |
159 | CurrentRun = &Runs.back(); |
160 | } |
161 | uint64_t Used = |
162 | std::min(a: static_cast<uint64_t>(BlockSize), b: StreamBytesRemaining); |
163 | CurrentRun->ByteLen += Used; |
164 | StreamBytesRemaining -= Used; |
165 | CurrentBlock = NextBlock; |
166 | Blocks = Blocks.drop_front(); |
167 | } |
168 | return Runs; |
169 | } |
170 | |
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(x: R, y&: Offset); |
175 | Offset -= R.ByteLen; |
176 | } |
177 | llvm_unreachable("Invalid offset!" ); |
178 | } |
179 | |
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(Fmt: "Stream {0}: Not present" , Items&: StreamIdx); |
186 | return; |
187 | } |
188 | if (Size + Offset > File.getStreamByteSize(StreamIndex: StreamIdx)) { |
189 | formatLine( |
190 | Fmt: "Stream {0}: Invalid offset and size, range out of stream bounds" , |
191 | Items&: StreamIdx); |
192 | return; |
193 | } |
194 | |
195 | auto S = File.createIndexedStream(SN: StreamIdx); |
196 | if (!S) { |
197 | NewLine(); |
198 | formatLine(Fmt: "Stream {0}: Not present" , Items&: StreamIdx); |
199 | return; |
200 | } |
201 | |
202 | uint64_t End = |
203 | (Size == 0) ? S->getLength() : std::min(a: Offset + Size, b: S->getLength()); |
204 | Size = End - Offset; |
205 | |
206 | formatLine(Fmt: "Stream {0}: {1} (dumping {2:N} / {3:N} bytes)" , Items&: StreamIdx, |
207 | Items&: StreamPurpose, Items&: Size, Items: S->getLength()); |
208 | AutoIndent Indent(*this); |
209 | BinaryStreamRef Slice(*S); |
210 | BinarySubstreamRef Substream; |
211 | Substream.Offset = Offset; |
212 | Substream.StreamData = Slice.drop_front(N: Offset).keep_front(N: Size); |
213 | |
214 | auto Layout = File.getStreamLayout(StreamIdx); |
215 | formatMsfStreamData(Label, File, Stream: Layout, Substream); |
216 | } |
217 | |
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(BlockSize: File.getBlockSize(), Layout: 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(args&: FoundRun, args&: RunOffset) = findRun(Offset: Substream.Offset, Runs); |
233 | assert(FoundRun.ByteLen >= RunOffset); |
234 | uint64_t Len = FoundRun.ByteLen - RunOffset; |
235 | Len = std::min(a: Len, b: Reader.bytesRemaining()); |
236 | uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset; |
237 | ArrayRef<uint8_t> Data; |
238 | consumeError(Err: Reader.readBytes(Buffer&: Data, Size: Len)); |
239 | OS << format_bytes_with_ascii(Bytes: Data, FirstByteOffset: Base, NumPerLine: 32, ByteGroupSize: 4, |
240 | IndentLevel: CurrentIndent + IndentSpaces, Upper: true); |
241 | if (Reader.bytesRemaining() > 0) { |
242 | NewLine(); |
243 | OS << formatv(Fmt: " {0}" , |
244 | Vals: fmt_align(Item: "<discontinuity>" , Where: AlignStyle::Center, Amount: 114, Fill: '-')); |
245 | } |
246 | Substream.Offset += Len; |
247 | } |
248 | NewLine(); |
249 | OS << ")" ; |
250 | } |
251 | |
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(Fmt: "Block {0} (\n" , Vals: uint32_t(Blocks.front())); |
261 | uint64_t UsedBytes = |
262 | std::min(a: L, b: static_cast<uint64_t>(File.getBlockSize())); |
263 | ArrayRef<uint8_t> BlockData = |
264 | cantFail(ValOrErr: File.getBlockData(BlockIndex: Blocks.front(), NumBytes: File.getBlockSize())); |
265 | uint64_t BaseOffset = Blocks.front(); |
266 | BaseOffset *= File.getBlockSize(); |
267 | OS << format_bytes_with_ascii(Bytes: BlockData, FirstByteOffset: BaseOffset, NumPerLine: 32, ByteGroupSize: 4, |
268 | IndentLevel: CurrentIndent + IndentSpaces, Upper: true); |
269 | NewLine(); |
270 | OS << ")" ; |
271 | NewLine(); |
272 | L -= UsedBytes; |
273 | Blocks = Blocks.drop_front(); |
274 | } |
275 | } |
276 | |
277 | bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint64_t Size) { |
278 | if (IsItemExcluded(Item: TypeName, IncludeFilters&: IncludeTypeFilters, ExcludeFilters&: ExcludeTypeFilters)) |
279 | return true; |
280 | if (Size < Filters.SizeThreshold) |
281 | return true; |
282 | return false; |
283 | } |
284 | |
285 | bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { |
286 | return IsItemExcluded(Item: SymbolName, IncludeFilters&: IncludeSymbolFilters, ExcludeFilters&: ExcludeSymbolFilters); |
287 | } |
288 | |
289 | bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { |
290 | return IsItemExcluded(Item: CompilandName, IncludeFilters&: IncludeCompilandFilters, |
291 | ExcludeFilters&: ExcludeCompilandFilters); |
292 | } |
293 | |
294 | WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) |
295 | : OS(P.OS), UseColor(P.hasColor()) { |
296 | if (UseColor) |
297 | applyColor(C); |
298 | } |
299 | |
300 | WithColor::~WithColor() { |
301 | if (UseColor) |
302 | OS.resetColor(); |
303 | } |
304 | |
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(Color: raw_ostream::GREEN, Bold: false); |
312 | return; |
313 | case PDB_ColorItem::Address: |
314 | OS.changeColor(Color: raw_ostream::YELLOW, /*bold=*/Bold: true); |
315 | return; |
316 | case PDB_ColorItem::Keyword: |
317 | OS.changeColor(Color: raw_ostream::MAGENTA, Bold: true); |
318 | return; |
319 | case PDB_ColorItem::Register: |
320 | case PDB_ColorItem::Offset: |
321 | OS.changeColor(Color: raw_ostream::YELLOW, Bold: false); |
322 | return; |
323 | case PDB_ColorItem::Type: |
324 | OS.changeColor(Color: raw_ostream::CYAN, Bold: true); |
325 | return; |
326 | case PDB_ColorItem::Identifier: |
327 | OS.changeColor(Color: raw_ostream::CYAN, Bold: false); |
328 | return; |
329 | case PDB_ColorItem::Path: |
330 | OS.changeColor(Color: raw_ostream::CYAN, Bold: false); |
331 | return; |
332 | case PDB_ColorItem::Padding: |
333 | case PDB_ColorItem::SectionHeader: |
334 | OS.changeColor(Color: raw_ostream::RED, Bold: true); |
335 | return; |
336 | case PDB_ColorItem::LiteralValue: |
337 | OS.changeColor(Color: raw_ostream::GREEN, Bold: true); |
338 | return; |
339 | } |
340 | } |
341 | |