; 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
; 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
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;
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;
None,
Address,
Type,
+ Comment,
+ Padding,
Keyword,
Offset,
Identifier,
#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"
#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;
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();
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;
}
}
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) {}
#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"
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 {
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;
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();
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;
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));
"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",
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"),
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;
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;
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 {