1 //===- MinimalTypeDumper.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 "MinimalTypeDumper.h"
10
11 #include "TypeReferenceTracker.h"
12
13 #include "llvm-pdbutil.h"
14 #include "llvm/DebugInfo/CodeView/CVRecord.h"
15 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
16 #include "llvm/DebugInfo/CodeView/CodeView.h"
17 #include "llvm/DebugInfo/CodeView/Formatters.h"
18 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
19 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
20 #include "llvm/DebugInfo/PDB/Native/FormatUtil.h"
21 #include "llvm/DebugInfo/PDB/Native/LinePrinter.h"
22 #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
23 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
24 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
25 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
26 #include "llvm/Object/COFF.h"
27 #include "llvm/Support/FormatVariadic.h"
28 #include "llvm/Support/MathExtras.h"
29
30 using namespace llvm;
31 using namespace llvm::codeview;
32 using namespace llvm::pdb;
33
formatClassOptions(uint32_t IndentLevel,ClassOptions Options,TpiStream * Stream,TypeIndex CurrentTypeIndex)34 static std::string formatClassOptions(uint32_t IndentLevel,
35 ClassOptions Options, TpiStream *Stream,
36 TypeIndex CurrentTypeIndex) {
37 std::vector<std::string> Opts;
38
39 if (Stream && Stream->supportsTypeLookup() &&
40 !opts::dump::DontResolveForwardRefs &&
41 ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) {
42 // If we're able to resolve forward references, do that.
43 Expected<TypeIndex> ETI =
44 Stream->findFullDeclForForwardRef(CurrentTypeIndex);
45 if (!ETI) {
46 consumeError(ETI.takeError());
47 PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (??\?)");
48 } else {
49 const char *Direction = (*ETI == CurrentTypeIndex)
50 ? "="
51 : ((*ETI < CurrentTypeIndex) ? "<-" : "->");
52 std::string Formatted =
53 formatv("forward ref ({0} {1})", Direction, *ETI).str();
54 PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted));
55 }
56 } else {
57 PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
58 }
59
60 PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options,
61 "has ctor / dtor");
62 PUSH_FLAG(ClassOptions, ContainsNestedClass, Options,
63 "contains nested class");
64 PUSH_FLAG(ClassOptions, HasConversionOperator, Options,
65 "conversion operator");
66 PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name");
67 PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
68 PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
69 PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options,
70 "overloaded operator");
71 PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options,
72 "overloaded operator=");
73 PUSH_FLAG(ClassOptions, Packed, Options, "packed");
74 PUSH_FLAG(ClassOptions, Scoped, Options, "scoped");
75 PUSH_FLAG(ClassOptions, Sealed, Options, "sealed");
76
77 return typesetItemList(Opts, 4, IndentLevel, " | ");
78 }
79
pointerOptions(PointerOptions Options)80 static std::string pointerOptions(PointerOptions Options) {
81 std::vector<std::string> Opts;
82 PUSH_FLAG(PointerOptions, Flat32, Options, "flat32");
83 PUSH_FLAG(PointerOptions, Volatile, Options, "volatile");
84 PUSH_FLAG(PointerOptions, Const, Options, "const");
85 PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned");
86 PUSH_FLAG(PointerOptions, Restrict, Options, "restrict");
87 PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt");
88 if (Opts.empty())
89 return "None";
90 return join(Opts, " | ");
91 }
92
modifierOptions(ModifierOptions Options)93 static std::string modifierOptions(ModifierOptions Options) {
94 std::vector<std::string> Opts;
95 PUSH_FLAG(ModifierOptions, Const, Options, "const");
96 PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile");
97 PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned");
98 if (Opts.empty())
99 return "None";
100 return join(Opts, " | ");
101 }
102
formatCallingConvention(CallingConvention Convention)103 static std::string formatCallingConvention(CallingConvention Convention) {
104 switch (Convention) {
105 RETURN_CASE(CallingConvention, AlphaCall, "alphacall");
106 RETURN_CASE(CallingConvention, AM33Call, "am33call");
107 RETURN_CASE(CallingConvention, ArmCall, "armcall");
108 RETURN_CASE(CallingConvention, ClrCall, "clrcall");
109 RETURN_CASE(CallingConvention, FarC, "far cdecl");
110 RETURN_CASE(CallingConvention, FarFast, "far fastcall");
111 RETURN_CASE(CallingConvention, FarPascal, "far pascal");
112 RETURN_CASE(CallingConvention, FarStdCall, "far stdcall");
113 RETURN_CASE(CallingConvention, FarSysCall, "far syscall");
114 RETURN_CASE(CallingConvention, Generic, "generic");
115 RETURN_CASE(CallingConvention, Inline, "inline");
116 RETURN_CASE(CallingConvention, M32RCall, "m32rcall");
117 RETURN_CASE(CallingConvention, MipsCall, "mipscall");
118 RETURN_CASE(CallingConvention, NearC, "cdecl");
119 RETURN_CASE(CallingConvention, NearFast, "fastcall");
120 RETURN_CASE(CallingConvention, NearPascal, "pascal");
121 RETURN_CASE(CallingConvention, NearStdCall, "stdcall");
122 RETURN_CASE(CallingConvention, NearSysCall, "near syscall");
123 RETURN_CASE(CallingConvention, NearVector, "vectorcall");
124 RETURN_CASE(CallingConvention, PpcCall, "ppccall");
125 RETURN_CASE(CallingConvention, SHCall, "shcall");
126 RETURN_CASE(CallingConvention, SH5Call, "sh5call");
127 RETURN_CASE(CallingConvention, ThisCall, "thiscall");
128 RETURN_CASE(CallingConvention, TriCall, "tricall");
129 }
130 return formatUnknownEnum(Convention);
131 }
132
formatPointerMode(PointerMode Mode)133 static std::string formatPointerMode(PointerMode Mode) {
134 switch (Mode) {
135 RETURN_CASE(PointerMode, LValueReference, "ref");
136 RETURN_CASE(PointerMode, Pointer, "pointer");
137 RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer");
138 RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer");
139 RETURN_CASE(PointerMode, RValueReference, "rvalue ref");
140 }
141 return formatUnknownEnum(Mode);
142 }
143
memberAccess(MemberAccess Access)144 static std::string memberAccess(MemberAccess Access) {
145 switch (Access) {
146 RETURN_CASE(MemberAccess, None, "");
147 RETURN_CASE(MemberAccess, Private, "private");
148 RETURN_CASE(MemberAccess, Protected, "protected");
149 RETURN_CASE(MemberAccess, Public, "public");
150 }
151 return formatUnknownEnum(Access);
152 }
153
methodKind(MethodKind Kind)154 static std::string methodKind(MethodKind Kind) {
155 switch (Kind) {
156 RETURN_CASE(MethodKind, Vanilla, "");
157 RETURN_CASE(MethodKind, Virtual, "virtual");
158 RETURN_CASE(MethodKind, Static, "static");
159 RETURN_CASE(MethodKind, Friend, "friend");
160 RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual");
161 RETURN_CASE(MethodKind, PureVirtual, "pure virtual");
162 RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual");
163 }
164 return formatUnknownEnum(Kind);
165 }
166
pointerKind(PointerKind Kind)167 static std::string pointerKind(PointerKind Kind) {
168 switch (Kind) {
169 RETURN_CASE(PointerKind, Near16, "ptr16");
170 RETURN_CASE(PointerKind, Far16, "far ptr16");
171 RETURN_CASE(PointerKind, Huge16, "huge ptr16");
172 RETURN_CASE(PointerKind, BasedOnSegment, "segment based");
173 RETURN_CASE(PointerKind, BasedOnValue, "value based");
174 RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based");
175 RETURN_CASE(PointerKind, BasedOnAddress, "address based");
176 RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based");
177 RETURN_CASE(PointerKind, BasedOnType, "type based");
178 RETURN_CASE(PointerKind, BasedOnSelf, "self based");
179 RETURN_CASE(PointerKind, Near32, "ptr32");
180 RETURN_CASE(PointerKind, Far32, "far ptr32");
181 RETURN_CASE(PointerKind, Near64, "ptr64");
182 }
183 return formatUnknownEnum(Kind);
184 }
185
memberAttributes(const MemberAttributes & Attrs)186 static std::string memberAttributes(const MemberAttributes &Attrs) {
187 std::vector<std::string> Opts;
188 std::string Access = memberAccess(Attrs.getAccess());
189 std::string Kind = methodKind(Attrs.getMethodKind());
190 if (!Access.empty())
191 Opts.push_back(Access);
192 if (!Kind.empty())
193 Opts.push_back(Kind);
194 MethodOptions Flags = Attrs.getFlags();
195 PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo");
196 PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit");
197 PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct");
198 PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated");
199 PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed");
200 return join(Opts, " ");
201 }
202
formatPointerAttrs(const PointerRecord & Record)203 static std::string formatPointerAttrs(const PointerRecord &Record) {
204 PointerMode Mode = Record.getMode();
205 PointerOptions Opts = Record.getOptions();
206 PointerKind Kind = Record.getPointerKind();
207 return std::string(formatv("mode = {0}, opts = {1}, kind = {2}",
208 formatPointerMode(Mode), pointerOptions(Opts),
209 pointerKind(Kind)));
210 }
211
formatFunctionOptions(FunctionOptions Options)212 static std::string formatFunctionOptions(FunctionOptions Options) {
213 std::vector<std::string> Opts;
214
215 PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
216 PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
217 "constructor with virtual bases");
218 PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
219 if (Opts.empty())
220 return "None";
221 return join(Opts, " | ");
222 }
223
visitTypeBegin(CVType & Record,TypeIndex Index)224 Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
225 CurrentTypeIndex = Index;
226 // formatLine puts the newline at the beginning, so we use formatLine here
227 // to start a new line, and then individual visit methods use format to
228 // append to the existing line.
229 P.formatLine("{0} | {1} [size = {2}",
230 fmt_align(Index, AlignStyle::Right, Width),
231 formatTypeLeafKind(Record.kind()), Record.length());
232 if (Hashes) {
233 std::string H;
234 if (Index.toArrayIndex() >= HashValues.size()) {
235 H = "(not present)";
236 } else {
237 uint32_t Hash = HashValues[Index.toArrayIndex()];
238 Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
239 if (!MaybeHash)
240 return MaybeHash.takeError();
241 uint32_t OurHash = *MaybeHash;
242 OurHash %= NumHashBuckets;
243 if (Hash == OurHash)
244 H = "0x" + utohexstr(Hash);
245 else
246 H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
247 }
248 P.format(", hash = {0}", H);
249 }
250 if (RefTracker) {
251 if (RefTracker->isTypeReferenced(Index))
252 P.format(", referenced");
253 else
254 P.format(", unreferenced");
255 }
256 P.format("]");
257 P.Indent(Width + 3);
258 return Error::success();
259 }
260
visitTypeEnd(CVType & Record)261 Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
262 P.Unindent(Width + 3);
263 if (RecordBytes) {
264 AutoIndent Indent(P, 9);
265 P.formatBinary("Bytes", Record.RecordData, 0);
266 }
267 return Error::success();
268 }
269
visitMemberBegin(CVMemberRecord & Record)270 Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
271 P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
272 return Error::success();
273 }
274
visitMemberEnd(CVMemberRecord & Record)275 Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
276 if (RecordBytes) {
277 AutoIndent Indent(P, 2);
278 P.formatBinary("Bytes", Record.Data, 0);
279 }
280 return Error::success();
281 }
282
getTypeName(TypeIndex TI) const283 StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
284 if (TI.isNoneType())
285 return "";
286 return Types.getTypeName(TI);
287 }
288
visitKnownRecord(CVType & CVR,FieldListRecord & FieldList)289 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
290 FieldListRecord &FieldList) {
291 if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
292 return EC;
293
294 return Error::success();
295 }
296
visitKnownRecord(CVType & CVR,StringIdRecord & String)297 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
298 StringIdRecord &String) {
299 P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
300 return Error::success();
301 }
302
visitKnownRecord(CVType & CVR,ArgListRecord & Args)303 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
304 ArgListRecord &Args) {
305 auto Indices = Args.getIndices();
306 if (Indices.empty())
307 return Error::success();
308
309 auto Max = std::max_element(Indices.begin(), Indices.end());
310 uint32_t W = NumDigits(Max->getIndex()) + 2;
311
312 for (auto I : Indices)
313 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
314 getTypeName(I));
315 return Error::success();
316 }
317
visitKnownRecord(CVType & CVR,StringListRecord & Strings)318 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
319 StringListRecord &Strings) {
320 auto Indices = Strings.getIndices();
321 if (Indices.empty())
322 return Error::success();
323
324 auto Max = std::max_element(Indices.begin(), Indices.end());
325 uint32_t W = NumDigits(Max->getIndex()) + 2;
326
327 for (auto I : Indices)
328 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
329 getTypeName(I));
330 return Error::success();
331 }
332
visitKnownRecord(CVType & CVR,ClassRecord & Class)333 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
334 ClassRecord &Class) {
335 P.format(" `{0}`", Class.Name);
336 if (Class.hasUniqueName())
337 P.formatLine("unique name: `{0}`", Class.UniqueName);
338 P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
339 Class.VTableShape, Class.DerivationList, Class.FieldList);
340 P.formatLine("options: {0}, sizeof {1}",
341 formatClassOptions(P.getIndentLevel(), Class.Options, Stream,
342 CurrentTypeIndex),
343 Class.Size);
344 return Error::success();
345 }
346
visitKnownRecord(CVType & CVR,UnionRecord & Union)347 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
348 UnionRecord &Union) {
349 P.format(" `{0}`", Union.Name);
350 if (Union.hasUniqueName())
351 P.formatLine("unique name: `{0}`", Union.UniqueName);
352 P.formatLine("field list: {0}", Union.FieldList);
353 P.formatLine("options: {0}, sizeof {1}",
354 formatClassOptions(P.getIndentLevel(), Union.Options, Stream,
355 CurrentTypeIndex),
356 Union.Size);
357 return Error::success();
358 }
359
visitKnownRecord(CVType & CVR,EnumRecord & Enum)360 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
361 P.format(" `{0}`", Enum.Name);
362 if (Enum.hasUniqueName())
363 P.formatLine("unique name: `{0}`", Enum.UniqueName);
364 P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
365 Enum.UnderlyingType);
366 P.formatLine("options: {0}",
367 formatClassOptions(P.getIndentLevel(), Enum.Options, Stream,
368 CurrentTypeIndex));
369 return Error::success();
370 }
371
visitKnownRecord(CVType & CVR,ArrayRecord & AT)372 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
373 if (AT.Name.empty()) {
374 P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
375 AT.IndexType, AT.ElementType);
376 } else {
377 P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
378 AT.Name, AT.Size, AT.IndexType, AT.ElementType);
379 }
380 return Error::success();
381 }
382
visitKnownRecord(CVType & CVR,VFTableRecord & VFT)383 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
384 VFTableRecord &VFT) {
385 P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
386 VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
387 P.formatLine("method names: ");
388 if (!VFT.MethodNames.empty()) {
389 std::string Sep =
390 formatv("\n{0}",
391 fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
392 .str();
393 P.print(join(VFT.MethodNames, Sep));
394 }
395 return Error::success();
396 }
397
visitKnownRecord(CVType & CVR,MemberFuncIdRecord & Id)398 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
399 MemberFuncIdRecord &Id) {
400 P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
401 Id.FunctionType, Id.ClassType);
402 return Error::success();
403 }
404
visitKnownRecord(CVType & CVR,ProcedureRecord & Proc)405 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
406 ProcedureRecord &Proc) {
407 P.formatLine("return type = {0}, # args = {1}, param list = {2}",
408 Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
409 P.formatLine("calling conv = {0}, options = {1}",
410 formatCallingConvention(Proc.CallConv),
411 formatFunctionOptions(Proc.Options));
412 return Error::success();
413 }
414
visitKnownRecord(CVType & CVR,MemberFunctionRecord & MF)415 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
416 MemberFunctionRecord &MF) {
417 P.formatLine("return type = {0}, # args = {1}, param list = {2}",
418 MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
419 P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
420 MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
421 P.formatLine("calling conv = {0}, options = {1}",
422 formatCallingConvention(MF.CallConv),
423 formatFunctionOptions(MF.Options));
424 return Error::success();
425 }
426
visitKnownRecord(CVType & CVR,FuncIdRecord & Func)427 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
428 FuncIdRecord &Func) {
429 P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
430 Func.FunctionType, Func.ParentScope);
431 return Error::success();
432 }
433
visitKnownRecord(CVType & CVR,TypeServer2Record & TS)434 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
435 TypeServer2Record &TS) {
436 P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
437 return Error::success();
438 }
439
visitKnownRecord(CVType & CVR,PointerRecord & Ptr)440 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
441 PointerRecord &Ptr) {
442 P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
443 formatPointerAttrs(Ptr));
444 return Error::success();
445 }
446
visitKnownRecord(CVType & CVR,ModifierRecord & Mod)447 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
448 ModifierRecord &Mod) {
449 P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
450 modifierOptions(Mod.Modifiers));
451 return Error::success();
452 }
453
visitKnownRecord(CVType & CVR,VFTableShapeRecord & Shape)454 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
455 VFTableShapeRecord &Shape) {
456 return Error::success();
457 }
458
visitKnownRecord(CVType & CVR,UdtModSourceLineRecord & U)459 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
460 UdtModSourceLineRecord &U) {
461 P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
462 U.SourceFile.getIndex(), U.LineNumber);
463 return Error::success();
464 }
465
visitKnownRecord(CVType & CVR,UdtSourceLineRecord & U)466 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
467 UdtSourceLineRecord &U) {
468 P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
469 U.SourceFile.getIndex(), U.LineNumber);
470 return Error::success();
471 }
472
visitKnownRecord(CVType & CVR,BitFieldRecord & BF)473 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
474 BitFieldRecord &BF) {
475 P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
476 BF.BitOffset, BF.BitSize);
477 return Error::success();
478 }
479
visitKnownRecord(CVType & CVR,MethodOverloadListRecord & Overloads)480 Error MinimalTypeDumpVisitor::visitKnownRecord(
481 CVType &CVR, MethodOverloadListRecord &Overloads) {
482 for (auto &M : Overloads.Methods)
483 P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
484 M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
485 return Error::success();
486 }
487
visitKnownRecord(CVType & CVR,BuildInfoRecord & BI)488 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
489 BuildInfoRecord &BI) {
490 auto Indices = BI.ArgIndices;
491 if (Indices.empty())
492 return Error::success();
493
494 auto Max = std::max_element(Indices.begin(), Indices.end());
495 uint32_t W = NumDigits(Max->getIndex()) + 2;
496
497 for (auto I : Indices)
498 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
499 getTypeName(I));
500 return Error::success();
501 }
502
visitKnownRecord(CVType & CVR,LabelRecord & R)503 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
504 std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
505 P.format(" type = {0}", Type);
506 return Error::success();
507 }
508
visitKnownRecord(CVType & CVR,PrecompRecord & Precomp)509 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
510 PrecompRecord &Precomp) {
511 P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
512 " precomp path = {3}",
513 Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
514 Precomp.PrecompFilePath);
515 return Error::success();
516 }
517
visitKnownRecord(CVType & CVR,EndPrecompRecord & EP)518 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
519 EndPrecompRecord &EP) {
520 P.format(" signature = {0:X+}", EP.Signature);
521 return Error::success();
522 }
523
visitKnownMember(CVMemberRecord & CVR,NestedTypeRecord & Nested)524 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
525 NestedTypeRecord &Nested) {
526 P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
527 return Error::success();
528 }
529
visitKnownMember(CVMemberRecord & CVR,OneMethodRecord & Method)530 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
531 OneMethodRecord &Method) {
532 P.format(" [name = `{0}`]", Method.Name);
533 AutoIndent Indent(P);
534 P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
535 Method.VFTableOffset, memberAttributes(Method.Attrs));
536 return Error::success();
537 }
538
visitKnownMember(CVMemberRecord & CVR,OverloadedMethodRecord & Method)539 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
540 OverloadedMethodRecord &Method) {
541 P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
542 Method.Name, Method.NumOverloads, Method.MethodList);
543 return Error::success();
544 }
545
visitKnownMember(CVMemberRecord & CVR,DataMemberRecord & Field)546 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
547 DataMemberRecord &Field) {
548 P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
549 Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
550 return Error::success();
551 }
552
visitKnownMember(CVMemberRecord & CVR,StaticDataMemberRecord & Field)553 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
554 StaticDataMemberRecord &Field) {
555 P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
556 memberAttributes(Field.Attrs));
557 return Error::success();
558 }
559
visitKnownMember(CVMemberRecord & CVR,EnumeratorRecord & Enum)560 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
561 EnumeratorRecord &Enum) {
562 P.format(" [{0} = {1}]", Enum.Name,
563 toString(Enum.Value, 10, Enum.Value.isSigned()));
564 return Error::success();
565 }
566
visitKnownMember(CVMemberRecord & CVR,BaseClassRecord & Base)567 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
568 BaseClassRecord &Base) {
569 AutoIndent Indent(P);
570 P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
571 memberAttributes(Base.Attrs));
572 return Error::success();
573 }
574
visitKnownMember(CVMemberRecord & CVR,VirtualBaseClassRecord & Base)575 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
576 VirtualBaseClassRecord &Base) {
577 AutoIndent Indent(P);
578 P.formatLine(
579 "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
580 Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
581 P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
582 return Error::success();
583 }
584
visitKnownMember(CVMemberRecord & CVR,ListContinuationRecord & Cont)585 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
586 ListContinuationRecord &Cont) {
587 P.format(" continuation = {0}", Cont.ContinuationIndex);
588 return Error::success();
589 }
590
visitKnownMember(CVMemberRecord & CVR,VFPtrRecord & VFP)591 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
592 VFPtrRecord &VFP) {
593 P.format(" type = {0}", VFP.Type);
594 return Error::success();
595 }
596