Index: llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test =================================================================== --- llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test +++ llvm/trunk/test/DebugInfo/PDB/DIA/pdbdump-symbol-format.test @@ -40,29 +40,31 @@ ; 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: (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: (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: llvm/trunk/test/tools/llvm-pdbdump/class-layout.test =================================================================== --- llvm/trunk/test/tools/llvm-pdbdump/class-layout.test +++ llvm/trunk/test/tools/llvm-pdbdump/class-layout.test @@ -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: llvm/trunk/tools/llvm-pdbdump/LinePrinter.h =================================================================== --- llvm/trunk/tools/llvm-pdbdump/LinePrinter.h +++ llvm/trunk/tools/llvm-pdbdump/LinePrinter.h @@ -70,6 +70,8 @@ None, Address, Type, + Comment, + Padding, Keyword, Offset, Identifier, Index: llvm/trunk/tools/llvm-pdbdump/LinePrinter.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/LinePrinter.cpp +++ llvm/trunk/tools/llvm-pdbdump/LinePrinter.cpp @@ -99,6 +99,9 @@ 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 @@ 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: llvm/trunk/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h =================================================================== --- llvm/trunk/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h +++ llvm/trunk/tools/llvm-pdbdump/PrettyClassDefinitionDumper.h @@ -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: llvm/trunk/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp +++ llvm/trunk/tools/llvm-pdbdump/PrettyClassDefinitionDumper.cpp @@ -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 @@ 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(); + 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 @@ 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()) { - 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(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() + << " (" << 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 @@ Child->dump(*this); } + if (NextPaddingByte >= 0) { + uint32_t Amount = Size - NextPaddingByte; + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Padding).get() << " (" << 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: llvm/trunk/tools/llvm-pdbdump/PrettyTypeDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/PrettyTypeDumper.cpp +++ llvm/trunk/tools/llvm-pdbdump/PrettyTypeDumper.cpp @@ -96,9 +96,8 @@ 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: llvm/trunk/tools/llvm-pdbdump/PrettyVariableDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/PrettyVariableDumper.cpp +++ llvm/trunk/tools/llvm-pdbdump/PrettyVariableDumper.cpp @@ -43,13 +43,15 @@ 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 @@ if (isa(*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 @@ 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: llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.h =================================================================== --- llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.h +++ llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.h @@ -17,7 +17,8 @@ namespace opts { namespace pretty { -enum class ClassDefinitionFormat { None, LayoutOnly, Full }; + +enum class ClassDefinitionFormat { None, Standard }; extern llvm::cl::opt Compilands; extern llvm::cl::opt Symbols; @@ -28,7 +29,6 @@ extern llvm::cl::opt All; extern llvm::cl::opt ExcludeCompilerGenerated; -extern llvm::cl::opt ClassFormat; extern llvm::cl::opt NoEnumDefs; extern llvm::cl::list ExcludeTypes; extern llvm::cl::list ExcludeSymbols; @@ -36,6 +36,8 @@ extern llvm::cl::list IncludeTypes; extern llvm::cl::list IncludeSymbols; extern llvm::cl::list IncludeCompilands; +extern llvm::cl::opt OnlyPaddingClasses; +extern llvm::cl::opt ClassFormat; } namespace raw { Index: llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.cpp =================================================================== --- llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ llvm/trunk/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -122,6 +122,14 @@ cl::cat(TypeCategory), cl::sub(PrettySubcommand)); cl::opt Typedefs("typedefs", cl::desc("Display typedef types"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt + 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 Lines("lines", cl::desc("Line tables"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); @@ -161,6 +169,10 @@ "include-compilands", cl::desc("Include only compilands those which match a regular expression"), cl::ZeroOrMore, cl::cat(FilterCategory), cl::sub(PrettySubcommand)); +cl::opt 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 ExcludeCompilerGenerated( "no-compiler-generated", @@ -170,16 +182,6 @@ ExcludeSystemLibraries("no-system-libs", cl::desc("Don't show symbols from system libraries"), cl::cat(FilterCategory), cl::sub(PrettySubcommand)); -cl::opt 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 NoEnumDefs("no-enum-definitions", cl::desc("Don't display full enum definitions"),