OSDN Git Service

[llvm-pdbdump] Display padding bytes on record layout
authorZachary Turner <zturner@google.com>
Mon, 10 Apr 2017 19:33:29 +0000 (19:33 +0000)
committerZachary Turner <zturner@google.com>
Mon, 10 Apr 2017 19:33:29 +0000 (19:33 +0000)
When dumping classes, show where padding occurs, and at the end of the
class print statistics about how many bytes total of padding exist in a
class.

Since PDB doesn't specifically contain information about padding, we have
to mimic this by sort of reversing a small portion of the record layout
algorithm (e.g. looking at offsets and sizes and trying to determine
whether something is part of the same field or a new field).

Differential Revision: https://reviews.llvm.org/D31800

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

test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test
test/tools/llvm-pdbdump/class-layout.test
tools/llvm-pdbdump/LinePrinter.cpp
tools/llvm-pdbdump/LinePrinter.h
tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp
tools/llvm-pdbdump/PrettyClassDefinitionDumper.h
tools/llvm-pdbdump/PrettyTypeDumper.cpp
tools/llvm-pdbdump/PrettyVariableDumper.cpp
tools/llvm-pdbdump/llvm-pdbdump.cpp
tools/llvm-pdbdump/llvm-pdbdump.h

index 697a723..60a1953 100644 (file)
 
 ; TYPES_2: Classes
 ; TYPES_2: struct MemberTest {
-; TYPES_2: data +0x00 MemberTest::NestedEnum m_nested_enum
-; TYPES_2: data +0x04 int m_typedef
-; TYPES_2: data +0x08 bool m_bool
-; TYPES_2: data +0x09 char m_char
-; TYPES_2: data +0x0a wchar_t m_wchar_t
-; TYPES_2: data +0x0c int m_int
-; TYPES_2: data +0x10 unsigned int m_unsigned
-; TYPES_2: data +0x14 long m_long
-; TYPES_2: data +0x18 unsigned long m_unsigned_long
-; TYPES_2: data +0x20 __int64 m_int64
-; TYPES_2: data +0x28 unsigned __int64 m_unsigned_int64
-; TYPES_2: data +0x30 float m_float
-; TYPES_2: data +0x38 double m_double
-; TYPES_2: data +0x40 void (__cdecl * m_pfn_2_args)(int, double)
-; TYPES_2: data +0x44 int m_multidimensional_array[2][3]
+; 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
+; TYPES_2: data +0x09 [sizeof=1] char m_char
+; TYPES_2: data +0x0a [sizeof=2] wchar_t m_wchar_t
+; TYPES_2: data +0x0c [sizeof=4] int m_int
+; TYPES_2: data +0x10 [sizeof=4] unsigned int m_unsigned
+; TYPES_2: data +0x14 [sizeof=4] long m_long
+; TYPES_2: data +0x18 [sizeof=4] unsigned long m_unsigned_long
+; TYPES_2: <padding> (4 bytes)
+; TYPES_2: data +0x20 [sizeof=8] __int64 m_int64
+; TYPES_2: data +0x28 [sizeof=8] unsigned __int64 m_unsigned_int64
+; TYPES_2: data +0x30 [sizeof=4] float m_float
+; TYPES_2: <padding> (4 bytes)
+; TYPES_2: data +0x38 [sizeof=8] double m_double
+; TYPES_2: data +0x40 [sizeof=4] void  (__cdecl * m_pfn_2_args)(int, double)
+; TYPES_2: data +0x44 [sizeof=24] int m_multidimensional_array[2][3]
 ; TYPES_2: }
 
 ; GLOBALS: ---GLOBALS---
 ; GLOBALS-DAG: func [{{.*}}] (FPO) unsigned int __cdecl fpo_func(unsigned int n)
