# This testcase is part of GDB, the GNU debugger. # Copyright 2017-2023 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 . # 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(void);" \ "" \ " 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 {" \ " " \ "" \ " /* 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" } gdb_test_no_output "set language asm" gdb_test "ptype/o struct tuv" \ [string_to_regexp [multi_line \ "/* offset | size */ type = 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 */" \ " \}"]]