| # This testcase is part of GDB, the GNU debugger. |
| |
| # Copyright 2017-2021 Free Software Foundation, Inc. |
| |
| # This program is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 3 of the License, or |
| # (at your option) any later version. |
| # |
| # This program is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| |
| # This testcase exercises the "ptype /o" feature, which can be used to |
| # print the offsets and sizes of each field of a struct/union/class. |
| |
| standard_testfile .cc |
| |
| # Test only works on LP64 targets. That's how we guarantee that the |
| # expected holes will be present in the struct. |
| if { ![is_lp64_target] } { |
| untested "test work only on lp64 targets" |
| return 0 |
| } |
| |
| if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ |
| { debug c++ }] } { |
| return -1 |
| } |
| |
| # Test general offset printing, ctor/dtor printing, union, formatting. |
| gdb_test "ptype /o struct abc" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct abc \{" \ |
| " public:" \ |
| "/* 8 | 8 */ void *field1;" \ |
| "/* 16: 0 | 4 */ unsigned int field2 : 1;" \ |
| "/* XXX 7-bit hole */" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 20 | 4 */ int field3;" \ |
| "/* 24 | 1 */ signed char field4;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 32 | 8 */ uint64_t field5;" \ |
| "/* 40 | 8 */ union \{" \ |
| "/* 8 */ void *field6;" \ |
| "/* 4 */ int field7;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} field8;" \ |
| "/* 48 | 2 */ my_int_type field9;" \ |
| "/* XXX 6-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| # test "ptype /ox" |
| gdb_test "ptype /ox struct abc" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct abc {" \ |
| " public:" \ |
| "/* 0x0008 | 0x0008 */ void *field1;" \ |
| "/* 0x0010: 0x0 | 0x0004 */ unsigned int field2 : 1;" \ |
| "/* XXX 7-bit hole */" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 0x0014 | 0x0004 */ int field3;" \ |
| "/* 0x0018 | 0x0001 */ signed char field4;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 0x0020 | 0x0008 */ uint64_t field5;" \ |
| "/* 0x0028 | 0x0008 */ union \{" \ |
| "/* 0x0008 */ void *field6;" \ |
| "/* 0x0004 */ int field7;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} field8;" \ |
| "/* 0x0030 | 0x0002 */ my_int_type field9;" \ |
| "/* XXX 6-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| # Test "ptype /oTM". |
| gdb_test "ptype /oTM struct abc" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct abc \{" \ |
| " public:" \ |
| "/* 8 | 8 */ void *field1;" \ |
| "/* 16: 0 | 4 */ unsigned int field2 : 1;" \ |
| "/* XXX 7-bit hole */" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 20 | 4 */ int field3;" \ |
| "/* 24 | 1 */ signed char field4;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 32 | 8 */ uint64_t field5;" \ |
| "/* 40 | 8 */ union \{" \ |
| "/* 8 */ void *field6;" \ |
| "/* 4 */ int field7;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} field8;" \ |
| "/* 48 | 2 */ my_int_type field9;" \ |
| "" \ |
| " abc(void);" \ |
| " ~abc();" \ |
| "" \ |
| " typedef short my_int_type;" \ |
| "/* XXX 6-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| # Test "ptype /TMo". This should be the same as "ptype /o". |
| gdb_test "ptype /TMo struct abc" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct abc \{" \ |
| " public:" \ |
| "/* 8 | 8 */ void *field1;" \ |
| "/* 16: 0 | 4 */ unsigned int field2 : 1;" \ |
| "/* XXX 7-bit hole */" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 20 | 4 */ int field3;" \ |
| "/* 24 | 1 */ signed char field4;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 32 | 8 */ uint64_t field5;" \ |
| "/* 40 | 8 */ union \{" \ |
| "/* 8 */ void *field6;" \ |
| "/* 4 */ int field7;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} field8;" \ |
| "/* 48 | 2 */ my_int_type field9;" \ |
| "/* XXX 6-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| # Test nested structs. |
| gdb_test "ptype /o struct pqr" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct pqr \{" \ |
| "/* 0 | 4 */ int ff1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 8 | 40 */ struct xyz \{" \ |
| "/* 8 | 4 */ int f1;" \ |
| "/* 12 | 1 */ signed char f2;" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 16 | 8 */ void *f3;" \ |
| "/* 24 | 24 */ struct tuv \{" \ |
| "/* 24 | 4 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 32 | 8 */ signed char *a2;" \ |
| "/* 40 | 4 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} f4;" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \} ff2;" \ |
| "/* 48 | 1 */ signed char ff3;" \ |
| "/* XXX 7-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| # Test nested struct with /x |
| gdb_test "ptype /ox struct pqr" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct pqr \{" \ |
| "/* 0x0000 | 0x0004 */ int ff1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 0x0008 | 0x0028 */ struct xyz \{" \ |
| "/* 0x0008 | 0x0004 */ int f1;" \ |
| "/* 0x000c | 0x0001 */ signed char f2;" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 0x0010 | 0x0008 */ void *f3;" \ |
| "/* 0x0018 | 0x0018 */ struct tuv \{" \ |
| "/* 0x0018 | 0x0004 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 0x0020 | 0x0008 */ signed char *a2;" \ |
| "/* 0x0028 | 0x0004 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} f4;" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \} ff2;" \ |
| "/* 0x0030 | 0x0001 */ signed char ff3;" \ |
| "/* XXX 7-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| |
| # Test that the offset is properly reset when we are printing a union |
| # and go inside two inner structs. |
| # This also tests a struct inside a struct inside a union. |
| gdb_test "ptype /o union qwe" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = union qwe \{" \ |
| "/* 24 */ struct tuv \{" \ |
| "/* 0 | 4 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 8 | 8 */ signed char *a2;" \ |
| "/* 16 | 4 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} fff1;" \ |
| "/* 40 */ struct xyz \{" \ |
| "/* 0 | 4 */ int f1;" \ |
| "/* 4 | 1 */ signed char f2;" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 8 | 8 */ void *f3;" \ |
| "/* 16 | 24 */ struct tuv \{" \ |
| "/* 16 | 4 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 24 | 8 */ signed char *a2;" \ |
| "/* 32 | 4 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} f4;" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \} fff2;" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \}"]] |
| |
| # Test printing a struct that contains a union, and that also |
| # contains a struct. |
| gdb_test "ptype /o struct poi" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct poi \{" \ |
| "/* 0 | 4 */ int f1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 8 | 40 */ union qwe \{" \ |
| "/* 24 */ struct tuv \{" \ |
| "/* 8 | 4 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 16 | 8 */ signed char *a2;" \ |
| "/* 24 | 4 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} fff1;" \ |
| "/* 40 */ struct xyz \{" \ |
| "/* 8 | 4 */ int f1;" \ |
| "/* 12 | 1 */ signed char f2;" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 16 | 8 */ void *f3;" \ |
| "/* 24 | 24 */ struct tuv \{" \ |
| "/* 24 | 4 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 32 | 8 */ signed char *a2;" \ |
| "/* 40 | 4 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} f4;" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \} fff2;" \ |
| "/* XXX 32-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \} f2;" \ |
| "/* 48 | 2 */ uint16_t f3;" \ |
| "/* XXX 6-byte hole */" \ |
| "/* 56 | 56 */ struct pqr \{" \ |
| "/* 56 | 4 */ int ff1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 64 | 40 */ struct xyz \{" \ |
| "/* 64 | 4 */ int f1;" \ |
| "/* 68 | 1 */ signed char f2;" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 72 | 8 */ void *f3;" \ |
| "/* 80 | 24 */ struct tuv \{" \ |
| "/* 80 | 4 */ int a1;" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 88 | 8 */ signed char *a2;" \ |
| "/* 96 | 4 */ int a3;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \} f4;" \ |
| "" \ |
| " /* total size (bytes): 40 */" \ |
| " \} ff2;" \ |
| "/* 104 | 1 */ signed char ff3;" \ |
| "/* XXX 7-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \} f4;" \ |
| "" \ |
| " /* total size (bytes): 112 */" \ |
| " \}"]] |
| |
| # Test printing a struct with several bitfields, laid out in various |
| # ways. |
| # |
| # Because dealing with bitfields and offsets is difficult, it can be |
| # tricky to confirm that the output of this command is accurate. A |
| # nice way to do that is to use GDB's "x" command and print the actual |
| # memory layout of the struct. In order to differentiate between |
| # bitfields and non-bitfield variables, one can assign "-1" to every |
| # bitfield in the struct. An example of the output of "x" using |
| # "struct tyu" is: |
| # |
| # (gdb) x/24xb &e |
| # 0x7fffffffd540: 0xff 0xff 0xff 0x1f 0x00 0x00 0x00 0x00 |
| # 0x7fffffffd548: 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff |
| # 0x7fffffffd550: 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
| gdb_test "ptype /o struct tyu" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct tyu \{" \ |
| "/* 0: 0 | 4 */ int a1 : 1;" \ |
| "/* 0: 1 | 4 */ int a2 : 3;" \ |
| "/* 0: 4 | 4 */ int a3 : 23;" \ |
| "/* 3: 3 | 1 */ signed char a4 : 2;" \ |
| "/* XXX 3-bit hole */" \ |
| "/* XXX 4-byte hole */" \ |
| "/* 8 | 8 */ int64_t a5;" \ |
| "/* 16: 0 | 4 */ int a6 : 5;" \ |
| "/* 16: 5 | 8 */ int64_t a7 : 3;" \ |
| "/* XXX 7-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 24 */" \ |
| " \}"]] |
| |
| gdb_test "ptype /o struct asd" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct asd \{" \ |
| "/* 0 | 32 */ struct asd::jkl \{" \ |
| "/* 0 | 8 */ signed char *f1;" \ |
| "/* 8 | 8 */ union \{" \ |
| "/* 8 */ void *ff1;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} f2;" \ |
| "/* 16 | 8 */ union \{" \ |
| "/* 8 */ signed char *ff2;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} f3;" \ |
| "/* 24: 0 | 4 */ int f4 : 5;" \ |
| "/* 24: 5 | 4 */ unsigned int f5 : 1;" \ |
| "/* XXX 2-bit hole */" \ |
| "/* XXX 1-byte hole */" \ |
| "/* 26 | 2 */ short f6;" \ |
| "/* XXX 4-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 32 */" \ |
| " \} f7;" \ |
| "/* 32 | 8 */ unsigned long f8;" \ |
| "/* 40 | 8 */ signed char *f9;" \ |
| "/* 48: 0 | 4 */ int f10 : 4;" \ |
| "/* 48: 4 | 4 */ unsigned int f11 : 1;" \ |
| "/* 48: 5 | 4 */ unsigned int f12 : 1;" \ |
| "/* 48: 6 | 4 */ unsigned int f13 : 1;" \ |
| "/* 48: 7 | 4 */ unsigned int f14 : 1;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 56 | 8 */ void *f15;" \ |
| "/* 64 | 8 */ void *f16;" \ |
| "" \ |
| " /* total size (bytes): 72 */" \ |
| " \}"]] |
| |
| # Test that we don't print any header when issuing a "ptype /o" on a |
| # non-struct, non-union, non-class type. |
| gdb_test "ptype /o int" "int" |
| gdb_test "ptype /o uint8_t" "char" |
| |
| # Test that the "whatis" command doesn't print anything related to the |
| # "offsets" feature, even when receiving the "/o" parameter. |
| set test "whatis /o asd" |
| gdb_test_multiple "$test" "$test" { |
| -re "^$test\r\ntype = asd\r\n$gdb_prompt $" { |
| pass $test |
| } |
| } |
| |
| # Test that printing a struct with a static member of itself doesn't |
| # get us into an infinite loop. |
| gdb_test "ptype/o static_member" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct static_member \{" \ |
| " static static_member Empty;" \ |
| "/* 0 | 4 */ int abc;" \ |
| "" \ |
| " /* total size (bytes): 4 */" \ |
| " \}"]] |
| |
| # Test that the "no data fields" text is indented properly. |
| gdb_test "ptype/o empty_member" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct empty_member \{" \ |
| "/* 0 | 1 */ struct {" \ |
| " <no data fields>" \ |
| "" \ |
| " /* total size (bytes): 1 */" \ |
| " } empty;" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 4 | 4 */ int an_int;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \}"]] |
| |
| with_test_prefix "with_hex_default" { |
| # Test setting default display to hex |
| gdb_test_no_output "set print type hex on" |
| gdb_test "show print type hex" \ |
| "Display of struct members offsets and sizes in hexadecimal is on" |
| |
| # test "ptype /o" is now equivalent to "ptype /ox" |
| gdb_test "ptype /o struct abc" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct abc \{" \ |
| " public:" \ |
| "/* 0x0008 | 0x0008 */ void *field1;" \ |
| "/* 0x0010: 0x0 | 0x0004 */ unsigned int field2 : 1;" \ |
| "/* XXX 7-bit hole */" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 0x0014 | 0x0004 */ int field3;" \ |
| "/* 0x0018 | 0x0001 */ signed char field4;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 0x0020 | 0x0008 */ uint64_t field5;" \ |
| "/* 0x0028 | 0x0008 */ union \{" \ |
| "/* 0x0008 */ void *field6;" \ |
| "/* 0x0004 */ int field7;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} field8;" \ |
| "/* 0x0030 | 0x0002 */ my_int_type field9;" \ |
| "/* XXX 6-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| gdb_test "ptype /od struct abc" \ |
| [string_to_regexp [multi_line \ |
| "/* offset | size */ type = struct abc \{" \ |
| " public:" \ |
| "/* 8 | 8 */ void *field1;" \ |
| "/* 16: 0 | 4 */ unsigned int field2 : 1;" \ |
| "/* XXX 7-bit hole */" \ |
| "/* XXX 3-byte hole */" \ |
| "/* 20 | 4 */ int field3;" \ |
| "/* 24 | 1 */ signed char field4;" \ |
| "/* XXX 7-byte hole */" \ |
| "/* 32 | 8 */ uint64_t field5;" \ |
| "/* 40 | 8 */ union \{" \ |
| "/* 8 */ void *field6;" \ |
| "/* 4 */ int field7;" \ |
| "" \ |
| " /* total size (bytes): 8 */" \ |
| " \} field8;" \ |
| "/* 48 | 2 */ my_int_type field9;" \ |
| "/* XXX 6-byte padding */" \ |
| "" \ |
| " /* total size (bytes): 56 */" \ |
| " \}"]] |
| |
| # restore |
| gdb_test_no_output "set print type hex off" |
| } |