diff options
| author | Alexandre Ganea <alexandre.ganea@ubisoft.com> | 2018-04-09 20:17:56 +0000 | 
|---|---|---|
| committer | Alexandre Ganea <alexandre.ganea@ubisoft.com> | 2018-04-09 20:17:56 +0000 | 
| commit | d9e96741c464f35722bbbb4d11bb7fa7c6f4fb21 (patch) | |
| tree | 450fa7f2b11c3954d3beb58f6d587468428b5f8b /llvm/tools/llvm-pdbutil | |
| parent | 69a2e18b4aeafad0e30091fb5f9c191e6d1c2df4 (diff) | |
| download | bcm5719-llvm-d9e96741c464f35722bbbb4d11bb7fa7c6f4fb21.tar.gz bcm5719-llvm-d9e96741c464f35722bbbb4d11bb7fa7c6f4fb21.zip  | |
[Debuginfo][COFF] Minimal serialization support for precompiled types records
This change adds support for the LF_PRECOMP and LF_ENDPRECOMP records required
to read/write Microsoft precompiled types .objs.
See https://en.wikipedia.org/wiki/Precompiled_header#Microsoft_Visual_C_and_C++
This also adds handling for the .debug$P section, which is actually a .debug$T
section in disguise, found only in precompiled .objs.
Differential Revision: https://reviews.llvm.org/D45283
llvm-svn: 329613
Diffstat (limited to 'llvm/tools/llvm-pdbutil')
| -rw-r--r-- | llvm/tools/llvm-pdbutil/InputFile.cpp | 3 | ||||
| -rw-r--r-- | llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp | 1097 | 
2 files changed, 558 insertions, 542 deletions
diff --git a/llvm/tools/llvm-pdbutil/InputFile.cpp b/llvm/tools/llvm-pdbutil/InputFile.cpp index 3695cbfc4f9..7b5af7e9692 100644 --- a/llvm/tools/llvm-pdbutil/InputFile.cpp +++ b/llvm/tools/llvm-pdbutil/InputFile.cpp @@ -95,7 +95,8 @@ static inline bool isDebugSSection(object::SectionRef Section,  static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {    BinaryStreamReader Reader; -  if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader)) +  if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) && +      !isCodeViewDebugSubsection(Section, ".debug$P", Reader))      return false;    cantFail(Reader.readArray(Types, Reader.bytesRemaining()));    return true; diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index fae89920e0b..3425c58b642 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -1,541 +1,556 @@ -//===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===// -// -//                     The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "MinimalTypeDumper.h" - -#include "FormatUtil.h" -#include "LinePrinter.h" - -#include "llvm/DebugInfo/CodeView/CVRecord.h" -#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/DebugInfo/CodeView/CodeView.h" -#include "llvm/DebugInfo/CodeView/Formatters.h" -#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" -#include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/PDB/Native/TpiHashing.h" -#include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/MathExtras.h" - -using namespace llvm; -using namespace llvm::codeview; -using namespace llvm::pdb; - -static std::string formatClassOptions(uint32_t IndentLevel, -                                      ClassOptions Options) { -  std::vector<std::string> Opts; -  PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, -            "has ctor / dtor"); -  PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, -            "contains nested class"); -  PUSH_FLAG(ClassOptions, HasConversionOperator, Options, -            "conversion operator"); -  PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); -  PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name"); -  PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin"); -  PUSH_FLAG(ClassOptions, Nested, Options, "is nested"); -  PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options, -            "overloaded operator"); -  PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options, -            "overloaded operator="); -  PUSH_FLAG(ClassOptions, Packed, Options, "packed"); -  PUSH_FLAG(ClassOptions, Scoped, Options, "scoped"); -  PUSH_FLAG(ClassOptions, Sealed, Options, "sealed"); - -  return typesetItemList(Opts, 4, IndentLevel, " | "); -} - -static std::string pointerOptions(PointerOptions Options) { -  std::vector<std::string> Opts; -  PUSH_FLAG(PointerOptions, Flat32, Options, "flat32"); -  PUSH_FLAG(PointerOptions, Volatile, Options, "volatile"); -  PUSH_FLAG(PointerOptions, Const, Options, "const"); -  PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned"); -  PUSH_FLAG(PointerOptions, Restrict, Options, "restrict"); -  PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt"); -  if (Opts.empty()) -    return "None"; -  return join(Opts, " | "); -} - -static std::string modifierOptions(ModifierOptions Options) { -  std::vector<std::string> Opts; -  PUSH_FLAG(ModifierOptions, Const, Options, "const"); -  PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile"); -  PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned"); -  if (Opts.empty()) -    return "None"; -  return join(Opts, " | "); -} - -static std::string formatCallingConvention(CallingConvention Convention) { -  switch (Convention) { -    RETURN_CASE(CallingConvention, AlphaCall, "alphacall"); -    RETURN_CASE(CallingConvention, AM33Call, "am33call"); -    RETURN_CASE(CallingConvention, ArmCall, "armcall"); -    RETURN_CASE(CallingConvention, ClrCall, "clrcall"); -    RETURN_CASE(CallingConvention, FarC, "far cdecl"); -    RETURN_CASE(CallingConvention, FarFast, "far fastcall"); -    RETURN_CASE(CallingConvention, FarPascal, "far pascal"); -    RETURN_CASE(CallingConvention, FarStdCall, "far stdcall"); -    RETURN_CASE(CallingConvention, FarSysCall, "far syscall"); -    RETURN_CASE(CallingConvention, Generic, "generic"); -    RETURN_CASE(CallingConvention, Inline, "inline"); -    RETURN_CASE(CallingConvention, M32RCall, "m32rcall"); -    RETURN_CASE(CallingConvention, MipsCall, "mipscall"); -    RETURN_CASE(CallingConvention, NearC, "cdecl"); -    RETURN_CASE(CallingConvention, NearFast, "fastcall"); -    RETURN_CASE(CallingConvention, NearPascal, "pascal"); -    RETURN_CASE(CallingConvention, NearStdCall, "stdcall"); -    RETURN_CASE(CallingConvention, NearSysCall, "near syscall"); -    RETURN_CASE(CallingConvention, NearVector, "vectorcall"); -    RETURN_CASE(CallingConvention, PpcCall, "ppccall"); -    RETURN_CASE(CallingConvention, SHCall, "shcall"); -    RETURN_CASE(CallingConvention, SH5Call, "sh5call"); -    RETURN_CASE(CallingConvention, ThisCall, "thiscall"); -    RETURN_CASE(CallingConvention, TriCall, "tricall"); -  } -  return formatUnknownEnum(Convention); -} - -static std::string formatPointerMode(PointerMode Mode) { -  switch (Mode) { -    RETURN_CASE(PointerMode, LValueReference, "ref"); -    RETURN_CASE(PointerMode, Pointer, "pointer"); -    RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer"); -    RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer"); -    RETURN_CASE(PointerMode, RValueReference, "rvalue ref"); -  } -  return formatUnknownEnum(Mode); -} - -static std::string memberAccess(MemberAccess Access) { -  switch (Access) { -    RETURN_CASE(MemberAccess, None, ""); -    RETURN_CASE(MemberAccess, Private, "private"); -    RETURN_CASE(MemberAccess, Protected, "protected"); -    RETURN_CASE(MemberAccess, Public, "public"); -  } -  return formatUnknownEnum(Access); -} - -static std::string methodKind(MethodKind Kind) { -  switch (Kind) { -    RETURN_CASE(MethodKind, Vanilla, ""); -    RETURN_CASE(MethodKind, Virtual, "virtual"); -    RETURN_CASE(MethodKind, Static, "static"); -    RETURN_CASE(MethodKind, Friend, "friend"); -    RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual"); -    RETURN_CASE(MethodKind, PureVirtual, "pure virtual"); -    RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual"); -  } -  return formatUnknownEnum(Kind); -} - -static std::string pointerKind(PointerKind Kind) { -  switch (Kind) { -    RETURN_CASE(PointerKind, Near16, "ptr16"); -    RETURN_CASE(PointerKind, Far16, "far ptr16"); -    RETURN_CASE(PointerKind, Huge16, "huge ptr16"); -    RETURN_CASE(PointerKind, BasedOnSegment, "segment based"); -    RETURN_CASE(PointerKind, BasedOnValue, "value based"); -    RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based"); -    RETURN_CASE(PointerKind, BasedOnAddress, "address based"); -    RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based"); -    RETURN_CASE(PointerKind, BasedOnType, "type based"); -    RETURN_CASE(PointerKind, BasedOnSelf, "self based"); -    RETURN_CASE(PointerKind, Near32, "ptr32"); -    RETURN_CASE(PointerKind, Far32, "far ptr32"); -    RETURN_CASE(PointerKind, Near64, "ptr64"); -  } -  return formatUnknownEnum(Kind); -} - -static std::string memberAttributes(const MemberAttributes &Attrs) { -  std::vector<std::string> Opts; -  std::string Access = memberAccess(Attrs.getAccess()); -  std::string Kind = methodKind(Attrs.getMethodKind()); -  if (!Access.empty()) -    Opts.push_back(Access); -  if (!Kind.empty()) -    Opts.push_back(Kind); -  MethodOptions Flags = Attrs.getFlags(); -  PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo"); -  PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit"); -  PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct"); -  PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated"); -  PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed"); -  return join(Opts, " "); -} - -static std::string formatPointerAttrs(const PointerRecord &Record) { -  PointerMode Mode = Record.getMode(); -  PointerOptions Opts = Record.getOptions(); -  PointerKind Kind = Record.getPointerKind(); -  return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode), -                 pointerOptions(Opts), pointerKind(Kind)); -} - -static std::string formatFunctionOptions(FunctionOptions Options) { -  std::vector<std::string> Opts; - -  PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt"); -  PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options, -            "constructor with virtual bases"); -  PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor"); -  if (Opts.empty()) -    return "None"; -  return join(Opts, " | "); -} - -Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { -  // formatLine puts the newline at the beginning, so we use formatLine here -  // to start a new line, and then individual visit methods use format to -  // append to the existing line. -  if (!Hashes) { -    P.formatLine("{0} | {1} [size = {2}]", -                 fmt_align(Index, AlignStyle::Right, Width), -                 formatTypeLeafKind(Record.Type), Record.length()); -  } else { -    std::string H; -    if (Index.toArrayIndex() >= HashValues.size()) { -      H = "(not present)"; -    } else { -      uint32_t Hash = HashValues[Index.toArrayIndex()]; -      Expected<uint32_t> MaybeHash = hashTypeRecord(Record); -      if (!MaybeHash) -        return MaybeHash.takeError(); -      uint32_t OurHash = *MaybeHash; -      OurHash %= NumHashBuckets; -      if (Hash == OurHash) -        H = "0x" + utohexstr(Hash); -      else -        H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash); -    } -    P.formatLine("{0} | {1} [size = {2}, hash = {3}]", -                 fmt_align(Index, AlignStyle::Right, Width), -                 formatTypeLeafKind(Record.Type), Record.length(), H); -  } -  P.Indent(Width + 3); -  return Error::success(); -} -Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { -  P.Unindent(Width + 3); -  if (RecordBytes) { -    AutoIndent Indent(P, 9); -    P.formatBinary("Bytes", Record.RecordData, 0); -  } -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) { -  P.formatLine("- {0}", formatTypeLeafKind(Record.Kind)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) { -  if (RecordBytes) { -    AutoIndent Indent(P, 2); -    P.formatBinary("Bytes", Record.Data, 0); -  } -  return Error::success(); -} - -StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const { -  if (TI.isNoneType()) -    return ""; -  return Types.getTypeName(TI); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               FieldListRecord &FieldList) { -  if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this)) -    return EC; - -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               StringIdRecord &String) { -  P.format(" ID: {0}, String: {1}", String.getId(), String.getString()); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               ArgListRecord &Args) { -  auto Indices = Args.getIndices(); -  if (Indices.empty()) -    return Error::success(); - -  auto Max = std::max_element(Indices.begin(), Indices.end()); -  uint32_t W = NumDigits(Max->getIndex()) + 2; - -  for (auto I : Indices) -    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), -                 getTypeName(I)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               StringListRecord &Strings) { -  auto Indices = Strings.getIndices(); -  if (Indices.empty()) -    return Error::success(); - -  auto Max = std::max_element(Indices.begin(), Indices.end()); -  uint32_t W = NumDigits(Max->getIndex()) + 2; - -  for (auto I : Indices) -    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), -                 getTypeName(I)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               ClassRecord &Class) { -  P.format(" `{0}`", Class.Name); -  if (Class.hasUniqueName()) -    P.formatLine("unique name: `{0}`", Class.UniqueName); -  P.formatLine("vtable: {0}, base list: {1}, field list: {2}", -               Class.VTableShape, Class.DerivationList, Class.FieldList); -  P.formatLine("options: {0}", -               formatClassOptions(P.getIndentLevel(), Class.Options)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               UnionRecord &Union) { -  P.format(" `{0}`", Union.Name); -  if (Union.hasUniqueName()) -    P.formatLine("unique name: `{0}`", Union.UniqueName); -  P.formatLine("field list: {0}", Union.FieldList); -  P.formatLine("options: {0}", -               formatClassOptions(P.getIndentLevel(), Union.Options)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { -  P.format(" `{0}`", Enum.Name); -  if (Enum.hasUniqueName()) -    P.formatLine("unique name: `{0}`", Enum.UniqueName); -  P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, -               Enum.UnderlyingType); -  P.formatLine("options: {0}", -               formatClassOptions(P.getIndentLevel(), Enum.Options)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) { -  if (AT.Name.empty()) { -    P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size, -                 AT.IndexType, AT.ElementType); -  } else { -    P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}", -                 AT.Name, AT.Size, AT.IndexType, AT.ElementType); -  } -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               VFTableRecord &VFT) { -  P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}", -               VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable); -  P.formatLine("method names: "); -  if (!VFT.MethodNames.empty()) { -    std::string Sep = -        formatv("\n{0}", -                fmt_repeat(' ', P.getIndentLevel() + strlen("method names: "))) -            .str(); -    P.print(join(VFT.MethodNames, Sep)); -  } -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               MemberFuncIdRecord &Id) { -  P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name, -               Id.FunctionType, Id.ClassType); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               ProcedureRecord &Proc) { -  P.formatLine("return type = {0}, # args = {1}, param list = {2}", -               Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList); -  P.formatLine("calling conv = {0}, options = {1}", -               formatCallingConvention(Proc.CallConv), -               formatFunctionOptions(Proc.Options)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               MemberFunctionRecord &MF) { -  P.formatLine("return type = {0}, # args = {1}, param list = {2}", -               MF.ReturnType, MF.ParameterCount, MF.ArgumentList); -  P.formatLine("class type = {0}, this type = {1}, this adjust = {2}", -               MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment); -  P.formatLine("calling conv = {0}, options = {1}", -               formatCallingConvention(MF.CallConv), -               formatFunctionOptions(MF.Options)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               FuncIdRecord &Func) { -  P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name, -               Func.FunctionType, Func.ParentScope); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               TypeServer2Record &TS) { -  P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               PointerRecord &Ptr) { -  P.formatLine("referent = {0}, {1}", Ptr.ReferentType, -               formatPointerAttrs(Ptr)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               ModifierRecord &Mod) { -  P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType, -               modifierOptions(Mod.Modifiers)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               VFTableShapeRecord &Shape) { -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               UdtModSourceLineRecord &U) { -  P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module, -               U.SourceFile.getIndex(), U.LineNumber); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               UdtSourceLineRecord &U) { -  P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT, -               U.SourceFile.getIndex(), U.LineNumber); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               BitFieldRecord &BF) { -  P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type, -               BF.BitOffset, BF.BitSize); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord( -    CVType &CVR, MethodOverloadListRecord &Overloads) { -  for (auto &M : Overloads.Methods) -    P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]", -                 M.Type, M.VFTableOffset, memberAttributes(M.Attrs)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, -                                               BuildInfoRecord &BI) { -  auto Indices = BI.ArgIndices; -  if (Indices.empty()) -    return Error::success(); - -  auto Max = std::max_element(Indices.begin(), Indices.end()); -  uint32_t W = NumDigits(Max->getIndex()) + 2; - -  for (auto I : Indices) -    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W), -                 getTypeName(I)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) { -  std::string Type = (R.Mode == LabelType::Far) ? "far" : "near"; -  P.format(" type = {0}", Type); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               NestedTypeRecord &Nested) { -  P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               OneMethodRecord &Method) { -  P.format(" [name = `{0}`]", Method.Name); -  AutoIndent Indent(P); -  P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type, -               Method.VFTableOffset, memberAttributes(Method.Attrs)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               OverloadedMethodRecord &Method) { -  P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]", -           Method.Name, Method.NumOverloads, Method.MethodList); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               DataMemberRecord &Field) { -  P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name, -           Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               StaticDataMemberRecord &Field) { -  P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type, -           memberAttributes(Field.Attrs)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               EnumeratorRecord &Enum) { -  P.format(" [{0} = {1}]", Enum.Name, -           Enum.Value.toString(10, Enum.Value.isSigned())); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               BaseClassRecord &Base) { -  AutoIndent Indent(P); -  P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset, -               memberAttributes(Base.Attrs)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               VirtualBaseClassRecord &Base) { -  AutoIndent Indent(P); -  P.formatLine( -      "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}", -      Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex); -  P.formatLine("attrs = {0}", memberAttributes(Base.Attrs)); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               ListContinuationRecord &Cont) { -  P.format(" continuation = {0}", Cont.ContinuationIndex); -  return Error::success(); -} - -Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR, -                                               VFPtrRecord &VFP) { -  P.format(" type = {0}", VFP.Type); -  return Error::success(); -} +//===- MinimalTypeDumper.cpp ---------------------------------- *- C++ --*-===//
 +//
 +//                     The LLVM Compiler Infrastructure
 +//
 +// This file is distributed under the University of Illinois Open Source
 +// License. See LICENSE.TXT for details.
 +//
 +//===----------------------------------------------------------------------===//
 +
 +#include "MinimalTypeDumper.h"
 +
 +#include "FormatUtil.h"
 +#include "LinePrinter.h"
 +
 +#include "llvm/DebugInfo/CodeView/CVRecord.h"
 +#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
 +#include "llvm/DebugInfo/CodeView/CodeView.h"
 +#include "llvm/DebugInfo/CodeView/Formatters.h"
 +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
 +#include "llvm/DebugInfo/CodeView/TypeRecord.h"
 +#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
 +#include "llvm/Support/FormatVariadic.h"
 +#include "llvm/Support/MathExtras.h"
 +
 +using namespace llvm;
 +using namespace llvm::codeview;
 +using namespace llvm::pdb;
 +
 +static std::string formatClassOptions(uint32_t IndentLevel,
 +                                      ClassOptions Options) {
 +  std::vector<std::string> Opts;
 +  PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options,
 +            "has ctor / dtor");
 +  PUSH_FLAG(ClassOptions, ContainsNestedClass, Options,
 +            "contains nested class");
 +  PUSH_FLAG(ClassOptions, HasConversionOperator, Options,
 +            "conversion operator");
 +  PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref");
 +  PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name");
 +  PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin");
 +  PUSH_FLAG(ClassOptions, Nested, Options, "is nested");
 +  PUSH_FLAG(ClassOptions, HasOverloadedOperator, Options,
 +            "overloaded operator");
 +  PUSH_FLAG(ClassOptions, HasOverloadedAssignmentOperator, Options,
 +            "overloaded operator=");
 +  PUSH_FLAG(ClassOptions, Packed, Options, "packed");
 +  PUSH_FLAG(ClassOptions, Scoped, Options, "scoped");
 +  PUSH_FLAG(ClassOptions, Sealed, Options, "sealed");
 +
 +  return typesetItemList(Opts, 4, IndentLevel, " | ");
 +}
 +
 +static std::string pointerOptions(PointerOptions Options) {
 +  std::vector<std::string> Opts;
 +  PUSH_FLAG(PointerOptions, Flat32, Options, "flat32");
 +  PUSH_FLAG(PointerOptions, Volatile, Options, "volatile");
 +  PUSH_FLAG(PointerOptions, Const, Options, "const");
 +  PUSH_FLAG(PointerOptions, Unaligned, Options, "unaligned");
 +  PUSH_FLAG(PointerOptions, Restrict, Options, "restrict");
 +  PUSH_FLAG(PointerOptions, WinRTSmartPointer, Options, "winrt");
 +  if (Opts.empty())
 +    return "None";
 +  return join(Opts, " | ");
 +}
 +
 +static std::string modifierOptions(ModifierOptions Options) {
 +  std::vector<std::string> Opts;
 +  PUSH_FLAG(ModifierOptions, Const, Options, "const");
 +  PUSH_FLAG(ModifierOptions, Volatile, Options, "volatile");
 +  PUSH_FLAG(ModifierOptions, Unaligned, Options, "unaligned");
 +  if (Opts.empty())
 +    return "None";
 +  return join(Opts, " | ");
 +}
 +
 +static std::string formatCallingConvention(CallingConvention Convention) {
 +  switch (Convention) {
 +    RETURN_CASE(CallingConvention, AlphaCall, "alphacall");
 +    RETURN_CASE(CallingConvention, AM33Call, "am33call");
 +    RETURN_CASE(CallingConvention, ArmCall, "armcall");
 +    RETURN_CASE(CallingConvention, ClrCall, "clrcall");
 +    RETURN_CASE(CallingConvention, FarC, "far cdecl");
 +    RETURN_CASE(CallingConvention, FarFast, "far fastcall");
 +    RETURN_CASE(CallingConvention, FarPascal, "far pascal");
 +    RETURN_CASE(CallingConvention, FarStdCall, "far stdcall");
 +    RETURN_CASE(CallingConvention, FarSysCall, "far syscall");
 +    RETURN_CASE(CallingConvention, Generic, "generic");
 +    RETURN_CASE(CallingConvention, Inline, "inline");
 +    RETURN_CASE(CallingConvention, M32RCall, "m32rcall");
 +    RETURN_CASE(CallingConvention, MipsCall, "mipscall");
 +    RETURN_CASE(CallingConvention, NearC, "cdecl");
 +    RETURN_CASE(CallingConvention, NearFast, "fastcall");
 +    RETURN_CASE(CallingConvention, NearPascal, "pascal");
 +    RETURN_CASE(CallingConvention, NearStdCall, "stdcall");
 +    RETURN_CASE(CallingConvention, NearSysCall, "near syscall");
 +    RETURN_CASE(CallingConvention, NearVector, "vectorcall");
 +    RETURN_CASE(CallingConvention, PpcCall, "ppccall");
 +    RETURN_CASE(CallingConvention, SHCall, "shcall");
 +    RETURN_CASE(CallingConvention, SH5Call, "sh5call");
 +    RETURN_CASE(CallingConvention, ThisCall, "thiscall");
 +    RETURN_CASE(CallingConvention, TriCall, "tricall");
 +  }
 +  return formatUnknownEnum(Convention);
 +}
 +
 +static std::string formatPointerMode(PointerMode Mode) {
 +  switch (Mode) {
 +    RETURN_CASE(PointerMode, LValueReference, "ref");
 +    RETURN_CASE(PointerMode, Pointer, "pointer");
 +    RETURN_CASE(PointerMode, PointerToDataMember, "data member pointer");
 +    RETURN_CASE(PointerMode, PointerToMemberFunction, "member fn pointer");
 +    RETURN_CASE(PointerMode, RValueReference, "rvalue ref");
 +  }
 +  return formatUnknownEnum(Mode);
 +}
 +
 +static std::string memberAccess(MemberAccess Access) {
 +  switch (Access) {
 +    RETURN_CASE(MemberAccess, None, "");
 +    RETURN_CASE(MemberAccess, Private, "private");
 +    RETURN_CASE(MemberAccess, Protected, "protected");
 +    RETURN_CASE(MemberAccess, Public, "public");
 +  }
 +  return formatUnknownEnum(Access);
 +}
 +
 +static std::string methodKind(MethodKind Kind) {
 +  switch (Kind) {
 +    RETURN_CASE(MethodKind, Vanilla, "");
 +    RETURN_CASE(MethodKind, Virtual, "virtual");
 +    RETURN_CASE(MethodKind, Static, "static");
 +    RETURN_CASE(MethodKind, Friend, "friend");
 +    RETURN_CASE(MethodKind, IntroducingVirtual, "intro virtual");
 +    RETURN_CASE(MethodKind, PureVirtual, "pure virtual");
 +    RETURN_CASE(MethodKind, PureIntroducingVirtual, "pure intro virtual");
 +  }
 +  return formatUnknownEnum(Kind);
 +}
 +
 +static std::string pointerKind(PointerKind Kind) {
 +  switch (Kind) {
 +    RETURN_CASE(PointerKind, Near16, "ptr16");
 +    RETURN_CASE(PointerKind, Far16, "far ptr16");
 +    RETURN_CASE(PointerKind, Huge16, "huge ptr16");
 +    RETURN_CASE(PointerKind, BasedOnSegment, "segment based");
 +    RETURN_CASE(PointerKind, BasedOnValue, "value based");
 +    RETURN_CASE(PointerKind, BasedOnSegmentValue, "segment value based");
 +    RETURN_CASE(PointerKind, BasedOnAddress, "address based");
 +    RETURN_CASE(PointerKind, BasedOnSegmentAddress, "segment address based");
 +    RETURN_CASE(PointerKind, BasedOnType, "type based");
 +    RETURN_CASE(PointerKind, BasedOnSelf, "self based");
 +    RETURN_CASE(PointerKind, Near32, "ptr32");
 +    RETURN_CASE(PointerKind, Far32, "far ptr32");
 +    RETURN_CASE(PointerKind, Near64, "ptr64");
 +  }
 +  return formatUnknownEnum(Kind);
 +}
 +
 +static std::string memberAttributes(const MemberAttributes &Attrs) {
 +  std::vector<std::string> Opts;
 +  std::string Access = memberAccess(Attrs.getAccess());
 +  std::string Kind = methodKind(Attrs.getMethodKind());
 +  if (!Access.empty())
 +    Opts.push_back(Access);
 +  if (!Kind.empty())
 +    Opts.push_back(Kind);
 +  MethodOptions Flags = Attrs.getFlags();
 +  PUSH_FLAG(MethodOptions, Pseudo, Flags, "pseudo");
 +  PUSH_FLAG(MethodOptions, NoInherit, Flags, "noinherit");
 +  PUSH_FLAG(MethodOptions, NoConstruct, Flags, "noconstruct");
 +  PUSH_FLAG(MethodOptions, CompilerGenerated, Flags, "compiler-generated");
 +  PUSH_FLAG(MethodOptions, Sealed, Flags, "sealed");
 +  return join(Opts, " ");
 +}
 +
 +static std::string formatPointerAttrs(const PointerRecord &Record) {
 +  PointerMode Mode = Record.getMode();
 +  PointerOptions Opts = Record.getOptions();
 +  PointerKind Kind = Record.getPointerKind();
 +  return formatv("mode = {0}, opts = {1}, kind = {2}", formatPointerMode(Mode),
 +                 pointerOptions(Opts), pointerKind(Kind));
 +}
 +
 +static std::string formatFunctionOptions(FunctionOptions Options) {
 +  std::vector<std::string> Opts;
 +
 +  PUSH_FLAG(FunctionOptions, CxxReturnUdt, Options, "returns cxx udt");
 +  PUSH_FLAG(FunctionOptions, ConstructorWithVirtualBases, Options,
 +            "constructor with virtual bases");
 +  PUSH_FLAG(FunctionOptions, Constructor, Options, "constructor");
 +  if (Opts.empty())
 +    return "None";
 +  return join(Opts, " | ");
 +}
 +
 +Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) {
 +  // formatLine puts the newline at the beginning, so we use formatLine here
 +  // to start a new line, and then individual visit methods use format to
 +  // append to the existing line.
 +  if (!Hashes) {
 +    P.formatLine("{0} | {1} [size = {2}]",
 +                 fmt_align(Index, AlignStyle::Right, Width),
 +                 formatTypeLeafKind(Record.Type), Record.length());
 +  } else {
 +    std::string H;
 +    if (Index.toArrayIndex() >= HashValues.size()) {
 +      H = "(not present)";
 +    } else {
 +      uint32_t Hash = HashValues[Index.toArrayIndex()];
 +      Expected<uint32_t> MaybeHash = hashTypeRecord(Record);
 +      if (!MaybeHash)
 +        return MaybeHash.takeError();
 +      uint32_t OurHash = *MaybeHash;
 +      OurHash %= NumHashBuckets;
 +      if (Hash == OurHash)
 +        H = "0x" + utohexstr(Hash);
 +      else
 +        H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash);
 +    }
 +    P.formatLine("{0} | {1} [size = {2}, hash = {3}]",
 +                 fmt_align(Index, AlignStyle::Right, Width),
 +                 formatTypeLeafKind(Record.Type), Record.length(), H);
 +  }
 +  P.Indent(Width + 3);
 +  return Error::success();
 +}
 +Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) {
 +  P.Unindent(Width + 3);
 +  if (RecordBytes) {
 +    AutoIndent Indent(P, 9);
 +    P.formatBinary("Bytes", Record.RecordData, 0);
 +  }
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitMemberBegin(CVMemberRecord &Record) {
 +  P.formatLine("- {0}", formatTypeLeafKind(Record.Kind));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitMemberEnd(CVMemberRecord &Record) {
 +  if (RecordBytes) {
 +    AutoIndent Indent(P, 2);
 +    P.formatBinary("Bytes", Record.Data, 0);
 +  }
 +  return Error::success();
 +}
 +
 +StringRef MinimalTypeDumpVisitor::getTypeName(TypeIndex TI) const {
 +  if (TI.isNoneType())
 +    return "";
 +  return Types.getTypeName(TI);
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               FieldListRecord &FieldList) {
 +  if (auto EC = codeview::visitMemberRecordStream(FieldList.Data, *this))
 +    return EC;
 +
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               StringIdRecord &String) {
 +  P.format(" ID: {0}, String: {1}", String.getId(), String.getString());
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               ArgListRecord &Args) {
 +  auto Indices = Args.getIndices();
 +  if (Indices.empty())
 +    return Error::success();
 +
 +  auto Max = std::max_element(Indices.begin(), Indices.end());
 +  uint32_t W = NumDigits(Max->getIndex()) + 2;
 +
 +  for (auto I : Indices)
 +    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
 +                 getTypeName(I));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               StringListRecord &Strings) {
 +  auto Indices = Strings.getIndices();
 +  if (Indices.empty())
 +    return Error::success();
 +
 +  auto Max = std::max_element(Indices.begin(), Indices.end());
 +  uint32_t W = NumDigits(Max->getIndex()) + 2;
 +
 +  for (auto I : Indices)
 +    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
 +                 getTypeName(I));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               ClassRecord &Class) {
 +  P.format(" `{0}`", Class.Name);
 +  if (Class.hasUniqueName())
 +    P.formatLine("unique name: `{0}`", Class.UniqueName);
 +  P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
 +               Class.VTableShape, Class.DerivationList, Class.FieldList);
 +  P.formatLine("options: {0}",
 +               formatClassOptions(P.getIndentLevel(), Class.Options));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               UnionRecord &Union) {
 +  P.format(" `{0}`", Union.Name);
 +  if (Union.hasUniqueName())
 +    P.formatLine("unique name: `{0}`", Union.UniqueName);
 +  P.formatLine("field list: {0}", Union.FieldList);
 +  P.formatLine("options: {0}",
 +               formatClassOptions(P.getIndentLevel(), Union.Options));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) {
 +  P.format(" `{0}`", Enum.Name);
 +  if (Enum.hasUniqueName())
 +    P.formatLine("unique name: `{0}`", Enum.UniqueName);
 +  P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList,
 +               Enum.UnderlyingType);
 +  P.formatLine("options: {0}",
 +               formatClassOptions(P.getIndentLevel(), Enum.Options));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, ArrayRecord &AT) {
 +  if (AT.Name.empty()) {
 +    P.formatLine("size: {0}, index type: {1}, element type: {2}", AT.Size,
 +                 AT.IndexType, AT.ElementType);
 +  } else {
 +    P.formatLine("name: {0}, size: {1}, index type: {2}, element type: {3}",
 +                 AT.Name, AT.Size, AT.IndexType, AT.ElementType);
 +  }
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               VFTableRecord &VFT) {
 +  P.formatLine("offset: {0}, complete class: {1}, overridden vftable: {2}",
 +               VFT.VFPtrOffset, VFT.CompleteClass, VFT.OverriddenVFTable);
 +  P.formatLine("method names: ");
 +  if (!VFT.MethodNames.empty()) {
 +    std::string Sep =
 +        formatv("\n{0}",
 +                fmt_repeat(' ', P.getIndentLevel() + strlen("method names: ")))
 +            .str();
 +    P.print(join(VFT.MethodNames, Sep));
 +  }
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               MemberFuncIdRecord &Id) {
 +  P.formatLine("name = {0}, type = {1}, class type = {2}", Id.Name,
 +               Id.FunctionType, Id.ClassType);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               ProcedureRecord &Proc) {
 +  P.formatLine("return type = {0}, # args = {1}, param list = {2}",
 +               Proc.ReturnType, Proc.ParameterCount, Proc.ArgumentList);
 +  P.formatLine("calling conv = {0}, options = {1}",
 +               formatCallingConvention(Proc.CallConv),
 +               formatFunctionOptions(Proc.Options));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               MemberFunctionRecord &MF) {
 +  P.formatLine("return type = {0}, # args = {1}, param list = {2}",
 +               MF.ReturnType, MF.ParameterCount, MF.ArgumentList);
 +  P.formatLine("class type = {0}, this type = {1}, this adjust = {2}",
 +               MF.ClassType, MF.ThisType, MF.ThisPointerAdjustment);
 +  P.formatLine("calling conv = {0}, options = {1}",
 +               formatCallingConvention(MF.CallConv),
 +               formatFunctionOptions(MF.Options));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               FuncIdRecord &Func) {
 +  P.formatLine("name = {0}, type = {1}, parent scope = {2}", Func.Name,
 +               Func.FunctionType, Func.ParentScope);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               TypeServer2Record &TS) {
 +  P.formatLine("name = {0}, age = {1}, guid = {2}", TS.Name, TS.Age, TS.Guid);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               PointerRecord &Ptr) {
 +  P.formatLine("referent = {0}, {1}", Ptr.ReferentType,
 +               formatPointerAttrs(Ptr));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               ModifierRecord &Mod) {
 +  P.formatLine("referent = {0}, modifiers = {1}", Mod.ModifiedType,
 +               modifierOptions(Mod.Modifiers));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               VFTableShapeRecord &Shape) {
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               UdtModSourceLineRecord &U) {
 +  P.formatLine("udt = {0}, mod = {1}, file = {2}, line = {3}", U.UDT, U.Module,
 +               U.SourceFile.getIndex(), U.LineNumber);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               UdtSourceLineRecord &U) {
 +  P.formatLine("udt = {0}, file = {1}, line = {2}", U.UDT,
 +               U.SourceFile.getIndex(), U.LineNumber);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               BitFieldRecord &BF) {
 +  P.formatLine("type = {0}, bit offset = {1}, # bits = {2}", BF.Type,
 +               BF.BitOffset, BF.BitSize);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(
 +    CVType &CVR, MethodOverloadListRecord &Overloads) {
 +  for (auto &M : Overloads.Methods)
 +    P.formatLine("- Method [type = {0}, vftable offset = {1}, attrs = {2}]",
 +                 M.Type, M.VFTableOffset, memberAttributes(M.Attrs));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               BuildInfoRecord &BI) {
 +  auto Indices = BI.ArgIndices;
 +  if (Indices.empty())
 +    return Error::success();
 +
 +  auto Max = std::max_element(Indices.begin(), Indices.end());
 +  uint32_t W = NumDigits(Max->getIndex()) + 2;
 +
 +  for (auto I : Indices)
 +    P.formatLine("{0}: `{1}`", fmt_align(I, AlignStyle::Right, W),
 +                 getTypeName(I));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
 +  std::string Type = (R.Mode == LabelType::Far) ? "far" : "near";
 +  P.format(" type = {0}", Type);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               PrecompRecord &Precomp) {
 +  P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
 +           " precomp path = {3}",
 +           Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
 +           Precomp.PrecompFilePath);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
 +                                               EndPrecompRecord &EP) {
 +  P.format(" signature = {0:X+}", EP.Signature);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               NestedTypeRecord &Nested) {
 +  P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               OneMethodRecord &Method) {
 +  P.format(" [name = `{0}`]", Method.Name);
 +  AutoIndent Indent(P);
 +  P.formatLine("type = {0}, vftable offset = {1}, attrs = {2}", Method.Type,
 +               Method.VFTableOffset, memberAttributes(Method.Attrs));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               OverloadedMethodRecord &Method) {
 +  P.format(" [name = `{0}`, # overloads = {1}, overload list = {2}]",
 +           Method.Name, Method.NumOverloads, Method.MethodList);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               DataMemberRecord &Field) {
 +  P.format(" [name = `{0}`, Type = {1}, offset = {2}, attrs = {3}]", Field.Name,
 +           Field.Type, Field.FieldOffset, memberAttributes(Field.Attrs));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               StaticDataMemberRecord &Field) {
 +  P.format(" [name = `{0}`, type = {1}, attrs = {2}]", Field.Name, Field.Type,
 +           memberAttributes(Field.Attrs));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               EnumeratorRecord &Enum) {
 +  P.format(" [{0} = {1}]", Enum.Name,
 +           Enum.Value.toString(10, Enum.Value.isSigned()));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               BaseClassRecord &Base) {
 +  AutoIndent Indent(P);
 +  P.formatLine("type = {0}, offset = {1}, attrs = {2}", Base.Type, Base.Offset,
 +               memberAttributes(Base.Attrs));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               VirtualBaseClassRecord &Base) {
 +  AutoIndent Indent(P);
 +  P.formatLine(
 +      "base = {0}, vbptr = {1}, vbptr offset = {2}, vtable index = {3}",
 +      Base.BaseType, Base.VBPtrType, Base.VBPtrOffset, Base.VTableIndex);
 +  P.formatLine("attrs = {0}", memberAttributes(Base.Attrs));
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               ListContinuationRecord &Cont) {
 +  P.format(" continuation = {0}", Cont.ContinuationIndex);
 +  return Error::success();
 +}
 +
 +Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
 +                                               VFPtrRecord &VFP) {
 +  P.format(" type = {0}", VFP.Type);
 +  return Error::success();
 +}
  | 

