Improve llvm-pdbdump output display.
authorZachary Turner <zturner@google.com>
Fri, 13 Feb 2015 01:23:51 +0000 (01:23 +0000)
committerZachary Turner <zturner@google.com>
Fri, 13 Feb 2015 01:23:51 +0000 (01:23 +0000)
This patch adds a number of improvements to llvm-pdbdump.

1) Dumping of the entire global scope, and not only those
   symbols that live in individual compilands.
2) Prepend class name to member functions and data
3) Improved display of bitfields.
4) Support for dumping more kinds of data symbols.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@229012 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/DebugInfo/PDB/DIA/DIARawSymbol.h
include/llvm/DebugInfo/PDB/IPDBRawSymbol.h
include/llvm/DebugInfo/PDB/PDBExtras.h
include/llvm/DebugInfo/PDB/PDBSymbolData.h
include/llvm/DebugInfo/PDB/PDBTypes.h
lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp
lib/DebugInfo/PDB/PDBExtras.cpp
lib/DebugInfo/PDB/PDBSymbolData.cpp
lib/DebugInfo/PDB/PDBSymbolExe.cpp
lib/DebugInfo/PDB/PDBSymbolFunc.cpp

index 6336b319a4f08c74507085d1c5031d6f6df1c9d0..002a46e60ffb36a795320549afba5592ec745fc5 100644 (file)
@@ -97,6 +97,7 @@ public:
   std::string getUndecoratedName() const override;
   uint32_t getUnmodifiedTypeId() const override;
   uint32_t getUpperBoundId() const override;
+  Variant getValue() const override;
   uint32_t getVirtualBaseDispIndex() const override;
   uint32_t getVirtualBaseOffset() const override;
   uint32_t getVirtualTableShapeId() const override;
@@ -193,6 +194,8 @@ public:
   bool isVirtualBaseClass() const override;
   bool isVirtualInheritance() const override;
   bool isVolatileType() const override;
+  bool wasInlined() const override;
+  std::string getUnused() const override;
 
 private:
   const DIASession &Session;
index 5dd103ff100395ca0b9d653055f6a82336d7a56b..dd949b25654b7f28e0bde6c49c50b3cf31352aec 100644 (file)
@@ -106,6 +106,7 @@ public:
   virtual std::string getUndecoratedName() const = 0;
   virtual uint32_t getUnmodifiedTypeId() const = 0;
   virtual uint32_t getUpperBoundId() const = 0;
+  virtual Variant getValue() const = 0;
   virtual uint32_t getVirtualBaseDispIndex() const = 0;
   virtual uint32_t getVirtualBaseOffset() const = 0;
   virtual uint32_t getVirtualTableShapeId() const = 0;
@@ -202,6 +203,8 @@ public:
   virtual bool isVirtualBaseClass() const = 0;
   virtual bool isVirtualInheritance() const = 0;
   virtual bool isVolatileType() const = 0;
+  virtual bool wasInlined() const = 0;
+  virtual std::string getUnused() const = 0;
 };
 
 } // namespace llvm
index 5a768e74e54b3fd29517aec3e1deba9d3dcaf384..1dee6656ede9596e72e7cfd5c0f754ed3de1b0ae 100644 (file)
@@ -25,6 +25,8 @@ struct stream_indent {
 };
 raw_ostream &operator<<(raw_ostream &OS, const stream_indent &Indent);
 
+raw_ostream &operator<<(raw_ostream &OS, const PDB_VariantType &Value);
+raw_ostream &operator<<(raw_ostream &OS, const PDB_DataKind &Data);
 raw_ostream &operator<<(raw_ostream &OS, const PDB_RegisterId &Reg);
 raw_ostream &operator<<(raw_ostream &OS, const PDB_LocType &Loc);
 raw_ostream &operator<<(raw_ostream &OS, const PDB_ThunkOrdinal &Thunk);
@@ -32,6 +34,8 @@ raw_ostream &operator<<(raw_ostream &OS, const PDB_Checksum &Checksum);
 raw_ostream &operator<<(raw_ostream &OS, const PDB_Lang &Lang);
 raw_ostream &operator<<(raw_ostream &OS, const PDB_SymType &Tag);
 raw_ostream &operator<<(raw_ostream &OS, const PDB_UniqueId &Id);
