1 | //===- PrettyTypeDumper.cpp - PDBSymDumper type dumper *------------ 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 "PrettyTypeDumper.h" |
10 | |
11 | #include "PrettyBuiltinDumper.h" |
12 | #include "PrettyClassDefinitionDumper.h" |
13 | #include "PrettyEnumDumper.h" |
14 | #include "PrettyFunctionDumper.h" |
15 | #include "PrettyTypedefDumper.h" |
16 | #include "llvm-pdbutil.h" |
17 | |
18 | #include "llvm/DebugInfo/PDB/ConcreteSymbolEnumerator.h" |
19 | #include "llvm/DebugInfo/PDB/IPDBLineNumber.h" |
20 | #include "llvm/DebugInfo/PDB/IPDBSession.h" |
21 | #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" |
22 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" |
23 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" |
24 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" |
25 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" |
26 | #include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" |
27 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" |
28 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" |
29 | #include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h" |
30 | #include "llvm/DebugInfo/PDB/UDTLayout.h" |
31 | #include "llvm/Support/Compiler.h" |
32 | #include "llvm/Support/FormatVariadic.h" |
33 | |
34 | using namespace llvm; |
35 | using namespace llvm::pdb; |
36 | |
37 | using LayoutPtr = std::unique_ptr<ClassLayout>; |
38 | |
39 | typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2); |
40 | |
41 | static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) { |
42 | return S1->getName() < S2->getName(); |
43 | } |
44 | |
45 | static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) { |
46 | return S1->getSize() < S2->getSize(); |
47 | } |
48 | |
49 | static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) { |
50 | return S1->deepPaddingSize() < S2->deepPaddingSize(); |
51 | } |
52 | |
53 | static bool ComparePaddingPct(const LayoutPtr &S1, const LayoutPtr &S2) { |
54 | double Pct1 = (double)S1->deepPaddingSize() / (double)S1->getSize(); |
55 | double Pct2 = (double)S2->deepPaddingSize() / (double)S2->getSize(); |
56 | return Pct1 < Pct2; |
57 | } |
58 | |
59 | static bool ComparePaddingImmediate(const LayoutPtr &S1, const LayoutPtr &S2) { |
60 | return S1->immediatePadding() < S2->immediatePadding(); |
61 | } |
62 | |
63 | static bool ComparePaddingPctImmediate(const LayoutPtr &S1, |
64 | const LayoutPtr &S2) { |
65 | double Pct1 = (double)S1->immediatePadding() / (double)S1->getSize(); |
66 | double Pct2 = (double)S2->immediatePadding() / (double)S2->getSize(); |
67 | return Pct1 < Pct2; |
68 | } |
69 | |
70 | static CompareFunc getComparisonFunc(opts::pretty::ClassSortMode Mode) { |
71 | switch (Mode) { |
72 | case opts::pretty::ClassSortMode::Name: |
73 | return CompareNames; |
74 | case opts::pretty::ClassSortMode::Size: |
75 | return CompareSizes; |
76 | case opts::pretty::ClassSortMode::Padding: |
77 | return ComparePadding; |
78 | case opts::pretty::ClassSortMode::PaddingPct: |
79 | return ComparePaddingPct; |
80 | case opts::pretty::ClassSortMode::PaddingImmediate: |
81 | return ComparePaddingImmediate; |
82 | case opts::pretty::ClassSortMode::PaddingPctImmediate: |
83 | return ComparePaddingPctImmediate; |
84 | default: |
85 | return nullptr; |
86 | } |
87 | } |
88 | |
89 | template <typename Enumerator> |
90 | static std::vector<std::unique_ptr<ClassLayout>> |
91 | filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E, |
92 | uint32_t UnfilteredCount) { |
93 | std::vector<std::unique_ptr<ClassLayout>> Filtered; |
94 | |
95 | Filtered.reserve(n: UnfilteredCount); |
96 | CompareFunc Comp = getComparisonFunc(Mode: opts::pretty::ClassOrder); |
97 | |
98 | if (UnfilteredCount > 10000) { |
99 | errs() << formatv(Fmt: "Filtering and sorting {0} types" , Vals&: UnfilteredCount); |
100 | errs().flush(); |
101 | } |
102 | uint32_t Examined = 0; |
103 | uint32_t Discarded = 0; |
104 | while (auto Class = E.getNext()) { |
105 | ++Examined; |
106 | if (Examined % 10000 == 0) { |
107 | errs() << formatv(Fmt: "Examined {0}/{1} items. {2} items discarded\n" , |
108 | Vals&: Examined, Vals&: UnfilteredCount, Vals&: Discarded); |
109 | errs().flush(); |
110 | } |
111 | |
112 | if (Class->getUnmodifiedTypeId() != 0) { |
113 | ++Discarded; |
114 | continue; |
115 | } |
116 | |
117 | if (Printer.IsTypeExcluded(TypeName: Class->getName(), Size: Class->getLength())) { |
118 | ++Discarded; |
119 | continue; |
120 | } |
121 | |
122 | auto Layout = std::make_unique<ClassLayout>(std::move(Class)); |
123 | if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) { |
124 | ++Discarded; |
125 | continue; |
126 | } |
127 | if (Layout->immediatePadding() < opts::pretty::ImmediatePaddingThreshold) { |
128 | ++Discarded; |
129 | continue; |
130 | } |
131 | |
132 | Filtered.push_back(std::move(Layout)); |
133 | } |
134 | |
135 | if (Comp) |
136 | llvm::sort(C&: Filtered, Comp); |
137 | return Filtered; |
138 | } |
139 | |
140 | TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} |
141 | |
142 | template <typename T> |
143 | static bool isTypeExcluded(LinePrinter &Printer, const T &Symbol) { |
144 | return false; |
145 | } |
146 | |
147 | static bool isTypeExcluded(LinePrinter &Printer, |
148 | const PDBSymbolTypeEnum &Enum) { |
149 | if (Printer.IsTypeExcluded(TypeName: Enum.getName(), Size: Enum.getLength())) |
150 | return true; |
151 | // Dump member enums when dumping their class definition. |
152 | if (nullptr != Enum.getClassParent()) |
153 | return true; |
154 | return false; |
155 | } |
156 | |
157 | static bool isTypeExcluded(LinePrinter &Printer, |
158 | const PDBSymbolTypeTypedef &Typedef) { |
159 | return Printer.IsTypeExcluded(TypeName: Typedef.getName(), Size: Typedef.getLength()); |
160 | } |
161 | |
162 | template <typename SymbolT> |
163 | static void dumpSymbolCategory(LinePrinter &Printer, const PDBSymbolExe &Exe, |
164 | TypeDumper &TD, StringRef Label) { |
165 | if (auto Children = Exe.findAllChildren<SymbolT>()) { |
166 | Printer.NewLine(); |
167 | WithColor(Printer, PDB_ColorItem::Identifier).get() << Label; |
168 | Printer << ": (" << Children->getChildCount() << " items)" ; |
169 | Printer.Indent(); |
170 | while (auto Child = Children->getNext()) { |
171 | if (isTypeExcluded(Printer, *Child)) |
172 | continue; |
173 | |
174 | Printer.NewLine(); |
175 | Child->dump(TD); |
176 | } |
177 | Printer.Unindent(); |
178 | } |
179 | } |
180 | |
181 | static void printClassDecl(LinePrinter &Printer, |
182 | const PDBSymbolTypeUDT &Class) { |
183 | if (Class.getUnmodifiedTypeId() != 0) { |
184 | if (Class.isConstType()) |
185 | WithColor(Printer, PDB_ColorItem::Keyword).get() << "const " ; |
186 | if (Class.isVolatileType()) |
187 | WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile " ; |
188 | if (Class.isUnalignedType()) |
189 | WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned " ; |
190 | } |
191 | WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " " ; |
192 | WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); |
193 | } |
194 | |
195 | void TypeDumper::start(const PDBSymbolExe &Exe) { |
196 | if (opts::pretty::Enums) |
197 | dumpSymbolCategory<PDBSymbolTypeEnum>(Printer, Exe, TD&: *this, Label: "Enums" ); |
198 | |
199 | if (opts::pretty::Funcsigs) |
200 | dumpSymbolCategory<PDBSymbolTypeFunctionSig>(Printer, Exe, TD&: *this, |
201 | Label: "Function Signatures" ); |
202 | |
203 | if (opts::pretty::Typedefs) |
204 | dumpSymbolCategory<PDBSymbolTypeTypedef>(Printer, Exe, TD&: *this, Label: "Typedefs" ); |
205 | |
206 | if (opts::pretty::Arrays) |
207 | dumpSymbolCategory<PDBSymbolTypeArray>(Printer, Exe, TD&: *this, Label: "Arrays" ); |
208 | |
209 | if (opts::pretty::Pointers) |
210 | dumpSymbolCategory<PDBSymbolTypePointer>(Printer, Exe, TD&: *this, Label: "Pointers" ); |
211 | |
212 | if (opts::pretty::VTShapes) |
213 | dumpSymbolCategory<PDBSymbolTypeVTableShape>(Printer, Exe, TD&: *this, |
214 | Label: "VFTable Shapes" ); |
215 | |
216 | if (opts::pretty::Classes) { |
217 | if (auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>()) { |
218 | uint32_t All = Classes->getChildCount(); |
219 | |
220 | Printer.NewLine(); |
221 | WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes" ; |
222 | |
223 | bool Precompute = false; |
224 | Precompute = |
225 | (opts::pretty::ClassOrder != opts::pretty::ClassSortMode::None); |
226 | |
227 | // If we're using no sort mode, then we can start getting immediate output |
228 | // from the tool by just filtering as we go, rather than processing |
229 | // everything up front so that we can sort it. This makes the tool more |
230 | // responsive. So only precompute the filtered/sorted set of classes if |
231 | // necessary due to the specified options. |
232 | std::vector<LayoutPtr> Filtered; |
233 | uint32_t Shown = All; |
234 | if (Precompute) { |
235 | Filtered = filterAndSortClassDefs(Printer, E&: *Classes, UnfilteredCount: All); |
236 | |
237 | Shown = Filtered.size(); |
238 | } |
239 | |
240 | Printer << ": (Showing " << Shown << " items" ; |
241 | if (Shown < All) |
242 | Printer << ", " << (All - Shown) << " filtered" ; |
243 | Printer << ")" ; |
244 | Printer.Indent(); |
245 | |
246 | // If we pre-computed, iterate the filtered/sorted list, otherwise iterate |
247 | // the DIA enumerator and filter on the fly. |
248 | if (Precompute) { |
249 | for (auto &Class : Filtered) |
250 | dumpClassLayout(Class: *Class); |
251 | } else { |
252 | while (auto Class = Classes->getNext()) { |
253 | if (Printer.IsTypeExcluded(TypeName: Class->getName(), Size: Class->getLength())) |
254 | continue; |
255 | |
256 | // No point duplicating a full class layout. Just print the modified |
257 | // declaration and continue. |
258 | if (Class->getUnmodifiedTypeId() != 0) { |
259 | Printer.NewLine(); |
260 | printClassDecl(Printer, Class: *Class); |
261 | continue; |
262 | } |
263 | |
264 | auto Layout = std::make_unique<ClassLayout>(args: std::move(Class)); |
265 | if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) |
266 | continue; |
267 | |
268 | dumpClassLayout(Class: *Layout); |
269 | } |
270 | } |
271 | |
272 | Printer.Unindent(); |
273 | } |
274 | } |
275 | } |
276 | |
277 | void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { |
278 | assert(opts::pretty::Enums); |
279 | |
280 | EnumDumper Dumper(Printer); |
281 | Dumper.start(Symbol); |
282 | } |
283 | |
284 | void TypeDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { |
285 | BuiltinDumper BD(Printer); |
286 | BD.start(Symbol); |
287 | } |
288 | |
289 | void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) { |
290 | printClassDecl(Printer, Class: Symbol); |
291 | } |
292 | |
293 | void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { |
294 | assert(opts::pretty::Typedefs); |
295 | |
296 | TypedefDumper Dumper(Printer); |
297 | Dumper.start(Symbol); |
298 | } |
299 | |
300 | void TypeDumper::dump(const PDBSymbolTypeArray &Symbol) { |
301 | auto ElementType = Symbol.getElementType(); |
302 | |
303 | ElementType->dump(Dumper&: *this); |
304 | Printer << "[" ; |
305 | WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getCount(); |
306 | Printer << "]" ; |
307 | } |
308 | |
309 | void TypeDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { |
310 | FunctionDumper Dumper(Printer); |
311 | Dumper.start(Symbol, Name: nullptr, Pointer: FunctionDumper::PointerType::None); |
312 | } |
313 | |
314 | void TypeDumper::dump(const PDBSymbolTypePointer &Symbol) { |
315 | std::unique_ptr<PDBSymbol> P = Symbol.getPointeeType(); |
316 | |
317 | if (auto *FS = dyn_cast<PDBSymbolTypeFunctionSig>(Val: P.get())) { |
318 | FunctionDumper Dumper(Printer); |
319 | FunctionDumper::PointerType PT = |
320 | Symbol.isReference() ? FunctionDumper::PointerType::Reference |
321 | : FunctionDumper::PointerType::Pointer; |
322 | Dumper.start(Symbol: *FS, Name: nullptr, Pointer: PT); |
323 | return; |
324 | } |
325 | |
326 | if (auto *UDT = dyn_cast<PDBSymbolTypeUDT>(Val: P.get())) { |
327 | printClassDecl(Printer, Class: *UDT); |
328 | } else if (P) { |
329 | P->dump(Dumper&: *this); |
330 | } |
331 | |
332 | if (auto Parent = Symbol.getClassParent()) { |
333 | auto UDT = llvm::unique_dyn_cast<PDBSymbolTypeUDT>(Val: std::move(Parent)); |
334 | if (UDT) |
335 | Printer << " " << UDT->getName() << "::" ; |
336 | } |
337 | |
338 | if (Symbol.isReference()) |
339 | Printer << "&" ; |
340 | else if (Symbol.isRValueReference()) |
341 | Printer << "&&" ; |
342 | else |
343 | Printer << "*" ; |
344 | } |
345 | |
346 | void TypeDumper::dump(const PDBSymbolTypeVTableShape &Symbol) { |
347 | Printer.format(Fmt: "<vtshape ({0} methods)>" , Items: Symbol.getCount()); |
348 | } |
349 | |
350 | void TypeDumper::dumpClassLayout(const ClassLayout &Class) { |
351 | assert(opts::pretty::Classes); |
352 | |
353 | if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) { |
354 | WithColor(Printer, PDB_ColorItem::Keyword).get() |
355 | << Class.getClass().getUdtKind() << " " ; |
356 | WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); |
357 | } else { |
358 | ClassDefinitionDumper Dumper(Printer); |
359 | Dumper.start(Class); |
360 | } |
361 | } |
362 | |