Files
clang-p2996/lldb/test/Shell/SymbolFile/NativePDB/global-classes.cpp
Zequan Wu d3492ed016 [LLDB][NativePDB] Fix struct layout when it has anonymous unions.
Previously, lldb mistook fields in anonymous union in a struct as the direct
field of the struct, which causes lldb crashes due to multiple fields sharing
the same offset in a struct. This patch fixes it.

MSVC generated pdb doesn't have the debug info entity representing a anonymous
union in a struct. It looks like the following:
```
struct S {
union {
  char c;
  int i;
};
};

0x1004 | LF_FIELDLIST [size = 40]
         - LF_MEMBER [name = `c`, Type = 0x0070 (char), offset = 0, attrs = public]
         - LF_MEMBER [name = `i`, Type = 0x0074 (int), offset = 0, attrs = public]
0x1005 | LF_STRUCTURE [size = 32] `S`
         unique name: `.?AUS@@`
         vtable: <no type>, base list: <no type>, field list: 0x1004
```
Clang generated pdb is similar, though due to the [[ https://github.com/llvm/llvm-project/issues/57999 | bug ]],
it's not more useful than the debug info above. But that's not very relavent,
lldb should still be able to understand MSVC geneerated pdb.
```
0x1003 | LF_UNION [size = 60] `S::<unnamed-tag>`
         unique name: `.?AT<unnamed-type-$S1>@S@@`
         field list: <no type>
         options: forward ref (= 0x1003) | has unique name | is nested, sizeof 0
0x1004 | LF_FIELDLIST [size = 40]
         - LF_MEMBER [name = `c`, Type = 0x0070 (char), offset = 0, attrs = public]
         - LF_MEMBER [name = `i`, Type = 0x0074 (int), offset = 0, attrs = public]
         - LF_NESTTYPE [name = ``, parent = 0x1003]
0x1005 | LF_STRUCTURE [size = 32] `S`
         unique name: `.?AUS@@`
         vtable: <no type>, base list: <no type>, field list: 0x1004
         options: contains nested class | has unique name, sizeof 4
0x1006 | LF_FIELDLIST [size = 28]
         - LF_MEMBER [name = `c`, Type = 0x0070 (char), offset = 0, attrs = public]
         - LF_MEMBER [name = `i`, Type = 0x0074 (int), offset = 0, attrs = public]
0x1007 | LF_UNION [size = 60] `S::<unnamed-tag>`
         unique name: `.?AT<unnamed-type-$S1>@S@@`
         field list: 0x1006
         options: has unique name | is nested | sealed, sizeof
```
This patch delays the FieldDecl creation when travesing LF_FIELDLIST so we know
if there are multiple fields are in the same offsets and are able to group them
into different anonymous unions based on offsets. Nested anonymous union will
be flatten into one anonymous union, because we simply don't have that info, but
they are equivalent in terms of union layout.

Differential Revision: https://reviews.llvm.org/D134849
2022-10-13 12:43:45 -07:00

388 lines
14 KiB
C++

// clang-format off
// REQUIRES: lld, x86
// Test that we can display tag types.
// RUN: %clang_cl --target=x86_64-windows-msvc -Od -Z7 \
// RUN: -Xclang -fkeep-static-consts -c /Fo%t.obj -- %s
// RUN: lld-link -debug:full -nodefaultlib -entry:main %t.obj -out:%t.exe -pdb:%t.pdb
// RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -f %t.exe -s \
// RUN: %p/Inputs/globals-classes.lldbinit | FileCheck %s
enum class EnumType : unsigned {
A = 1,
B = 2
};
class ClassNoPadding {
/* [ 0] */ unsigned char a = 86;
/* [ 1] */ char b = 'a';
/* [ 2] */ bool c = false;
/* [ 3] */ bool d = true;
/* [ 4] */ short e = -1234;
/* [ 6] */ unsigned short f = 8123;
/* [ 8] */ unsigned int g = 123908;
/* [12] */ int h = -890234;
/* [16] */ unsigned long i = 2908234;
/* [20] */ long j = 7234890;
/* [24] */ float k = 908234.12392;
/* [28] */ EnumType l = EnumType::A;
/* [32] */ double m = 23890.897423;
/* [40] */ unsigned long long n = 23490782;
/* [48] */ long long o = -923409823;
/* [56] */ int p[5] = { 2, 3, 5, 8, 13 };
};
class ClassWithPadding {
/* [ 0] */ char a = '0';
// char padding[1];
/* [ 2] */ short b = 50;
/* [ 4] */ char c[2] = { '0', '1' };
// char padding[2];
/* [ 8] */ int d = 100;
/* [12] */ char e = '0';
// char padding[3];
/* [16] */ int f = 200;
// char padding[4];
/* [24] */ long long g = 300;
/* [32] */ char h[3] = { '0', '1', '2' };
// char padding[5];
/* [40] */ long long i = 400;
/* [48] */ char j[2] = { '0', '1' };
// char padding[6];
/* [56] */ long long k = 500;
/* [64] */ char l = '0';
// char padding[7];
/* [72] */ long long m = 600;
} ;
struct EmptyBase {};
template<typename T>
struct BaseClass {
constexpr BaseClass(int N)
: BaseMember(N) {}
int BaseMember;
};
struct DerivedClass : public BaseClass<int> {
constexpr DerivedClass(int Base, int Derived)
: BaseClass(Base), DerivedMember(Derived) {}
int DerivedMember;
};
struct EBO : public EmptyBase {
constexpr EBO(int N) : Member(N) {}
int Member;
};
struct PaddedBases : public BaseClass<char>, public BaseClass<short>, BaseClass<int> {
constexpr PaddedBases(char CH, short S, int N, long long D)
: BaseClass<char>(CH), BaseClass<short>(S), BaseClass<int>(N), DerivedMember(D) {}
long long DerivedMember;
};
struct Statics {
static char a;
static bool b;
static short c;
static unsigned short d;
static unsigned int e;
static int f;
static unsigned long g;
static long h;
static float i;
static EnumType j;
static double k;
static unsigned long long l;
static long long m;
};
char Statics::a = 'a';
bool Statics::b = true;
short Statics::c = 1234;
unsigned short Statics::d = 2345;
unsigned int Statics::e = 3456;
int Statics::f = 4567;
unsigned long Statics::g = 5678;
long Statics::h = 6789;
float Statics::i = 7890.1234;
EnumType Statics::j = EnumType::A;
double Statics::k = 8901.2345;
unsigned long long Statics::l = 9012345;
long long Statics::m = 1234567;
struct Pointers {
void *a = nullptr;
char *b = &Statics::a;
bool *c = &Statics::b;
short *e = &Statics::c;
unsigned short *f = &Statics::d;
unsigned int *g = &Statics::e;
int *h = &Statics::f;
unsigned long *i = &Statics::g;
long *j = &Statics::h;
float *k = &Statics::i;
EnumType *l = &Statics::j;
double *m = &Statics::k;
unsigned long long *n = &Statics::l;
long long *o = &Statics::m;
};
struct References {
char &a = Statics::a;
bool &b = Statics::b;
short &c = Statics::c;
unsigned short &d = Statics::d;
unsigned int &e = Statics::e;
int &f = Statics::f;
unsigned long &g = Statics::g;
long &h = Statics::h;
float &i = Statics::i;
EnumType &j = Statics::j;
double &k = Statics::k;
unsigned long long &l = Statics::l;
long long &m = Statics::m;
};
constexpr ClassWithPadding ClassWithPaddingInstance;
// CHECK: (lldb) target variable -T ClassWithPaddingInstance
// CHECK-NEXT: (const ClassWithPadding) ClassWithPaddingInstance = {
// CHECK-NEXT: (char) a = '0'
// CHECK-NEXT: (short) b = 50
// CHECK-NEXT: (char[2]) c = "01"
// CHECK-NEXT: (int) d = 100
// CHECK-NEXT: (char) e = '0'
// CHECK-NEXT: (int) f = 200
// CHECK-NEXT: (long long) g = 300
// CHECK-NEXT: (char[3]) h = "012"
// CHECK-NEXT: (long long) i = 400
// CHECK-NEXT: (char[2]) j = "01"
// CHECK-NEXT: (long long) k = 500
// CHECK-NEXT: (char) l = '0'
// CHECK-NEXT: (long long) m = 600
// CHECK-NEXT: }
constexpr ClassNoPadding ClassNoPaddingInstance;
// CHECK: (lldb) target variable -T ClassNoPaddingInstance
// CHECK-NEXT: (const ClassNoPadding) ClassNoPaddingInstance = {
// CHECK-NEXT: (unsigned char) a = 'V'
// CHECK-NEXT: (char) b = 'a'
// CHECK-NEXT: (bool) c = false
// CHECK-NEXT: (bool) d = true
// CHECK-NEXT: (short) e = -1234
// CHECK-NEXT: (unsigned short) f = 8123
// CHECK-NEXT: (unsigned int) g = 123908
// CHECK-NEXT: (int) h = -890234
// CHECK-NEXT: (unsigned long) i = 2908234
// CHECK-NEXT: (long) j = 7234890
// CHECK-NEXT: (float) k = 908234.125
// CHECK-NEXT: (EnumType) l = A
// CHECK-NEXT: (double) m = 23890.897422999999
// CHECK-NEXT: (unsigned long long) n = 23490782
// CHECK-NEXT: (long long) o = -923409823
// CHECK-NEXT: (int[5]) p = {
// CHECK-NEXT: (int) [0] = 2
// CHECK-NEXT: (int) [1] = 3
// CHECK-NEXT: (int) [2] = 5
// CHECK-NEXT: (int) [3] = 8
// CHECK-NEXT: (int) [4] = 13
// CHECK-NEXT: }
// CHECK-NEXT: }
constexpr DerivedClass DC(10, 20);
// CHECK: (lldb) target variable -T DC
// CHECK-NEXT: (const DerivedClass) DC = {
// CHECK-NEXT: (BaseClass<int>) BaseClass<int> = {
// CHECK-NEXT: (int) BaseMember = 10
// CHECK-NEXT: }
// CHECK-NEXT: (int) DerivedMember = 20
// CHECK-NEXT: }
constexpr EBO EBOC(20);
// CHECK: (lldb) target variable -T EBOC
// CHECK-NEXT: (const EBO) EBOC = {
// CHECK-NEXT: (int) Member = 20
// CHECK-NEXT: }
constexpr PaddedBases PBC('a', 12, 120, 1200);
// CHECK: (lldb) target variable -T PBC
// CHECK-NEXT: (const PaddedBases) PBC = {
// CHECK-NEXT: (BaseClass<char>) BaseClass<char> = {
// CHECK-NEXT: (int) BaseMember = 97
// CHECK-NEXT: }
// CHECK-NEXT: (BaseClass<short>) BaseClass<short> = {
// CHECK-NEXT: (int) BaseMember = 12
// CHECK-NEXT: }
// CHECK-NEXT: (BaseClass<int>) BaseClass<int> = {
// CHECK-NEXT: (int) BaseMember = 120
// CHECK-NEXT: }
// CHECK-NEXT: (long long) DerivedMember = 1200
// CHECK-NEXT: }
constexpr struct {
int x = 12;
EBO EBOC{ 42 };
} UnnamedClassInstance;
// CHECK: (lldb) target variable -T UnnamedClassInstance
// CHECK-NEXT: (const <unnamed-type-UnnamedClassInstance>) UnnamedClassInstance = {
// CHECK-NEXT: (int) x = 12
// CHECK-NEXT: (EBO) EBOC = {
// CHECK-NEXT: (int) Member = 42
// CHECK-NEXT: }
// CHECK-NEXT: }
constexpr Pointers PointersInstance;
// CHECK: (lldb) target variable -T PointersInstance
// CHECK-NEXT: (const Pointers) PointersInstance = {
// CHECK-NEXT: (void *) a = {{.*}}
// CHECK-NEXT: (char *) b = {{.*}}
// CHECK-NEXT: (bool *) c = {{.*}}
// CHECK-NEXT: (short *) e = {{.*}}
// CHECK-NEXT: (unsigned short *) f = {{.*}}
// CHECK-NEXT: (unsigned int *) g = {{.*}}
// CHECK-NEXT: (int *) h = {{.*}}
// CHECK-NEXT: (unsigned long *) i = {{.*}}
// CHECK-NEXT: (long *) j = {{.*}}
// CHECK-NEXT: (float *) k = {{.*}}
// CHECK-NEXT: (EnumType *) l = {{.*}}
// CHECK-NEXT: (double *) m = {{.*}}
// CHECK-NEXT: (unsigned long long *) n = {{.*}}
// CHECK-NEXT: (long long *) o = {{.*}}
// CHECK-NEXT: }
constexpr References ReferencesInstance;
// CHECK: (lldb) target variable -T ReferencesInstance
// CHECK-NEXT: (const References) ReferencesInstance = {
// CHECK-NEXT: (char &) a = {{.*}}
// CHECK-NEXT: (bool &) b = {{.*}}
// CHECK-NEXT: (short &) c = {{.*}}
// CHECK-NEXT: (unsigned short &) d = {{.*}}
// CHECK-NEXT: (unsigned int &) e = {{.*}}
// CHECK-NEXT: (int &) f = {{.*}}
// CHECK-NEXT: (unsigned long &) g = {{.*}}
// CHECK-NEXT: (long &) h = {{.*}}
// CHECK-NEXT: (float &) i = {{.*}}
// CHECK-NEXT: (EnumType &) j = {{.*}}
// CHECK-NEXT: (double &) k = {{.*}}
// CHECK-NEXT: (unsigned long long &) l = {{.*}}
// CHECK-NEXT: (long long &) m = {{.*}}
// CHECK-NEXT: }
// CHECK: Dumping clang ast for 1 modules.
// CHECK: TranslationUnitDecl {{.*}}
// CHECK: |-CXXRecordDecl {{.*}} class ClassWithPadding definition
// CHECK: | |-FieldDecl {{.*}} a 'char'
// CHECK: | |-FieldDecl {{.*}} b 'short'
// CHECK: | |-FieldDecl {{.*}} c 'char[2]'
// CHECK: | |-FieldDecl {{.*}} d 'int'
// CHECK: | |-FieldDecl {{.*}} e 'char'
// CHECK: | |-FieldDecl {{.*}} f 'int'
// CHECK: | |-FieldDecl {{.*}} g 'long long'
// CHECK: | |-FieldDecl {{.*}} h 'char[3]'
// CHECK: | |-FieldDecl {{.*}} i 'long long'
// CHECK: | |-FieldDecl {{.*}} j 'char[2]'
// CHECK: | |-FieldDecl {{.*}} k 'long long'
// CHECK: | |-FieldDecl {{.*}} l 'char'
// CHECK: | `-FieldDecl {{.*}} m 'long long'
// CHECK: |-VarDecl {{.*}} ClassWithPaddingInstance 'const ClassWithPadding'
// CHECK: |-CXXRecordDecl {{.*}} class ClassNoPadding definition
// CHECK: | |-FieldDecl {{.*}} a 'unsigned char'
// CHECK: | |-FieldDecl {{.*}} b 'char'
// CHECK: | |-FieldDecl {{.*}} c 'bool'
// CHECK: | |-FieldDecl {{.*}} d 'bool'
// CHECK: | |-FieldDecl {{.*}} e 'short'
// CHECK: | |-FieldDecl {{.*}} f 'unsigned short'
// CHECK: | |-FieldDecl {{.*}} g 'unsigned int'
// CHECK: | |-FieldDecl {{.*}} h 'int'
// CHECK: | |-FieldDecl {{.*}} i 'unsigned long'
// CHECK: | |-FieldDecl {{.*}} j 'long'
// CHECK: | |-FieldDecl {{.*}} k 'float'
// CHECK: | |-FieldDecl {{.*}} l 'EnumType'
// CHECK: | |-FieldDecl {{.*}} m 'double'
// CHECK: | |-FieldDecl {{.*}} n 'unsigned long long'
// CHECK: | |-FieldDecl {{.*}} o 'long long'
// CHECK: | `-FieldDecl {{.*}} p 'int[5]'
// CHECK: |-VarDecl {{.*}} ClassNoPaddingInstance 'const ClassNoPadding'
// CHECK: |-EnumDecl {{.*}} EnumType
// CHECK: | |-EnumConstantDecl {{.*}} A 'EnumType'
// CHECK: | `-EnumConstantDecl {{.*}} B 'EnumType'
// CHECK: |-CXXRecordDecl {{.*}} struct DerivedClass definition
// CHECK: | |-public 'BaseClass<int>'
// CHECK: | |-CXXConstructorDecl {{.*}} DerivedClass 'void (int, int)'
// CHECK: | | |-ParmVarDecl {{.*}} 'int'
// CHECK: | | `-ParmVarDecl {{.*}} 'int'
// CHECK: | `-FieldDecl {{.*}} DerivedMember 'int'
// CHECK: |-VarDecl {{.*}} DC 'const DerivedClass'
// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<int> definition
// CHECK: | |-CXXMethodDecl {{.*}} BaseClass 'void (int)'
// CHECK: | | `-ParmVarDecl {{.*}} 'int'
// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
// CHECK: |-CXXRecordDecl {{.*}} struct EBO definition
// CHECK: | |-public 'EmptyBase'
// CHECK: | |-CXXConstructorDecl {{.*}} EBO 'void (int)'
// CHECK: | | `-ParmVarDecl {{.*}} 'int'
// CHECK: | `-FieldDecl {{.*}} Member 'int'
// CHECK: |-VarDecl {{.*}} EBOC 'const EBO'
// CHECK: |-CXXRecordDecl {{.*}} struct EmptyBase definition
// CHECK: |-CXXRecordDecl {{.*}} struct PaddedBases definition
// CHECK: | |-public 'BaseClass<char>'
// CHECK: | |-public 'BaseClass<short>'
// CHECK: | |-public 'BaseClass<int>'
// CHECK: | |-CXXConstructorDecl {{.*}} PaddedBases 'void (char, short, int, long long)'
// CHECK: | | |-ParmVarDecl {{.*}} 'char'
// CHECK: | | |-ParmVarDecl {{.*}} 'short'
// CHECK: | | |-ParmVarDecl {{.*}} 'int'
// CHECK: | | `-ParmVarDecl {{.*}} 'long long'
// CHECK: | `-FieldDecl {{.*}} DerivedMember 'long long'
// CHECK: |-VarDecl {{.*}} PBC 'const PaddedBases'
// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<char> definition
// CHECK: | |-CXXMethodDecl {{.*}} BaseClass 'void (int)'
// CHECK: | | `-ParmVarDecl {{.*}} 'int'
// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
// CHECK: |-CXXRecordDecl {{.*}} struct BaseClass<short> definition
// CHECK: | |-CXXMethodDecl {{.*}} BaseClass 'void (int)'
// CHECK: | | `-ParmVarDecl {{.*}} 'int'
// CHECK: | `-FieldDecl {{.*}} BaseMember 'int'
// CHECK: |-CXXRecordDecl {{.*}} struct <unnamed-type-UnnamedClassInstance> definition
// CHECK: | |-FieldDecl {{.*}} x 'int'
// CHECK: | `-FieldDecl {{.*}} EBOC 'EBO'
// CHECK: |-VarDecl {{.*}} UnnamedClassInstance 'const <unnamed-type-UnnamedClassInstance>'
// CHECK: |-CXXRecordDecl {{.*}} struct Pointers definition
// CHECK: | |-FieldDecl {{.*}} a 'void *'
// CHECK: | |-FieldDecl {{.*}} b 'char *'
// CHECK: | |-FieldDecl {{.*}} c 'bool *'
// CHECK: | |-FieldDecl {{.*}} e 'short *'
// CHECK: | |-FieldDecl {{.*}} f 'unsigned short *'
// CHECK: | |-FieldDecl {{.*}} g 'unsigned int *'
// CHECK: | |-FieldDecl {{.*}} h 'int *'
// CHECK: | |-FieldDecl {{.*}} i 'unsigned long *'
// CHECK: | |-FieldDecl {{.*}} j 'long *'
// CHECK: | |-FieldDecl {{.*}} k 'float *'
// CHECK: | |-FieldDecl {{.*}} l 'EnumType *'
// CHECK: | |-FieldDecl {{.*}} m 'double *'
// CHECK: | |-FieldDecl {{.*}} n 'unsigned long long *'
// CHECK: | `-FieldDecl {{.*}} o 'long long *'
// CHECK: |-VarDecl {{.*}} PointersInstance 'const Pointers'
// CHECK: |-CXXRecordDecl {{.*}} struct References definition
// CHECK: | |-FieldDecl {{.*}} a 'char &'
// CHECK: | |-FieldDecl {{.*}} b 'bool &'
// CHECK: | |-FieldDecl {{.*}} c 'short &'
// CHECK: | |-FieldDecl {{.*}} d 'unsigned short &'
// CHECK: | |-FieldDecl {{.*}} e 'unsigned int &'
// CHECK: | |-FieldDecl {{.*}} f 'int &'
// CHECK: | |-FieldDecl {{.*}} g 'unsigned long &'
// CHECK: | |-FieldDecl {{.*}} h 'long &'
// CHECK: | |-FieldDecl {{.*}} i 'float &'
// CHECK: | |-FieldDecl {{.*}} j 'EnumType &'
// CHECK: | |-FieldDecl {{.*}} k 'double &'
// CHECK: | |-FieldDecl {{.*}} l 'unsigned long long &'
// CHECK: | `-FieldDecl {{.*}} m 'long long &'
// CHECK: `-VarDecl {{.*}} ReferencesInstance 'const References'
int main(int argc, char **argv) {
return 0;
}