+
+raw_ostream &operator<<(raw_ostream &OS, const Variant &Value);
 raw_ostream &operator<<(raw_ostream &OS, const VersionInfo &Version);
 raw_ostream &operator<<(raw_ostream &OS, const TagStats &Stats);
 }
index a67d08f9ad679da276e43d46c245c7d1a4227a2d..c99ef63f8e75bb16ac928992d9a0b15fd38a3a7e 100644 (file)
@@ -49,7 +49,7 @@ public:
   FORWARD_SYMBOL_METHOD(getToken)
   FORWARD_SYMBOL_METHOD(getTypeId)
   FORWARD_SYMBOL_METHOD(isUnalignedType)
-  // FORWARD_SYMBOL_METHOD(getValue)
+  FORWARD_SYMBOL_METHOD(getValue)
   FORWARD_SYMBOL_METHOD(getVirtualAddress)
   FORWARD_SYMBOL_METHOD(isVolatileType)
 };
index 0009b02505a46f2c717b3007fa82100c720ed4fa..81d9a357107b2aa5b14a32d06c97e3efa4bdcb9c 100644 (file)
@@ -433,6 +433,44 @@ struct VersionInfo {
   uint32_t QFE;
 };
 
+enum PDB_VariantType {
+  Empty,
+  Unknown,
+  Int8,
+  Int16,
+  Int32,
+  Int64,
+  Single,
+  Double,
+  UInt8,
+  UInt16,
+  UInt32,
+  UInt64,
+  Bool,
+};
+
+struct Variant {
+  Variant()
+    : Type(PDB_VariantType::Empty) {
+  }
+
+  PDB_VariantType Type;
+  union {
+    bool Bool;
+    int8_t Int8;
+    int16_t Int16;
+    int32_t Int32;
+    int64_t Int64;
+    float Single;
+    double Double;
+    uint8_t UInt8;
+    uint16_t UInt16;
+    uint32_t UInt32;
+    uint64_t UInt64;
+    void* Pointer;
+  };
+};
+
 } // namespace llvm
 
 namespace std {
index 530d3b5deb5e78e8f88fba57b7a9976e10285c76..e9e5ce2fc7b7b6fef7700e222ddf0f2d36e389f7 100644 (file)
 using namespace llvm;
 
 namespace {
+Variant VariantFromVARIANT(const VARIANT &V) {
+  Variant Result;
+  switch (V.vt) {
+  case VT_I1:
+    Result.Int8 = V.cVal;
+    Result.Type = PDB_VariantType::Int8;
+    break;
+  case VT_I2:
+    Result.Int16 = V.iVal;
+    Result.Type = PDB_VariantType::Int16;
+    break;
+  case VT_I4:
+    Result.Int32 = V.intVal;
+    Result.Type = PDB_VariantType::Int32;
+    break;
+  case VT_I8:
+    Result.Int64 = V.llVal;
+    Result.Type = PDB_VariantType::Int64;
+    break;
+  case VT_UI1:
+    Result.UInt8 = V.bVal;
+    Result.Type = PDB_VariantType::UInt8;
+    break;
+  case VT_UI2:
+    Result.UInt16 = V.uiVal;
+    Result.Type = PDB_VariantType::UInt16;
+    break;
+  case VT_UI4:
+    Result.UInt32 = V.uintVal;
+    Result.Type = PDB_VariantType::UInt32;
+    break;
+  case VT_UI8:
+    Result.UInt64 = V.ullVal;
+    Result.Type = PDB_VariantType::UInt64;
+    break;
+  case VT_BOOL:
+    Result.Bool = (V.boolVal == VARIANT_TRUE) ? true : false;
+    Result.Type = PDB_VariantType::Bool;
+    break;
+  case VT_R4:
+    Result.Single = V.fltVal;
+    Result.Type = PDB_VariantType::Single;
+    break;
+  case VT_R8:
+    Result.Double = V.dblVal;
+    Result.Type = PDB_VariantType::Double;
+    break;
+  default:
+    Result.Type = PDB_VariantType::Unknown;
+    break;
+  }
+  return Result;
+}
+
 template <typename ArgType>
 ArgType PrivateGetDIAValue(IDiaSymbol *Symbol,
                            HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) {
@@ -93,6 +147,18 @@ void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name,
   }
   ::SysFreeString(Value);
 }
+
+void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name,
+                  IDiaSymbol *Symbol,
+                  HRESULT (__stdcall IDiaSymbol::*Method)(VARIANT *)) {
+  VARIANT Value;
+  Value.vt = VT_EMPTY;
+  if (S_OK != (Symbol->*Method)(&Value))
+    return;
+  Variant V = VariantFromVARIANT(Value);
+  OS.indent(Indent);
+  OS << V;
+}
 }
 
 namespace llvm {
@@ -276,6 +342,9 @@ void DIARawSymbol::dump(raw_ostream &OS, int Indent,
   RAW_METHOD_DUMP(OS, get_virtualBaseClass)
   RAW_METHOD_DUMP(OS, get_isVirtualInheritance)
   RAW_METHOD_DUMP(OS, get_volatileType)
+  RAW_METHOD_DUMP(OS, get_wasInlined)
+  RAW_METHOD_DUMP(OS, get_unused)
+  RAW_METHOD_DUMP(OS, get_value)
 }
 
 std::unique_ptr<IPDBEnumSymbols>
@@ -611,6 +680,15 @@ uint32_t DIARawSymbol::getUpperBoundId() const {
   return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_upperBoundId);
 }
 
