[llvm-pdbdump] More advanced class definition dumping.
authorZachary Turner <zturner@google.com>
Wed, 12 Apr 2017 23:18:21 +0000 (23:18 +0000)
committerZachary Turner <zturner@google.com>
Wed, 12 Apr 2017 23:18:21 +0000 (23:18 +0000)
Previously the dumping of class definitions was very primitive,
and it made it hard to do more than the most trivial of output
formats when dumping.  As such, we would only dump one line for
each field, and then dump non-layout items like nested types
and enums.

With this patch, we do a complete analysis of the object
hierarchy including aggregate types, bases, virtual bases,
vftable analysis, etc.  The only immediately visible effects
of this are that a) we can now dump a line for the vfptr where
before we would treat that as padding, and b) we now don't
treat virtual bases that come at the end of a class as padding
since we have a more detailed analysis of the class's storage
usage.

In subsequent patches, we should be able to use this analysis
to display a complete graphical view of a class's layout including
recursing arbitrarily deep into an object's base class / aggregate
member hierarchy.

llvm-svn: 300133

39 files changed:
llvm/include/llvm/DebugInfo/PDB/DIA/DIARawSymbol.h
llvm/include/llvm/DebugInfo/PDB/DIA/DIASession.h
llvm/include/llvm/DebugInfo/PDB/IPDBRawSymbol.h
llvm/include/llvm/DebugInfo/PDB/IPDBSession.h
llvm/include/llvm/DebugInfo/PDB/Native/NativeRawSymbol.h
llvm/include/llvm/DebugInfo/PDB/Native/NativeSession.h
llvm/include/llvm/DebugInfo/PDB/PDBSymbol.h
llvm/include/llvm/DebugInfo/PDB/PDBSymbolExe.h
llvm/include/llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h
llvm/include/llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h
llvm/include/llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h
llvm/include/llvm/DebugInfo/PDB/UDTLayout.h [new file with mode: 0644]
llvm/include/llvm/Support/Casting.h
llvm/lib/DebugInfo/PDB/CMakeLists.txt
llvm/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp
llvm/lib/DebugInfo/PDB/DIA/DIASession.cpp
llvm/lib/DebugInfo/PDB/Native/NativeRawSymbol.cpp
llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp
llvm/lib/DebugInfo/PDB/PDBSymbol.cpp
llvm/lib/DebugInfo/PDB/PDBSymbolExe.cpp
llvm/lib/DebugInfo/PDB/PDBSymbolTypeUDT.cpp
llvm/lib/DebugInfo/PDB/UDTLayout.cpp [new file with mode: 0644]
llvm/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test
llvm/test/DebugInfo/PDB/Inputs/symbolformat.pdb
llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp [new file with mode: 0644]
llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb [new file with mode: 0644]
llvm/test/tools/llvm-pdbdump/class-layout.test
llvm/test/tools/llvm-pdbdump/enum-layout.test
llvm/test/tools/llvm-pdbdump/simple-padding.test [new file with mode: 0644]
llvm/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
llvm/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
llvm/tools/llvm-pdbdump/PrettyFunctionDumper.cpp
llvm/tools/llvm-pdbdump/PrettyTypeDumper.cpp
llvm/tools/llvm-pdbdump/PrettyTypedefDumper.cpp
llvm/tools/llvm-pdbdump/PrettyVariableDumper.cpp
llvm/tools/llvm-pdbdump/PrettyVariableDumper.h
llvm/tools/llvm-pdbdump/llvm-pdbdump.cpp
llvm/tools/llvm-pdbdump/llvm-pdbdump.h
llvm/unittests/DebugInfo/PDB/PDBApiTest.cpp

index 1e40c46..c0633cb 100644 (file)
@@ -102,6 +102,7 @@ public:
   uint32_t getVirtualBaseDispIndex() const override;
   uint32_t getVirtualBaseOffset() const override;
   uint32_t getVirtualTableShapeId() const override;
+  std::unique_ptr<PDBSymbolTypeVTable> getVirtualBaseTableType() const override;
   PDB_DataKind getDataKind() const override;
   PDB_SymType getSymTag() const override;
   PDB_UniqueId getGuid() const override;
index 3504425..3f58186 100644 (file)
@@ -31,7 +31,7 @@ public:
 
   uint64_t getLoadAddress() const override;
   void setLoadAddress(uint64_t Address) override;
-  std::unique_ptr<PDBSymbolExe> getGlobalScope() override;
+  std::unique_ptr<PDBSymbolExe> getGlobalScope() const override;
   std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const override;
 
   std::unique_ptr<PDBSymbol>
index 49866b8..4c28e19 100644 (file)
@@ -21,6 +21,9 @@ class raw_ostream;
 
 namespace pdb {
 
+class PDBSymbolTypeVTable;
+class PDBSymbolTypeVTableShape;
+
 /// IPDBRawSymbol defines an interface used to represent an arbitrary symbol.
 /// It exposes a monolithic interface consisting of accessors for the union of
 /// all properties that are valid for any symbol type.  This interface is then
@@ -110,6 +113,8 @@ public:
   virtual Variant getValue() const = 0;
   virtual uint32_t getVirtualBaseDispIndex() const = 0;
   virtual uint32_t getVirtualBaseOffset() const = 0;
+  virtual std::unique_ptr<PDBSymbolTypeVTable>
+  getVirtualBaseTableType() const = 0;
   virtual uint32_t getVirtualTableShapeId() const = 0;
   virtual PDB_DataKind getDataKind() const = 0;
   virtual PDB_SymType getSymTag() const = 0;
index 696736a..85d9fe1 100644 (file)
@@ -29,20 +29,12 @@ public:
 
   virtual uint64_t getLoadAddress() const = 0;
   virtual void setLoadAddress(uint64_t Address) = 0;
-  virtual std::unique_ptr<PDBSymbolExe> getGlobalScope() = 0;
+  virtual std::unique_ptr<PDBSymbolExe> getGlobalScope() const = 0;
   virtual std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const = 0;
 
   template <typename T>
   std::unique_ptr<T> getConcreteSymbolById(uint32_t SymbolId) const {
-    auto Symbol(getSymbolById(SymbolId));
-    if (!Symbol)
-      return nullptr;
-
-    T *ConcreteSymbol = dyn_cast<T>(Symbol.get());
-    if (!ConcreteSymbol)
-      return nullptr;
-    (void)Symbol.release();
-    return std::unique_ptr<T>(ConcreteSymbol);
+    return unique_dyn_cast_or_null<T>(getSymbolById(SymbolId));
   }
 
   virtual std::unique_ptr<PDBSymbol>
index 655bed9..cffb5d0 100644 (file)
@@ -101,6 +101,7 @@ public:
   uint32_t getVirtualBaseDispIndex() const override;
   uint32_t getVirtualBaseOffset() const override;
   uint32_t getVirtualTableShapeId() const override;
+  std::unique_ptr<PDBSymbolTypeVTable> getVirtualBaseTableType() const override;
   PDB_DataKind getDataKind() const override;
   PDB_SymType getSymTag() const override;
   PDB_UniqueId getGuid() const override;
index bbe2077..e6da266 100644 (file)
@@ -32,7 +32,7 @@ public:
 
   uint64_t getLoadAddress() const override;
   void setLoadAddress(uint64_t Address) override;
-  std::unique_ptr<PDBSymbolExe> getGlobalScope() override;
+  std::unique_ptr<PDBSymbolExe> getGlobalScope() const override;
   std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const override;
 
   std::unique_ptr<PDBSymbol>
index 652f213..b114b7a 100644 (file)
@@ -62,6 +62,7 @@ class PDBSymbol {
 protected:
   PDBSymbol(const IPDBSession &PDBSession,
             std::unique_ptr<IPDBRawSymbol> Symbol);
+  PDBSymbol(PDBSymbol &Symbol);
 
 public:
   static std::unique_ptr<PDBSymbol>
@@ -91,12 +92,6 @@ public:
     return Enumerator->getNext();
   }
 
-  template <typename T> T *cast() { return llvm::dyn_cast<T>(this); }
-
-  template <typename T> const T *cast() const {
-    return llvm::dyn_cast<T>(this);
-  }
-
   std::unique_ptr<PDBSymbol> clone() const;
 
   template <typename T>
@@ -128,18 +123,11 @@ protected:
 
   template <typename ConcreteType>
   std::unique_ptr<ConcreteType> getConcreteSymbolByIdHelper(uint32_t Id) const {
-    auto Sym = getSymbolByIdHelper(Id);
-    if (!Sym)
-      return nullptr;
-    ConcreteType *Result = Sym->cast<ConcreteType>();
-    if (!Result)
-      return nullptr;
-    Sym.release();
-    return std::unique_ptr<ConcreteType>(Result);
+    return unique_dyn_cast_or_null<ConcreteType>(getSymbolByIdHelper(Id));
   }
 
   const IPDBSession &Session;
-  const std::unique_ptr<IPDBRawSymbol> RawSymbol;
+  std::unique_ptr<IPDBRawSymbol> RawSymbol;
 };
 
 } // namespace llvm
