1 //===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "MinimalTypeDumper.h"
12 #include "FormatUtil.h"
13 #include "LinePrinter.h"
15 #include "llvm/DebugInfo/CodeView/CVRecord.h"
16 #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
17 #include "llvm/DebugInfo/CodeView/CodeView.h"
18 #include "llvm/DebugInfo/CodeView/Formatters.h"
19 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
20 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
21 #include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
22 #include "llvm/Support/FormatVariadic.h"
23 #include "llvm/Support/MathExtras.h"
26 using namespace llvm::codeview;
27 using namespace llvm::pdb;
29 static StringRef getLeafTypeName(TypeLeafKind K) {
31 #define TYPE_RECORD(EnumName, value, name) \
34 #include "llvm/DebugInfo/CodeView/CodeViewTypes.def"
36 llvm_unreachable("Unknown type leaf kind!");
41 static std::string formatClassOptions(uint32_t IndentLevel,
42 ClassOptions Options) {
43 std::vector<std::string> Opts;
44 PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options,
46 PUSH_FLAG(ClassOptions, ContainsNestedClass, Options,
47 "contains nested class");
48 PUSH_FLAG(ClassOptions, HasConversionOperator, Options,
49 "conversion operator");
50 PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
51 PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name");
52 PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
53 PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
54 PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options,
55 "overloaded operator");
56 PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options,
57 "overloaded operator=");
58 PUSH_FLAG(ClassOptions, Packed, Options, "packed");
59 PUSH_FLAG(ClassOptions, Scoped, Options, "scoped");
60 PUSH_FLAG(ClassOptions, Sealed, Options, "sealed");
62 return typesetItemList(Opts, 4, IndentLevel, " | ");
65 static std::string pointerOptions(PointerOptions Options) {
66 std::vector<std::string> Opts;
67 PUSH_FLAG(PointerOptions, Flat32, Options, "flat32");
68 PUSH_FLAG(PointerOptions, Volatile, Options, "volatile");
69 PUSH_FLAG(PointerOptions, Const, Options, "const");
70 PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned");
71 PUSH_FLAG(PointerOptions, Restrict, Options, "restrict");
72 PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt");
75 return join(Opts, " | ");
78 static std::string modifierOptions(ModifierOptions Options) {
79 std::vector<std::string> Opts;
80 PUSH_FLAG(ModifierOptions, Const, Options, "const");
81 PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile");
82 PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned");
85 return join(Opts, " | ");
88 static std::string formatCallingConvention(CallingConvention Convention) {
90 RETURN_CASE(CallingConvention, AlphaCall, "alphacall");
91 RETURN_CASE(CallingConvention, AM33Call, "am33call");
92 RETURN_CASE(CallingConvention, ArmCall, "armcall");
93 RETURN_CASE(CallingConvention, ClrCall, "clrcall");
94 RETURN_CASE(CallingConvention, FarC, "far cdecl");
95 RETURN_CASE(CallingConvention, FarFast, "far fastcall");
96 RETURN_CASE(CallingConvention, FarPascal, "far pascal");
97 RETURN_CASE(CallingConvention, FarStdCall, "far stdcall");
98 RETURN_CASE(CallingConvention, FarSysCall, "far syscall");
99 RETURN_CASE(CallingConvention, Generic, "generic");
100 RETURN_CASE(CallingConvention, Inline, "inline");
101 RETURN_CASE(CallingConvention, M32RCall, "m32rcall");
102 RETURN_CASE(CallingConvention, MipsCall, "mipscall");
103 RETURN_CASE(CallingConvention, NearC, "cdecl");
104 RETURN_CASE(CallingConvention, NearFast, "fastcall");
105 RETURN_CASE(CallingConvention, NearPascal, "pascal");
106 RETURN_CASE(CallingConvention, NearStdCall, "stdcall");
107 RETURN_CASE(CallingConvention, NearSysCall, "near syscall");
108 RETURN_CASE(CallingConvention, NearVector, "vectorcall");
109 RETURN_CASE(CallingConvention, PpcCall, "ppccall");
110 RETURN_CASE(CallingConvention, SHCall, "shcall");
111 RETURN_CASE(CallingConvention, SH5Call, "sh5call");
112 RETURN_CASE(CallingConvention, ThisCall, "thiscall");
113 RETURN_CASE(CallingConvention, TriCall, "tricall");
115 return formatUnknownEnum(Convention);
118 static std::string formatPointerMode(PointerMode Mode) {
120 RETURN_CASE(PointerMode, LValueReference, "ref");
121 RETURN_CASE(PointerMode, Pointer, "pointer");
122 RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer");
123 RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer");
124 RETURN_CASE(PointerMode, RValueReference, "rvalue ref");
126 return formatUnknownEnum(Mode);
129 static std::string memberAccess(MemberAccess Access) {
131 RETURN_CASE(MemberAccess, None, "");
132 RETURN_CASE(MemberAccess, Private, "private");
133 RETURN_CASE(MemberAccess, Protected, "protected");
134 RETURN_CASE(MemberAccess, Public, "public");
136 return formatUnknownEnum(Access);
139 static std::string methodKind(MethodKind Kind) {
141 RETURN_CASE(MethodKind, Vanilla, "");
142 RETURN_CASE(MethodKind, Virtual, "virtual");
143 RETURN_CASE(MethodKind, Static, "static");
144 RETURN_CASE(MethodKind, Friend, "friend");
145 RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual");
146 RETURN_CASE(MethodKind, PureVirtual, "pure virtual");
147 RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual");
149 return formatUnknownEnum(Kind);
152 static std::string pointerKind(PointerKind Kind) {
154 RETURN_CASE(PointerKind, Near16, "ptr16");
155 RETURN_CASE(PointerKind, Far16, "far ptr16");
156 RETURN_CASE(PointerKind, Huge16, "huge ptr16");
157 RETURN_CASE(PointerKind, BasedOnSegment, "segment based");
158 RETURN_CASE(PointerKind, BasedOnValue, "value based");
159 RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based");
160 RETURN_CASE(PointerKind, BasedOnAddress, "address based");
161 RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based");
162 RETURN_CASE(PointerKind, BasedOnType, "type based");
163 RETURN_CASE(PointerKind, BasedOnSelf, "self based");
164 RETURN_CASE(PointerKind, Near32, "ptr32");
165 RETURN_CASE(PointerKind, Far32, "far ptr32");
166 RETURN_CASE(PointerKind, Near64, "ptr64");
168 return formatUnknownEnum(Kind);
171 static std::string memberAttributes(const MemberAttributes &Attrs) {
172 std::vector<std::string> Opts;
173 std::string Access = memberAccess(Attrs.getAccess());
174 std::string Kind = methodKind(Attrs.getMethodKind());
176 Opts.push_back(Access);
178 Opts.push_back(Kind);
179 MethodOptions Flags = Attrs.getFlags();
180 PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo");
181 PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit");
182 PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct");
183 PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated");
184 PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed");
185 return join(Opts, " ");
188 static std::string formatPointerAttrs(const PointerRecord &Record) {
189 PointerMode Mode = Record.getMode();
190 PointerOptions Opts = Record.getOptions();
191 PointerKind Kind = Record.getPointerKind();
192 return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode),
193 pointerOptions(Opts), pointerKind(Kind));
196 static std::string formatFunctionOptions(FunctionOptions Options) {
197 std::vector<std::string> Opts;
199 PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
200 PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
201 "constructor with virtual bases");
202 PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
205 return join(Opts, " | ");
208 Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
209 // formatLine puts the newline at the beginning, so we use formatLine here
210 // to start a new line, and then individual visit methods use format to
211 // append to the existing line.
213 P.formatLine("{0} | {1} [size = {2}]",
214 fmt_align(Index, AlignStyle::Right, Width),
215 getLeafTypeName(Record.Type), Record.length());
218 if (Index.toArrayIndex() >= HashValues.size()) {
221 uint32_t Hash = HashValues[Index.toArrayIndex()];
222 Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
224 return MaybeHash.takeError();
225 uint32_t OurHash = *MaybeHash;
226 OurHash %= NumHashBuckets;
228 H = "0x" + utohexstr(Hash);
230 H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
232 P.formatLine("{0} | {1} [size = {2}, hash = {3}]",
233 fmt_align(Index, AlignStyle::Right, Width),
234 getLeafTypeName(Record.Type), Record.length(), H);
237 return Error::success();
239 Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
240 P.Unindent(Width + 3);
242 AutoIndent Indent(P, 9);
243 P.formatBinary("Bytes", Record.RecordData, 0);
245 return Error::success();
248 Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
249 P.formatLine("- {0}", getLeafTypeName(Record.Kind));
250 return Error::success();
253 Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
255 AutoIndent Indent(P, 2);
256 P.formatBinary("Bytes", Record.Data, 0);
258 return Error::success();
261 StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
264 return Types.getTypeName(TI);
267 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
268 FieldListRecord &FieldList) {
269 if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
272 return Error::success();
275 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
276 StringIdRecord &String) {
277 P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
278 return Error::success();
281 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
282 ArgListRecord &Args) {
283 auto Indices = Args.getIndices();
285 return Error::success();
287 auto Max = std::max_element(Indices.begin(), Indices.end());
288 uint32_t W = NumDigits(Max->getIndex()) + 2;
290 for (auto I : Indices)
291 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
293 return Error::success();
296 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
297 StringListRecord &Strings) {
298 auto Indices = Strings.getIndices();
300 return Error::success();
302 auto Max = std::max_element(Indices.begin(), Indices.end());
303 uint32_t W = NumDigits(Max->getIndex()) + 2;
305 for (auto I : Indices)
306 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
308 return Error::success();
311 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
312 ClassRecord &Class) {
313 P.format(" `{0}`", Class.Name);
314 if (Class.hasUniqueName())
315 P.formatLine("unique name: `{0}`", Class.UniqueName);
316 P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
317 Class.VTableShape, Class.DerivationList, Class.FieldList);
318 P.formatLine("options: {0}",
319 formatClassOptions(P.getIndentLevel(), Class.Options));
320 return Error::success();
323 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
324 UnionRecord &Union) {
325 P.format(" `{0}`", Union.Name);
326 if (Union.hasUniqueName())
327 P.formatLine("unique name: `{0}`", Union.UniqueName);
328 P.formatLine("field list: {0}", Union.FieldList);
329 P.formatLine("options: {0}",
330 formatClassOptions(P.getIndentLevel(), Union.Options));
331 return Error::success();
334 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
335 P.format(" `{0}`", Enum.Name);
336 if (Enum.hasUniqueName())
337 P.formatLine("unique name: `{0}`", Enum.UniqueName);
338 P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
339 Enum.UnderlyingType);
340 P.formatLine("options: {0}",
341 formatClassOptions(P.getIndentLevel(), Enum.Options));
342 return Error::success();
345 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
346 if (AT.Name.empty()) {
347 P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
348 AT.IndexType, AT.ElementType);
350 P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
351 AT.Name, AT.Size, AT.IndexType, AT.ElementType);
353 return Error::success();
356 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
357 VFTableRecord &VFT) {
358 P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
359 VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
360 P.formatLine("method names: ");
361 if (!VFT.MethodNames.empty()) {
364 fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
366 P.print(join(VFT.MethodNames, Sep));
368 return Error::success();
371 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
372 MemberFuncIdRecord &Id) {
373 P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
374 Id.FunctionType, Id.ClassType);
375 return Error::success();
378 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
379 ProcedureRecord &Proc) {
380 P.formatLine("return type = {0}, # args = {1}, param list = {2}",
381 Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
382 P.formatLine("calling conv = {0}, options = {1}",
383 formatCallingConvention(Proc.CallConv),
384 formatFunctionOptions(Proc.Options));
385 return Error::success();
388 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
389 MemberFunctionRecord &MF) {
390 P.formatLine("return type = {0}, # args = {1}, param list = {2}",
391 MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
392 P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
393 MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
394 P.formatLine("calling conv = {0}, options = {1}",
395 formatCallingConvention(MF.CallConv),
396 formatFunctionOptions(MF.Options));
397 return Error::success();
400 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
401 FuncIdRecord &Func) {
402 P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
403 Func.FunctionType, Func.ParentScope);
404 return Error::success();
407 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
408 TypeServer2Record &TS) {
409 P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
410 return Error::success();
413 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
414 PointerRecord &Ptr) {
415 P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
416 formatPointerAttrs(Ptr));
417 return Error::success();
420 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
421 ModifierRecord &Mod) {
422 P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
423 modifierOptions(Mod.Modifiers));
424 return Error::success();
427 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
428 VFTableShapeRecord &Shape) {
429 return Error::success();
432 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
433 UdtModSourceLineRecord &U) {
434 P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
435 U.SourceFile.getIndex(), U.LineNumber);
436 return Error::success();
439 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
440 UdtSourceLineRecord &U) {
441 P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
442 U.SourceFile.getIndex(), U.LineNumber);
443 return Error::success();
446 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
447 BitFieldRecord &BF) {
448 P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
449 BF.BitOffset, BF.BitSize);
450 return Error::success();
453 Error MinimalTypeDumpVisitor::visitKnownRecord(
454 CVType &CVR, MethodOverloadListRecord &Overloads) {
455 for (auto &M : Overloads.Methods)
456 P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
457 M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
458 return Error::success();
461 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
462 BuildInfoRecord &BI) {
463 auto Indices = BI.ArgIndices;
465 return Error::success();
467 auto Max = std::max_element(Indices.begin(), Indices.end());
468 uint32_t W = NumDigits(Max->getIndex()) + 2;
470 for (auto I : Indices)
471 P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
473 return Error::success();
476 Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
477 std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
478 P.format(" type = {0}", Type);
479 return Error::success();
482 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
483 NestedTypeRecord &Nested) {
484 P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
485 return Error::success();
488 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
489 OneMethodRecord &Method) {
490 P.format(" [name = `{0}`]", Method.Name);
491 AutoIndent Indent(P);
492 P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
493 Method.VFTableOffset, memberAttributes(Method.Attrs));
494 return Error::success();
497 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
498 OverloadedMethodRecord &Method) {
499 P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
500 Method.Name, Method.NumOverloads, Method.MethodList);
501 return Error::success();
504 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
505 DataMemberRecord &Field) {
506 P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
507 Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
508 return Error::success();
511 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
512 StaticDataMemberRecord &Field) {
513 P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
514 memberAttributes(Field.Attrs));
515 return Error::success();
518 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
519 EnumeratorRecord &Enum) {
520 P.format(" [{0} = {1}]", Enum.Name,
521 Enum.Value.toString(10, Enum.Value.isSigned()));
522 return Error::success();
525 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
526 BaseClassRecord &Base) {
527 AutoIndent Indent(P);
528 P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
529 memberAttributes(Base.Attrs));
530 return Error::success();
533 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
534 VirtualBaseClassRecord &Base) {
535 AutoIndent Indent(P);
537 "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
538 Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
539 P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
540 return Error::success();
543 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
544 ListContinuationRecord &Cont) {
545 P.format(" continuation = {0}", Cont.ContinuationIndex);
546 return Error::success();
549 Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
551 P.format(" type = {0}", VFP.Type);
552 return Error::success();