+Variant DIARawSymbol::getValue() const {
+  VARIANT Value;
+  Value.vt = VT_EMPTY;
+  if (S_OK != Symbol->get_value(&Value))
+    return Variant();
+
+  return VariantFromVARIANT(Value);
+}
+
 uint32_t DIARawSymbol::getVirtualBaseDispIndex() const {
   return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualBaseDispIndex);
 }
@@ -1006,3 +1084,11 @@ bool DIARawSymbol::isVirtualInheritance() const {
 bool DIARawSymbol::isVolatileType() const {
   return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_volatileType);
 }
+
+bool DIARawSymbol::wasInlined() const {
+  return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_wasInlined);
+}
+
+std::string DIARawSymbol::getUnused() const {
+  return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_unused);
+}
index 28870870de5a9e20c3b0a1f54123f119c1254ca1..9ead764ae76d4dd54da989b81b9962b4e2b1ecb7 100644 (file)
@@ -25,6 +25,41 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const stream_indent &Indent) {
   return OS;
 }
 
+raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_VariantType &Type) {
+  switch (Type) {
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Bool, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Single, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Double, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int8, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int16, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int32, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int64, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt8, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt16, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt32, OS)
+    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt64, OS)
+    default:
+      OS << "Unknown";
+  }
+  return OS;
+}
+
+raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_DataKind &Data) {
+  switch (Data) {
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Unknown, "unknown", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Local, "local", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, StaticLocal, "static local", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Param, "param", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, ObjectPtr, "this ptr", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, FileStatic, "static global", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Global, "global", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Member, "member", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, StaticMember, "static member", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Constant, "const", OS)
+  }
+  return OS;
+}
+
 raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_RegisterId &Reg) {
   switch (Reg) {
     CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, AL, OS)
@@ -82,16 +117,16 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_RegisterId &Reg) {
 
 raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_LocType &Loc) {
   switch (Loc) {
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Static, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, TLS, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, RegRel, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, ThisRel, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Enregistered, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, BitField, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Slot, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, IlRel, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, MetaData, OS)
-    CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Constant, OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Static, "static", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, TLS, "tls", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, RegRel, "regrel", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, ThisRel, "thisrel", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Enregistered, "register", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, BitField, "bitfield", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Slot, "slot", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, IlRel, "IL rel", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, MetaData, "metadata", OS)
+    CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Constant, "constant", OS)
   default:
     OS << "Unknown";
   }
@@ -200,6 +235,48 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_UniqueId &Id) {
   return OS;
 }
 
