| 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 | |