-; GLOBALS-DAG: data [{{.*}}] static void* g_global_pointer
-; GLOBALS-DAG: data [{{.*}}] static int g_global_int
-; GLOBALS-DAG: data [{{.*}}] static int g_array[3]
-; GLOBALS-DAG: data [{{.*}}] static int (* g_pointer_to_array)[3]
-; GLOBALS-DAG: data [{{.*}}] static const int* g_pointer_to_const_int
-; GLOBALS-DAG: data int* const g_const_pointer_to_int = 0
-; GLOBALS-DAG: data const int* const g_const_pointer_to_const_int = 0
+; GLOBALS-DAG: data [{{.*}}, sizeof=4] static void* g_global_pointer
+; GLOBALS-DAG: data [{{.*}}, sizeof=4] static int g_global_int
+; GLOBALS-DAG: data [{{.*}}, sizeof=12] static int g_array[3]
+; GLOBALS-DAG: data [{{.*}}, sizeof=4] static int (* g_pointer_to_array)[3]
+; GLOBALS-DAG: data [{{.*}}, sizeof=4] static const int* g_pointer_to_const_int
+; GLOBALS-DAG: data [sizeof=4] int* const g_const_pointer_to_int = 0
+; GLOBALS-DAG: data [sizeof=4] const int* const g_const_pointer_to_const_int = 0
index 93127b1..e2921d2 100644 (file)
@@ -46,9 +46,9 @@
 
 ; BITFIELD_TEST: ---TYPES---
 ; BITFIELD_TEST: struct BitFieldTest::A {
-; BITFIELD_TEST-NEXT: +0x00 int Bits1 : 1
-; BITFIELD_TEST-NEXT: +0x00 int Bits2 : 2
-; BITFIELD_TEST-NEXT: +0x00 int Bits3 : 3
-; BITFIELD_TEST-NEXT: +0x00 int Bits4 : 4
-; BITFIELD_TEST-NEXT: +0x00 int Bits22 : 22
-; BITFIELD_TEST-NEXT: +0x04 int Offset0x04
+; 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
+; BITFIELD_TEST-NEXT: +0x00 [sizeof=4] int Bits4 : 4
+; BITFIELD_TEST-NEXT: +0x00 [sizeof=4] int Bits22 : 22
+; BITFIELD_TEST-NEXT: +0x04 [sizeof=4] int Offset0x04
index d376810..e5dd66f 100644 (file)
@@ -99,6 +99,9 @@ void WithColor::applyColor(PDB_ColorItem C) {
   case PDB_ColorItem::None:
     OS.resetColor();
     return;
+  case PDB_ColorItem::Comment:
+    OS.changeColor(raw_ostream::GREEN, false);
+    return;
   case PDB_ColorItem::Address:
     OS.changeColor(raw_ostream::YELLOW, /*bold=*/true);
     return;
@@ -118,6 +121,7 @@ void WithColor::applyColor(PDB_ColorItem C) {
   case PDB_ColorItem::Path:
     OS.changeColor(raw_ostream::CYAN, false);
     return;
+  case PDB_ColorItem::Padding:
   case PDB_ColorItem::SectionHeader:
     OS.changeColor(raw_ostream::RED, true);
     return;
index 87c51e3..8b3d875 100644 (file)
@@ -70,6 +70,8 @@ enum class PDB_ColorItem {
   None,
   Address,
   Type,
+  Comment,
+  Padding,
   Keyword,
   Offset,
   Identifier,
index 07e1662..b48ed23 100644 (file)
@@ -16,6 +16,8 @@
 #include "PrettyVariableDumper.h"
 #include "llvm-pdbdump.h"
 
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/DebugInfo/PDB/IPDBSession.h"
 #include "llvm/DebugInfo/PDB/PDBExtras.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolData.h"
@@ -26,6 +28,7 @@
 #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/Support/Format.h"
 
 using namespace llvm;
@@ -34,11 +37,56 @@ 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);
 
-  std::string Name = Class.getName();
+  uint32_t Size = Class.getLength();
+  uint32_t FirstFieldOffset = 0;
+  BitVector Padding;
+  analyzePadding(Class, Padding, FirstFieldOffset);
+
+  if (opts::pretty::OnlyPaddingClasses && (Padding.count() == 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();
 
@@ -66,24 +114,23 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
   auto Children = Class.findAllChildren();
   Printer.Indent();
   int DumpedCount = 0;
-  while (auto Child = Children->getNext()) {
 
-    if (opts::pretty::ClassFormat ==
-        opts::pretty::ClassDefinitionFormat::LayoutOnly) {
-      if (auto Data = Child->cast<PDBSymbolData>()) {
-        switch (Data->getLocationType()) {
-        case PDB_LocType::ThisRel:
-        case PDB_LocType::BitField:
-          break;
-        default:
-          // All other types of data field do not occupy any storage (e.g. are
-          // const), so in layout mode we skip them.
-          continue;
+  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);
         }
-      } else {
-        // Only data symbols affect record layout, so skip any non-data symbols
-        // if we're in record layout mode.
-        continue;
       }
     }
 
@@ -100,10 +147,27 @@ void ClassDefinitionDumper::start(const PDBSymbolTypeUDT &Class) {
     Child->dump(*this);
   }
 
+  if (NextPaddingByte >= 0) {
+    uint32_t Amount = Size - NextPaddingByte;
+    Printer.NewLine();
+    WithColor(Printer, PDB_ColorItem::Padding).get() << "<padding> (" << Amount
+                                                     << " bytes)";
+  }
   Printer.Unindent();
   if (DumpedCount > 0)
     Printer.NewLine();
   Printer << "}";
+  Printer.NewLine();
+  if (Padding.count() > 0) {
+    APFloat Pct(100.0 * (double)Padding.count() /
+                (double)(Size - FirstFieldOffset));
+    SmallString<8> PctStr;
+    Pct.toString(PctStr, 4);
+    WithColor(Printer, PDB_ColorItem::Padding).get()
+        << "Total padding " << Padding.count() << " bytes (" << PctStr
+        << "% of class size)";
+    Printer.NewLine();
+  }
 }
 
 void ClassDefinitionDumper::dump(const PDBSymbolTypeBaseClass &Symbol) {}
index e145707..8f0c35c 100644 (file)
@@ -10,6 +10,8 @@
 #ifndef LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H
 #define LLVM_TOOLS_LLVMPDBDUMP_PRETTYCLASSDEFINITIONDUMPER_H
 
+#include "llvm/ADT/BitVector.h"
+
 #include "llvm/DebugInfo/PDB/PDBSymDumper.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolData.h"
 #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
index 023806c..12a47d2 100644 (file)
@@ -96,9 +96,8 @@ void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) {
   if (Printer.IsTypeExcluded(Symbol.getName()))
     return;
 
-  Printer.NewLine();
-
   if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) {
+    Printer.NewLine();
     WithColor(Printer, PDB_ColorItem::Keyword).get() << "class ";
     WithColor(Printer, PDB_ColorItem::Identifier).get() << Symbol.getName();
   } else {
index 8b85f97..65f0139 100644 (file)
@@ -43,13 +43,15 @@ void VariableDumper::start(const PDBSymbolData &Var) {
 
   auto VarType = Var.getType();
 
+  uint64_t Length = VarType->getRawSymbol().getLength();
+
   switch (auto LocType = Var.getLocationType()) {
   case PDB_LocType::Static:
     Printer.NewLine();
     Printer << "data [";
     WithColor(Printer, PDB_ColorItem::Address).get()
         << format_hex(Var.getVirtualAddress(), 10);
-    Printer << "] ";
+    Printer << ", sizeof=" << Length << "] ";
     WithColor(Printer, PDB_ColorItem::Keyword).get() << "static ";
     dumpSymbolTypeAndName(*VarType, Var.getName());
     break;
@@ -57,7 +59,7 @@ void VariableDumper::start(const PDBSymbolData &Var) {
     if (isa<PDBSymbolTypeEnum>(*VarType))
       break;
     Printer.NewLine();
-    Printer << "data ";
+    Printer << "data [sizeof=" << Length << "] ";
     dumpSymbolTypeAndName(*VarType, Var.getName());
     Printer << " = ";
     WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getValue();
@@ -66,21 +68,23 @@ void VariableDumper::start(const PDBSymbolData &Var) {
     Printer.NewLine();
     Printer << "data ";
     WithColor(Printer, PDB_ColorItem::Offset).get()
-        << "+" << format_hex(Var.getOffset(), 4) << " ";
+        << "+" << format_hex(Var.getOffset(), 4) << " [sizeof=" << Length
+        << "] ";
     dumpSymbolTypeAndName(*VarType, Var.getName());
     break;
   case PDB_LocType::BitField:
     Printer.NewLine();
     Printer << "data ";
     WithColor(Printer, PDB_ColorItem::Offset).get()
-        << "+" << format_hex(Var.getOffset(), 4) << " ";
+        << "+" << format_hex(Var.getOffset(), 4) << " [sizeof=" << Length
+        << "] ";
     dumpSymbolTypeAndName(*VarType, Var.getName());
     Printer << " : ";
     WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Var.getLength();
     break;
   default:
     Printer.NewLine();
-    Printer << "data ";
+    Printer << "data [sizeof=" << Length << "] ";
     Printer << "unknown(" << LocType << ") ";
     WithColor(Printer, PDB_ColorItem::Identifier).get() << Var.getName();
     break;
index 3d3d253..8a749ba 100644 (file)
@@ -122,6 +122,14 @@ 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<bool> Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory),
                     cl::sub(PrettySubcommand));
@@ -161,6 +169,10 @@ cl::list<std::string> IncludeCompilands(
     "include-compilands",
     cl::desc("Include only compilands those which match a regular expression"),
     cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
+cl::opt<bool> OnlyPaddingClasses(
+    "only-padding-classes", cl::desc("When dumping classes, only display those "
+                                     "with non-zero amounts of padding bytes"),
+    cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand));
 
 cl::opt<bool> ExcludeCompilerGenerated(
     "no-compiler-generated",
@@ -170,16 +182,6 @@ cl::opt<bool>
     ExcludeSystemLibraries("no-system-libs",
                            cl::desc("Don't show symbols from system libraries"),
                            cl::cat(FilterCategory), cl::sub(PrettySubcommand));
-cl::opt<ClassDefinitionFormat> ClassFormat(
-    "class-definitions", cl::desc("Class definition format"),
-    cl::init(ClassDefinitionFormat::Full),
-    cl::values(clEnumValN(ClassDefinitionFormat::Full, "full",
-                          "Display complete class definition"),
-               clEnumValN(ClassDefinitionFormat::LayoutOnly, "layout",
-                          "Display only members which affect record layout"),
-               clEnumValN(ClassDefinitionFormat::None, "none",
-                          "Don't display class definitions")),
-    cl::cat(FilterCategory), cl::sub(PrettySubcommand));
 
 cl::opt<bool> NoEnumDefs("no-enum-definitions",
                          cl::desc("Don't display full enum definitions"),
index 0fdcab2..a335d30 100644 (file)
@@ -17,7 +17,8 @@
 namespace opts {
 
 namespace pretty {
-enum class ClassDefinitionFormat { None, LayoutOnly, Full };
+
+enum class ClassDefinitionFormat { None, Standard };
 
 extern llvm::cl::opt<bool> Compilands;
 extern llvm::cl::opt<bool> Symbols;
@@ -28,7 +29,6 @@ extern llvm::cl::opt<bool> Typedefs;
 extern llvm::cl::opt<bool> All;
 extern llvm::cl::opt<bool> ExcludeCompilerGenerated;
 
-extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
 extern llvm::cl::opt<bool> NoEnumDefs;
 extern llvm::cl::list<std::string> ExcludeTypes;
 extern llvm::cl::list<std::string> ExcludeSymbols;
@@ -36,6 +36,8 @@ extern llvm::cl::list<std::string> ExcludeCompilands;
 extern llvm::cl::list<std::string> IncludeTypes;
 extern llvm::cl::list<std::string> IncludeSymbols;
 extern llvm::cl::list<std::string> IncludeCompilands;
+extern llvm::cl::opt<bool> OnlyPaddingClasses;
+extern llvm::cl::opt<ClassDefinitionFormat> ClassFormat;
 }
 
 namespace raw {