From cda7ff9ddcefe0051d173e7c126c679063d29fbb Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Thu, 21 Mar 2019 18:02:34 +0000 Subject: [PATCH] [llvm-pdbutil] Add -type-ref-stats to help find unused type info Summary: This considers module symbol streams and the global symbol stream to be roots. Most types that this considers "unreferenced" are referenced by LF_UDT_MOD_SRC_LINE id records, which VC seems to always include. Essentially, they are types that the user can only find in the debugger if they call them by name, they cannot be found by traversing a symbol. In practice, around 80% of type information in a PDB is referenced by a symbol. That seems like a reasonable number. I don't really plan to do anything with this tool. It mostly just exists for informational purposes, and to confirm that we probably don't need to implement type reference tracking in LLD. We can continue to merge all types as we do today without wasting space. Reviewers: zturner, aganea Subscribers: mgorny, hiraditya, arphaman, jdoerfert, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59620 llvm-svn: 356692 --- .../DebugInfo/CodeView/TypeIndexDiscovery.cpp | 6 +- .../DebugInfo/PDB/pdb-type-ref-stats.test | 577 ++++++++++++++++++ llvm/tools/llvm-pdbutil/CMakeLists.txt | 1 + llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp | 67 +- llvm/tools/llvm-pdbutil/DumpOutputStyle.h | 4 + llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp | 22 +- llvm/tools/llvm-pdbutil/MinimalTypeDumper.h | 7 +- .../llvm-pdbutil/TypeReferenceTracker.cpp | 160 +++++ .../tools/llvm-pdbutil/TypeReferenceTracker.h | 69 +++ llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp | 5 + llvm/tools/llvm-pdbutil/llvm-pdbutil.h | 1 + 11 files changed, 898 insertions(+), 21 deletions(-) create mode 100644 llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test create mode 100644 llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp create mode 100644 llvm/tools/llvm-pdbutil/TypeReferenceTracker.h diff --git a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp index b01511e06e79..e84e1c9cea78 100644 --- a/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp +++ b/llvm/lib/DebugInfo/CodeView/TypeIndexDiscovery.cpp @@ -363,14 +363,16 @@ static bool discoverTypeIndices(ArrayRef Content, SymbolKind Kind, // values. One idea is to define some structures representing these types // that would allow the use of offsetof(). switch (Kind) { - case SymbolKind::S_GPROC32: - case SymbolKind::S_LPROC32: case SymbolKind::S_GPROC32_ID: case SymbolKind::S_LPROC32_ID: case SymbolKind::S_LPROC32_DPC: case SymbolKind::S_LPROC32_DPC_ID: Refs.push_back({TiRefKind::IndexRef, 24, 1}); // LF_FUNC_ID break; + case SymbolKind::S_GPROC32: + case SymbolKind::S_LPROC32: + Refs.push_back({TiRefKind::TypeRef, 24, 1}); // Type + break; case SymbolKind::S_UDT: Refs.push_back({TiRefKind::TypeRef, 0, 1}); // UDT break; diff --git a/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test b/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test new file mode 100644 index 000000000000..c7e220408ee3 --- /dev/null +++ b/llvm/test/DebugInfo/PDB/pdb-type-ref-stats.test @@ -0,0 +1,577 @@ +RUN: llvm-pdbutil dump -types -type-ref-stats %p/Inputs/every-class.pdb \ +RUN: | FileCheck %s + +CHECK: Types (TPI Stream) +CHECK: ============================================================ +CHECK: Showing 157 records +CHECK: 0x1000 | LF_ARGLIST [size = 16, referenced] +CHECK: 0x0603 (void*): `void*` +CHECK: 0x0023 (unsigned __int64): `unsigned __int64` +CHECK: 0x1001 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 2, param list = 0x1000 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1002 | LF_ARGLIST [size = 16, referenced] +CHECK: 0x0603 (void*): `void*` +CHECK: 0x0075 (unsigned): `unsigned` +CHECK: 0x1003 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 2, param list = 0x1002 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1004 | LF_POINTER [size = 12, referenced] +CHECK: referent = 0x0670 (char*), mode = pointer, opts = None, kind = ptr64 +CHECK: 0x1005 | LF_ARGLIST [size = 16, referenced] +CHECK: 0x0074 (int): `int` +CHECK: 0x1004: `char**` +CHECK: 0x1006 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0074 (int), # args = 2, param list = 0x1005 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1007 | LF_FIELDLIST [size = 4, referenced] +CHECK: 0x1008 | LF_STRUCTURE [size = 124, referenced] `main::__l2::` +CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name | scoped, sizeof 1 +CHECK: 0x1009 | LF_STRUCTURE [size = 88, referenced] `main::__l2::Scoped` +CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name | scoped, sizeof 1 +CHECK: 0x100A | LF_FIELDLIST [size = 48, unreferenced] +CHECK: - LF_ENUMERATE [native = 0] +CHECK: - LF_ENUMERATE [com = 1] +CHECK: - LF_ENUMERATE [managed = 2] +CHECK: 0x100B | LF_ENUM [size = 116, unreferenced] `__vc_attributes::event_sourceAttribute::type_e` +CHECK: unique name: `.?AW4type_e@event_sourceAttribute@__vc_attributes@@` +CHECK: field list: 0x100A, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x100C | LF_FIELDLIST [size = 28, unreferenced] +CHECK: - LF_ENUMERATE [speed = 0] +CHECK: - LF_ENUMERATE [size = 1] +CHECK: 0x100D | LF_ENUM [size = 124, unreferenced] `__vc_attributes::event_sourceAttribute::optimize_e` +CHECK: unique name: `.?AW4optimize_e@event_sourceAttribute@__vc_attributes@@` +CHECK: field list: 0x100C, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x100E | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::event_sourceAttribute` +CHECK: unique name: `.?AUevent_sourceAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1016) | has unique name, sizeof 0 +CHECK: 0x100F | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x100E, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x1010 | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x100B: `__vc_attributes::event_sourceAttribute::type_e` +CHECK: 0x1011 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1010 +CHECK: class type = 0x100E, this type = 0x100F, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1012 | LF_ARGLIST [size = 8, referenced] +CHECK: 0x1013 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 0, param list = 0x1012 +CHECK: class type = 0x100E, this type = 0x100F, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1014 | LF_METHODLIST [size = 20, unreferenced] +CHECK: - Method [type = 0x1011, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x1013, vftable offset = -1, attrs = public] +CHECK: 0x1015 | LF_FIELDLIST [size = 128, unreferenced] +CHECK: - LF_NESTTYPE [name = `type_e`, parent = 0x100B] +CHECK: - LF_NESTTYPE [name = `optimize_e`, parent = 0x100D] +CHECK: - LF_METHOD [name = `event_sourceAttribute`, # overloads = 2, overload list = 0x1014] +CHECK: - LF_MEMBER [name = `type`, Type = 0x100B, offset = 0, attrs = public] +CHECK: - LF_MEMBER [name = `optimize`, Type = 0x100D, offset = 4, attrs = public] +CHECK: - LF_MEMBER [name = `decorate`, Type = 0x0030 (bool), offset = 8, attrs = public] +CHECK: 0x1016 | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::event_sourceAttribute` +CHECK: unique name: `.?AUevent_sourceAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x1015 +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 12 +CHECK: 0x1017 | LF_FIELDLIST [size = 68, unreferenced] +CHECK: - LF_ENUMERATE [eBoolean = 0] +CHECK: - LF_ENUMERATE [eInteger = 1] +CHECK: - LF_ENUMERATE [eFloat = 2] +CHECK: - LF_ENUMERATE [eDouble = 3] +CHECK: 0x1018 | LF_ENUM [size = 148, unreferenced] `__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e` +CHECK: unique name: `.?AW4type_e@v1_alttypeAttribute@helper_attributes@__vc_attributes@@` +CHECK: field list: 0x1017, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x1019 | LF_STRUCTURE [size = 140, unreferenced] `__vc_attributes::helper_attributes::v1_alttypeAttribute` +CHECK: unique name: `.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x101E) | has unique name, sizeof 0 +CHECK: 0x101A | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x1019, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x101B | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x1018: `__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e` +CHECK: 0x101C | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x101B +CHECK: class type = 0x1019, this type = 0x101A, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x101D | LF_FIELDLIST [size = 64, unreferenced] +CHECK: - LF_NESTTYPE [name = `type_e`, parent = 0x1018] +CHECK: - LF_ONEMETHOD [name = `v1_alttypeAttribute`] +CHECK: type = 0x101C, vftable offset = -1, attrs = public +CHECK: - LF_MEMBER [name = `type`, Type = 0x1018, offset = 0, attrs = public] +CHECK: 0x101E | LF_STRUCTURE [size = 140, unreferenced] `__vc_attributes::helper_attributes::v1_alttypeAttribute` +CHECK: unique name: `.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x101D +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 4 +CHECK: 0x101F | LF_FIELDLIST [size = 756, unreferenced] +CHECK: - LF_ENUMERATE [eAnyUsage = 0] +CHECK: - LF_ENUMERATE [eCoClassUsage = 1] +CHECK: - LF_ENUMERATE [eCOMInterfaceUsage = 2] +CHECK: - LF_ENUMERATE [eInterfaceUsage = 6] +CHECK: - LF_ENUMERATE [eMemberUsage = 8] +CHECK: - LF_ENUMERATE [eMethodUsage = 16] +CHECK: - LF_ENUMERATE [eInterfaceMethodUsage = 32] +CHECK: - LF_ENUMERATE [eInterfaceMemberUsage = 64] +CHECK: - LF_ENUMERATE [eCoClassMemberUsage = 128] +CHECK: - LF_ENUMERATE [eCoClassMethodUsage = 256] +CHECK: - LF_ENUMERATE [eGlobalMethodUsage = 768] +CHECK: - LF_ENUMERATE [eGlobalDataUsage = 1024] +CHECK: - LF_ENUMERATE [eClassUsage = 2048] +CHECK: - LF_ENUMERATE [eInterfaceParameterUsage = 4096] +CHECK: - LF_ENUMERATE [eMethodParameterUsage = 12288] +CHECK: - LF_ENUMERATE [eIDLModuleUsage = 16384] +CHECK: - LF_ENUMERATE [eAnonymousUsage = 32768] +CHECK: - LF_ENUMERATE [eTypedefUsage = 65536] +CHECK: - LF_ENUMERATE [eUnionUsage = 131072] +CHECK: - LF_ENUMERATE [eEnumUsage = 262144] +CHECK: - LF_ENUMERATE [eDefineTagUsage = 524288] +CHECK: - LF_ENUMERATE [eStructUsage = 1048576] +CHECK: - LF_ENUMERATE [eLocalUsage = 2097152] +CHECK: - LF_ENUMERATE [ePropertyUsage = 4194304] +CHECK: - LF_ENUMERATE [eEventUsage = 8388608] +CHECK: - LF_ENUMERATE [eTemplateUsage = 16777216] +CHECK: - LF_ENUMERATE [eModuleUsage = 16777216] +CHECK: - LF_ENUMERATE [eIllegalUsage = 33554432] +CHECK: - LF_ENUMERATE [eAsynchronousUsage = 67108864] +CHECK: - LF_ENUMERATE [eAnyIDLUsage = 4161535] +CHECK: 0x1020 | LF_ENUM [size = 140, unreferenced] `__vc_attributes::helper_attributes::usageAttribute::usage_e` +CHECK: unique name: `.?AW4usage_e@usageAttribute@helper_attributes@__vc_attributes@@` +CHECK: field list: 0x101F, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x1021 | LF_STRUCTURE [size = 128, unreferenced] `__vc_attributes::helper_attributes::usageAttribute` +CHECK: unique name: `.?AUusageAttribute@helper_attributes@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1026) | has unique name, sizeof 0 +CHECK: 0x1022 | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x1021, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x1023 | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x0075 (unsigned): `unsigned` +CHECK: 0x1024 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1023 +CHECK: class type = 0x1021, this type = 0x1022, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1025 | LF_FIELDLIST [size = 60, unreferenced] +CHECK: - LF_NESTTYPE [name = `usage_e`, parent = 0x1020] +CHECK: - LF_ONEMETHOD [name = `usageAttribute`] +CHECK: type = 0x1024, vftable offset = -1, attrs = public +CHECK: - LF_MEMBER [name = `value`, Type = 0x0075 (unsigned), offset = 0, attrs = public] +CHECK: 0x1026 | LF_STRUCTURE [size = 128, unreferenced] `__vc_attributes::helper_attributes::usageAttribute` +CHECK: unique name: `.?AUusageAttribute@helper_attributes@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x1025 +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 4 +CHECK: 0x1027 | LF_FIELDLIST [size = 76, unreferenced] +CHECK: - LF_ENUMERATE [apartment = 1] +CHECK: - LF_ENUMERATE [single = 2] +CHECK: - LF_ENUMERATE [free = 3] +CHECK: - LF_ENUMERATE [neutral = 4] +CHECK: - LF_ENUMERATE [both = 5] +CHECK: 0x1028 | LF_ENUM [size = 120, unreferenced] `__vc_attributes::threadingAttribute::threading_e` +CHECK: unique name: `.?AW4threading_e@threadingAttribute@__vc_attributes@@` +CHECK: field list: 0x1027, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x1029 | LF_STRUCTURE [size = 100, unreferenced] `__vc_attributes::threadingAttribute` +CHECK: unique name: `.?AUthreadingAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1030) | has unique name, sizeof 0 +CHECK: 0x102A | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x1029, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x102B | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x1028: `__vc_attributes::threadingAttribute::threading_e` +CHECK: 0x102C | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x102B +CHECK: class type = 0x1029, this type = 0x102A, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x102D | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 0, param list = 0x1012 +CHECK: class type = 0x1029, this type = 0x102A, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x102E | LF_METHODLIST [size = 20, unreferenced] +CHECK: - Method [type = 0x102C, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x102D, vftable offset = -1, attrs = public] +CHECK: 0x102F | LF_FIELDLIST [size = 68, unreferenced] +CHECK: - LF_NESTTYPE [name = `threading_e`, parent = 0x1028] +CHECK: - LF_METHOD [name = `threadingAttribute`, # overloads = 2, overload list = 0x102E] +CHECK: - LF_MEMBER [name = `value`, Type = 0x1028, offset = 0, attrs = public] +CHECK: 0x1030 | LF_STRUCTURE [size = 100, unreferenced] `__vc_attributes::threadingAttribute` +CHECK: unique name: `.?AUthreadingAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x102F +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 4 +CHECK: 0x1031 | LF_FIELDLIST [size = 48, unreferenced] +CHECK: - LF_ENUMERATE [never = 0] +CHECK: - LF_ENUMERATE [allowed = 1] +CHECK: - LF_ENUMERATE [always = 2] +CHECK: 0x1032 | LF_ENUM [size = 116, unreferenced] `__vc_attributes::aggregatableAttribute::type_e` +CHECK: unique name: `.?AW4type_e@aggregatableAttribute@__vc_attributes@@` +CHECK: field list: 0x1031, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x1033 | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::aggregatableAttribute` +CHECK: unique name: `.?AUaggregatableAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x103A) | has unique name, sizeof 0 +CHECK: 0x1034 | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x1033, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x1035 | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x1032: `__vc_attributes::aggregatableAttribute::type_e` +CHECK: 0x1036 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1035 +CHECK: class type = 0x1033, this type = 0x1034, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1037 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 0, param list = 0x1012 +CHECK: class type = 0x1033, this type = 0x1034, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1038 | LF_METHODLIST [size = 20, unreferenced] +CHECK: - Method [type = 0x1036, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x1037, vftable offset = -1, attrs = public] +CHECK: 0x1039 | LF_FIELDLIST [size = 68, unreferenced] +CHECK: - LF_NESTTYPE [name = `type_e`, parent = 0x1032] +CHECK: - LF_METHOD [name = `aggregatableAttribute`, # overloads = 2, overload list = 0x1038] +CHECK: - LF_MEMBER [name = `type`, Type = 0x1032, offset = 0, attrs = public] +CHECK: 0x103A | LF_STRUCTURE [size = 108, unreferenced] `__vc_attributes::aggregatableAttribute` +CHECK: unique name: `.?AUaggregatableAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x1039 +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 4 +CHECK: 0x103B | LF_ENUM [size = 120, unreferenced] `__vc_attributes::event_receiverAttribute::type_e` +CHECK: unique name: `.?AW4type_e@event_receiverAttribute@__vc_attributes@@` +CHECK: field list: 0x100A, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x103C | LF_STRUCTURE [size = 112, unreferenced] `__vc_attributes::event_receiverAttribute` +CHECK: unique name: `.?AUevent_receiverAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1045) | has unique name, sizeof 0 +CHECK: 0x103D | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x103C, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x103E | LF_ARGLIST [size = 16, unreferenced] +CHECK: 0x103B: `__vc_attributes::event_receiverAttribute::type_e` +CHECK: 0x0030 (bool): `bool` +CHECK: 0x103F | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 2, param list = 0x103E +CHECK: class type = 0x103C, this type = 0x103D, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1040 | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x103B: `__vc_attributes::event_receiverAttribute::type_e` +CHECK: 0x1041 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1040 +CHECK: class type = 0x103C, this type = 0x103D, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1042 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 0, param list = 0x1012 +CHECK: class type = 0x103C, this type = 0x103D, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1043 | LF_METHODLIST [size = 28, unreferenced] +CHECK: - Method [type = 0x103F, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x1041, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x1042, vftable offset = -1, attrs = public] +CHECK: 0x1044 | LF_FIELDLIST [size = 96, unreferenced] +CHECK: - LF_NESTTYPE [name = `type_e`, parent = 0x103B] +CHECK: - LF_METHOD [name = `event_receiverAttribute`, # overloads = 3, overload list = 0x1043] +CHECK: - LF_MEMBER [name = `type`, Type = 0x103B, offset = 0, attrs = public] +CHECK: - LF_MEMBER [name = `layout_dependent`, Type = 0x0030 (bool), offset = 4, attrs = public] +CHECK: 0x1045 | LF_STRUCTURE [size = 112, unreferenced] `__vc_attributes::event_receiverAttribute` +CHECK: unique name: `.?AUevent_receiverAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x1044 +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 8 +CHECK: 0x1046 | LF_FIELDLIST [size = 92, unreferenced] +CHECK: - LF_ENUMERATE [dll = 1] +CHECK: - LF_ENUMERATE [exe = 2] +CHECK: - LF_ENUMERATE [service = 3] +CHECK: - LF_ENUMERATE [unspecified = 4] +CHECK: - LF_ENUMERATE [EXE = 2] +CHECK: - LF_ENUMERATE [SERVICE = 3] +CHECK: 0x1047 | LF_ENUM [size = 104, unreferenced] `__vc_attributes::moduleAttribute::type_e` +CHECK: unique name: `.?AW4type_e@moduleAttribute@__vc_attributes@@` +CHECK: field list: 0x1046, underlying type: 0x0074 (int) +CHECK: options: has unique name | is nested +CHECK: 0x1048 | LF_STRUCTURE [size = 96, unreferenced] `__vc_attributes::moduleAttribute` +CHECK: unique name: `.?AUmoduleAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1053) | has unique name, sizeof 0 +CHECK: 0x1049 | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x1048, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x104A | LF_MODIFIER [size = 12, unreferenced] +CHECK: referent = 0x0070 (char), modifiers = const +CHECK: 0x104B | LF_POINTER [size = 12, unreferenced] +CHECK: referent = 0x104A, mode = pointer, opts = None, kind = ptr64 +CHECK: 0x104C | LF_ARGLIST [size = 68, unreferenced] +CHECK: 0x1047: `__vc_attributes::moduleAttribute::type_e` +CHECK: 0x104B: `const char*` +CHECK: 0x104B: `const char*` +CHECK: 0x104B: `const char*` +CHECK: 0x0074 (int): `int` +CHECK: 0x0030 (bool): `bool` +CHECK: 0x104B: `const char*` +CHECK: 0x0074 (int): `int` +CHECK: 0x104B: `const char*` +CHECK: 0x104B: `const char*` +CHECK: 0x0074 (int): `int` +CHECK: 0x0030 (bool): `bool` +CHECK: 0x0030 (bool): `bool` +CHECK: 0x104B: `const char*` +CHECK: 0x104B: `const char*` +CHECK: 0x104D | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 15, param list = 0x104C +CHECK: class type = 0x1048, this type = 0x1049, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x104E | LF_ARGLIST [size = 12, unreferenced] +CHECK: 0x1047: `__vc_attributes::moduleAttribute::type_e` +CHECK: 0x104F | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x104E +CHECK: class type = 0x1048, this type = 0x1049, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1050 | LF_MFUNCTION [size = 28, unreferenced] +CHECK: return type = 0x0003 (void), # args = 0, param list = 0x1012 +CHECK: class type = 0x1048, this type = 0x1049, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x1051 | LF_METHODLIST [size = 28, unreferenced] +CHECK: - Method [type = 0x104D, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x104F, vftable offset = -1, attrs = public] +CHECK: - Method [type = 0x1050, vftable offset = -1, attrs = public] +CHECK: 0x1052 | LF_FIELDLIST [size = 356, unreferenced] +CHECK: - LF_NESTTYPE [name = `type_e`, parent = 0x1047] +CHECK: - LF_METHOD [name = `moduleAttribute`, # overloads = 3, overload list = 0x1051] +CHECK: - LF_MEMBER [name = `type`, Type = 0x1047, offset = 0, attrs = public] +CHECK: - LF_MEMBER [name = `name`, Type = 0x104B, offset = 8, attrs = public] +CHECK: - LF_MEMBER [name = `version`, Type = 0x104B, offset = 16, attrs = public] +CHECK: - LF_MEMBER [name = `uuid`, Type = 0x104B, offset = 24, attrs = public] +CHECK: - LF_MEMBER [name = `lcid`, Type = 0x0074 (int), offset = 32, attrs = public] +CHECK: - LF_MEMBER [name = `control`, Type = 0x0030 (bool), offset = 36, attrs = public] +CHECK: - LF_MEMBER [name = `helpstring`, Type = 0x104B, offset = 40, attrs = public] +CHECK: - LF_MEMBER [name = `helpstringcontext`, Type = 0x0074 (int), offset = 48, attrs = public] +CHECK: - LF_MEMBER [name = `helpstringdll`, Type = 0x104B, offset = 56, attrs = public] +CHECK: - LF_MEMBER [name = `helpfile`, Type = 0x104B, offset = 64, attrs = public] +CHECK: - LF_MEMBER [name = `helpcontext`, Type = 0x0074 (int), offset = 72, attrs = public] +CHECK: - LF_MEMBER [name = `hidden`, Type = 0x0030 (bool), offset = 76, attrs = public] +CHECK: - LF_MEMBER [name = `restricted`, Type = 0x0030 (bool), offset = 77, attrs = public] +CHECK: - LF_MEMBER [name = `custom`, Type = 0x104B, offset = 80, attrs = public] +CHECK: - LF_MEMBER [name = `resource_name`, Type = 0x104B, offset = 88, attrs = public] +CHECK: 0x1053 | LF_STRUCTURE [size = 96, unreferenced] `__vc_attributes::moduleAttribute` +CHECK: unique name: `.?AUmoduleAttribute@__vc_attributes@@` +CHECK: vtable: , base list: , field list: 0x1052 +CHECK: options: has ctor / dtor | contains nested class | has unique name, sizeof 96 +CHECK: 0x1054 | LF_STRUCTURE [size = 48, referenced] `Nested::F` +CHECK: unique name: `.?AUF@Nested@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1057) | has unique name | is nested, sizeof 0 +CHECK: 0x1055 | LF_FIELDLIST [size = 16, referenced] +CHECK: - LF_NESTTYPE [name = `F`, parent = 0x1054] +CHECK: 0x1056 | LF_STRUCTURE [size = 44, referenced] `Nested` +CHECK: unique name: `.?AUNested@@` +CHECK: vtable: , base list: , field list: 0x1055 +CHECK: options: contains nested class | has unique name, sizeof 1 +CHECK: 0x1057 | LF_STRUCTURE [size = 48, referenced] `Nested::F` +CHECK: unique name: `.?AUF@Nested@@` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name | is nested, sizeof 1 +CHECK: 0x1058 | LF_STRUCTURE [size = 52, referenced] `Constructor` +CHECK: unique name: `.?AUConstructor@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x105C) | has unique name, sizeof 0 +CHECK: 0x1059 | LF_POINTER [size = 12, referenced] +CHECK: referent = 0x1058, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x105A | LF_MFUNCTION [size = 28, referenced] +CHECK: return type = 0x0003 (void), # args = 0, param list = 0x1012 +CHECK: class type = 0x1058, this type = 0x1059, this adjust = 0 +CHECK: calling conv = cdecl, options = constructor +CHECK: 0x105B | LF_FIELDLIST [size = 24, referenced] +CHECK: - LF_ONEMETHOD [name = `Constructor`] +CHECK: type = 0x105A, vftable offset = -1, attrs = public +CHECK: 0x105C | LF_STRUCTURE [size = 52, referenced] `Constructor` +CHECK: unique name: `.?AUConstructor@@` +CHECK: vtable: , base list: , field list: 0x105B +CHECK: options: has ctor / dtor | has unique name, sizeof 1 +CHECK: 0x105D | LF_CLASS [size = 40, referenced] `Class` +CHECK: unique name: `.?AVClass@@` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name, sizeof 1 +CHECK: 0x105E | LF_UNION [size = 32, referenced] `Union` +CHECK: unique name: `.?ATUnion@@` +CHECK: field list: 0x1007 +CHECK: options: has unique name | sealed, sizeof 1 +CHECK: 0x105F | LF_STRUCTURE [size = 48, referenced] `Operator` +CHECK: unique name: `.?AUOperator@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1064) | has unique name, sizeof 0 +CHECK: 0x1060 | LF_POINTER [size = 12, referenced] +CHECK: referent = 0x105F, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x1061 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x0074 (int): `int` +CHECK: 0x1062 | LF_MFUNCTION [size = 28, referenced] +CHECK: return type = 0x0074 (int), # args = 1, param list = 0x1061 +CHECK: class type = 0x105F, this type = 0x1060, this adjust = 0 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1063 | LF_FIELDLIST [size = 24, referenced] +CHECK: - LF_ONEMETHOD [name = `operator+`] +CHECK: type = 0x1062, vftable offset = -1, attrs = public +CHECK: 0x1064 | LF_STRUCTURE [size = 48, referenced] `Operator` +CHECK: unique name: `.?AUOperator@@` +CHECK: vtable: , base list: , field list: 0x1063 +CHECK: options: has unique name | overloaded operator, sizeof 1 +CHECK: 0x1065 | LF_FIELDLIST [size = 12, referenced] +CHECK: - LF_ENUMERATE [A = 0] +CHECK: 0x1066 | LF_ENUM [size = 36, referenced] `Enum` +CHECK: unique name: `.?AW4Enum@@` +CHECK: field list: 0x1065, underlying type: 0x0074 (int) +CHECK: options: has unique name +CHECK: 0x1067 | LF_STRUCTURE [size = 40, referenced] `Cast` +CHECK: unique name: `.?AUCast@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x106B) | has unique name, sizeof 0 +CHECK: 0x1068 | LF_POINTER [size = 12, referenced] +CHECK: referent = 0x1067, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x1069 | LF_MFUNCTION [size = 28, referenced] +CHECK: return type = 0x0074 (int), # args = 0, param list = 0x1012 +CHECK: class type = 0x1067, this type = 0x1068, this adjust = 0 +CHECK: calling conv = cdecl, options = None +CHECK: 0x106A | LF_FIELDLIST [size = 28, referenced] +CHECK: - LF_ONEMETHOD [name = `operator int`] +CHECK: type = 0x1069, vftable offset = -1, attrs = public +CHECK: 0x106B | LF_STRUCTURE [size = 40, referenced] `Cast` +CHECK: unique name: `.?AUCast@@` +CHECK: vtable: , base list: , field list: 0x106A +CHECK: options: conversion operator | has unique name | overloaded operator, sizeof 1 +CHECK: 0x106C | LF_STRUCTURE [size = 44, referenced] `Nothing` +CHECK: unique name: `.?AUNothing@@` +CHECK: vtable: , base list: , field list: 0x1007 +CHECK: options: has unique name, sizeof 1 +CHECK: 0x106D | LF_STRUCTURE [size = 52, referenced] `Assignment` +CHECK: unique name: `.?AUAssignment@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (-> 0x1073) | has unique name, sizeof 0 +CHECK: 0x106E | LF_POINTER [size = 12, referenced] +CHECK: referent = 0x106D, mode = ref, opts = None, kind = ptr64 +CHECK: 0x106F | LF_POINTER [size = 12, referenced] +CHECK: referent = 0x106D, mode = pointer, opts = const, kind = ptr64 +CHECK: 0x1070 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x106D: `Assignment` +CHECK: 0x1071 | LF_MFUNCTION [size = 28, referenced] +CHECK: return type = 0x106E, # args = 1, param list = 0x1070 +CHECK: class type = 0x106D, this type = 0x106F, this adjust = 0 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1072 | LF_FIELDLIST [size = 24, referenced] +CHECK: - LF_ONEMETHOD [name = `operator=`] +CHECK: type = 0x1071, vftable offset = -1, attrs = public +CHECK: 0x1073 | LF_STRUCTURE [size = 52, referenced] `Assignment` +CHECK: unique name: `.?AUAssignment@@` +CHECK: vtable: , base list: , field list: 0x1072 +CHECK: options: has unique name | overloaded operator | overloaded operator=, sizeof 1 +CHECK: 0x1074 | LF_STRUCTURE [size = 44, referenced] `Nothing` +CHECK: unique name: `.?AUNothing@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (<- 0x106C) | has unique name, sizeof 0 +CHECK: 0x1075 | LF_MODIFIER [size = 12, referenced] +CHECK: referent = 0x1074, modifiers = const +CHECK: 0x1076 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1075: `const Nothing` +CHECK: 0x1077 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1076 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1078 | LF_MODIFIER [size = 12, referenced] +CHECK: referent = 0x1074, modifiers = volatile +CHECK: 0x1079 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1078: `volatile Nothing` +CHECK: 0x107A | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1079 +CHECK: calling conv = cdecl, options = None +CHECK: 0x107B | LF_MODIFIER [size = 12, referenced] +CHECK: referent = 0x1074, modifiers = const | volatile +CHECK: 0x107C | LF_ARGLIST [size = 12, referenced] +CHECK: 0x107B: `const volatile Nothing` +CHECK: 0x107D | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x107C +CHECK: calling conv = cdecl, options = None +CHECK: 0x107E | LF_MODIFIER [size = 12, referenced] +CHECK: referent = 0x1074, modifiers = unaligned +CHECK: 0x107F | LF_ARGLIST [size = 12, referenced] +CHECK: 0x107E: `__unaligned Nothing` +CHECK: 0x1080 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x107F +CHECK: calling conv = cdecl, options = None +CHECK: 0x1081 | LF_UNION [size = 32, referenced] `Union` +CHECK: unique name: `.?ATUnion@@` +CHECK: field list: +CHECK: options: forward ref (<- 0x105E) | has unique name, sizeof 0 +CHECK: 0x1082 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1081: `Union` +CHECK: 0x1083 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1082 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1084 | LF_STRUCTURE [size = 124, referenced] `main::__l2::` +CHECK: unique name: `.?AU@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (<- 0x1008) | has unique name | scoped, sizeof 0 +CHECK: 0x1085 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1084: `main::__l2::` +CHECK: 0x1086 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1085 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1087 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1070 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1088 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1067: `Cast` +CHECK: 0x1089 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1088 +CHECK: calling conv = cdecl, options = None +CHECK: 0x108A | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1058: `Constructor` +CHECK: 0x108B | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x108A +CHECK: calling conv = cdecl, options = None +CHECK: 0x108C | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1054: `Nested::F` +CHECK: 0x108D | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x108C +CHECK: calling conv = cdecl, options = None +CHECK: 0x108E | LF_STRUCTURE [size = 44, referenced] `Nested` +CHECK: unique name: `.?AUNested@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (<- 0x1056) | has unique name, sizeof 0 +CHECK: 0x108F | LF_ARGLIST [size = 12, referenced] +CHECK: 0x108E: `Nested` +CHECK: 0x1090 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x108F +CHECK: calling conv = cdecl, options = None +CHECK: 0x1091 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1074: `Nothing` +CHECK: 0x1092 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1091 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1093 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x105F: `Operator` +CHECK: 0x1094 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1093 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1095 | LF_STRUCTURE [size = 88, referenced] `main::__l2::Scoped` +CHECK: unique name: `.?AUScoped@?1??main@@YAHHPEAPEAD@Z@`aa6523bc` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (<- 0x1009) | has unique name | scoped, sizeof 0 +CHECK: 0x1096 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1095: `main::__l2::Scoped` +CHECK: 0x1097 | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1096 +CHECK: calling conv = cdecl, options = None +CHECK: 0x1098 | LF_CLASS [size = 40, referenced] `Class` +CHECK: unique name: `.?AVClass@@` +CHECK: vtable: , base list: , field list: +CHECK: options: forward ref (<- 0x105D) | has unique name, sizeof 0 +CHECK: 0x1099 | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1098: `Class` +CHECK: 0x109A | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x1099 +CHECK: calling conv = cdecl, options = None +CHECK: 0x109B | LF_ARGLIST [size = 12, referenced] +CHECK: 0x1066: `Enum` +CHECK: 0x109C | LF_PROCEDURE [size = 16, referenced] +CHECK: return type = 0x0003 (void), # args = 1, param list = 0x109B +CHECK: calling conv = cdecl, options = None + +CHECK: Type Reference Statistics +CHECK: ============================================================ +CHECK: Records referenced: 84 / 157 53.50% +CHECK: Bytes referenced: 2,188 / 7,500 29.17% diff --git a/llvm/tools/llvm-pdbutil/CMakeLists.txt b/llvm/tools/llvm-pdbutil/CMakeLists.txt index e403d54eef58..56b9e19926d7 100644 --- a/llvm/tools/llvm-pdbutil/CMakeLists.txt +++ b/llvm/tools/llvm-pdbutil/CMakeLists.txt @@ -30,5 +30,6 @@ add_llvm_tool(llvm-pdbutil PrettyTypedefDumper.cpp PrettyVariableDumper.cpp StreamUtil.cpp + TypeReferenceTracker.cpp YAMLOutputStyle.cpp ) diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp index 23f9af6ab10c..f3f25669fc51 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -13,6 +13,7 @@ #include "MinimalSymbolDumper.h" #include "MinimalTypeDumper.h" #include "StreamUtil.h" +#include "TypeReferenceTracker.h" #include "llvm-pdbutil.h" #include "llvm/ADT/STLExtras.h" @@ -60,7 +61,12 @@ using namespace llvm::msf; using namespace llvm::pdb; DumpOutputStyle::DumpOutputStyle(InputFile &File) - : File(File), P(2, false, outs()) {} + : File(File), P(2, false, outs()) { + if (opts::dump::DumpTypeRefStats) + RefTracker.reset(new TypeReferenceTracker(File)); +} + +DumpOutputStyle::~DumpOutputStyle() {} PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } @@ -76,6 +82,10 @@ void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) { } Error DumpOutputStyle::dump() { + // Walk symbols & globals if we are supposed to mark types referenced. + if (opts::dump::DumpTypeRefStats) + RefTracker->mark(); + if (opts::dump::DumpSummary) { if (auto EC = dumpFileSummary()) return EC; @@ -187,6 +197,11 @@ Error DumpOutputStyle::dump() { return EC; } + if (opts::dump::DumpTypeRefStats) { + if (auto EC = dumpTypeRefStats()) + return EC; + } + if (opts::dump::DumpSectionHeaders) { if (auto EC = dumpSectionHeaders()) return EC; @@ -1227,14 +1242,15 @@ static void buildDepSet(LazyRandomTypeCollection &Types, static void dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, - uint32_t NumTypeRecords, uint32_t NumHashBuckets, + TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords, + uint32_t NumHashBuckets, FixedStreamArray HashValues, TpiStream *Stream, bool Bytes, bool Extras) { Printer.formatLine("Showing {0:N} records", NumTypeRecords); uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); - MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, NumHashBuckets, HashValues, Stream); if (auto EC = codeview::visitTypeStream(Types, V)) { @@ -1245,12 +1261,13 @@ dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, static void dumpPartialTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, + TypeReferenceTracker *RefTracker, TpiStream &Stream, ArrayRef TiList, bool Bytes, bool Extras, bool Deps) { uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); - MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, + MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, Stream.getNumHashBuckets(), Stream.getHashValues(), &Stream); @@ -1314,8 +1331,8 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { Types.reset(Reader, 100); if (opts::dump::DumpTypes) { - dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData, - false); + dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr, + opts::dump::DumpTypeData, false); } else if (opts::dump::DumpTypeExtras) { auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); @@ -1384,18 +1401,22 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); + // Only emit notes about referenced/unreferenced for types. + TypeReferenceTracker *MaybeTracker = + (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr; + // Enable resolving forward decls. Stream.buildHashMap(); if (DumpTypes || !Indices.empty()) { if (Indices.empty()) - dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(), + dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(), Stream.getNumHashBuckets(), Stream.getHashValues(), &Stream, DumpBytes, DumpExtras); else { std::vector TiList(Indices.begin(), Indices.end()); - dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, - opts::dump::DumpTypeDependents); + dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes, + DumpExtras, opts::dump::DumpTypeDependents); } } @@ -1520,6 +1541,34 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() { return Error::success(); } +Error DumpOutputStyle::dumpTypeRefStats() { + printHeader(P, "Type Reference Statistics"); + AutoIndent Indent(P); + + // Sum the byte size of all type records, and the size and count of all + // referenced records. + size_t TotalRecs = File.types().size(); + size_t RefRecs = 0; + size_t TotalBytes = 0; + size_t RefBytes = 0; + auto &Types = File.types(); + for (Optional TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) { + CVType Type = File.types().getType(*TI); + TotalBytes += Type.length(); + if (RefTracker->isTypeReferenced(*TI)) { + ++RefRecs; + RefBytes += Type.length(); + } + } + + P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs, + (double)RefRecs / TotalRecs); + P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes, + (double)RefBytes / TotalBytes); + + return Error::success(); +} + Error DumpOutputStyle::dumpGSIRecords() { printHeader(P, "GSI Records"); diff --git a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h index c95490e95df4..a55522389a74 100644 --- a/llvm/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/llvm/tools/llvm-pdbutil/DumpOutputStyle.h @@ -34,6 +34,7 @@ class COFFObjectFile; namespace pdb { class GSIHashTable; class InputFile; +class TypeReferenceTracker; struct StatCollection { struct Stat { @@ -62,6 +63,7 @@ class DumpOutputStyle : public OutputStyle { public: DumpOutputStyle(InputFile &File); + ~DumpOutputStyle() override; Error dump() override; @@ -89,6 +91,7 @@ private: Error dumpNewFpo(PDBFile &File); Error dumpTpiStream(uint32_t StreamIdx); Error dumpTypesFromObjectFile(); + Error dumpTypeRefStats(); Error dumpModules(); Error dumpModuleFiles(); Error dumpModuleSymsForPdb(); @@ -104,6 +107,7 @@ private: void dumpSectionHeaders(StringRef Label, DbgHeaderType Type); InputFile &File; + std::unique_ptr RefTracker; LinePrinter P; SmallVector StreamPurposes; }; diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp index a6622480c9ff..dff4423dc160 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -10,6 +10,7 @@ #include "FormatUtil.h" #include "LinePrinter.h" +#include "TypeReferenceTracker.h" #include "llvm-pdbutil.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" @@ -221,11 +222,10 @@ Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { // formatLine puts the newline at the beginning, so we use formatLine here // to start a new line, and then individual visit methods use format to // append to the existing line. - if (!Hashes) { - P.formatLine("{0} | {1} [size = {2}]", - fmt_align(Index, AlignStyle::Right, Width), - formatTypeLeafKind(Record.Type), Record.length()); - } else { + P.formatLine("{0} | {1} [size = {2}", + fmt_align(Index, AlignStyle::Right, Width), + formatTypeLeafKind(Record.Type), Record.length()); + if (Hashes) { std::string H; if (Index.toArrayIndex() >= HashValues.size()) { H = "(not present)"; @@ -241,13 +241,19 @@ Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { else H = "0x" + utohexstr(Hash) + ", our hash = 0x" + utohexstr(OurHash); } - P.formatLine("{0} | {1} [size = {2}, hash = {3}]", - fmt_align(Index, AlignStyle::Right, Width), - formatTypeLeafKind(Record.Type), Record.length(), H); + P.format(", hash = {0}", H); } + if (RefTracker) { + if (RefTracker->isTypeReferenced(Index)) + P.format(", referenced"); + else + P.format(", unreferenced"); + } + P.format("]"); P.Indent(Width + 3); return Error::success(); } + Error MinimalTypeDumpVisitor::visitTypeEnd(CVType &Record) { P.Unindent(Width + 3); if (RecordBytes) { diff --git a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h index 0374e042e423..6bc456d47ac4 100644 --- a/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h +++ b/llvm/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -20,17 +20,19 @@ class LazyRandomTypeCollection; namespace pdb { class LinePrinter; class TpiStream; +class TypeReferenceTracker; class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { public: MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, bool Hashes, codeview::LazyRandomTypeCollection &Types, + TypeReferenceTracker *RefTracker, uint32_t NumHashBuckets, FixedStreamArray HashValues, pdb::TpiStream *Stream) : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes), - Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues), - Stream(Stream) {} + Types(Types), RefTracker(RefTracker), NumHashBuckets(NumHashBuckets), + HashValues(HashValues), Stream(Stream) {} Error visitTypeBegin(codeview::CVType &Record, codeview::TypeIndex Index) override; @@ -56,6 +58,7 @@ private: bool RecordBytes = false; bool Hashes = false; codeview::LazyRandomTypeCollection &Types; + pdb::TypeReferenceTracker *RefTracker = nullptr; uint32_t NumHashBuckets; codeview::TypeIndex CurrentTypeIndex; FixedStreamArray HashValues; diff --git a/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp new file mode 100644 index 000000000000..f184f02e01ee --- /dev/null +++ b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.cpp @@ -0,0 +1,160 @@ +//===- TypeReferenceTracker.cpp ------------------------------- *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TypeReferenceTracker.h" + +#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" + +using namespace llvm; +using namespace llvm::pdb; +using namespace llvm::codeview; + +// LazyRandomTypeCollection doesn't appear to expose the number of records, so +// just iterate up front to find out. +static uint32_t getNumRecordsInCollection(LazyRandomTypeCollection &Types) { + uint32_t NumTypes = 0; + for (Optional TI = Types.getFirst(); TI; TI = Types.getNext(*TI)) + ++NumTypes; + return NumTypes; +} + +TypeReferenceTracker::TypeReferenceTracker(InputFile &File) + : File(File), Types(File.types()), + Ids(File.isPdb() ? &File.ids() : nullptr) { + NumTypeRecords = getNumRecordsInCollection(Types); + TypeReferenced.resize(NumTypeRecords, false); + + // If this is a PDB, ids are stored separately, so make a separate bit vector. + if (Ids) { + NumIdRecords = getNumRecordsInCollection(*Ids); + IdReferenced.resize(NumIdRecords, false); + } + + // Get the TpiStream pointer for forward decl resolution if this is a pdb. + // Build the hash map to enable resolving forward decls. + if (File.isPdb()) { + Tpi = &cantFail(File.pdb().getPDBTpiStream()); + Tpi->buildHashMap(); + } +} + +void TypeReferenceTracker::mark() { + // Walk type roots: + // - globals + // - modi symbols + // - LF_UDT_MOD_SRC_LINE? VC always links these in. + for (SymbolGroup SG : File.symbol_groups()) { + if (File.isObj()) { + for (const auto &SS : SG.getDebugSubsections()) { + // FIXME: Are there other type-referencing subsections? Inlinees? + // Probably for IDs. + if (SS.kind() != DebugSubsectionKind::Symbols) + continue; + + CVSymbolArray Symbols; + BinaryStreamReader Reader(SS.getRecordData()); + cantFail(Reader.readArray(Symbols, Reader.getLength())); + for (const CVSymbol &S : Symbols) + addTypeRefsFromSymbol(S); + } + } else if (SG.hasDebugStream()) { + for (const CVSymbol &S : SG.getPdbModuleStream().getSymbolArray()) + addTypeRefsFromSymbol(S); + } + } + + // Walk globals and mark types referenced from globals. + if (File.isPdb() && File.pdb().hasPDBGlobalsStream()) { + SymbolStream &SymStream = cantFail(File.pdb().getPDBSymbolStream()); + GlobalsStream &GS = cantFail(File.pdb().getPDBGlobalsStream()); + for (uint32_t PubSymOff : GS.getGlobalsTable()) { + CVSymbol Sym = SymStream.readRecord(PubSymOff); + addTypeRefsFromSymbol(Sym); + } + } + + // FIXME: Should we walk Ids? +} + +void TypeReferenceTracker::addOneTypeRef(TiRefKind RefKind, TypeIndex RefTI) { + // If it's simple or already seen, no need to add to work list. + BitVector &TypeOrIdReferenced = + (Ids && RefKind == TiRefKind::IndexRef) ? IdReferenced : TypeReferenced; + if (RefTI.isSimple() || TypeOrIdReferenced.test(RefTI.toArrayIndex())) + return; + + // Otherwise, mark it seen and add it to the work list. + TypeOrIdReferenced.set(RefTI.toArrayIndex()); + RefWorklist.push_back({RefKind, RefTI}); +} + +void TypeReferenceTracker::addTypeRefsFromSymbol(const CVSymbol &Sym) { + SmallVector DepList; + // FIXME: Check for failure. + discoverTypeIndicesInSymbol(Sym, DepList); + addReferencedTypes(Sym.content(), DepList); + markReferencedTypes(); +} + +void TypeReferenceTracker::addReferencedTypes(ArrayRef RecData, + ArrayRef DepList) { + for (const auto &Ref : DepList) { + // FIXME: Report OOB slice instead of truncating. + ArrayRef ByteSlice = + RecData.drop_front(Ref.Offset).take_front(4 * Ref.Count); + ArrayRef TIs( + reinterpret_cast(ByteSlice.data()), + ByteSlice.size() / 4); + + // If this is a PDB and this is an item reference, track it in the IPI + // bitvector. Otherwise, it's a type ref, or there is only one stream. + for (TypeIndex RefTI : TIs) + addOneTypeRef(Ref.Kind, RefTI); + } +} + +void TypeReferenceTracker::markReferencedTypes() { + while (!RefWorklist.empty()) { + TiRefKind RefKind; + TypeIndex RefTI; + std::tie(RefKind, RefTI) = RefWorklist.pop_back_val(); + Optional Rec = (Ids && RefKind == TiRefKind::IndexRef) + ? Ids->tryGetType(RefTI) + : Types.tryGetType(RefTI); + if (!Rec) + continue; // FIXME: Report a reference to a non-existant type. + + SmallVector DepList; + // FIXME: Check for failure. + discoverTypeIndices(*Rec, DepList); + addReferencedTypes(Rec->content(), DepList); + + // If this is a tag kind and this is a PDB input, mark the complete type as + // referenced. + // FIXME: This limitation makes this feature somewhat useless on object file + // inputs. + if (Tpi) { + switch (Rec->kind()) { + default: + break; + case LF_CLASS: + case LF_INTERFACE: + case LF_STRUCTURE: + case LF_UNION: + case LF_ENUM: + addOneTypeRef(TiRefKind::TypeRef, + cantFail(Tpi->findFullDeclForForwardRef(RefTI))); + break; + } + } + } +} diff --git a/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h new file mode 100644 index 000000000000..8861731ab6ee --- /dev/null +++ b/llvm/tools/llvm-pdbutil/TypeReferenceTracker.h @@ -0,0 +1,69 @@ +//===- TypeReferenceTracker.h --------------------------------- *- C++ --*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H +#define LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H + +#include "InputFile.h" + +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/DebugInfo/CodeView/CVRecord.h" +#include "llvm/DebugInfo/CodeView/TypeIndex.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace pdb { + +class TpiStream; + +/// Maintains bitvector to track whether a type was referenced by a symbol +/// record. +class TypeReferenceTracker { +public: + TypeReferenceTracker(InputFile &File); + + // Do the work of marking referenced types. + void mark(); + + // Return true if a symbol record transitively references this type. + bool isTypeReferenced(codeview::TypeIndex TI) { + return TI.toArrayIndex() <= NumTypeRecords && + TypeReferenced.test(TI.toArrayIndex()); + } + +private: + void addTypeRefsFromSymbol(const codeview::CVSymbol &Sym); + + // Mark types on this list as referenced. + void addReferencedTypes(ArrayRef RecData, + ArrayRef Refs); + + // Consume all types on the worklist. + void markReferencedTypes(); + + void addOneTypeRef(codeview::TiRefKind RefKind, codeview::TypeIndex RefTI); + + InputFile &File; + codeview::LazyRandomTypeCollection &Types; + codeview::LazyRandomTypeCollection *Ids = nullptr; + TpiStream *Tpi = nullptr; + BitVector TypeReferenced; + BitVector IdReferenced; + SmallVector, 10> + RefWorklist; + uint32_t NumTypeRecords = 0; + uint32_t NumIdRecords = 0; +}; + +} // namespace pdb +} // namespace llvm + +#endif // LLVM_TOOLS_LLVMPDBDUMP_TYPEREFERENCETRACKER_H diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp index 418ba135dc79..8a2a6f8194ba 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -476,6 +476,11 @@ cl::opt DumpTypeData( "type-data", cl::desc("dump CodeView type record raw bytes from TPI stream"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt + DumpTypeRefStats("type-ref-stats", + cl::desc("dump statistics on the number and size of types " + "transitively referenced by symbol records"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); cl::opt DumpTypeExtras("type-extras", cl::desc("dump type hashes and index offsets"), diff --git a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h index a54bfcdb7222..1b98d5230e4e 100644 --- a/llvm/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/llvm/tools/llvm-pdbutil/llvm-pdbutil.h @@ -155,6 +155,7 @@ extern llvm::cl::opt DumpTypeData; extern llvm::cl::opt DumpTypeExtras; extern llvm::cl::list DumpTypeIndex; extern llvm::cl::opt DumpTypeDependents; +extern llvm::cl::opt DumpTypeRefStats; extern llvm::cl::opt DumpSectionHeaders; extern llvm::cl::opt DumpIds;