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
34using namespace llvm;
35using namespace llvm::pdb;
36
37using LayoutPtr = std::unique_ptr<ClassLayout>;
38
39typedef bool (*CompareFunc)(const LayoutPtr &S1, const LayoutPtr &S2);
40
41static bool CompareNames(const LayoutPtr &S1, const LayoutPtr &S2) {
42 return S1->getName() < S2->getName();
43}
44
45static bool CompareSizes(const LayoutPtr &S1, const LayoutPtr &S2) {
46 return S1->getSize() < S2->getSize();
47}
48
49static bool ComparePadding(const LayoutPtr &S1, const LayoutPtr &S2) {
50 return S1->deepPaddingSize() < S2->deepPaddingSize();
51}
52
53static 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
59static bool ComparePaddingImmediate(const LayoutPtr &S1, const LayoutPtr &S2) {
60 return S1->immediatePadding() < S2->immediatePadding();
61}
62
63static 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
70static 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
89template <typename Enumerator>
90static std::vector<std::unique_ptr<ClassLayout>>
91filterAndSortClassDefs(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
140TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
141
142template <typename T>
143static bool isTypeExcluded(LinePrinter &Printer, const T &Symbol) {
144 return false;
145}
146
147static 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
157static bool isTypeExcluded(LinePrinter &Printer,
158 const PDBSymbolTypeTypedef &Typedef) {
159 return Printer.IsTypeExcluded(TypeName: Typedef.getName(), Size: Typedef.getLength());
160}
161
162template <typename SymbolT>
163static 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
181static 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
195void 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
277void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) {
278 assert(opts::pretty::Enums);
279
280 EnumDumper Dumper(Printer);
281 Dumper.start(Symbol);
282}
283
284void TypeDumper::dump(const PDBSymbolTypeBuiltin &Symbol) {
285 BuiltinDumper BD(Printer);
286 BD.start(Symbol);
287}
288
289void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
290 printClassDecl(Printer, Class: Symbol);
291}
292
293void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
294 assert(opts::pretty::Typedefs);
295
296 TypedefDumper Dumper(Printer);
297 Dumper.start(Symbol);
298}
299
300void 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
309void TypeDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {
310 FunctionDumper Dumper(Printer);
311 Dumper.start(Symbol, Name: nullptr, Pointer: FunctionDumper::PointerType::None);
312}
313
314void 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
346void TypeDumper::dump(const PDBSymbolTypeVTableShape &Symbol) {
347 Printer.format(Fmt: "<vtshape ({0} methods)>", Items: Symbol.getCount());
348}
349
350void 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