index 5b3f50d..2c2d746 100644 (file)
@@ -37,6 +37,8 @@ public:
   FORWARD_SYMBOL_METHOD(getSignature)
   FORWARD_SYMBOL_METHOD(getSymbolsFileName)
 
+  uint32_t getPointerByteSize() const;
+
 private:
   void dumpChildren(raw_ostream &OS, StringRef Label, PDB_SymType ChildType,
                     int Indent) const;
index 0924efb..d607a3d 100644 (file)
@@ -13,6 +13,9 @@
 #include "PDBSymbol.h"
 #include "PDBTypes.h"
 
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h"
+
 namespace llvm {
 
 class raw_ostream;
index 47a4525..e9e7fe8 100644 (file)
@@ -10,7 +10,9 @@
 #ifndef LLVM_DEBUGINFO_PDB_PDBSYMBOLTYPEUDT_H
 #define LLVM_DEBUGINFO_PDB_PDBSYMBOLTYPEUDT_H
 
+#include "IPDBSession.h"
 #include "PDBSymbol.h"
+#include "PDBSymbolTypeBaseClass.h"
 #include "PDBTypes.h"
 
 namespace llvm {
@@ -18,11 +20,17 @@ namespace llvm {
 class raw_ostream;
 
 namespace pdb {
+
 class PDBSymbolTypeUDT : public PDBSymbol {
 public:
   PDBSymbolTypeUDT(const IPDBSession &PDBSession,
                    std::unique_ptr<IPDBRawSymbol> UDTSymbol);
 
+  std::unique_ptr<PDBSymbolTypeUDT> clone() const {
+    return getSession().getConcreteSymbolById<PDBSymbolTypeUDT>(
+        getSymIndexId());
+  }
+
   DECLARE_PDB_SYMBOL_CONCRETE_TYPE(PDB_SymType::UDT)
 
   void dump(PDBSymDumper &Dumper) const override;
index 17612ff..e270c2b 100644 (file)
@@ -28,6 +28,7 @@ public:
   void dump(PDBSymDumper &Dumper) const override;
 
   FORWARD_SYMBOL_ID_METHOD(getClassParent)
+  FORWARD_SYMBOL_METHOD(getOffset)
   FORWARD_SYMBOL_METHOD(isConstType)
   FORWARD_SYMBOL_ID_METHOD(getLexicalParent)
   FORWARD_SYMBOL_ID_METHOD(getType)
diff --git a/llvm/include/llvm/DebugInfo/PDB/UDTLayout.h b/llvm/include/llvm/DebugInfo/PDB/UDTLayout.h
new file mode 100644 (file)
index 0000000..20b7034
--- /dev/null
@@ -0,0 +1,145 @@
+//===- UDTLayout.h - UDT layout info ----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_DEBUGINFO_PDB_UDTLAYOUT_H
+#define LLVM_DEBUGINFO_PDB_UDTLAYOUT_H
+
+#include "PDBSymbol.h"
+#include "PDBTypes.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+
+#include <list>
+#include <memory>
+
+namespace llvm {
+
+class raw_ostream;
+
+namespace pdb {
+
+class PDBSymTypeBaseClass;
+class PDBSymbolData;
+class PDBSymbolTypeUDT;
+class PDBSymbolTypeVTable;
+
+class ClassLayout;
+class BaseClassLayout;
+class StorageItemBase;
+class UDTLayoutBase;
+
+class StorageItemBase {
+public:
+  StorageItemBase(const UDTLayoutBase &Parent, const PDBSymbol &Symbol,
+                  const std::string &Name, uint32_t OffsetInParent,
+                  uint32_t Size);
+  virtual ~StorageItemBase() {}
+
+  virtual uint32_t deepPaddingSize() const;
+
+  const UDTLayoutBase &getParent() const { return Parent; }
+  StringRef getName() const { return Name; }
+  uint32_t getOffsetInParent() const { return OffsetInParent; }
+  uint32_t getSize() const { return SizeOf; }
+  const PDBSymbol &getSymbol() const { return Symbol; }
+
+protected:
+  const UDTLayoutBase &Parent;
+  const PDBSymbol &Symbol;
+  BitVector UsedBytes;
+  std::string Name;
+  uint32_t OffsetInParent = 0;
+  uint32_t SizeOf = 0;
+};
+
+class DataMemberLayoutItem : public StorageItemBase {
+public:
+  DataMemberLayoutItem(const UDTLayoutBase &Parent,
+                       std::unique_ptr<PDBSymbolData> DataMember);
+
+  virtual uint32_t deepPaddingSize() const;
+
+  const PDBSymbolData &getDataMember();
+
+private:
+  std::unique_ptr<PDBSymbolData> DataMember;
+  std::unique_ptr<ClassLayout> UdtLayout;
+};
+
+class VTableLayoutItem : public StorageItemBase {
+public:
+  VTableLayoutItem(const UDTLayoutBase &Parent,
+                   std::unique_ptr<PDBSymbolTypeVTable> VTable);
+
+private:
+  std::unique_ptr<PDBSymbolTypeVTable> VTable;
+  std::vector<std::unique_ptr<PDBSymbolFunc>> VTableFuncs;
+};
+
+class UDTLayoutBase {
+public:
+  UDTLayoutBase(const PDBSymbol &Symbol, const std::string &Name,
+                uint32_t Size);
+
+  uint32_t shallowPaddingSize() const;
+  uint32_t deepPaddingSize() const;
+
+  const BitVector &usedBytes() const { return UsedBytes; }
+
+  uint32_t getClassSize() const { return SizeOf; }
+
+  const PDBSymbol &getSymbol() const { return Symbol; }
+
+  ArrayRef<std::unique_ptr<StorageItemBase>> layout_items() const {
+    return ChildStorage;
+  }
+
+  ArrayRef<std::unique_ptr<PDBSymbol>> other_items() const {
+    return NonStorageItems;
+  }
+
+protected:
+  void initializeChildren(const PDBSymbol &Sym);
+
+  void addChildToLayout(std::unique_ptr<StorageItemBase> Child);
+
+  uint32_t SizeOf = 0;
+  std::string Name;
+
+  const PDBSymbol &Symbol;
+  BitVector UsedBytes;
+  std::vector<std::unique_ptr<PDBSymbol>> NonStorageItems;
+  std::vector<std::unique_ptr<StorageItemBase>> ChildStorage;
+  std::vector<std::list<StorageItemBase *>> ChildrenPerByte;
+  std::vector<BaseClassLayout *> BaseClasses;
+  VTableLayoutItem *VTable = nullptr;
+};
+
+class ClassLayout : public UDTLayoutBase {
+public:
+  explicit ClassLayout(std::unique_ptr<PDBSymbolTypeUDT> UDT);
+
+private:
+  std::unique_ptr<PDBSymbolTypeUDT> Type;
+};
+
+class BaseClassLayout : public UDTLayoutBase, public StorageItemBase {
+public:
+  BaseClassLayout(const UDTLayoutBase &Parent,
+                  std::unique_ptr<PDBSymbolTypeBaseClass> Base);
+
+private:
+  std::unique_ptr<PDBSymbolTypeBaseClass> Base;
+  bool IsVirtualBase;
+};
+}
+} // namespace llvm
+
+#endif // LLVM_DEBUGINFO_PDB_UDTLAYOUT_H
index 3a0c553..89d2af0 100644 (file)
@@ -384,7 +384,7 @@ LLVM_NODISCARD inline auto unique_dyn_cast_or_null(std::unique_ptr<Y> &Val)
     -> decltype(cast<X>(Val)) {
   if (!Val)
     return nullptr;
-  return unique_dyn_cast(Val);
+  return unique_dyn_cast<X, Y>(Val);
 }
 
 template <class X, class Y>
index 1295d2a..f87a0b0 100644 (file)
@@ -101,6 +101,7 @@ add_llvm_library(LLVMDebugInfoPDB
   PDBSymbolUnknown.cpp
   PDBSymbolUsingNamespace.cpp
   PDBSymDumper.cpp
+  UDTLayout.cpp
   ${PDB_IMPL_SOURCES}
 
   ADDITIONAL_HEADER_DIRS
index 6182dab..5e8c0bd 100644 (file)
@@ -14,6 +14,9 @@
 #include "llvm/DebugInfo/PDB/DIA/DIAEnumSymbols.h"
 #include "llvm/DebugInfo/PDB/DIA/DIASession.h"
 #include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -717,6 +720,18 @@ uint32_t DIARawSymbol::getVirtualTableShapeId() const {
   return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualTableShapeId);
 }
 
+std::unique_ptr<PDBSymbolTypeVTable>
+DIARawSymbol::getVirtualBaseTableType() const {
+  CComPtr<IDiaSymbol> TableType;
+  if (FAILED(Symbol->get_virtualBaseTableType(&TableType)) || !TableType)
+    return nullptr;
+
+  auto RawVT = llvm::make_unique<DIARawSymbol>(Session, TableType);
+  auto Pointer =
+      llvm::make_unique<PDBSymbolTypePointer>(Session, std::move(RawVT));
+  return unique_dyn_cast<PDBSymbolTypeVTable>(Pointer->getPointeeType());
+}
+
 PDB_DataKind DIARawSymbol::getDataKind() const {
   return PrivateGetDIAValue<DWORD, PDB_DataKind>(Symbol,
                                                  &IDiaSymbol::get_dataKind);
index 7077bda..6ecf335 100644 (file)
@@ -140,7 +140,7 @@ void DIASession::setLoadAddress(uint64_t Address) {
   Session->put_loadAddress(Address);
 }
 
-std::unique_ptr<PDBSymbolExe> DIASession::getGlobalScope() {
+std::unique_ptr<PDBSymbolExe> DIASession::getGlobalScope() const {
   CComPtr<IDiaSymbol> GlobalScope;
   if (S_OK != Session->get_globalScope(&GlobalScope))
     return nullptr;
index 4841ded..3aba35a 100644 (file)
@@ -13,6 +13,8 @@
 #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
 #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
 #include "llvm/DebugInfo/PDB/PDBExtras.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h"
 #include "llvm/Support/ConvertUTF.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -318,6 +320,11 @@ uint32_t NativeRawSymbol::getVirtualTableShapeId() const {
   return 0;
 }
 
+std::unique_ptr<PDBSymbolTypeVTable>
+NativeRawSymbol::getVirtualBaseTableType() const {
+  return nullptr;
+}
+
 PDB_DataKind NativeRawSymbol::getDataKind() const {
   return PDB_DataKind::Unknown;
 }
index 3a83a32..7e6843b 100644 (file)
@@ -70,8 +70,9 @@ uint64_t NativeSession::getLoadAddress() const { return 0; }
 
 void NativeSession::setLoadAddress(uint64_t Address) {}
 
-std::unique_ptr<PDBSymbolExe> NativeSession::getGlobalScope() {
-  auto RawSymbol = llvm::make_unique<NativeExeSymbol>(*this);
+std::unique_ptr<PDBSymbolExe> NativeSession::getGlobalScope() const {
+  auto RawSymbol =
+      llvm::make_unique<NativeExeSymbol>(const_cast<NativeSession &>(*this));
   auto PdbSymbol(PDBSymbol::create(*this, std::move(RawSymbol)));
   std::unique_ptr<PDBSymbolExe> ExeSymbol(
     static_cast<PDBSymbolExe *>(PdbSymbol.release()));
index 2c8438f..14eb6ba 100644 (file)
@@ -54,6 +54,9 @@ PDBSymbol::PDBSymbol(const IPDBSession &PDBSession,
                      std::unique_ptr<IPDBRawSymbol> Symbol)
     : Session(PDBSession), RawSymbol(std::move(Symbol)) {}
 
+PDBSymbol::PDBSymbol(PDBSymbol &Symbol)
+    : Session(Symbol.Session), RawSymbol(std::move(Symbol.RawSymbol)) {}
+
 PDBSymbol::~PDBSymbol() = default;
 
 #define FACTORY_SYMTAG_CASE(Tag, Type)                                         \
@@ -100,12 +103,6 @@ PDBSymbol::create(const IPDBSession &PDBSession,
   }
 }
 
-#define TRY_DUMP_TYPE(Type)                                                    \
-  if (const Type *DerivedThis = this->cast<Type>())                            \
-    Dumper.dump(OS, Indent, *DerivedThis);
-
-#define ELSE_TRY_DUMP_TYPE(Type, Dumper) else TRY_DUMP_TYPE(Type, Dumper)
-
 void PDBSymbol::defaultDump(raw_ostream &OS, int Indent) const {
   RawSymbol->dump(OS, Indent);
 }
index b9fcac7..7417167 100644 (file)
@@ -10,6 +10,7 @@
 #include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
 
 #include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
 
 #include <utility>
 
@@ -23,3 +24,13 @@ PDBSymbolExe::PDBSymbolExe(const IPDBSession &PDBSession,
 }
 
 void PDBSymbolExe::dump(PDBSymDumper &Dumper) const { Dumper.dump(*this); }
+
+uint32_t PDBSymbolExe::getPointerByteSize() const {
+  auto Pointer = findOneChild<PDBSymbolTypePointer>();
+  if (Pointer)
+    return Pointer->getLength();
+
+  if (getMachineType() == PDB_Machine::x86)
+    return 4;
+  return 8;
+}
index 4a9a9ed..15dc153 100644 (file)
@@ -9,7 +9,15 @@
 
 #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
 
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
 #include "llvm/DebugInfo/PDB/PDBSymDumper.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTableShape.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
 
 #include <utility>
 
diff --git a/llvm/lib/DebugInfo/PDB/UDTLayout.cpp b/llvm/lib/DebugInfo/PDB/UDTLayout.cpp
new file mode 100644 (file)
index 0000000..71443fe
--- /dev/null
@@ -0,0 +1,194 @@
+//===- UDTLayout.cpp --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+
+#include "llvm/DebugInfo/PDB/IPDBSession.h"
+#include "llvm/DebugInfo/PDB/PDBSymbol.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeBaseClass.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
+
+#include <utility>
+
+using namespace llvm;
+using namespace llvm::pdb;
+
+static std::unique_ptr<PDBSymbol> getSymbolType(const PDBSymbol &Symbol) {
+  const IPDBSession &Session = Symbol.getSession();
+  const IPDBRawSymbol &RawSymbol = Symbol.getRawSymbol();
+  uint32_t TypeId = RawSymbol.getTypeId();
+  return Session.getSymbolById(TypeId);
+}
+
+static uint32_t getTypeLength(const PDBSymbol &Symbol) {
+  auto SymbolType = getSymbolType(Symbol);
+  const IPDBRawSymbol &RawType = SymbolType->getRawSymbol();
+
+  return RawType.getLength();
+}
+
+StorageItemBase::StorageItemBase(const UDTLayoutBase &Parent,
+                                 const PDBSymbol &Symbol,
+                                 const std::string &Name,
+                                 uint32_t OffsetInParent, uint32_t Size)
+    : Parent(Parent), Symbol(Symbol), Name(Name), SizeOf(Size),
+      OffsetInParent(OffsetInParent) {
+  UsedBytes.resize(SizeOf, true);
+}
+
+uint32_t StorageItemBase::deepPaddingSize() const {
+  // sizeof(Field) - sizeof(typeof(Field)) is trailing padding.
+  return SizeOf - getTypeLength(Symbol);
+}
+
+DataMemberLayoutItem::DataMemberLayoutItem(
+    const UDTLayoutBase &Parent, std::unique_ptr<PDBSymbolData> DataMember)
+    : StorageItemBase(Parent, *DataMember, DataMember->getName(),
+                      DataMember->getOffset(), getTypeLength(*DataMember)),
+      DataMember(std::move(DataMember)) {
+  auto Type = this->DataMember->getType();
+  if (auto UDT = unique_dyn_cast<PDBSymbolTypeUDT>(Type)) {
+    // UDT data members might have padding in between fields, but otherwise
+    // a member should occupy its entire storage.
+    UsedBytes.resize(SizeOf, false);
+    UdtLayout = llvm::make_unique<ClassLayout>(std::move(UDT));
+  }
+}
+
+const PDBSymbolData &DataMemberLayoutItem::getDataMember() {
+  return *dyn_cast<PDBSymbolData>(&Symbol);
+}
+
+uint32_t DataMemberLayoutItem::deepPaddingSize() const {
+  uint32_t Result = StorageItemBase::deepPaddingSize();
+  if (UdtLayout)
+    Result += UdtLayout->deepPaddingSize();
+  return Result;
+}
+
+VTableLayoutItem::VTableLayoutItem(const UDTLayoutBase &Parent,
+                                   std::unique_ptr<PDBSymbolTypeVTable> VTable)
+    : StorageItemBase(Parent, *VTable, "<vtbl>", 0, getTypeLength(*VTable)),
+      VTable(std::move(VTable)) {
+  // initialize vtbl methods.
+  auto VTableType = cast<PDBSymbolTypePointer>(this->VTable->getType());
+  uint32_t PointerSize = VTableType->getLength();
+
+  if (auto Shape = unique_dyn_cast<PDBSymbolTypeVTableShape>(
+          VTableType->getPointeeType())) {
+    VTableFuncs.resize(Shape->getCount());
+
+    auto ParentFunctions = Parent.getSymbol().findAllChildren<PDBSymbolFunc>();
+    while (auto Func = ParentFunctions->getNext()) {
+      if (Func->isVirtual()) {
+        uint32_t Index = Func->getVirtualBaseOffset();
+        assert(Index % PointerSize == 0);
+        Index /= PointerSize;
+
+        // Don't allow a compiler generated function to overwrite a user
+        // function in the VTable.  Not sure why this happens, but a function
+        // named __vecDelDtor sometimes shows up on top of the destructor.
+        if (Func->isCompilerGenerated() && VTableFuncs[Index])
+          continue;
+        VTableFuncs[Index] = std::move(Func);
+      }
+    }
+  }
+}
+
+UDTLayoutBase::UDTLayoutBase(const PDBSymbol &Symbol, const std::string &Name,
+                             uint32_t Size)
+    : Symbol(Symbol), Name(Name), SizeOf(Size) {
+  UsedBytes.resize(Size);
+  ChildrenPerByte.resize(Size);
+  initializeChildren(Symbol);
+}
+
+ClassLayout::ClassLayout(std::unique_ptr<PDBSymbolTypeUDT> UDT)
+    : UDTLayoutBase(*UDT, UDT->getName(), UDT->getLength()),
+      Type(std::move(UDT)) {}
+
+BaseClassLayout::BaseClassLayout(const UDTLayoutBase &Parent,
+                                 std::unique_ptr<PDBSymbolTypeBaseClass> Base)
+    : UDTLayoutBase(*Base, Base->getName(), Base->getLength()),
+      StorageItemBase(Parent, *Base, Base->getName(), Base->getOffset(),
+                      Base->getLength()),
+      Base(std::move(Base)) {
+  IsVirtualBase = this->Base->isVirtualBaseClass();
+}
+
+uint32_t UDTLayoutBase::shallowPaddingSize() const {
+  return UsedBytes.size() - UsedBytes.count();
+}
+
+uint32_t UDTLayoutBase::deepPaddingSize() const {
+  uint32_t Result = shallowPaddingSize();
+  for (auto &Child : ChildStorage)
+    Result += Child->deepPaddingSize();
+  return Result;
+}
+
+void UDTLayoutBase::initializeChildren(const PDBSymbol &Sym) {
+  auto Children = Sym.findAllChildren();
+  while (auto Child = Children->getNext()) {
+    if (auto Data = unique_dyn_cast<PDBSymbolData>(Child)) {
+      if (Data->getDataKind() == PDB_DataKind::Member) {
+        auto DM =
+            llvm::make_unique<DataMemberLayoutItem>(*this, std::move(Data));
+
+        addChildToLayout(std::move(DM));
+      } else {
+        NonStorageItems.push_back(std::move(Data));
+      }
+      continue;
+    }
+
+    if (auto Base = unique_dyn_cast<PDBSymbolTypeBaseClass>(Child)) {
+      auto BL = llvm::make_unique<BaseClassLayout>(*this, std::move(Base));
+      BaseClasses.push_back(BL.get());
+
+      addChildToLayout(std::move(BL));
+      continue;
+    }
+
+    if (auto VT = unique_dyn_cast<PDBSymbolTypeVTable>(Child)) {
+      auto VTLayout = llvm::make_unique<VTableLayoutItem>(*this, std::move(VT));
+
+      VTable = VTLayout.get();
+
+      addChildToLayout(std::move(VTLayout));
+      continue;
+    }
+
+    NonStorageItems.push_back(std::move(Child));
+  }
+}
+
+void UDTLayoutBase::addChildToLayout(std::unique_ptr<StorageItemBase> Child) {
+  uint32_t Begin = Child->getOffsetInParent();
+  uint32_t End = Begin + Child->getSize();
+  UsedBytes.set(Begin, End);
+  while (Begin != End) {
+    ChildrenPerByte[Begin].push_back(Child.get());
+    ++Begin;
+  }
+
+  auto Loc = std::upper_bound(
+      ChildStorage.begin(), ChildStorage.end(), Begin,
+      [](uint32_t Off, const std::unique_ptr<StorageItemBase> &Item) {
+        return Off < Item->getOffsetInParent();
+      });
+
+  ChildStorage.insert(Loc, std::move(Child));
+}
\ No newline at end of file
index 60a1953..0bb3e00 100644 (file)
 ; TYPES_FORMAT-DAG: typedef class A ClassAType
 
 ; TYPES_1: Classes
-; TYPES_1: struct A {
+; TYPES_1: struct A [sizeof = 4] {
 ; TYPES_1: virtual void PureFunc() = 0
 ; TYPES_1: virtual void VirtualFunc()
 ; TYPES_1: void RegularFunc()
 ; TYPES_1: }
 
 ; TYPES_2: Classes
-; TYPES_2: struct MemberTest {
+; TYPES_2: struct MemberTest [sizeof = 96] {
 ; TYPES_2: data +0x00 [sizeof=4] MemberTest::NestedEnum m_nested_enum
 ; TYPES_2: data +0x04 [sizeof=4] int m_typedef
 ; TYPES_2: data +0x08 [sizeof=1] bool m_bool
index 0e509f3..9272f31 100644 (file)
Binary files a/llvm/test/DebugInfo/PDB/Inputs/symbolformat.pdb and b/llvm/test/DebugInfo/PDB/Inputs/symbolformat.pdb differ
diff --git a/llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp b/llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.cpp
new file mode 100644 (file)
index 0000000..b10839b
--- /dev/null
@@ -0,0 +1,122 @@
+// Compile with "cl /c /Zi /GR- SimplePaddingTest.cpp"
+// Link with "link SimplePaddingTest.obj /debug /nodefaultlib /entry:main"
+
+#include <stdint.h>
+
+extern "C" using at_exit_handler = void();
+
+int atexit(at_exit_handler handler) { return 0; }
+
+struct SimplePadNoPadding {
+  int32_t X;
+  int32_t Y;
+  // No padding anywhere, sizeof(T) = 8
+} A;
+
+struct SimplePadUnion {
+  union {
+    int32_t X;
+    int64_t Y;
+    struct {
+      int32_t X;
+      // 4 bytes of padding here
+      int64_t Y;
+    } Z;
+  };
+  // Since the padding occurs at a location that is occupied by other storage
+  // (namely the Y member), the storage will still be considered used, and so
+  // there will be no unused bytes in the larger class.  But in the debug
+  // info for the nested struct, we should see padding.
+  // sizeof(SimplePadUnion) == sizeof(Z) == 16
+} B;
+
+struct SimplePadNoPadding2 {
+  bool A;
+  bool B;
+  bool C;
+  bool D;
+  // No padding anywhere, sizeof(T) = 4
+} C;
+
+struct alignas(4) SimplePadFields1 {
+  char A;
+  char B;
+  char C;
+  // 1 byte of padding here, sizeof(T) = 4
+} E;
+
+struct SimplePadFields2 {
+  int32_t Y;
+  char X;
+} F;
+
+struct SimplePadBase {
+  // Make sure this class is 4 bytes, and the derived class requires 8 byte
+  // alignment, so that padding is inserted between base and derived.
+  int32_t X;
+  // No padding here
+} G;
+
+struct SimplePadDerived : public SimplePadBase {
+  // 4 bytes of padding here due to Y requiring 8 byte alignment.
+  // Thus, sizeof(T) = 16
+  int64_t Y;
+} H;
+
+struct SimplePadEmptyBase1 {};
+struct SimplePadEmptyBase2 {};
+
+struct SimplePadEmpty : public SimplePadEmptyBase1, SimplePadEmptyBase2 {
+  // Bases have to occupy at least 1 byte of storage, so this requires
+  // 2 bytes of padding, plus 1 byte for each base, yielding sizeof(T) = 8
+  int32_t X;
+} I;
+
+struct SimplePadVfptr {
+  virtual ~SimplePadVfptr() {}
+  static void operator delete(void *ptr, size_t sz) {}
+  int32_t X;
+} J;
+
+struct NonEmptyBase1 {
+  bool X;
+};
+
+struct NonEmptyBase2 {
+  bool Y;
+};
+
+struct SimplePadMultiInherit : public NonEmptyBase1, public NonEmptyBase2 {
+  // X and Y from the 2 bases will get squished together, leaving 2 bytes
+  // of padding necessary for proper alignment of an int32.
+  // Therefore, sizeof(T) = 2 + 2 + 4 = 8
+  int32_t X;
+} K;
+
+struct SimplePadMultiInherit2 : public SimplePadFields1, SimplePadFields2 {
+  // There should be 1 byte of padding after the first class, and
+  // 3 bytes of padding after the second class.
+  int32_t X;
+} L;
+
+struct OneLevelInherit : public NonEmptyBase1 {
+  short Y;
+};
+
+struct SimplePadTwoLevelInherit : public OneLevelInherit {
+  // OneLevelInherit has nested padding because of its base,
+  // and then padding again because of this class.  So each
+  // class should be 4 bytes, yielding sizeof(T) = 12.
+  int64_t Z;
+} M;
+
+struct SimplePadAggregate {
+  NonEmptyBase1 X;
+  int32_t Y;
+  // the presence of X will cause 3 bytes of padding to be injected.
+} N;
+
+int main(int argc, char **argv) {
+
+  return 0;
+}
diff --git a/llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb b/llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb
new file mode 100644 (file)
index 0000000..44207d6
Binary files /dev/null and b/llvm/test/tools/llvm-pdbdump/Inputs/SimplePaddingTest.pdb differ
index e2921d2..c0083d1 100644 (file)
 ; GLOBALS_TEST-DAG: GlobalsTest::Enum GlobalsTest::EnumVar
 
 ; MEMBERS_TEST: ---TYPES---
-; MEMBERS_TEST: class MembersTest::A {
+; MEMBERS_TEST: class MembersTest::A [sizeof = 16] {
 ; MEMBERS_TEST-DAG: typedef int NestedTypedef
 ; MEMBERS_TEST-DAG: enum NestedEnum
 ; MEMBERS_TEST: void MemberFunc()
-; MEMBERS_TEST-DAG: int IntMemberVar
-; MEMBERS_TEST-DAG: double DoubleMemberVar
+; MEMBERS_TEST-DAG: data +0x00 [sizeof=4] int IntMemberVar
+; MEMBERS_TEST-NEXT: <padding> (4 bytes)
+; MEMBERS_TEST-NEXT: data +0x08 [sizeof=8] double DoubleMemberVar
 ; MEMBERS_TEST: }
 
 ; BASE_CLASS_A: ---TYPES---
-; BASE_CLASS_A: class BaseClassTest::A {}
+; BASE_CLASS_A: class BaseClassTest::A [sizeof = 1] {}
 
 ; BASE_CLASS_B: ---TYPES---
-; BASE_CLASS_B: class BaseClassTest::B
+; BASE_CLASS_B: class BaseClassTest::B [sizeof = 4]
 ; BASE_CLASS_B-NEXT: : public virtual BaseClassTest::A {
 
 ; BASE_CLASS_C: ---TYPES---
-; BASE_CLASS_C: class BaseClassTest::C
+; BASE_CLASS_C: class BaseClassTest::C [sizeof = 4]
 ; BASE_CLASS_C-NEXT: : public virtual BaseClassTest::A {
 
 ; BASE_CLASS_D: ---TYPES---
-; BASE_CLASS_D: class BaseClassTest::D
+; BASE_CLASS_D: class BaseClassTest::D [sizeof = 8]
 ; BASE_CLASS_D-DAG: protected BaseClassTest::B
 ; BASE_CLASS_D-DAG: private BaseClassTest::C
 ; BASE_CLASS_D-DAG: protected virtual BaseClassTest::A
 
 ; UDT_KIND_TEST: ---TYPES---
-; UDT_KIND_TEST-DAG: union UdtKindTest::C {}
-; UDT_KIND_TEST-DAG: class UdtKindTest::B {}
-; UDT_KIND_TEST-DAG: struct UdtKindTest::A {}
+; UDT_KIND_TEST-DAG: union UdtKindTest::C [sizeof = 1] {}
+; UDT_KIND_TEST-DAG: class UdtKindTest::B [sizeof = 1] {}
+; UDT_KIND_TEST-DAG: struct UdtKindTest::A [sizeof = 1] {}
 
 ; BITFIELD_TEST: ---TYPES---
-; BITFIELD_TEST: struct BitFieldTest::A {
+; BITFIELD_TEST: struct BitFieldTest::A [sizeof = 8] {
 ; BITFIELD_TEST-NEXT: +0x00 [sizeof=4] int Bits1 : 1
 ; BITFIELD_TEST-NEXT: +0x00 [sizeof=4] int Bits2 : 2
 ; BITFIELD_TEST-NEXT: +0x00 [sizeof=4] int Bits3 : 3
index 21e1867..df447c6 100644 (file)
@@ -10,7 +10,7 @@
 
 ; MEMBER_ENUM: ---TYPES---
 ; MEMBER_ENUM: Classes:
-; MEMBER_ENUM: struct __vc_attributes::threadingAttribute {
+; MEMBER_ENUM: struct __vc_attributes::threadingAttribute [sizeof = 4] {
 ; MEMBER_ENUM-NEXT: enum threading_e {
 ; MEMBER_ENUM-NEXT: apartment = 1
 ; MEMBER_ENUM-NEXT: single = 2
diff --git a/llvm/test/tools/llvm-pdbdump/simple-padding.test b/llvm/test/tools/llvm-pdbdump/simple-padding.test
new file mode 100644 (file)
index 0000000..4096d28
--- /dev/null
@@ -0,0 +1,94 @@
+; RUN: llvm-pdbdump pretty -classes -class-definitions=layout \
+; RUN:     -include-types=SimplePad %p/Inputs/SimplePaddingTest.pdb > %t
+
+; RUN: FileCheck -input-file=%t %s -check-prefix=NO_PADDING
+; RUN: FileCheck -input-file=%t %s -check-prefix=UNION
+; RUN: FileCheck -input-file=%t %s -check-prefix=NESTED_UNION
+; RUN: FileCheck -input-file=%t %s -check-prefix=PAD_FROM_FIELDS1
+; RUN: FileCheck -input-file=%t %s -check-prefix=PAD_FROM_FIELDS2
+; RUN: FileCheck -input-file=%t %s -check-prefix=NO_PAD_IN_BASE
+; RUN: FileCheck -input-file=%t %s -check-prefix=PAD_IN_DERIVED
+; RUN: FileCheck -input-file=%t %s -check-prefix=EMPTY_BASE
+; RUN: FileCheck -input-file=%t %s -check-prefix=VFPTR
+; RUN: FileCheck -input-file=%t %s -check-prefix=MULTIPLE_INHERIT
+; RUN: FileCheck -input-file=%t %s -check-prefix=MULTIPLE_INHERIT2
+; RUN: FileCheck -input-file=%t %s -check-prefix=DEEP_INHERIT
+; RUN: FileCheck -input-file=%t %s -check-prefix=AGGREGATE
+
+; NO_PADDING:      struct SimplePadNoPadding [sizeof = 8] {
+; NO_PADDING-NEXT:   data +0x00 [sizeof=4] int X
+; NO_PADDING-NEXT:   data +0x04 [sizeof=4] int Y
+; NO_PADDING-NEXT: }
+
+; UNION:      struct SimplePadUnion [sizeof = 16] {
+; UNION-NEXT:   data +0x00 [sizeof=4] int X
+; UNION-NEXT:   data +0x00 [sizeof=8] __int64 Y
+; UNION-NEXT:   data +0x00 [sizeof=16] SimplePadUnion::
+; UNION-NEXT: }
+
+; NESTED_UNION:      struct {{SimplePadUnion::.*}} [sizeof = 16] {
+; NESTED_UNION-NEXT:   data +0x00 [sizeof=4] int X
+; NESTED_UNION-NEXT:   <padding> (4 bytes)
+; NESTED_UNION-NEXT:   data +0x08 [sizeof=8] __int64 Y
+; NESTED_UNION-NEXT: }
+
+; PAD_FROM_FIELDS1:      struct SimplePadFields1 [sizeof = 4] {
+; PAD_FROM_FIELDS1-NEXT:   data +0x00 [sizeof=1] char A
+; PAD_FROM_FIELDS1-NEXT:   data +0x01 [sizeof=1] char B
+; PAD_FROM_FIELDS1-NEXT:   data +0x02 [sizeof=1] char C
+; PAD_FROM_FIELDS1-NEXT:   <padding> (1 bytes)
+; PAD_FROM_FIELDS1-NEXT: }
+
+; PAD_FROM_FIELDS2:      struct SimplePadFields2 [sizeof = 8] {
+; PAD_FROM_FIELDS2-NEXT:   data +0x00 [sizeof=4] int Y
+; PAD_FROM_FIELDS2-NEXT:   data +0x04 [sizeof=1] char X
+; PAD_FROM_FIELDS2-NEXT:   <padding> (3 bytes)
+; PAD_FROM_FIELDS2-NEXT: }
+
+; NO_PAD_IN_BASE:      struct SimplePadBase [sizeof = 4] {
+; NO_PAD_IN_BASE-NEXT:   data +0x00 [sizeof=4] int X
+; NO_PAD_IN_BASE-NEXT: }
+
+; PAD_IN_DERIVED:      struct SimplePadDerived [sizeof = 16]
+; PAD_IN_DERIVED-NEXT:   public SimplePadBase {
+; PAD_IN_DERIVED-NEXT:   <padding> (4 bytes)
+; PAD_IN_DERIVED-NEXT:   data +0x08 [sizeof=8] __int64 Y
+; PAD_IN_DERIVED-NEXT: }
+
+; EMPTY_BASE:      struct SimplePadEmpty [sizeof = 8]
+; EMPTY_BASE-NEXT:   : public SimplePadEmptyBase1
+; EMPTY_BASE-NEXT:   , public SimplePadEmptyBase2 {
+; EMPTY_BASE-NEXT:   <padding> (2 bytes)
+; EMPTY_BASE-NEXT:   data +0x04 [sizeof=4] int X
+; EMPTY_BASE-NEXT: }
+
+; VFPTR:      struct SimplePadVfptr [sizeof = 8] {
+; VFPTR-NEXT:   data +0x00 [sizeof=4] __vfptr
+; VFPTR-NEXT:   data +0x04 [sizeof=4] int X
+; VFPTR-NEXT: }
+
+; MULTIPLE_INHERIT:      struct SimplePadMultiInherit [sizeof = 8]
+; MULTIPLE_INHERIT-NEXT:   : public NonEmptyBase1
+; MULTIPLE_INHERIT-NEXT:   , public NonEmptyBase2 {
+; MULTIPLE_INHERIT-NEXT:   <padding> (2 bytes)
+; MULTIPLE_INHERIT-NEXT:   data +0x04 [sizeof=4] int X
+; MULTIPLE_INHERIT-NEXT: }
+
+; MULTIPLE_INHERIT2:      SimplePadMultiInherit2 [sizeof = 16]
+; MULTIPLE_INHERIT2-NEXT:   : public SimplePadFields1
+; MULTIPLE_INHERIT2-NEXT:   , public SimplePadFields2 {
+; MULTIPLE_INHERIT2-NEXT:   data +0x0c [sizeof=4] int X
+; MULTIPLE_INHERIT2-NEXT: }
+
+; DEEP_INHERIT:      struct SimplePadTwoLevelInherit [sizeof = 16]
+; DEEP_INHERIT-NEXT:   : public OneLevelInherit {
+; DEEP_INHERIT-NEXT:   <padding> (4 bytes)
+; DEEP_INHERIT-NEXT:   data +0x08 [sizeof=8] __int64 Z
+; DEEP_INHERIT-NEXT: }
+
+
+; AGGREGATE:      struct SimplePadAggregate [sizeof = 8] {
+; AGGREGATE-NEXT:   data +0x00 [sizeof=1] NonEmptyBase1 X
+; AGGREGATE-NEXT:   <padding> (3 bytes)
+; AGGREGATE-NEXT:   data +0x04 [sizeof=4] int Y
+; AGGREGATE-NEXT: }
index b48ed23..2e1e54e 100644 (file)
@@ -28,7 +28,8 @@
 #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolTypeVTable.h"
-#include "llvm/Support/Compiler.h"
+#include "llvm/DebugInfo/PDB/UDTLayout.h"
+
 #include "llvm/Support/Format.h"
 
 using namespace llvm;
@@ -37,58 +38,23 @@ using namespace llvm::pdb;
 ClassDefinitionDumper::ClassDefinitionDumper(LinePrinter &P)
     : PDBSymDumper(true), Printer(P) {}
 
-static void analyzePadding(const PDBSymbolTypeUDT &Class, BitVector &Padding,
-                           uint32_t &FirstFieldOffset) {
-  Padding.resize(Class.getLength(), true);
-  auto Children = Class.findAllChildren<PDBSymbolData>();
-  bool IsFirst = true;
-  FirstFieldOffset = Class.getLength();
-
-  while (auto Data = Children->getNext()) {
-    // Ignore data members which are not relative to this.  Usually these are
-    // static data members or constexpr and occupy no space.  We also need to
-    // handle BitFields since the PDB doesn't consider them ThisRel, but they
-    // still occupy space in the record layout.
-    auto LocType = Data->getLocationType();
-    if (LocType != PDB_LocType::ThisRel && LocType != PDB_LocType::BitField)
-      continue;
-
-    uint64_t Start = Data->getOffset();
-    if (IsFirst) {
-      FirstFieldOffset = Start;
-      IsFirst = false;
-    }
-
-    auto VarType = Data->getType();
-    uint64_t Size = VarType->getRawSymbol().getLength();
-    Padding.reset(Start, Start + Size);
-  }
-
-  // Unmark anything that comes before the first field so it doesn't get
-  // counted as padding.  In reality this is going to be vptrs or base class
-  // members, but we don't correctly handle that yet.
-  // FIXME: Handle it.
-  Padding.reset(0, FirstFieldOffset);
-}
-
 void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
   assert(opts::pretty::ClassFormat !=
          opts::pretty::ClassDefinitionFormat::None);
 
   uint32_t Size = Class.getLength();
-  uint32_t FirstFieldOffset = 0;
-  BitVector Padding;
-  analyzePadding(Class, Padding, FirstFieldOffset);
 
-  if (opts::pretty::OnlyPaddingClasses && (Padding.count() == 0))
+  ClassLayout Layout(Class.clone());
+
+  if (opts::pretty::OnlyPaddingClasses && (Layout.shallowPaddingSize() == 0))
     return;
 
   Printer.NewLine();
-  WithColor(Printer, PDB_ColorItem::Comment).get() << "// sizeof = " << Size;
-  Printer.NewLine();
 
   WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " ";
   WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName();
+  WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size
+                                                   << "]";
 
   auto Bases = Class.findAllChildren<PDBSymbolTypeBaseClass>();
   if (Bases->getChildCount() > 0) {
@@ -111,60 +77,54 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
   }
 
   Printer << " {";
-  auto Children = Class.findAllChildren();
   Printer.Indent();
-  int DumpedCount = 0;
-
-  int NextPaddingByte = Padding.find_first();
-  while (auto Child = Children->getNext()) {
-    if (auto Data = llvm::dyn_cast<PDBSymbolData>(Child.get())) {
-      if (Data->getDataKind() == PDB_DataKind::Member && NextPaddingByte >= 0) {
-        // If there are padding bytes remaining, see if this field is the first
-        // to cross a padding boundary, and print a padding field indicator if
-        // so.
-        int Off = Data->getOffset();
-        if (Off > NextPaddingByte) {
-          uint32_t Amount = Off - NextPaddingByte;
-          Printer.NewLine();
-          WithColor(Printer, PDB_ColorItem::Padding).get()
-              << "<padding> (" << Amount << " bytes)";
-          assert(Padding.find_next_unset(NextPaddingByte) == Off);
-          NextPaddingByte = Padding.find_next(Off);
-        }
-      }
-    }
 
-    if (auto Func = Child->cast<PDBSymbolFunc>()) {
-      if (Func->isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
-        continue;
+  // Dump non-layout items first, but only if we're not in layout-only mode.
+  if (opts::pretty::ClassFormat !=
+      opts::pretty::ClassDefinitionFormat::Layout) {
+    for (auto &Other : Layout.other_items())
+      Other->dump(*this);
+  }
 
-      if (Func->getLength() == 0 && !Func->isPureVirtual() &&
-          !Func->isIntroVirtualFunction())
-        continue;
+  const BitVector &UseMap = Layout.usedBytes();
+  int NextUnusedByte = Layout.usedBytes().find_first_unset();
+  // Next dump items which affect class layout.
+  for (auto &LayoutItem : Layout.layout_items()) {
+    if (NextUnusedByte >= 0) {
+      // If there are padding bytes remaining, see if this field is the first to
+      // cross a padding boundary, and print a padding field indicator if so.
+      int Off = LayoutItem->getOffsetInParent();
+      if (Off > NextUnusedByte) {
+        uint32_t Amount = Off - NextUnusedByte;
+        Printer.NewLine();
+        WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> ("
+                                                         << Amount << " bytes)";
+        assert(UseMap.find_next(NextUnusedByte) == Off);
+        NextUnusedByte = UseMap.find_next_unset(Off);
+      }
     }
-
-    ++DumpedCount;
-    Child->dump(*this);
+    LayoutItem->getSymbol().dump(*this);
   }
 
-  if (NextPaddingByte >= 0) {
-    uint32_t Amount = Size - NextPaddingByte;
+  if (NextUnusedByte >= 0 && Layout.getClassSize() > 1) {
+    uint32_t Amount = Layout.getClassSize() - NextUnusedByte;
     Printer.NewLine();
     WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
                                                      << " bytes)";
+    DumpedAnything = true;
   }
+
   Printer.Unindent();
-  if (DumpedCount > 0)
+  if (DumpedAnything)
     Printer.NewLine();
   Printer << "}";
   Printer.NewLine();
-  if (Padding.count() > 0) {
-    APFloat Pct(100.0 * (double)Padding.count() /
-                (double)(Size - FirstFieldOffset));
+  if (Layout.deepPaddingSize() > 0) {
+    APFloat Pct(100.0 * (double)Layout.deepPaddingSize() / (double)Size);
     SmallString<8> PctStr;
     Pct.toString(PctStr, 4);
     WithColor(Printer, PDB_ColorItem::Padding).get()
-        << "Total padding " << Padding.count() << " bytes (" << PctStr
+        << "Total padding " << Layout.deepPaddingSize() << " bytes (" << PctStr
         << "% of class size)";
     Printer.NewLine();
   }
@@ -175,23 +135,35 @@ void ClassDefinitionDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {}
 void ClassDefinitionDumper::dump(const PDBSymbolData &Symbol) {
   VariableDumper Dumper(Printer);
   Dumper.start(Symbol);
+  DumpedAnything = true;
 }
 
 void ClassDefinitionDumper::dump(const PDBSymbolFunc &Symbol) {
   if (Printer.IsSymbolExcluded(Symbol.getName()))
     return;
+  if (Symbol.isCompilerGenerated() && opts::pretty::ExcludeCompilerGenerated)
+    return;
+  if (Symbol.getLength() == 0 && !Symbol.isPureVirtual() &&
+      !Symbol.isIntroVirtualFunction())
+    return;
 
+  DumpedAnything = true;
   Printer.NewLine();
   FunctionDumper Dumper(Printer);
   Dumper.start(Symbol, FunctionDumper::PointerType::None);
 }
 
-void ClassDefinitionDumper::dump(const PDBSymbolTypeVTable &Symbol) {}
+void ClassDefinitionDumper::dump(const PDBSymbolTypeVTable &Symbol) {
+  VariableDumper Dumper(Printer);
+  Dumper.start(Symbol);
+  DumpedAnything = true;
+}
 
 void ClassDefinitionDumper::dump(const PDBSymbolTypeEnum &Symbol) {
   if (Printer.IsTypeExcluded(Symbol.getName()))
     return;
 
+  DumpedAnything = true;
   Printer.NewLine();
   EnumDumper Dumper(Printer);
   Dumper.start(Symbol);
@@ -201,6 +173,7 @@ void ClassDefinitionDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
   if (Printer.IsTypeExcluded(Symbol.getName()))
     return;
 
+  DumpedAnything = true;
   Printer.NewLine();
   TypedefDumper Dumper(Printer);
   Dumper.start(Symbol);
index 8f0c35c..94ae07d 100644 (file)
@@ -21,6 +21,8 @@
 #include <unordered_map>
 
 namespace llvm {
+class BitVector;
+
 namespace pdb {
 
 class LinePrinter;
@@ -40,6 +42,10 @@ public:
   void dump(const PDBSymbolTypeVTable &Symbol) override;
 
 private:
+  bool maybeDumpSymbol(std::unique_ptr<PDBSymbolData> Data,
+                       const BitVector &Padding, int &NextUnusedByte);
+  bool maybeDumpSymbol(std::unique_ptr<PDBSymbolFunc> Data);
+  bool DumpedAnything = false;
   LinePrinter &Printer;
 };
 }
index 0e0da02..b0be33c 100644 (file)
@@ -233,7 +233,7 @@ void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) {
   if (!PointeeType)
     return;
 
-  if (auto FuncSig = PointeeType->cast<PDBSymbolTypeFunctionSig>()) {
+  if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
     FunctionDumper NestedDumper(Printer);
     PointerType Pointer =
         Symbol.isReference() ? PointerType::Reference : PointerType::Pointer;
index 12a47d2..2857b07 100644 (file)
 using namespace llvm;
 using namespace llvm::pdb;
 
+template <typename Enumerator>
+static std::vector<std::unique_ptr<PDBSymbolTypeUDT>>
+filterClassDefs(LinePrinter &Printer, Enumerator &E) {
+  std::vector<std::unique_ptr<PDBSymbolTypeUDT>> Filtered;
+  while (auto Class = E.getNext()) {
+    if (Class->getUnmodifiedTypeId() != 0)
+      continue;
+
+    if (Printer.IsTypeExcluded(Class->getName()))
+      continue;
+
+    Filtered.push_back(std::move(Class));
+  }
+
+  return Filtered;
+}
+
 TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
 
 void TypeDumper::start(const PDBSymbolExe &Exe) {
@@ -53,11 +70,19 @@ void TypeDumper::start(const PDBSymbolExe &Exe) {
 
   if (opts::pretty::Classes) {
     auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>();
+    auto Filtered = filterClassDefs(Printer, *Classes);
+
     Printer.NewLine();
+    uint32_t Shown = Filtered.size();
+    uint32_t All = Classes->getChildCount();
+
     WithColor(Printer, PDB_ColorItem::Identifier).get() << "Classes";
-    Printer << ": (" << Classes->getChildCount() << " items)";
+    Printer << ": (Showing " << Shown << " items";
+    if (Shown < All)
+      Printer << ", " << (All - Shown) << " filtered";
+    Printer << ")";
     Printer.Indent();
-    while (auto Class = Classes->getNext())
+    for (auto &Class : Filtered)
       Class->dump(*this);
     Printer.Unindent();
   }
@@ -91,11 +116,6 @@ void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) {
 void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
   assert(opts::pretty::Classes);
 
-  if (Symbol.getUnmodifiedTypeId() != 0)
-    return;
-  if (Printer.IsTypeExcluded(Symbol.getName()))
-    return;
-
   if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) {
     Printer.NewLine();
     WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
index 861f7e2..2d8e915 100644 (file)
@@ -54,7 +54,7 @@ void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) {
   if (Symbol.isVolatileType())
     WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
   auto PointeeType = Symbol.getPointeeType();
-  if (auto FuncSig = PointeeType->cast<PDBSymbolTypeFunctionSig>()) {
+  if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
     FunctionDumper::PointerType Pointer = FunctionDumper::PointerType::Pointer;
     if (Symbol.isReference())
       Pointer = FunctionDumper::PointerType::Reference;
index 65f0139..ef9a9b5 100644 (file)
@@ -91,6 +91,19 @@ void VariableDumper::start(const PDBSymbolData &Var) {
   }
 }
 
+void VariableDumper::start(const PDBSymbolTypeVTable &Var) {
+  Printer.NewLine();
+  Printer << "data ";
+  auto VTableType = cast<PDBSymbolTypePointer>(Var.getType());
+  uint32_t PointerSize = VTableType->getLength();
+
+  WithColor(Printer, PDB_ColorItem::Offset).get()
+      << "+" << format_hex(Var.getOffset(), 4) << " [sizeof=" << PointerSize
+      << "] ";
+
+  WithColor(Printer, PDB_ColorItem::Identifier).get() << " __vfptr";
+}
+
 void VariableDumper::dump(const PDBSymbolTypeArray &Symbol) {
   auto ElementType = Symbol.getElementType();
   assert(ElementType);
@@ -157,12 +170,12 @@ void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) {
   if (!PointeeType)
     return;
   PointeeType->dump(*this);
-  if (auto Func = PointeeType->cast<PDBSymbolTypeFunctionSig>()) {
+  if (auto FuncSig = unique_dyn_cast<PDBSymbolTypeFunctionSig>(PointeeType)) {
     // A hack to get the calling convention in the right spot.
     Printer << " (";
-    PDB_CallingConv CC = Func->getCallingConvention();
+    PDB_CallingConv CC = FuncSig->getCallingConvention();
     WithColor(Printer, PDB_ColorItem::Keyword).get() << CC << " ";
-  } else if (isa<PDBSymbolTypeArray>(PointeeType.get())) {
+  } else if (isa<PDBSymbolTypeArray>(PointeeType)) {
     Printer << " (";
   }
   Printer << (Symbol.isReference() ? "&" : "*");
@@ -177,8 +190,8 @@ void VariableDumper::dumpRight(const PDBSymbolTypePointer &Symbol) {
   assert(PointeeType);
   if (!PointeeType)
     return;
-  if (isa<PDBSymbolTypeFunctionSig>(PointeeType.get()) ||
-      isa<PDBSymbolTypeArray>(PointeeType.get())) {
+  if (isa<PDBSymbolTypeFunctionSig>(PointeeType) ||
+      isa<PDBSymbolTypeArray>(PointeeType)) {
     Printer << ")";
   }
   PointeeType->dumpRight(*this);
index eec389b..ba9fdb1 100644 (file)
@@ -25,6 +25,7 @@ public:
   VariableDumper(LinePrinter &P);
 
   void start(const PDBSymbolData &Var);
+  void start(const PDBSymbolTypeVTable &Var);
 
   void dump(const PDBSymbolTypeArray &Symbol) override;
   void dump(const PDBSymbolTypeBuiltin &Symbol) override;
index 8a749ba..a486c8a 100644 (file)
@@ -96,7 +96,7 @@ cl::SubCommand
                       "Analyze various aspects of a PDB's structure");
 
 cl::OptionCategory TypeCategory("Symbol Type Options");
-cl::OptionCategory FilterCategory("Filtering Options");
+cl::OptionCategory FilterCategory("Filtering and Sorting Options");
 cl::OptionCategory OtherOptions("Other Options");
 
 namespace pretty {
@@ -122,14 +122,17 @@ cl::opt<bool> Enums("enums", cl::desc("Display enum types"),
                     cl::cat(TypeCategory), cl::sub(PrettySubcommand));
 cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"),
                        cl::cat(TypeCategory), cl::sub(PrettySubcommand));
-cl::opt<ClassDefinitionFormat>
-    ClassFormat("class-definitions", cl::desc("Class definition format"),
-                cl::init(ClassDefinitionFormat::Standard),
-                cl::values(clEnumValN(ClassDefinitionFormat::Standard, "full",
-                                      "Display complete class definition"),
-                           clEnumValN(ClassDefinitionFormat::None, "none",
-                                      "Don't display class definitions")),
-                cl::cat(TypeCategory), cl::sub(PrettySubcommand));
+cl::opt<ClassDefinitionFormat> ClassFormat(
+    "class-definitions", cl::desc("Class definition format"),
+    cl::init(ClassDefinitionFormat::Standard),
+    cl::values(
+        clEnumValN(ClassDefinitionFormat::Standard, "full",
+                   "Display complete class definition"),
+        clEnumValN(ClassDefinitionFormat::Layout, "layout",
+                   "Only display members that contribute to class size."),
+        clEnumValN(ClassDefinitionFormat::None, "none",
+                   "Don't display class definitions")),
+    cl::cat(TypeCategory), cl::sub(PrettySubcommand));
 
 cl::opt<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory),
                     cl::sub(PrettySubcommand));
index a335d30..c572176 100644 (file)
@@ -18,7 +18,7 @@ namespace opts {
 
 namespace pretty {
 
-enum class ClassDefinitionFormat { None, Standard };
+enum class ClassDefinitionFormat { None, Layout, Standard };
 
 extern llvm::cl::opt<bool> Compilands;
 extern llvm::cl::opt<bool> Symbols;
index ba09a8e..6afe83c 100644 (file)
@@ -63,7 +63,7 @@ namespace {
 class MockSession : public IPDBSession {
   uint64_t getLoadAddress() const override { return 0; }
   void setLoadAddress(uint64_t Address) override {}
-  std::unique_ptr<PDBSymbolExe> getGlobalScope() override {
+  std::unique_ptr<PDBSymbolExe> getGlobalScope() const override {
     return nullptr;
   }
   std::unique_ptr<PDBSymbol> getSymbolById(uint32_t SymbolId) const override {
@@ -226,6 +226,7 @@ public:
   MOCK_SYMBOL_ACCESSOR(getMachineType)
   MOCK_SYMBOL_ACCESSOR(getThunkOrdinal)
   MOCK_SYMBOL_ACCESSOR(getLength)
+  MOCK_SYMBOL_ACCESSOR(getVirtualBaseTableType)
   MOCK_SYMBOL_ACCESSOR(getLiveRangeLength)
   MOCK_SYMBOL_ACCESSOR(getVirtualAddress)
   MOCK_SYMBOL_ACCESSOR(getUdtKind)