+raw_ostream &llvm::operator<<(raw_ostream &OS, const Variant &Value) {
+  switch (Value.Type) {
+    case PDB_VariantType::Bool:
+      OS << (Value.Bool ? "true" : "false");
+      break;
+    case PDB_VariantType::Double:
+      OS << Value.Double;
+      break;
+    case PDB_VariantType::Int16:
+      OS << Value.Int16;
+      break;
+    case PDB_VariantType::Int32:
+      OS << Value.Int32;
+      break;
+    case PDB_VariantType::Int64:
+      OS << Value.Int64;
+      break;
+    case PDB_VariantType::Int8:
+      OS << Value.Int8;
+      break;
+    case PDB_VariantType::Single:
+      OS << Value.Single;
+      break;
+    case PDB_VariantType::UInt16:
+      OS << Value.Double;
+      break;
+    case PDB_VariantType::UInt32:
+      OS << Value.UInt32;
+      break;
+    case PDB_VariantType::UInt64:
+      OS << Value.UInt64;
+      break;
+    case PDB_VariantType::UInt8:
+      OS << Value.UInt8;
+      break;
+    default:
+      OS << Value.Type;
+  }
+  OS << " {" << Value.Type << "}";
+  return OS;
+}
+
 raw_ostream &llvm::operator<<(raw_ostream &OS, const VersionInfo &Version) {
   OS << Version.Major << "." << Version.Minor << "." << Version.Build;
   return OS;
index bffad8af7513d324fdddda80e55d3a583bb7b132..3c4be3ad2d08a55c17370ba92dd44ad67dbc8a34 100644 (file)
@@ -8,8 +8,10 @@
 //===----------------------------------------------------------------------===//
 
 #include <utility>
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
 #include "llvm/DebugInfo/PDB/PDBExtras.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
 
 #include "llvm/Support/Format.h"
 
@@ -21,48 +23,69 @@ PDBSymbolData::PDBSymbolData(const IPDBSession &PDBSession,
 
 void PDBSymbolData::dump(raw_ostream &OS, int Indent,
                          PDB_DumpLevel Level) const {
-  OS.indent(Indent);
+  OS << stream_indent(Indent);
+  PDB_LocType Loc = getLocationType();
+  PDB_DataKind Kind = getDataKind();
   if (Level == PDB_DumpLevel::Compact) {
-    PDB_LocType Loc = getLocationType();
-    OS << Loc << " data [";
-    int Length;
     switch (Loc) {
-    case PDB_LocType::Static:
-      OS << format_hex(getRelativeVirtualAddress(), 10);
-      Length = getLength();
+    case PDB_LocType::Static: {
+      uint32_t RVA = getRelativeVirtualAddress();
+      OS << Kind << " data[";
+      if (RVA != 0)
+        OS << format_hex(RVA, 10);
+      else
+        OS << "???";
       break;
+    }
     case PDB_LocType::TLS:
+      OS << "threadlocal " << Kind << " data[";
       OS << getAddressSection() << ":" << format_hex(getAddressOffset(), 10);
       break;
     case PDB_LocType::RegRel:
-      OS << getRegisterId() << " + " << getOffset() << "]";
+      OS << "regrel " << Kind << " data[";
+      OS << getRegisterId() << " + " << getOffset();
       break;
-    case PDB_LocType::ThisRel:
-      OS << "this + " << getOffset() << "]";
+    case PDB_LocType::ThisRel: {
+      uint32_t Offset = getOffset();
+      OS << Kind << " data[this + " << format_hex(Offset, 4);
       break;
+    }
     case PDB_LocType::Enregistered:
-      OS << getRegisterId() << "]";
+      OS << "register " << Kind << " data[" << getRegisterId();
       break;
     case PDB_LocType::BitField: {
+      OS << "bitfield data[this + ";
       uint32_t Offset = getOffset();
       uint32_t BitPos = getBitPosition();
       uint32_t Length = getLength();
-      uint32_t StartBits = 8 - BitPos;
-      uint32_t MiddleBytes = (Length - StartBits) / 8;
-      uint32_t EndBits = Length - StartBits - MiddleBytes * 8;
-      OS << format_hex(Offset, 10) << ":" << BitPos;
-      OS << " - " << format_hex(Offset + MiddleBytes, 10) << ":" << EndBits;
+      OS << format_hex(Offset, 4) << ":" << BitPos << "," << Length;
       break;
     }
     case PDB_LocType::Slot:
       OS << getSlot();
+      break;
+    case PDB_LocType::Constant: {
+      OS << "constant data[";
+      OS << getValue();
+      break;
+    }
     case PDB_LocType::IlRel:
     case PDB_LocType::MetaData:
-    case PDB_LocType::Constant:
     default:
       OS << "???";
     }
-    OS << "] ";
+  }
+  OS << "] ";
+  if (Kind == PDB_DataKind::Member || Kind == PDB_DataKind::StaticMember) {
+    uint32_t ClassId = getClassParentId();
+    if (auto Class = Session.getSymbolById(ClassId)) {
+      if (auto UDT = dyn_cast<PDBSymbolTypeUDT>(Class.get()))
+        OS << UDT->getName();
+      else
+        OS << "{class " << Class->getSymTag() << "}";
+      OS << "::";
+    }
   }
   OS << getName() << "\n";
+  OS.flush();
 }
\ No newline at end of file
index c058949991bfaac68b10fdce5a9f356fd5b272d6..4f999d9138e6d396e3783b3661763e156002d765 100644 (file)
@@ -29,10 +29,6 @@ void PDBSymbolExe::dump(raw_ostream &OS, int Indent,
 
   OS << "Summary for " << FileName << "\n";
 
-  TagStats Stats;
-  auto ChildrenEnum = getChildStats(Stats);
-  OS << stream_indent(Indent + 2) << "Children: " << Stats << "\n";
-
   uint64_t FileSize = 0;
   if (!llvm::sys::fs::file_size(FileName, FileSize))
     OS << "  Size: " << FileSize << " bytes\n";
@@ -47,4 +43,11 @@ void PDBSymbolExe::dump(raw_ostream &OS, int Indent,
   if (hasPrivateSymbols())
     OS << "HasPrivateSymbols ";
   OS << "\n";
+
+  TagStats Stats;
+  auto ChildrenEnum = getChildStats(Stats);
+  OS << stream_indent(Indent + 2) << "Children: " << Stats << "\n";
+  while (auto Child = ChildrenEnum->getNext()) {
+    Child->dump(OS, Indent+2, PDB_DumpLevel::Compact);
+  }
 }
index 17473c17a45cfb329dd68a8ef630b7b6ebd99f0f..4bd96080c7f7dfcb1c8551f073bae11ae6f7db6f 100644 (file)
 #include <utility>
 
 #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
 #include "llvm/DebugInfo/PDB/PDBSymbol.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
 
 #include "llvm/Support/Format.h"
 
@@ -25,16 +27,23 @@ PDBSymbolFunc::PDBSymbolFunc(const IPDBSession &PDBSession,
 void PDBSymbolFunc::dump(raw_ostream &OS, int Indent,
                          PDB_DumpLevel Level) const {
   if (Level == PDB_DumpLevel::Compact) {
+    OS << stream_indent(Indent);
+
     uint32_t FuncStart = getRelativeVirtualAddress();
     uint32_t FuncEnd = FuncStart + getLength();
-    OS << stream_indent(Indent);
-    OS << "[" << format_hex(FuncStart, 8);
-    if (auto DebugStart = findOneChild<PDBSymbolFuncDebugStart>())
-      OS << "+" << DebugStart->getRelativeVirtualAddress() - FuncStart;
-    OS << " - " << format_hex(FuncEnd, 8);
-    if (auto DebugEnd = findOneChild<PDBSymbolFuncDebugEnd>())
+    if (FuncStart == 0 && FuncEnd == 0) {
+      OS << "func [???]";
+    } else {
+      OS << "func ";
+      OS << "[" << format_hex(FuncStart, 8);
+      if (auto DebugStart = findOneChild<PDBSymbolFuncDebugStart>())
+        OS << "+" << DebugStart->getRelativeVirtualAddress() - FuncStart;
+      OS << " - " << format_hex(FuncEnd, 8);
+      if (auto DebugEnd = findOneChild<PDBSymbolFuncDebugEnd>())
         OS << "-" << FuncEnd - DebugEnd->getRelativeVirtualAddress();
-    OS << "] ";
+      OS << "] ";
+    }
+
     PDB_RegisterId Reg = getLocalBasePointerRegisterId();
     if (Reg == PDB_RegisterId::VFrame)
       OS << "(VFrame)";
@@ -42,6 +51,18 @@ void PDBSymbolFunc::dump(raw_ostream &OS, int Indent,
       OS << "(" << Reg << ")";
     else
       OS << "(FPO)";
-    OS << " " << getName() << "\n";
+
+    OS << " ";
+    uint32_t ClassId = getClassParentId();
+    if (ClassId != 0) {
+      if (auto Class = Session.getSymbolById(ClassId)) {
+        if (auto UDT = dyn_cast<PDBSymbolTypeUDT>(Class.get()))
+          OS << UDT->getName() << "::";
+        else
+          OS << "{class " << Class->getSymTag() << "}::";
+      }
+    }
+    OS << getName();
+    OS << "\n";
   }
 }