diff options
Diffstat (limited to 'gdb/testsuite')
162 files changed, 4932 insertions, 338 deletions
diff --git a/gdb/testsuite/gdb.ada/dyn-bit-offset.exp b/gdb/testsuite/gdb.ada/dyn-bit-offset.exp index 19d16b1..f8a4363 100644 --- a/gdb/testsuite/gdb.ada/dyn-bit-offset.exp +++ b/gdb/testsuite/gdb.ada/dyn-bit-offset.exp @@ -28,19 +28,52 @@ if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $flags] != ""} { return -1 } +# GCC needs to have fixes: +# - 809b46d2ccc ("Partially lift restriction from loc_list_from_tree_1") +# - d7f24e37d4b ("Fix oversight about big-endian targets in latest change") +set have_xfail [gnat_version_compare <= {16 1}] + clean_restart ${testfile} set bp_location [gdb_get_line_number "STOP" ${testdir}/exam.adb] runto "exam.adb:$bp_location" +set re_pass \ + [string_to_regexp \ + " = (discr => 3, array_field => (-5, -6, -7), field => -5, another_field => -6)"] +set re_xfail_le \ + [string_to_regexp \ + " = (discr => 3, array_field => (-5, -6, -7), field => -4, another_field => -4)"] +set re_xfail_be \ + [string_to_regexp \ + " = (discr => 3, array_field => (-5, -6, -7), field => -6, another_field => -6)"] + gdb_test_multiple "print spr" "" { - -re -wrap " = \\(discr => 3, array_field => \\(-5, -6, -7\\), field => -4, another_field => -6\\)" { + -re -wrap $re_pass { pass $gdb_test_name } - -re -wrap " = \\(discr => 3, array_field => \\(-5, -6, -7\\), field => -4, another_field => -4\\)" { - # A known GCC bug. - xfail $gdb_test_name + -re -wrap $re_xfail_le|$re_xfail_be { + if { $have_xfail } { + xfail $gdb_test_name + } else { + fail $gdb_test_name + } } } -gdb_test "print spr.field" " = -4" +set re_pass " = -5" +set re_xfail_le " = -4" +set re_xfail_be " = -6" + +gdb_test_multiple "print spr.field" "" { + -re -wrap $re_pass { + pass $gdb_test_name + } + -re -wrap $re_xfail_le|$re_xfail_be { + if { $have_xfail } { + xfail $gdb_test_name + } else { + fail $gdb_test_name + } + } +} diff --git a/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb b/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb index a882afd..5c7f70b 100644 --- a/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb +++ b/gdb/testsuite/gdb.ada/dyn-bit-offset/exam.adb @@ -36,7 +36,7 @@ procedure Exam is pragma No_Component_Reordering (Some_Packed_Record); SPR : Some_Packed_Record := (Discr => 3, - Field => -4, + Field => -5, Another_Field => -6, Array_Field => (-5, -6, -7)); diff --git a/gdb/testsuite/gdb.ada/finish-var-size.exp b/gdb/testsuite/gdb.ada/finish-var-size.exp index e038ed4..ae30086 100644 --- a/gdb/testsuite/gdb.ada/finish-var-size.exp +++ b/gdb/testsuite/gdb.ada/finish-var-size.exp @@ -22,7 +22,13 @@ require {expr [gcc_major_version] >= 12} standard_ada_testfile p -if {[gdb_compile_ada "${srcfile}" "${binfile}" executable debug] != ""} { +set opts {} +lappend opts debug +if { [have_fvar_tracking] } { + lappend opts additional_flags=-fvar-tracking +} + +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $opts] != ""} { return -1 } diff --git a/gdb/testsuite/gdb.ada/negative-bit-offset.exp b/gdb/testsuite/gdb.ada/negative-bit-offset.exp new file mode 100644 index 0000000..c5fcae1 --- /dev/null +++ b/gdb/testsuite/gdb.ada/negative-bit-offset.exp @@ -0,0 +1,36 @@ +# Copyright 2025 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/>. + +# Test negative DW_AT_bit_offset. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile prog + +# This particular output is only generated with -gdwarf-4. +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable \ + {debug additional_flags=-gdwarf-4}] != ""} { + return +} + +clean_restart ${testfile} + +set bp_location [gdb_get_line_number "STOP" ${testdir}/prog.adb] +runto "prog.adb:$bp_location" + +gdb_test "print xp" \ + [string_to_regexp "(x => 21, y => (-1, -2, -3, -4, -5, -6, -7, -8, -9, -10))"] diff --git a/gdb/testsuite/gdb.ada/negative-bit-offset/prog.adb b/gdb/testsuite/gdb.ada/negative-bit-offset/prog.adb new file mode 100644 index 0000000..e3c1775 --- /dev/null +++ b/gdb/testsuite/gdb.ada/negative-bit-offset/prog.adb @@ -0,0 +1,36 @@ +-- Copyright 2025 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/>. + +procedure Prog is + + type Small is range -32 .. 31; + for Small'Size use 6; + + type SomeArray is array (POSITIVE range <>) of Small; + + type SomePackedArray is array (POSITIVE range <>) of Small; + pragma Pack (SomePackedArray); + + type SomePackedRecord is record + X: Small; + Y: SomePackedArray (1 .. 10); + end record; + pragma Pack (SomePackedRecord); + + XP: SomePackedRecord := (21, (-1, -2, -3, -4, -5, -6, -7, -8, -9, -10)); + +begin + null; -- STOP +end; diff --git a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp index dcee040..5663b0d 100644 --- a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp +++ b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp @@ -58,7 +58,7 @@ gdb_test "starti" \ [multi_line \ "warning: watchpoint $num downgraded to software watchpoint" \ "" \ - "Program stopped\\." \ + "(Program|Thread \[^\r\n\]) stopped\\." \ ".*"] # Watchpoint should now have downgraded to a s/w watchpoint. diff --git a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c new file mode 100644 index 0000000..f55cee5 --- /dev/null +++ b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c @@ -0,0 +1,27 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +#include <alloca.h> + +int +main (int argc, char **argv) +{ + volatile __attribute__ ((__aligned__ (64))) int a; + volatile char *p = (char *) alloca (argc * 12); + p[2] = 'b'; + return 1; +} diff --git a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp index eb93127..06285ce 100644 --- a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp +++ b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp @@ -19,41 +19,65 @@ # This option places an `endbr32`/`endbr64` instruction at the start of # all functions, which can interfere with prologue analysis. -standard_testfile .c -set binfile ${binfile} +standard_testfile .c -stackalign.c require {is_any_target x86_64-*-* i?86-*-*} - require supports_fcf_protection -set opts {debug additional_flags=-fcf-protection=full} +# Tests if breakpoint set on main is placed past main's entry. +proc test_run {} { + # Get start address of function main. + set main_addr [get_integer_valueof &main -1] + gdb_assert {$main_addr != -1} -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $opts] != "" } { - untested "failed to compile" - return -} + set bp_addr -1 -clean_restart ${binfile} + # Put breakpoint on main, get the address where the breakpoint was installed. + gdb_test_multiple "break -q main" "break on main, get address" { + -re -wrap "Breakpoint $::decimal at ($::hex).*" { + set bp_addr $expect_out(1,string) -# Get start address of function main. -set main_addr [get_integer_valueof &main -1] -gdb_assert {$main_addr != -1} + # Convert to decimal. + set bp_addr [expr $bp_addr] -set bp_addr -1 + pass $gdb_test_name + } + } -# Put breakpoint on main, get the address where the breakpoint was installed. -gdb_test_multiple "break -q main" "break on main, get address" { - -re -wrap "Breakpoint $decimal at ($hex).*" { - set bp_addr $expect_out(1,string) + # Make sure some prologue was skipped. + gdb_assert {$bp_addr != -1 && $bp_addr > $main_addr} \ + "breakpoint placed past main's entry" +} - # Convert to decimal. - set bp_addr [expr $bp_addr] +with_test_prefix "skip-cf-protection" { + set opts {debug additional_flags=-fcf-protection=full} - pass $gdb_test_name + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + $opts] != "" } { + untested "failed to compile" + return } + + clean_restart ${binfile} + + test_run } -if { $bp_addr != -1 } { - # Make sure some prologue was skipped. - gdb_assert {$bp_addr > $main_addr} +# Now, make sure that the prologue analysis does not end up at function's entry +# when stack alignment sequence is generated right after 'endbr64'/'endbr32'. +# That could happen if GDB handled those incorrectly - there was a bug that +# checked for those two in incorrect order, which caused such issue. +with_test_prefix "skip-cf-protection-stackalign" { + # gcc is easier to make it produce the sequence of interest. + if { ![is_c_compiler_gcc] } { + unsupported "stackalign test part requires gcc compiler" + return + } + + if { [prepare_for_testing "failed to prepare" "${testfile}-stackalign" \ + $srcfile2 [list optimize=-O0 additional_flags=-fcf-protection=full]] } { + return + } + + test_run } diff --git a/gdb/testsuite/gdb.base/attach-deleted-exec.exp b/gdb/testsuite/gdb.base/attach-deleted-exec.exp index 82a7bb4..45fac0d 100644 --- a/gdb/testsuite/gdb.base/attach-deleted-exec.exp +++ b/gdb/testsuite/gdb.base/attach-deleted-exec.exp @@ -67,5 +67,49 @@ if { [regexp $re_nfs $filename] } { gdb_assert { [string equal $filename /proc/${testpid}/exe] } $test } +# Restart GDB. +clean_restart + +# Setup an empty sysroot. GDB will fail to find the executable within +# the sysroot. Additionally, the presence of a sysroot should prevent +# GDB from trying to load the executable from /proc/PID/exe. +set sysroot [standard_output_file "sysroot"] +gdb_test_no_output "set sysroot $sysroot" \ + "setup sysroot" + +# Attach to the inferior. GDB should complain about failing to find +# the executable. It is the name of the executable that GDB doesn't +# find that we're interesting in here. For native targets GDB should +# be looking for BINFILE, not /proc/PID/exe. +# +# For extended-remote targets things are unfortunately harder. Native +# GDB looks for BINFILE because it understands that GDB will be +# looking in the sysroot. But remote GDB doesn't know if GDB is using +# a sysroot or not. As such, gdbserver will return /proc/PID/exe if +# it knows that the file has been deleted locally. This isn't great +# if GDB then plans to look in a sysroot, but equally, if the remote +# file has been deleted, then the name GDB will return, will have had +# " (deleted" appended, so we're unlikely to get a hit in the sysroot +# either way. +if { [target_info gdb_protocol] == "extended-remote" } { + set filename_re "/proc/$testpid/exe" +} else { + set filename_re "\[^\r\n\]+/${testfile} \\(deleted\\)" +} + +verbose -log "APB: warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." + +gdb_test "attach $testpid" \ + [multi_line \ + "Attaching to process $decimal" \ + "warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." \ + ".*"] \ + "attach to inferior" + +# Check GDB hasn't managed to load an executable. +gdb_test "info inferior" \ + "\\*\[^)\]+\\)\\s*" \ + "confirm no executable is loaded." + # Cleanup. kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.base/bp-cond-failure.exp b/gdb/testsuite/gdb.base/bp-cond-failure.exp index d645454..4d03e7b 100644 --- a/gdb/testsuite/gdb.base/bp-cond-failure.exp +++ b/gdb/testsuite/gdb.base/bp-cond-failure.exp @@ -75,7 +75,7 @@ proc run_test { cond_eval access_type bpexpr nloc } { "Error in testing condition for breakpoint ${bp_num}.2:" \ "Cannot access memory at address 0x0" \ "" \ - "Breakpoint ${bp_num}.2, foo \\(c=49 ...\\) at \[^\r\n\]+:\[0-9\]+" \ + "(Thread \[^\r\n\]+ hit )?Breakpoint ${bp_num}.2, foo \\(c=49 ...\\) at \[^\r\n\]+:\[0-9\]+" \ "${::decimal}\\s+\[^\r\n\]+ breakpoint here\\. \[^\r\n\]+"] } else { gdb_test "continue" \ @@ -84,7 +84,7 @@ proc run_test { cond_eval access_type bpexpr nloc } { "Error in testing condition for breakpoint ${bp_num}:" \ "Cannot access memory at address 0x0" \ "" \ - "Breakpoint ${bp_num}, bar \\(\\) at \[^\r\n\]+:\[0-9\]+" \ + "(Thread \[^\r\n\]+ hit )?Breakpoint ${bp_num}, bar \\(\\) at \[^\r\n\]+:\[0-9\]+" \ "${::decimal}\\s+\[^\r\n\]+ breakpoint here\\. \[^\r\n\]+"] } } diff --git a/gdb/testsuite/gdb.base/bp-permanent.c b/gdb/testsuite/gdb.base/bp-permanent.c index d586acc..72e5e8a 100644 --- a/gdb/testsuite/gdb.base/bp-permanent.c +++ b/gdb/testsuite/gdb.base/bp-permanent.c @@ -101,7 +101,7 @@ test_signal_no_handler (void) } static void -test_signal_nested_handler () +test_signal_nested_handler (int sig) { test (); } diff --git a/gdb/testsuite/gdb.base/bp-permanent.exp b/gdb/testsuite/gdb.base/bp-permanent.exp index 62ce3f6..c6c6269 100644 --- a/gdb/testsuite/gdb.base/bp-permanent.exp +++ b/gdb/testsuite/gdb.base/bp-permanent.exp @@ -134,7 +134,7 @@ proc test {always_inserted sw_watchpoint} { unsupported "failed to stop at permanent breakpoint" return } - -re "Program received signal SIGTRAP.*$gdb_prompt $" { + -re "received signal SIGTRAP.*$gdb_prompt $" { pass $test } } @@ -174,7 +174,7 @@ proc test {always_inserted sw_watchpoint} { # disabled, it should act as if we hadn't created it in the first # place. IOW, we should get a random signal, and, the breakpoint's # command should not run. - gdb_test "continue" "Program received signal SIGTRAP.*" \ + gdb_test "continue" "received signal SIGTRAP.*" \ "disabled permanent breakpoint doesn't explain stop" gdb_test "info breakpoints" \ diff --git a/gdb/testsuite/gdb.base/break-dbg.cc b/gdb/testsuite/gdb.base/break-dbg.cc new file mode 100644 index 0000000..642ded0 --- /dev/null +++ b/gdb/testsuite/gdb.base/break-dbg.cc @@ -0,0 +1,31 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +volatile int global_var = 0; + +int +foo () +{ + return global_var; +} + +int +main () +{ + int res = foo (); + return res; +} diff --git a/gdb/testsuite/gdb.base/break-dbg.exp b/gdb/testsuite/gdb.base/break-dbg.exp new file mode 100644 index 0000000..3652b8e --- /dev/null +++ b/gdb/testsuite/gdb.base/break-dbg.exp @@ -0,0 +1,70 @@ +# Copyright 2025 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/>. */ + +# Some basic testing of 'set debug breakpoint on'. At one point a bug +# meant that some breakpoints would immediately trigger a segfault if +# GDB tried to run with breakpoint debugging turned on. +# +# Test is compiled as C++ only so 'catch catch/throw/rethrow' have a +# something to do. The original bug would trigger for any 'catch' +# style breakpoint, so C++ isn't really a hard requirement. + +standard_testfile .cc + +require allow_cplus_tests + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug c++}] } { + return +} + +if {![runto_main]} { + return +} + +gdb_test "catch catch" "^Catchpoint $decimal \\(catch\\)" +gdb_test "catch throw" "^Catchpoint $decimal \\(throw\\)" +gdb_test "catch rethrow" "^Catchpoint $decimal \\(rethrow\\)" + +gdb_test "catch exec" "^Catchpoint $decimal \\(exec\\)" +gdb_test "catch fork" "^Catchpoint $decimal \\(fork\\)" +gdb_test "catch vfork" "^Catchpoint $decimal \\(vfork\\)" + +gdb_test "catch load" "^Catchpoint $decimal \\(load\\)" +gdb_test "catch unload" "^Catchpoint $decimal \\(unload\\)" + +gdb_test "catch signal" "^Catchpoint $decimal \\(standard signals\\)" +gdb_test "catch syscall" "^Catchpoint $decimal \\(any syscall\\)" + +gdb_test "watch -l global_var" "\[Ww]atchpoint $decimal: -location global_var" + +gdb_test_no_output "set debug breakpoint on" + +set saw_bp_debug_line false +gdb_test_multiple "step" "" { + -re "^step\r\n" { + exp_continue + } + -re "^\\\[breakpoint\\\] \[^\r\n\]+\r\n" { + set saw_bp_debug_line true + exp_continue + } + -re "^$gdb_prompt $" { + gdb_assert { $saw_bp_debug_line } $gdb_test_name + } + -re "^\[^\r\n\]*\r\n" { + exp_continue + } +} diff --git a/gdb/testsuite/gdb.base/catch-fork-kill.exp b/gdb/testsuite/gdb.base/catch-fork-kill.exp index 0fd853b..224a8df 100644 --- a/gdb/testsuite/gdb.base/catch-fork-kill.exp +++ b/gdb/testsuite/gdb.base/catch-fork-kill.exp @@ -32,6 +32,8 @@ standard_testfile +require allow_fork_tests + # Build two programs -- one for fork, and another for vfork. set testfile_fork "${testfile}-fork" set testfile_vfork "${testfile}-vfork" diff --git a/gdb/testsuite/gdb.base/catch-fork-static.exp b/gdb/testsuite/gdb.base/catch-fork-static.exp index b171a6d..9d50d5d 100644 --- a/gdb/testsuite/gdb.base/catch-fork-static.exp +++ b/gdb/testsuite/gdb.base/catch-fork-static.exp @@ -21,9 +21,7 @@ # ld.so probes before reaching main, and ptrace flags were set then. But a # static executable would just keep running and never catch the fork. -# Until "catch fork" is implemented on other targets... -# -require {is_any_target "*-*-linux*" "*-*-openbsd*"} +require allow_fork_tests # Reusing foll-fork.c since it's a simple forking program. standard_testfile foll-fork.c diff --git a/gdb/testsuite/gdb.base/catch-signal-fork.exp b/gdb/testsuite/gdb.base/catch-signal-fork.exp index dea0cc4..2a33ee1 100644 --- a/gdb/testsuite/gdb.base/catch-signal-fork.exp +++ b/gdb/testsuite/gdb.base/catch-signal-fork.exp @@ -14,6 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. require {!target_info exists gdb,nosignals} +require allow_fork_tests standard_testfile diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c b/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c new file mode 100644 index 0000000..58fdec6 --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id-lib.c @@ -0,0 +1,522 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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 file contains a library that can be preloaded into GDB on Linux + using the LD_PRELOAD technique. + + The library intercepts calls to OPEN, CLOSE, READ, and PREAD in order to + fake the inode number of a shared memory mapping. + + When GDB creates a core file (e.g. with the 'gcore' command), then + shared memory mappings should be included in the generated core file. + + The 'id' for the shared memory mapping shares the inode slot in the + /proc/PID/smaps file, which is what GDB consults to decide which + mappings should be included in the core file. + + It is possible for a shared memory mapping to have an 'id' of zero. + + At one point there was a bug in GDB where mappings with an inode of zero + would not be included in the generated core file. This meant that most + shared memory mappings would be included in the generated core file, + but, if a shared memory mapping happened to get an 'id' of zero, then, + because this would appear as a zero inode in the smaps file, this shared + memory mapping would be excluded from the generated core file. + + This preload library spots when GDB opens a /proc/PID/smaps file and + immediately copies the contents of this file into an internal buffer. + The buffer is then scanned looking for a shared memory mapping, and, if + a shared memory mapping is found, its 'id' (in the inode position) is + changed to zero. + + Calls to read/pread are intercepted, and attempts to read from the smaps + file are then served from the modified buffer contents. + + The close calls are monitored and, when the smaps file is closed, the + internal buffer is released. + + This works with GDB (currently) because the requirements for access to + the smaps file are pretty simple. GDB opens the file and grabs the + entire contents with a single pread call and a large buffer. There's no + seeking within the file or anything like that. + + The intention is that this library is preloaded into a GDB session which + is then used to start an inferior and generate a core file. GDB will + then see the zero inode for the shared memory mapping and should, if the + bug is correctly fixed, still add the shared memory mapping to the + generated core file. */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <stdbool.h> +#include <assert.h> + +/* Logging. */ + +static void +log_msg (const char *fmt, ...) +{ +#ifdef LOGGING + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); +#endif /* LOGGING */ +} + +/* Error handling, message and exit. */ + +static void +error (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + + exit (EXIT_FAILURE); +} + +/* The type of the open() function. */ +typedef int (*open_func_type)(const char *pathname, int flags, ...); + +/* The type of the close() function. */ +typedef int (*close_func_type)(int fd); + +/* The type of the read() function. */ +typedef ssize_t (*read_func_type)(int fd, void *buf, size_t count); + +/* The type of the pread() function. */ +typedef ssize_t (*pread_func_type) (int fd, void *buf, size_t count, off_t offset); + +/* Structure that holds information about a /proc/PID/smaps file that has + been opened. */ +struct interesting_file +{ + /* The file descriptor for the opened file. */ + int fd; + + /* The read offset within the file. Set to zero when the file is + opened. Any 'read' calls will update this offset. */ + size_t offset; + + /* The size of the contents within the buffer. This is not the total + buffer size (which might be larger). Attempts to read beyond SIZE + indicate an attempt to read beyond the end of the file. */ + size_t size; + + /* The (possibly modified) contents of the file. */ + char *content; +}; + +/* We only track a single interesting file. Currently, for the use case + we imagine, GDB will only ever open one /proc/PID/smaps file at once. */ +struct interesting_file the_file = { -1, 0, 0, NULL }; + +/* Update the contents of the global THE_FILE buffer. It is assumed that + the file contents have already been loaded into THE_FILE's content + buffer. + + Look for any lines that represent a shared memory mapping and modify + the inode field (which holds the shared memory id) to be zero. */ +static void +update_file_content_buffer (void) +{ + assert (the_file.content != NULL); + + char *start = the_file.content; + do + { + /* Every line, even the last one, ends with a newline. */ + char *end = strchrnul (start, '\n'); + assert (end != NULL); + assert (*end != '\0'); + + /* Attribute lines start with an uppercase letter. The lines we want + to modify should start with a lower case hex character, + i.e. [0-9a-f]. Also, every line that we want to consider should + be long enough, but just in case, check the longest possible + filename that we care about. */ + if (isxdigit (*start) && (isdigit (*start) || islower (*start)) + && (end - start) > 23) + { + /* There are two possible filenames that we look for: + /SYSV%08x + /SYSV%08x (deleted) + The END pointer is pointing to the first character after the + filename. + + Setup OFFSET to be the offset from END to the start of the + filename. As we check the filename we set OFFSET to 0 if the + filename doesn't match one of the expected patterns. */ + size_t offset; + if (strncmp ((end - 13), "/SYSV", 5) == 0) + offset = 13; + else if (strncmp ((end - 23), "/SYSV", 5) == 0) + { + if (strncmp ((end - 10), " (deleted)", 10) == 0) + offset = 23; + else + offset = 0; + } + else + offset = 0; + + for (int i = 0; i < 8 && offset != 0; ++i) + { + if (!isdigit (*(end - offset + 5 + i))) + offset = 0; + } + + /* If OFFSET is non-zero then the filename on this line looks + like a shared memory mapping, and OFFSET is the offset from + END to the first character of the filename. */ + if (offset != 0) + { + log_msg ("[LD_PRELOAD] shared memory entry: %.*s\n", + offset, (end - offset)); + + /* Set PTR to the first character before the filename. This + should be a white space character. */ + char *ptr = end - offset - 1; + assert (isspace (*ptr)); + + /* Walk backwards until we find the inode field. */ + while (isspace (*ptr)) + --ptr; + + /* Now replace every character in the inode field, except the + first one, with a space character. */ + while (!isspace (*(ptr - 1))) + { + assert (isdigit (*ptr)); + *ptr = ' '; + --ptr; + } + + /* Replace the first character with '0'. */ + assert (isdigit (*ptr)); + *ptr = '0'; + + /* This print is checked for from GDB. */ + printf ("[LD_PRELOAD] updated a shared memory mapping\n"); + } + } + + /* Update START to point to the next line. The last line of the + file will be empty. */ + assert (*end == '\n'); + start = end; + while (*start == '\n') + ++start; + } + while (*start != '\0'); +} + +/* Return true if PATHNAME has for form "/proc/PID/smaps" (without the + quotes). Otherwise, return false. */ + +static bool +is_smaps_file (const char *pathname) +{ + if (strncmp (pathname, "/proc/", 6) == 0) + { + int idx = 6; + while (isdigit (pathname[idx])) + idx++; + if (idx > 6 && strcmp (&pathname[idx], "/smaps") == 0) + return true; + } + + return false; +} + +/* Return true if PATHNAME should be considered interesting. PATHNAME is + interesting if it has the form /proc/PID/smaps, and there is no + interesting file already opened. */ + +static bool +is_interesting_pathname (const char *pathname) +{ + return the_file.fd == -1 && is_smaps_file (pathname); +} + +/* Read the contents of an interesting file from FD (and open file + descriptor) into the global THE_FILE variable, making the file FD the + current interesting file. There should be no already open interesting + file when this function is called. + + The contents of the file FD are read into a memory buffer and updated so + that any shared memory mappings listed within FD (which will be an smaps + file) will have the id zero. */ + +static void +read_interesting_file_contents (int fd) +{ +#define BLOCK_SIZE 1024 + /* Slurp contents into a local buffer. */ + size_t buffer_size = 1024; + size_t offset = 0; + + assert (the_file.size == 0); + assert (the_file.content == NULL); + assert (the_file.fd == -1); + assert (the_file.offset == 0); + + do + { + the_file.content = (char *) realloc (the_file.content, buffer_size); + if (the_file.content == NULL) + error ("[LD_PRELOAD] Failed allocating memory: %s\n", strerror (errno)); + + ssize_t bytes_read = read (fd, the_file.content + offset, BLOCK_SIZE); + if (bytes_read == -1) + error ("[LD_PRELOAD] Failed reading file: %s\n", strerror (errno)); + + the_file.size += bytes_read; + + if (bytes_read < BLOCK_SIZE) + break; + + offset += BLOCK_SIZE; + buffer_size += BLOCK_SIZE; + } + while (true); + + /* Add a null terminator. This makes the update easier. We know + there will be space because we only break out of the loop above + when the last read returns less than BLOCK_SIZE bytes. This means + we allocated an extra BLOCK_SIZE bytes, but didn't fill them all. + This means there must be at least 1 byte available for the null. */ + the_file.content[the_file.size] = '\0'; + + /* Reset the seek pointer. */ + if (lseek (fd, 0, SEEK_SET) == (off_t) -1) + error ("[LD_PRELOAD] Failed to lseek in file: %s\n", strerror (errno)); + + /* Record the file descriptor, this is used in read, pread, and close + in order to spot when we need to intercept the call. */ + the_file.fd = fd; + + update_file_content_buffer (); +#undef BLOCK_SIZE +} + +/* Intercept calls to 'open'. If this is an attempt to open a + /proc/PID/smaps file then intercept it, load the file contents into a + buffer and update the file contents. For all other open requests, just + forward to the real open function. */ +int +open (const char *pathname, int flags, ...) +{ + /* Pointer to the real open function. */ + static open_func_type real_open = NULL; + + /* Mode is only used if the O_CREAT flag is set in FLAGS. */ + mode_t mode = 0; + + /* Set true if this is a /proc/PID/smaps file. */ + bool is_interesting = is_interesting_pathname (pathname); + + /* Check if O_CREAT is in flags. If it is, get the mode. */ + if (flags & O_CREAT) + { + va_list args; + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + } + + /* Debug. */ + if (is_interesting) + log_msg ("[LD_PRELOAD] Opening file: %s\n", pathname); + + /* Make sure we have a pointer to the real open() function. */ + if (real_open == NULL) + { + /* Get the address of the real open() function. */ + real_open = (open_func_type) dlsym (RTLD_NEXT, "open"); + if (real_open == NULL) + error ("[LD_PRELOAD] dlsym() error for 'open': %s\n", dlerror ()); + } + + /* Call the original open() function with the provided arguments. */ + int res = -1; + if (flags & O_CREAT) + res = real_open (pathname, flags, mode); + else + res = real_open (pathname, flags); + + if (res != -1 && is_interesting) + read_interesting_file_contents (res); + + return res; +} + +/* Like above, but for open64. */ + +int +open64 (const char *pathname, int flags, ...) +{ + /* Pointer to the real open64 function. */ + static open_func_type real_open64 = NULL; + + /* Mode is only used if the O_CREAT flag is set in FLAGS. */ + mode_t mode = 0; + + /* Set true if this is a /proc/PID/smaps file. */ + bool is_interesting = is_interesting_pathname (pathname); + + /* Check if O_CREAT is in flags. If it is, get the mode. */ + if (flags & O_CREAT) + { + va_list args; + va_start (args, flags); + mode = va_arg (args, mode_t); + va_end (args); + } + + /* Debug. */ + if (is_interesting) + log_msg ("[LD_PRELOAD] Opening file: %s\n", pathname); + + /* Make sure we have a pointer to the real open64() function. */ + if (real_open64 == NULL) + { + /* Get the address of the real open64() function. */ + real_open64 = (open_func_type) dlsym (RTLD_NEXT, "open64"); + if (real_open64 == NULL) + error ("[LD_PRELOAD] dlsym() error for 'open64': %s\n", dlerror ()); + } + + /* Call the original open64() function with the provided arguments. */ + int res = -1; + if (flags & O_CREAT) + res = real_open64 (pathname, flags, mode); + else + res = real_open64 (pathname, flags); + + if (res != -1 && is_interesting) + read_interesting_file_contents (res); + + return res; +} + +/* Intercept the 'close' function. If this is a previously opened + interesting file then clean up. Otherwise, forward to the normal close + function. */ +int +close (int fd) +{ + static close_func_type real_close = NULL; + + if (fd == the_file.fd) + { + the_file.fd = -1; + free (the_file.content); + the_file.content = NULL; + the_file.offset = 0; + the_file.size = 0; + log_msg ("[LD_PRELOAD] Closing file.\n"); + } + + /* Make sure we have a pointer to the real open() function. */ + if (real_close == NULL) + { + /* Get the address of the real open() function. */ + real_close = (close_func_type) dlsym (RTLD_NEXT, "close"); + if (real_close == NULL) + error ("[LD_PRELOAD] dlsym() error for 'close': %s\n", dlerror ()); + } + + return real_close (fd); +} + +/* Intercept 'pread' calls. If this is a pread from a previously opened + interesting file, then read from the in memory buffer. Otherwise, + forward to the real pread function. */ +ssize_t +pread (int fd, void *buf, size_t count, off_t offset) +{ + static pread_func_type real_pread = NULL; + + if (fd == the_file.fd) + { + size_t max; + + if (offset > the_file.size) + max = 0; + else + max = the_file.size - offset; + if (count > max) + count = max; + + memcpy (buf, the_file.content + offset, count); + log_msg ("[LD_PRELOAD] Read from file.\n"); + return count; + } + + if (real_pread == NULL) + { + /* Get the address of the real read() function. */ + real_pread = (pread_func_type) dlsym (RTLD_NEXT, "pread"); + if (real_pread == NULL) + error ("[LD_PRELOAD] dlsym() error for 'pread': %s\n", dlerror ()); + } + + return real_pread (fd, buf, count, offset); +} + +/* Intercept 'read' calls. If this is a read from a previously opened + interesting file, then read from the in memory buffer. Otherwise, + forward to the real read function. */ +ssize_t +read (int fd, void *buf, size_t count) +{ + static read_func_type real_read = NULL; + + if (fd == the_file.fd) + { + ssize_t bytes_read = pread (fd, buf, count, the_file.offset); + if (bytes_read > 0) + the_file.offset += bytes_read; + return bytes_read; + } + + if (real_read == NULL) + { + /* Get the address of the real read() function. */ + real_read = (read_func_type) dlsym (RTLD_NEXT, "read"); + if (real_read == NULL) + error ("[LD_PRELOAD] dlsym() error for 'read': %s\n", dlerror ()); + } + + return real_read (fd, buf, count); +} diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c new file mode 100644 index 0000000..92d2edf --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.c @@ -0,0 +1,63 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +#include <sys/ipc.h> +#include <sys/shm.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <time.h> + +void +breakpt (void) +{ + /* Nothing. */ +} + +int +main (void) +{ + /* Create a shared memory mapping. */ + int sid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | IPC_EXCL | 0777); + if (sid == -1) + { + perror ("shmget"); + exit (1); + } + + /* Attach the shared memory mapping. */ + void *addr = shmat (sid, NULL, SHM_RND); + if (addr == (void *) -1L) + { + perror ("shmat"); + exit (1); + } + + breakpt (); + + /* Mark the shared memory mapping as deleted -- once the last user + has finished with it. */ + if (shmctl (sid, IPC_RMID, NULL) != 0) + { + perror ("shmctl"); + exit (1); + } + + return 0; +} diff --git a/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp new file mode 100644 index 0000000..57c665e --- /dev/null +++ b/gdb/testsuite/gdb.base/corefile-shmem-zero-id.exp @@ -0,0 +1,228 @@ +# Copyright 2025 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 test script tries to check GDB's ability to create a core file +# (e.g. with 'gcore' command) when there's a shared memory mapping +# with the id zero. +# +# Testing this case is hard. Older kernels don't even seem to give +# out the shared memory id zero. And on new kernels you still cannot +# guarantee to grab the zero id for testing; the id might be in use by +# some other process, or the kernel might just not give out that id +# for some other reason. +# +# To figure out which mappings to include in the core file, GDB reads +# the /proc/PID/smaps file. There is a field in this file which for +# file backed mappings, holds the inode of the file. But for shared +# memory mappings this field holds the shared memory id. The problem +# was that GDB would ignore any entry in /proc/PID/smaps with an inode +# entry of zero, which would catch the shared memory mapping with a +# zero id. +# +# There was an attempt to write a test which spammed out requests for +# shared memory mappings and tried to find the one with id zero, but +# this was still really unreliable. +# +# This test takes a different approach. We compile a library which we +# preload into the GDB process. This library intercepts calls to +# open, close, read, and pread, and watches for an attempt to open the +# /proc/PID/smaps file. +# +# When we see that file being opened, we copy the file contents into a +# memory buffer and modify the buffer so that the inode field for any +# shared memory mappings is set to zero. We then intercept calls to +# read and pread and return results from that in memory buffer. +# +# The test executable itself create a shared memory mapping (which +# might have any id). +# +# GDB, with the pre-load library in place, start the inferior and then +# uses the 'gcore' command to dump a core file. When GDB opens the +# smaps file and reads from it, the preload library ensures that GDB +# sees an inode of zero. +# + +# This test only works on Linux +require isnative +require {!is_remote host} +require {!is_remote target} +require {istarget *-linux*} +require gcore_cmd_available + +standard_testfile .c -lib.c + +set libfile ${testfile}-lib +set libobj [standard_output_file ${libfile}.so] + +# Compile the preload library. We only get away with this as we +# limit this test to running when ISNATIVE is true. +if { [build_executable "build preload lib" $libobj $srcfile2 \ + {debug shlib libs=-ldl}] == -1 } { + return +} + +# Now compile the inferior executable. +if {[build_executable "build executable" $testfile $srcfile] == -1} { + return +} + +# Spawn GDB with LIBOBJ preloaded using LD_PRELOAD. +save_vars { env(LD_PRELOAD) env(ASAN_OPTIONS) } { + if { ![info exists env(LD_PRELOAD) ] + || $env(LD_PRELOAD) == "" } { + set env(LD_PRELOAD) "$libobj" + } else { + append env(LD_PRELOAD) ":$libobj" + } + + # Prevent address sanitizer error: + # ASan runtime does not come first in initial library list; you should + # either link runtime to your application or manually preload it with + # LD_PRELOAD. + append_environment_default ASAN_OPTIONS verify_asan_link_order 0 + + clean_restart $binfile + + # Start GDB with the modified environment, this means that, when + # using remote targets, gdbserver will also use the preload + # library. + if {![runto_main]} { + return + } +} + +gdb_breakpoint breakpt +gdb_continue_to_breakpoint "run to breakpt" + +# Check the /proc/PID/smaps file itself. The call to 'cat' should +# inherit the preload library, so should see the modified file +# contents. Check that the shared memory mapping line has an id of +# zero. This confirms that the preload library is working. If the +# preload library breaks then we'll start seeing non-zero shared +# memory ids, which always worked, so we'd never know that this test +# is broken! +# +# This check ensures the test is working as expected. +set shmem_line_count 0 +set fixup_line_count 0 +set inf_pid [get_inferior_pid] +gdb_test_multiple "shell cat /proc/${inf_pid}/smaps" "check smaps" { + -re "^\\\[LD_PRELOAD\\\] updated a shared memory mapping\r\n" { + incr fixup_line_count + exp_continue + } + -re "^\[^\r\n\]+($decimal)\\s+/SYSV\[0-9\]{8}(?: \\(deleted\\))?\r\n" { + set id $expect_out(1,string) + if { $id == 0 } { + incr shmem_line_count + } + exp_continue + } + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $shmem_line_count == 1 } \ + "single shared memory mapping found" + gdb_assert { $fixup_line_count == 1 } \ + "single fixup line found" + } + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } +} + +# Now generate a core file. This will use the preload library to read +# the smaps file. The code below is copied from 'proc gdb_gcore_cmd', +# but we don't use that as we also look for a message that is printed +# by the LD_PRELOAD library. This is an extra level of check that the +# preload library is triggering when needed. +set corefile [standard_output_file ${testfile}.core] +set saw_ld_preload_msg false +set saw_saved_msg false +with_timeout_factor 3 { + gdb_test_multiple "gcore $corefile" "save core file" { + -re "^\\\[LD_PRELOAD\\\] updated a shared memory mapping\r\n" { + # GDB actually reads the smaps file multiple times when + # creating a core file, so we'll see multiple of these + # fixup lines. + set saw_ld_preload_msg true + exp_continue + } + -re "^Saved corefile \[^\r\n\]+\r\n" { + set saw_saved_msg true + exp_continue + } + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $saw_saved_msg } \ + "saw 'Saved corefile' message" + + # If we're using a remote target then the message from + # the preload library will go to gdbservers stdout, + # not GDB's, so don't check for it. + if { [gdb_protocol_is_native] } { + gdb_assert { $saw_ld_preload_msg } \ + "saw LD_PRELOAD message from library" + } + } + } + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } +} + +# Restart GDB. This time we are _not_ using the preload library. We +# no longer need it as we are only analysing the core file now. +clean_restart $binfile + +# Load the core file. +gdb_test "core-file $corefile" \ + "Program terminated with signal SIGTRAP, Trace/breakpoint trap\\..*" \ + "load core file" + +# Look through the mappings. We _should_ see the shared memory +# mapping. We _should_not_ see any of the special '[blah]' style +# mappings, e.g. [vdso], [vstack], [vsyscalls], etc. +set saw_special_mapping false +set saw_shmem_mapping false +gdb_test_multiple "info proc mappings" "" { + -re "\r\nStart Addr\[^\r\n\]+File\\s*\r\n" { + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\\s+\\\[\\S+\\\]\\s*\r\n" { + set saw_special_mapping true + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\\s+/SYSV\[0-9\]+ \\(deleted\\)\\s*\r\n" { + set saw_shmem_mapping true + exp_continue + } + + -re "^$hex\\s+$hex\\s+$hex\\s+$hex\[^\r\n\]*\r\n" { + exp_continue + } + + -re "^$gdb_prompt $" { + with_test_prefix $gdb_test_name { + gdb_assert { $saw_shmem_mapping } \ + "check shared memory mapping exists" + gdb_assert { !$saw_special_mapping } \ + "check no special mappings added" + } + } +} diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 3abd049..01e3cc1 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -253,10 +253,39 @@ gdb_test "h" "List of classes of commands:(\[^\r\n\]*\[\r\n\])+aliases -- User-d gdb_test "help" "List of classes of commands:(\[^\r\n\]*\[\r\n\])+aliases -- User-defined aliases of other commands(\[^\r\n\]*\[\r\n\])+breakpoints -- Making program stop at certain points(\[^\r\n\]*\[\r\n\])+data -- Examining data(\[^\r\n\]*\[\r\n\])+files -- Specifying and examining files(\[^\r\n\]*\[\r\n\])+obscure -- Obscure features(\[^\r\n\]*\[\r\n\])+running -- Running the program(\[^\r\n\]*\[\r\n\])+stack -- Examining the stack(\[^\r\n\]*\[\r\n\])+status -- Status inquiries(\[^\r\n\]*\[\r\n\])+support -- Support facilities(\[^\r\n\]*\[\r\n\])+user-defined -- User-defined commands(\[^\r\n\]*\[\r\n\])+Type \"help\" followed by a class name for a list of commands in that class.(\[^\r\n\]*\[\r\n\])+Type \"help\" followed by command name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." #test handle gdb_test "handle" "Argument required .signal to handle.*" -#test info "i" abbreviation -gdb_test "i" "List of \"info\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help info\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "info \"i\" abbreviation" + +proc test_info_command { command message } { + set saw_info_header 0 + set saw_help_info 0 + set saw_command_abbrev 0 + gdb_test_multiple $command $message -lbl { + -re "\r\nList of \"info\" subcommands:" { + verbose "Info header displayed" + set saw_info_header 1 + exp_continue + } + -re "Type \"help info\" followed by subcommand name for full documentation\\." { + verbose "Help info displayed" + set saw_help_info 1 + exp_continue + } + -re "\r\nCommand name abbreviations are allowed if unambiguous\\." { + verbose "Command name abbreviations displayed" + set saw_command_abbrev 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_info_header && $saw_help_info + && $saw_command_abbrev } $gdb_test_name + } + } +} + +#test info "i" abbreviation +test_info_command "i" "info \"i\" abbreviation" #test info -gdb_test "info" "List of \"info\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help info\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." +test_info_command "info" "info" + #test ignore gdb_test "ignore" "Argument required .a breakpoint number.*" #test info address @@ -379,38 +408,52 @@ gdb_test "info registers" "The program has no registers now." gdb_test "info s" "No stack." "info stack \"s\" abbreviation" #test info stack gdb_test "info stack" "No stack." -#test info set -# Test improved to check three parts: -# 1) confirm -# 2) prompt -# 3) write -# And only succeed if all three are matched. -# This should fix an old problem on native solaris 2.8, -# where this test fails due to this line: + +#test "info set" and "show" commands +# The test needs to match the "prompt: ..." part to fix an old problem on native +# Solaris 2.8, where this test fails due to this line: # prompt: Gdb's prompt is "(gdb) ".^M -set set_confirm_seen 0 -set set_prompt_seen 0 -gdb_test_multiple "info set" "info set" { - -re "confirm: Whether to confirm potentially dangerous operations is o\[a-z\]*.(\[^\r\n\]*\[\r\n\])+history filename: The filename in which to record the command history is (\[^\r\n\]*\[\r\n\])+listsize: Number of source lines gdb will list by default is 10" { - verbose "Confirm dislayed" - set set_confirm_seen 1 - exp_continue - } - -re "Gdb's prompt is \"$gdb_prompt \"" { - verbose "GDB prompt displayed" - set set_prompt_seen 1 - exp_continue - } - -re "Writing into executable.*$gdb_prompt $" { - verbose "write displayed" - if { $set_prompt_seen && $set_confirm_seen } { - pass "info set" - } else { - verbose "prompt $set_prompt_seen confirm $set_confirm_seen" - fail "info set (incomplete output)" +proc test_info_set_show { command } { + set set_confirm_seen 0 + set set_history_filename_seen 0 + set set_listsize_seen 0 + set set_prompt_seen 0 + set set_write_seen 0 + gdb_test_multiple $command $command -lbl { + -re "\r\nconfirm: Whether to confirm potentially dangerous operations is o\[a-z\]+\\." { + verbose "Confirm displayed" + set set_confirm_seen 1 + exp_continue + } + -re "\r\nhistory filename: The filename in which to record the command history is \[^\r\n\]+\\." { + verbose "History filename displayed" + set set_history_filename_seen 1 + exp_continue + } + -re "\r\nlistsize: Number of source lines gdb will list by default is 10\\." { + verbose "Listsize displayed" + set set_listsize_seen 1 + exp_continue + } + -re "\r\nprompt: Gdb's prompt is \"$::gdb_prompt \"" { + verbose "GDB prompt displayed" + set set_prompt_seen 1 + exp_continue + } + -re "write: Writing into executable and core files is o\[a-z\]+\\." { + verbose "Write displayed" + set set_write_seen 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $set_confirm_seen && $set_history_filename_seen + && $set_listsize_seen && $set_prompt_seen + && $set_write_seen } $gdb_test_name } } } +test_info_set_show "info set" + gdb_test "info symbol" "Argument required .address.." #test info source gdb_test "info source" "No current source file..*" @@ -591,12 +634,41 @@ gdb_test "set history" "List of \"set history\" subcommands:(\[^\r\n\]*\[\r\n\]) gdb_test "set language" "Requires an argument. Valid arguments are auto, local, unknown, ada, asm, c, c.., d, fortran, go, minimal, modula-2, objective-c, opencl, pascal, rust." #test set listsize gdb_test "set listsize" "Argument required .integer to set it to.*" + +proc test_set_print { command message } { + set saw_info_header 0 + set saw_help_info 0 + set saw_command_abbrev 0 + gdb_test_multiple $command $message -lbl { + -re "\r\nList of \"set print\" subcommands:" { + verbose "Info header displayed" + set saw_info_header 1 + exp_continue + } + -re "Type \"help set print\" followed by subcommand name for full documentation\\." { + verbose "Help info displayed" + set saw_help_info 1 + exp_continue + } + -re "\r\nCommand name abbreviations are allowed if unambiguous\\." { + verbose "Command name abbreviations displayed" + set saw_command_abbrev 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_info_header && $saw_help_info + && $saw_command_abbrev } $gdb_test_name + } + } +} + #test set print "p" abbreviation -gdb_test "set p" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set print \"p\" abbreviation" +test_set_print "set p" "set print \"p\" abbreviation" #test set print "pr" abbreviation -gdb_test "set pr" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." "set print \"pr\" abbreviation" +test_set_print "set pr" "set print \"pr\" abbreviation" #test set print -gdb_test "set print" "List of \"set print\" subcommands:(\[^\r\n\]*\[\r\n\])+Type \"help set print\" followed by subcommand name for full documentation.(\[^\r\n\]*\[\r\n\])+Command name abbreviations are allowed if unambiguous." +test_set_print "set print" "set print" + #test set print address gdb_test_no_output "set print address" "set print address" #test set print array @@ -700,7 +772,7 @@ set show_conv_list \ {$_shell_exitsignal = void} \ {$_shell_exitcode = 0} \ {$_active_linker_namespaces = 1} \ - {$_current_linker_namespace = <error: No registers.>}\ + {$_linker_namespace = <error: No registers.>}\ } if [allow_python_tests] { append show_conv_list \ @@ -741,12 +813,47 @@ gdb_test "show history" "history expansion: *History expansion on command input gdb_test "show language" "The current source language is \"auto; currently c\"." #test show listsize gdb_test "show listsize" "Number of source lines gdb will list by default is 10." + +proc test_show_print { command } { + set saw_print_address 0 + set saw_print_frame_args 0 + set saw_print_symbol 0 + set saw_print_vtbl 0 + gdb_test_multiple $command $command -lbl { + -re "\r\nprint address: Printing of addresses is o\[a-z\]+\\." { + verbose "Print address displayed" + set saw_print_address 1 + exp_continue + } + -re "\r\nprint frame-arguments: Printing of non-scalar frame arguments is \[^\r\n\]+\\." { + verbose "Print frame-arguments displayed" + set saw_print_frame_args 1 + exp_continue + } + -re "\r\nprint symbol: Printing of symbols when printing pointers is o\[a-z\]+\\." { + verbose "Print symbol displayed" + set saw_print_symbol 1 + exp_continue + } + -re "\r\nprint vtbl: Printing of C\\+\\+ virtual function tables is o\[a-z\]+\\." { + verbose "Print vtbl displayed" + set saw_print_vtbl 1 + exp_continue + } + -re -wrap "" { + gdb_assert { $saw_print_address && $saw_print_frame_args + && $saw_print_symbol && $saw_print_vtbl } $gdb_test_name + } + } +} + #test show print "p" abbreviation -gdb_test "show p" ".*" +test_show_print "show p" #test show print "pr" abbreviation -gdb_test "show pr" ".*" +test_show_print "show pr" #test show print -gdb_test "show print" ".*" +test_show_print "show print" + #test show paths gdb_test "show paths" "Executable and object file path:.*" #test show print address @@ -792,30 +899,10 @@ gdb_test "show width" "Number of characters gdb thinks are in a line is.*" #test show write # This is only supported on targets which use exec.o. gdb_test "show write" "Writing into executable and core files is o.*" + #test show -set show_confirm_seen 0 -set show_prompt_seen 0 -gdb_test_multiple "show" "show" { - -re "confirm: *Whether to confirm potentially dangerous operations is on.(\[^\r\n\]*\[\r\n\])+history filename: *The filename in which to record the command history is (\[^\r\n\]*\[\r\n\])+history save: *Saving of the history record on exit is on.(\[^\r\n\]*\[\r\n\])+history size: *The size of the command history is(\[^\r\n\]*\[\r\n\])+listsize: *Number of source lines gdb will list by default is 10(\[^\r\n]*\[\r\n\])+print elements: *Limit on string chars or array elements to print is 200." { - verbose "Confirm displayed" - set show_confirm_seen 1 - exp_continue - } - -re "Gdb's prompt is \"$gdb_prompt \"" { - verbose "GDB prompt displayed" - set show_prompt_seen 1 - exp_continue - } - -re "Writing into executable.*$gdb_prompt $" { - verbose "write displayed" - if { $show_prompt_seen && $show_confirm_seen } { - pass "show" - } else { - verbose "prompt $show_prompt_seen confirm $show_confirm_seen" - fail "show (incomplete output)" - } - } -} +test_info_set_show "show" + #history saving should stay disabled gdb_test_no_output "set history save off" "set history save off" #test stepi "si" abbreviation diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp index 8f52199..4d3e8eb 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -112,7 +112,7 @@ proc_with_prefix test_conv_vars {} { gdb_test "print \$_active_linker_namespaces" "1" \ "1 namespace before starting inferior" - gdb_test "print \$_current_linker_namespace" "No registers." \ + gdb_test "print \$_linker_namespace" "No registers." \ "No current namespace before starting inferior" if { ![runto_main] } { @@ -121,7 +121,7 @@ proc_with_prefix test_conv_vars {} { gdb_test "print \$_active_linker_namespaces" "1" \ "Before activating namespaces" - gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[0\\\]\\\]\"" \ + gdb_test "print \$_linker_namespace" ".* = 0" \ "Still in the default namespace" gdb_breakpoint "inc" allow-pending @@ -130,10 +130,16 @@ proc_with_prefix test_conv_vars {} { foreach_with_prefix dl {3 2 1} { gdb_continue_to_breakpoint "inc" - gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[$dl\\\]\\\]\"" \ + gdb_test "print \$_linker_namespace" ".* = $dl" \ "Verify we're in namespace $dl" } + # Check that we display the namespace of the selected + # frame, not the lowermost one. + gdb_test "up" "\#1.*in main.*" + gdb_test "print \$_linker_namespace" ".* = 0" \ + "print namespace of selected frame" + gdb_continue_to_breakpoint "first dlclose" gdb_test "print \$_active_linker_namespaces" "4" "all SOs loaded" @@ -154,7 +160,7 @@ proc_with_prefix test_conv_vars {} { # breakpoints and pending breakpoints at the same time with # gdb_breakpoint. gdb_test "next" ".*assert.*" "load the first SO" - gdb_breakpoint "inc if \$_streq(\$_current_linker_namespace, \"\[\[2\]\]\")" + gdb_breakpoint "inc if \$_linker_namespace == 2" gdb_continue_to_breakpoint "inc" gdb_continue_to_end "" continue 1 } @@ -163,6 +169,12 @@ proc_with_prefix test_conv_vars {} { proc test_info_linker_namespaces {} { clean_restart $::binfile + # Check that "info linker-namespaces" while the inferior is not running + # doesn't crash. + gdb_test "info linker-namespaces" \ + "Current inferior does not support linker namespaces\\. Use \"info sharedlibrary\" instead\\." \ + "info linker-namespaces before running" + if { ![runto_main] } { return } diff --git a/gdb/testsuite/gdb.base/filename-completion.exp b/gdb/testsuite/gdb.base/filename-completion.exp index cb3fc90..c10941c 100644 --- a/gdb/testsuite/gdb.base/filename-completion.exp +++ b/gdb/testsuite/gdb.base/filename-completion.exp @@ -385,7 +385,7 @@ proc run_quoting_and_escaping_tests { root } { remove-symbol-file \ "target core" "target exec" "target tfile" \ "maint print c-tdesc" "save gdb-index" - "save gdb-index -dwarf-5" } + "save gdb-index -dwarf-5" "shell ls"} if { [allow_compile_tests] } { lappend all_cmds "compile file" } @@ -408,6 +408,31 @@ proc run_quoting_and_escaping_tests { root } { run_quoting_and_escaping_tests_1 $root $cmd } } + + # Some additional testing of shell command. Test 'shell' and '!' + # when there are multiple filenames on the command line. This + # focuses on completion of the final filename. There is also some + # testing of the shell command above, this tests completion within + # the line. + foreach_with_prefix shell_cmd { "shell " "!" "pipe print 1 | " } { + foreach suffix { "aaa/aa bb" "bb2/dir 1/unique file" } { + set dir $root/$suffix + + regsub -all " " "$dir" "\\ " dir_with_backslash + + with_test_prefix "suffix='$suffix'" { + with_test_prefix "with_backslash" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls $dir_with_backslash" + } + with_test_prefix "with double quotes" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls \"$dir\"" + } + with_test_prefix "with single quotes" { + run_quoting_and_escaping_tests_1 $root "${shell_cmd}ls '$dir'" + } + } + } + } } # Helper for run_unquoted_tests. ROOT is the root directory as setup diff --git a/gdb/testsuite/gdb.base/foll-exec-c++.exp b/gdb/testsuite/gdb.base/foll-exec-c++.exp new file mode 100644 index 0000000..d96310b --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-exec-c++.exp @@ -0,0 +1,24 @@ +# Copyright 2025 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 file is part of the gdb testsuite + +# See foll-exec.exp.tcl for test details. This file runs the test +# using the C++ compiler. + +require allow_cplus_tests +set lang c++ + +source $srcdir/$subdir/foll-exec.exp.tcl diff --git a/gdb/testsuite/gdb.base/foll-exec-c.exp b/gdb/testsuite/gdb.base/foll-exec-c.exp new file mode 100644 index 0000000..67f62cc --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-exec-c.exp @@ -0,0 +1,23 @@ +# Copyright 2025 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 file is part of the gdb testsuite + +# See foll-exec.exp.tcl for test details. This file runs the test +# using the C compiler. + +set lang c + +source $srcdir/$subdir/foll-exec.exp.tcl diff --git a/gdb/testsuite/gdb.base/foll-exec.c b/gdb/testsuite/gdb.base/foll-exec.c index a1c9b70..291f803 100644 --- a/gdb/testsuite/gdb.base/foll-exec.c +++ b/gdb/testsuite/gdb.base/foll-exec.c @@ -19,25 +19,38 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> - +#include <libgen.h> +#include <assert.h> #include <limits.h> int global_i = 100; +#ifndef EXECD_PROG +#define EXECD_PROG "execd-prog" +#endif + int main (int argc, char ** argv) { int local_j = global_i + 1; int local_k = local_j + 1; char prog[PATH_MAX]; - int len; + size_t len = PATH_MAX - 1; + + printf ("foll-exec is about to execlp(%s)...\n", EXECD_PROG); + + prog [len] = '\0'; + + strncpy (prog, dirname (argv[0]), len); + len -= strlen (prog); + assert (len > 0); - printf ("foll-exec is about to execlp(execd-prog)...\n"); + strncat (prog, "/", len); + len -= 1; + assert (len > 0); - strcpy (prog, argv[0]); - len = strlen (prog); - /* Replace "foll-exec" with "execd-prog". */ - memcpy (prog + len - 9, "execd-prog", 10); - prog[len + 1] = 0; + strncat (prog, EXECD_PROG, len); + len -= strlen (EXECD_PROG); + assert (len > 0); /* In the following function call, maximum line length exceed the limit 80. This is intentional and required for clang compiler such that complete @@ -45,7 +58,7 @@ int main (int argc, char ** argv) multi-line. */ execlp (prog, /* tbreak-execlp */ prog, "execlp arg1 from foll-exec", (char *) 0); - printf ("foll-exec is about to execl(execd-prog)...\n"); + printf ("foll-exec is about to execl(%s)...\n", EXECD_PROG); /* In the following function call, maximum line length exceed the limit 80. This is intentional and required for clang compiler such that complete @@ -61,7 +74,7 @@ int main (int argc, char ** argv) argv[0] = prog; - printf ("foll-exec is about to execv(execd-prog)...\n"); + printf ("foll-exec is about to execv(%s)...\n", EXECD_PROG); execv (prog, argv); /* tbreak-execv */ } diff --git a/gdb/testsuite/gdb.base/foll-exec.exp b/gdb/testsuite/gdb.base/foll-exec.exp.tcl index ad4c3516..8f96a55 100644 --- a/gdb/testsuite/gdb.base/foll-exec.exp +++ b/gdb/testsuite/gdb.base/foll-exec.exp.tcl @@ -22,33 +22,55 @@ require {istarget "*-linux*"} standard_testfile foll-exec.c -set testfile2 "execd-prog" -set srcfile2 ${testfile2}.c -set binfile2 [standard_output_file ${testfile2}] +# Compile a program that performs an exec as EXECER_LANG, and a +# program that will be exec'd as EXECEE_LANG. Either language can be +# 'c' or 'c++'. Then run various test associated with 'catch exec' +# using the compiled programs. +proc do_exec_tests { execer_lang execee_lang } { + global srcfile testfile + global gdb_prompt -set compile_options debug + # First compile the program to be exec'd, the execee. + set execee_base_filename "execd-prog" + set srcfile2 ${execee_base_filename}.c + set execee_testfile "execd-prog-${execee_lang}" + set execee_testfile_re [string_to_regexp $execee_testfile] + set execee_binfile [standard_output_file $execee_testfile] -# build the first test case -if { [gdb_compile "${srcdir}/${subdir}/${srcfile2}" "${binfile2}" executable $compile_options] != "" } { - untested "failed to compile" - return -1 -} + set execee_flags debug + if { $execee_lang == "c++" } { + lappend execee_flags "c++" + } -if { [is_remote target] } { - gdb_remote_download target $binfile2 -} + if { [build_executable "failed to build $execee_testfile" $execee_testfile \ + $srcfile2 $execee_flags] == -1 } { + return + } -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $compile_options] != "" } { - untested "failed to compile" - return -1 -} + if { [is_remote target] } { + gdb_remote_download target $execee_binfile + } -proc do_exec_tests {} { - global binfile srcfile srcfile2 testfile testfile2 - global gdb_prompt + + # Now compile the program to do the exec, the execer. + set execer_testfile "$testfile-${execee_lang}" + set execer_binfile [standard_output_file $execer_testfile] + + set execer_flags debug + if { $execer_lang == "c++" } { + lappend execer_flags "c++" + } + lappend execer_flags "additional_flags=-DEXECD_PROG=\"${execee_testfile}\"" + + if { [build_executable "failed to build $execer_testfile" $execer_testfile \ + $srcfile $execer_flags] == -1 } { + return + } + + # Now we can start running the tests. + clean_restart $execer_binfile # Start the program running, and stop at main. - # if {![runto_main]} { return } @@ -71,7 +93,7 @@ proc do_exec_tests {} { return } - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -120,7 +142,7 @@ proc do_exec_tests {} { set execd_line [gdb_get_line_number "after-exec" $srcfile2] send_gdb "next\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execlp call"} -re "$gdb_prompt $" {fail "step through execlp call"} timeout {fail "(timeout) step through execlp call"} @@ -160,7 +182,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -193,7 +215,7 @@ proc do_exec_tests {} { send_gdb "continue\n" gdb_expect { - -re ".*xecuting new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*$gdb_prompt $"\ + -re ".*xecuting new program:.*${execee_testfile_re}.*Catchpoint .*(exec\'d .*${execee_testfile_re}).*$gdb_prompt $"\ {pass "hit catch exec"} -re "$gdb_prompt $" {fail "hit catch exec"} timeout {fail "(timeout) hit catch exec"} @@ -210,7 +232,7 @@ proc do_exec_tests {} { # set msg "info shows catchpoint exec pathname" gdb_test_multiple "info breakpoints" $msg { - -re ".*catchpoint.*keep y.*exec, program \".*${testfile2}\".*$gdb_prompt $" { + -re ".*catchpoint.*keep y.*exec, program \".*${execee_testfile_re}\".*$gdb_prompt $" { pass $msg } } @@ -228,7 +250,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -269,7 +291,7 @@ proc do_exec_tests {} { # send_gdb "next 2\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execl call"} -re "$gdb_prompt $" {fail "step through execl call"} timeout {fail "(timeout) step through execl call"} @@ -295,7 +317,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -330,7 +352,7 @@ proc do_exec_tests {} { } send_gdb "next\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "step through execv call"} -re "$gdb_prompt $" {fail "step through execv call"} timeout {fail "(timeout) step through execv call"} @@ -356,7 +378,7 @@ proc do_exec_tests {} { # Explicitly kill this program, or a subsequent rerun actually runs # the exec'd program, not the original program... - clean_restart $binfile + clean_restart $execer_binfile # Start the program running, and stop at main. # @@ -370,13 +392,13 @@ proc do_exec_tests {} { # send_gdb "continue\n" gdb_expect { - -re ".*xecuting new program: .*${testfile2}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ + -re ".*xecuting new program: .*${execee_testfile_re}.*${srcfile2}:${execd_line}.*int local_j = argc;.*$gdb_prompt $"\ {pass "continue through exec"} -re "$gdb_prompt $" {fail "continue through exec"} timeout {fail "(timeout) continue through exec"} } } -clean_restart $binfile - -do_exec_tests +foreach_with_prefix execee_lang { c c++ } { + do_exec_tests $lang $execee_lang +} diff --git a/gdb/testsuite/gdb.base/foll-fork-syscall.c b/gdb/testsuite/gdb.base/foll-fork-syscall.c new file mode 100644 index 0000000..ef695f5 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.c @@ -0,0 +1,35 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +#include <unistd.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + int pid, x = 0; + + pid = fork (); + if (pid == 0) /* set breakpoint here */ + printf ("I am the child\n"); + else + printf ("I am the parent\n"); + + chdir ("."); + ++x; /* set exit breakpoint here */ + return 0; +} diff --git a/gdb/testsuite/gdb.base/foll-fork-syscall.exp b/gdb/testsuite/gdb.base/foll-fork-syscall.exp new file mode 100644 index 0000000..21ef334 --- /dev/null +++ b/gdb/testsuite/gdb.base/foll-fork-syscall.exp @@ -0,0 +1,143 @@ +# Copyright 2025 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/>. + +# Test catching syscalls with all permutations of follow-fork parent/child +# and detach-on-fork on/off. + +# Test relies on checking follow-fork output. Do not run if gdb debug is +# enabled because it will be redirected to the log. +require !gdb_debug_enabled +require {is_any_target "i?86-*-*" "x86_64-*-*"} +require allow_fork_tests + +standard_testfile + +if {[build_executable "failed to prepare" $testfile $srcfile debug]} { + return -1 +} + +proc setup_gdb {} { + global testfile + + clean_restart $testfile + + if {![runto_main]} { + return false + } + + # Set a breakpoint after the fork is "complete." + if {![gdb_breakpoint [gdb_get_line_number "set breakpoint here"]]} { + return false + } + + # Set exit breakpoint (to prevent inferior from exiting). + if {![gdb_breakpoint [gdb_get_line_number "set exit breakpoint here"]]} { + return false + } + return true +} + +# Check that fork catchpoints are supported, as an indicator for whether +# fork-following is supported. Return 1 if they are, else 0. + +proc_with_prefix check_fork_catchpoints {} { + global gdb_prompt + + if { ![setup_gdb] } { + return false + } + + # Verify that the system supports "catch fork". + gdb_test "catch fork" "Catchpoint \[0-9\]* \\(fork\\)" "insert first fork catchpoint" + set has_fork_catchpoints false + gdb_test_multiple "continue" "continue to first fork catchpoint" { + -re ".*Your system does not support this type\r\nof catchpoint.*$gdb_prompt $" { + unsupported "continue to first fork catchpoint" + } + -re ".*Catchpoint.*$gdb_prompt $" { + set has_fork_catchpoints true + pass "continue to first fork catchpoint" + } + } + + return $has_fork_catchpoints +} + +proc_with_prefix test_catch_syscall {follow-fork-mode detach-on-fork} { + # Start with shiny new gdb instance. + if {![setup_gdb]} { + return + } + + # The "Detaching..." and "Attaching..." messages may be hidden by + # default. + gdb_test_no_output "set verbose" + + # Setup modes to test. + gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}" + gdb_test_no_output "set detach-on-fork ${detach-on-fork}" + + gdb_test "catch fork" "Catchpoint . \\(fork\\)" + gdb_test "catch syscall chdir" "Catchpoint . \\(syscall 'chdir'.*\\)" + + # Which inferior we're expecting to follow. Assuming the parent + # will be inferior #1, and the child will be inferior #2. + if {${follow-fork-mode} == "parent"} { + set following_inf 1 + } else { + set followin_inf 2 + } + # Next stop should be the fork catchpoint. + set expected_re "" + append expected_re "Catchpoint . \\(forked process.*" + gdb_test "continue" $expected_re "continue to fork catchpoint" + + # Next stop should be the breakpoint after the fork. + set expected_re ".*" + if {${follow-fork-mode} == "child" || ${detach-on-fork} == "off"} { + append expected_re "\\\[New inferior.*" + } + if {${detach-on-fork} == "on"} { + append expected_re "\\\[Detaching after fork from " + if {${follow-fork-mode} == "parent"} { + append expected_re "child" + } else { + append expected_re "parent" + } + append expected_re " process.*" + } + append expected_re "Breakpoint .*set breakpoint here.*" + gdb_test "continue" $expected_re "continue to breakpoint after fork" + + # Next stop should be the syscall catchpoint. + set expected_re ".*Catchpoint . \\(call to syscall chdir\\).*" + gdb_test continue $expected_re "continue to chdir syscall" +} + +# Check for follow-fork support. +if {![check_fork_catchpoints]} { + untested "follow-fork not supported" + return +} + +# Test all permutations. +foreach_with_prefix follow-fork-mode {"parent" "child"} { + + # Do not run tests when not detaching from the parent. + # See breakpoints/13457 for discussion. + foreach_with_prefix detach-on-fork {"on"} { + test_catch_syscall ${follow-fork-mode} ${detach-on-fork} + } +} diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp index 94755c6..12db516 100644 --- a/gdb/testsuite/gdb.base/foll-fork.exp +++ b/gdb/testsuite/gdb.base/foll-fork.exp @@ -17,6 +17,8 @@ # enabled as it will be redirected to the log. require !gdb_debug_enabled +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile debug]} { diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp index 266df46..6ca7711 100644 --- a/gdb/testsuite/gdb.base/foll-vfork.exp +++ b/gdb/testsuite/gdb.base/foll-vfork.exp @@ -18,12 +18,7 @@ # either execs or exits --- since those events take somewhat different # code paths in GDB, both variants are exercised. -# Until "set follow-fork-mode" and "catch vfork" are implemented on -# other targets... -# -if {![istarget "*-linux*"]} { - continue -} +require allow_fork_tests standard_testfile .c -exit.c vforked-prog.c diff --git a/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp b/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp index 311d7ba..2d47d5d 100644 --- a/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp +++ b/gdb/testsuite/gdb.base/fork-no-detach-follow-child-dlopen.exp @@ -23,6 +23,7 @@ # in the source of the shlib, and "list" should display the source where # the program stopped. +require allow_fork_tests require allow_shlib_tests standard_testfile .c -shlib.c diff --git a/gdb/testsuite/gdb.base/fork-print-inferior-events.exp b/gdb/testsuite/gdb.base/fork-print-inferior-events.exp index 26ed2f9..19ace00 100644 --- a/gdb/testsuite/gdb.base/fork-print-inferior-events.exp +++ b/gdb/testsuite/gdb.base/fork-print-inferior-events.exp @@ -19,6 +19,8 @@ # inferior-events [on,off]', 'set follow-fork-mode [child,parent]' and # 'set detach-on-fork [on,off]' are the correct ones. +require allow_fork_tests + # This test relies on "run", so it cannot run on target remote stubs. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.base/fork-running-state.exp b/gdb/testsuite/gdb.base/fork-running-state.exp index 4b810a6..c446800 100644 --- a/gdb/testsuite/gdb.base/fork-running-state.exp +++ b/gdb/testsuite/gdb.base/fork-running-state.exp @@ -17,6 +17,8 @@ # in non-stop). GDB used to miss updating the parent/child running # states after a fork. +require allow_fork_tests + standard_testfile # The test proper. diff --git a/gdb/testsuite/gdb.base/infcall-failure.exp b/gdb/testsuite/gdb.base/infcall-failure.exp index 66bccd1..e7aeac1 100644 --- a/gdb/testsuite/gdb.base/infcall-failure.exp +++ b/gdb/testsuite/gdb.base/infcall-failure.exp @@ -131,7 +131,13 @@ proc_with_prefix run_cond_hits_segfault_test { async_p non_stop_p } { [multi_line \ "Continuing\\." \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + [string cat \ + [string_to_regexp \ + "Program received signal SIGSEGV, Segmentation fault."] \ + "("] \ + [string cat \ + [string_to_regexp "Address not mapped to object."] \ + ")?"] \ "${::hex} in func_segfault \\(\\) at \[^\r\n\]+:${::segv_line}" \ "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+" \ "Error in testing condition for breakpoint ${bp_1_num}:" \ @@ -161,7 +167,13 @@ proc_with_prefix run_call_hits_segfault_test { async_p non_stop_p } { gdb_test "call func_segfault ()" \ [multi_line \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + [string cat \ + [string_to_regexp \ + "Program received signal SIGSEGV, Segmentation fault."] \ + "("] \ + [string cat \ + [string_to_regexp "Address not mapped to object."] \ + ")?"] \ "${::hex} in func_segfault \\(\\) at \[^\r\n\]+:${::segv_line}" \ "${::decimal}\\s+\[^\r\n\]+Segfault here\[^\r\n\]+" \ "The program being debugged was signaled while in a function called from GDB\\." \ diff --git a/gdb/testsuite/gdb.base/inferior-died.exp b/gdb/testsuite/gdb.base/inferior-died.exp index 3992561..764a88d 100644 --- a/gdb/testsuite/gdb.base/inferior-died.exp +++ b/gdb/testsuite/gdb.base/inferior-died.exp @@ -13,10 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Until "set follow-fork-mode" and "catch fork" are implemented on -# other targets... -# -require {istarget "*-*-linux*"} +require allow_fork_tests require support_displaced_stepping diff --git a/gdb/testsuite/gdb.base/info-shared.exp b/gdb/testsuite/gdb.base/info-shared.exp index 6f1b2d6..e81b28e 100644 --- a/gdb/testsuite/gdb.base/info-shared.exp +++ b/gdb/testsuite/gdb.base/info-shared.exp @@ -79,6 +79,9 @@ proc check_info_shared { test expect1 expect2 } { } } +# Check that "info shared" before running doesn't crash. +check_info_shared "info sharedlibrary before running" 0 0 + # Start the inferior, and check neither of the libraries are loaded at # the start. if ![runto_main] { diff --git a/gdb/testsuite/gdb.base/interrupt-daemon.exp b/gdb/testsuite/gdb.base/interrupt-daemon.exp index 161f854..8b8c61d 100644 --- a/gdb/testsuite/gdb.base/interrupt-daemon.exp +++ b/gdb/testsuite/gdb.base/interrupt-daemon.exp @@ -16,6 +16,8 @@ # Make sure that we can interrupt an inferior that forks and moves to # its own session. +require allow_fork_tests + standard_testfile if {[build_executable "failed to build" $testfile $srcfile {debug}]} { diff --git a/gdb/testsuite/gdb.base/jit-elf-fork.exp b/gdb/testsuite/gdb.base/jit-elf-fork.exp index 81d3350..c1fa428 100644 --- a/gdb/testsuite/gdb.base/jit-elf-fork.exp +++ b/gdb/testsuite/gdb.base/jit-elf-fork.exp @@ -15,6 +15,7 @@ # Test fork handling of an inferior that has JIT-ed objfiles. +require allow_fork_tests require allow_shlib_tests load_lib jit-elf-helpers.exp diff --git a/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp b/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp index ef4bb88..57ec330 100644 --- a/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp +++ b/gdb/testsuite/gdb.base/kill-detach-inferiors-cmd.exp @@ -19,6 +19,7 @@ # commands. require can_spawn_for_attach +require allow_multi_inferior_tests standard_testfile set executable $testfile diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp index 52282bc..7936e53 100644 --- a/gdb/testsuite/gdb.base/maint.exp +++ b/gdb/testsuite/gdb.base/maint.exp @@ -52,6 +52,40 @@ if {[prepare_for_testing "failed to prepare" $testfile \ return -1 } +# Check "maint set per-command" warnings. We do this early so that +# the following tests don't need to expect them, as GDB only warns +# once. + +with_test_prefix "warnings" { + # Potential warning given by "maint set per-command time". + set maybe_per_command_warning \ + "(?:warning: per-thread run time information not available on this platform)?" + + # This one should not issue the "per-command time" warning. + with_test_prefix "per-command space" { + gdb_test_no_output "mt set per-command space on" + gdb_test_no_output "mt set per-command space off" + } + + # These might warn. "per-command on" enables all sub commands, so + # might trigger the "per-command time" warning. + foreach cmd {"per-command" "per-command time"} { + with_test_prefix $cmd { + # GDB only warns once, so restart between commands. + clean_restart $binfile + gdb_test "mt set $cmd on" "$maybe_per_command_warning" + gdb_test "mt set $cmd off" "command started" + gdb_test_no_output "mt set $cmd on" \ + "mt set $cmd on, again" + gdb_test "mt set $cmd off" "command started" \ + "mt set $cmd off, again" + } + } + + # We've already warned once above, so the following tests don't + # need to expect the warning. +} + set readnow_p [readnow] # The commands we test here produce many lines of output; disable "press @@ -205,8 +239,8 @@ set re \ "( Number of \"partial\" symbols read: $decimal" \ ")?( Number of psym tables \\(not yet expanded\\): $decimal" \ ")?( Total memory used for psymbol cache: $decimal" \ - ")?( Number of read CUs: $decimal" \ - " Number of unread CUs: $decimal" \ + ")?( Number of read units: $decimal" \ + " Number of unread units: $decimal" \ ")? Total memory used for objfile obstack: $decimal" \ " Total memory used for BFD obstack: $decimal" \ " Total memory used for string cache: $decimal" \ diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp index 61a240f..3facccb 100644 --- a/gdb/testsuite/gdb.base/multi-forks.exp +++ b/gdb/testsuite/gdb.base/multi-forks.exp @@ -13,11 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Until "set follow-fork-mode" and "catch fork" are implemented on -# other targets... -# -require {istarget "*-*-linux*"} - +require allow_fork_tests standard_testfile .c diff --git a/gdb/testsuite/gdb.base/options.exp b/gdb/testsuite/gdb.base/options.exp index 7822e4a..a0947e2 100644 --- a/gdb/testsuite/gdb.base/options.exp +++ b/gdb/testsuite/gdb.base/options.exp @@ -508,12 +508,26 @@ proc_with_prefix test-thread-apply {} { proc_with_prefix test-info-threads {} { test_gdb_complete_multiple "info threads " "" "" { "-gid" + "-running" + "-stopped" "ID" } + test_gdb_complete_multiple "info threads " "-" "" { + "-gid" + "-running" + "-stopped" + } + test_gdb_complete_unique \ - "info threads -" \ + "info threads -g" \ "info threads -gid" + test_gdb_complete_unique \ + "info threads -r" \ + "info threads -running" + test_gdb_complete_unique \ + "info threads -s" \ + "info threads -stopped" # "ID" isn't really something the user can type. test_gdb_complete_none "info threads I" diff --git a/gdb/testsuite/gdb.base/pie-fork.exp b/gdb/testsuite/gdb.base/pie-fork.exp index 48c01d9..86407b4 100644 --- a/gdb/testsuite/gdb.base/pie-fork.exp +++ b/gdb/testsuite/gdb.base/pie-fork.exp @@ -16,6 +16,8 @@ # Test that we can follow forks properly when the executable is # position-independent. +require allow_fork_tests + standard_testfile set opts [list debug pie] diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp index 22913ca..eaee010 100644 --- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp +++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp @@ -49,6 +49,11 @@ if {[build_executable "failed to prepare" $testfile $srcfile]} { # - run: use the run command # - attach: start a process outside of GDB and attach it proc do_test { action1 action2 } { + + if {$action1 == "add" && ![allow_multi_inferior_tests]} { + return + } + save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop on\"" clean_restart $::binfile diff --git a/gdb/testsuite/gdb.base/sigall.exp b/gdb/testsuite/gdb.base/sigall.exp index b23e3c5..461a92b 100644 --- a/gdb/testsuite/gdb.base/sigall.exp +++ b/gdb/testsuite/gdb.base/sigall.exp @@ -41,13 +41,14 @@ proc test_one_sig {nextsig} { setup_xfail "i*86-pc-linuxoldld-gnu" "i*86-pc-linuxaout-gnu" } # On Linux SPARC64 systems SIGLOST==SIGPWR and gdb identifies - # the raised signal as PWR. - if {$thissig == "LOST" && [istarget "sparc64-*-linux*"]} { + # the raised signal as PWR. Same for Cygwin. + if {$thissig == "LOST" + && ([istarget "sparc64-*-linux*"] || [istarget "*-*-cygwin*"])} { set esig "PWR" } gdb_test "continue" \ - "Continuing.*Program received signal SIG$esig.*" \ + "Continuing.* received signal SIG$esig.*" \ "get signal $esig" } @@ -177,7 +178,7 @@ gdb_test "handle SIGTERM stop print" \ "SIGTERM\[ \t\]*Yes\[ \t\]*Yes\[ \t\]*Yes.*" gdb_test "b handle_TERM" "Breakpoint \[0-9\]+ .*" gdb_test "continue" \ - "Continuing.*Program received signal SIGTERM.*" \ + "Continuing.* received signal SIGTERM.*" \ "get signal TERM" gdb_test "continue" "Breakpoint.*handle_TERM.*" "send signal TERM" gdb_continue_to_end "continue to sigall exit" diff --git a/gdb/testsuite/gdb.base/step-over-exit.exp b/gdb/testsuite/gdb.base/step-over-exit.exp index 2370f97..6dfa7bb 100644 --- a/gdb/testsuite/gdb.base/step-over-exit.exp +++ b/gdb/testsuite/gdb.base/step-over-exit.exp @@ -13,11 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -standard_testfile - # Test a thread is doing step-over a syscall instruction which is exit, # and GDBserver should cleanup its state of step-over properly. +# The testcase relies on follow-fork-mode child. +require allow_fork_tests + +standard_testfile + set syscall_insn "" # Define the syscall instruction for each target. diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index c10be3b..503671b 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -751,6 +751,280 @@ proc test_enable_styling_warning { } { } } +# Run an 'apropos' command. Each line of output starts with a +# non-default style (command style). Ensure that pagination triggers +# during the 'apropos' output such that, at the point pagination kicks +# in, a non-default style is in effect. +# +# Then, at the pagination prompt, quit the command. +# +# Next, run a command which switches to a different style, and then +# back to the current style. +# +# At one point, a bug in the pagination code would leave the +# non-default style from the 'apropos' command recorded as the current +# style, such that the second command would switch back to the earlier +# style. +proc test_pagination_cmd_after_quit_styling {} { + with_ansi_styling_terminal { + clean_restart + } + + # We're going to use 'apropos time'. Check that with a height of + # 12 lines, each line starts with a non-default style, and that we + # do see the pagination prompt. This means that there are more + # than 12 lines for this command. + with_test_prefix "validate apropos output" { + gdb_test_no_output "set height 12" + + set saw_pagination_prompt false + gdb_test_multiple "apropos time" "" { + -re "^apropos time\r\n" { + exp_continue + } + -re "^\033\\\[39;49;1;27m\[^\r\n\]+\r\n" { + exp_continue + } + -re "^$::pagination_prompt$" { + set saw_pagination_prompt true + send_gdb "q\n" + exp_continue + } + -re "^q\r\n" { + exp_continue + } + -re "^Quit\r\n" { + exp_continue + } + -re "^$::gdb_prompt $" { + gdb_assert { $saw_pagination_prompt } $gdb_test_name + } + -re "^\[^\r\n\]+\r\n" { + exp_continue + } + } + } + + # Now reduce the height to 10 and re-run 'apropos time'. Based on + # the previous check, we know that this is going to present the + # pagination prompt when a non-default style is in use. + gdb_test_no_output "set height 10" + + set saw_pagination_prompt false + gdb_test_multiple "apropos time" "" { + -re "$::pagination_prompt" { + set saw_pagination_prompt true + send_gdb "q\n" + exp_continue + } + -re "\r\n$::gdb_prompt $" { + gdb_assert { $saw_pagination_prompt } $gdb_test_name + } + } + + # The help output for this maintenance command switches to a + # different style, and then back to the default. If the + # pagination bug still exists, then this would switch back to the + # non-default style that was in use when pagination kicked in + # above. + gdb_test "maintenance time" \ + "^\"\033\\\[39;49;1;27mmaintenance time\033\\\[m\" takes a numeric argument\\." +} + +# Helper for test_pagination_prompt_styling. Return false if STR, a +# line that appears immediately before a pagination prompt, matches +# the pattern for needing a style reset at the end, but does not have +# the style reset. +# +# In all other cases, return true. So lines that don't match the +# known pattern for neededing a style reset will always return true, +# as will lines that match the pattern, and do have the style reset. +proc previous_line_is_ok { str } { + + # Create a copy of STR with all the '\033' characters removed. + # Then compare string lengths to get a count of the '\033' + # charactes present in STR. + regsub -all "\033" $str {} stripped + set count [expr [string length $str] - [string length $stripped]] + + # If STR switched styles, then it _must_ switch back again, + # otherwise the pagination prompt will be in the wrong style. + # This means that there _must_ be an even number of '\033' + # characters in STR. If there is not then we switched style, but + # failed to switch back. + if { [expr $count % 2] != 0 } { + return false + } + + # For lines that don't match this pattern, we cannot comment on + # where the style reset should occur, so lets just claim the line + # is fine. + if { ![regexp "\\s+$::hex - $::hex is \[^\r\n\]+ in " $str] } { + return true + } + + # This line did match the above pattern, so we know that a style + # reset _must_ occur at the end of the line. If it doesn't then + # this line is not OK. + if { ![regexp "\033\\\[m$" $str] } { + return false + } + + # All tests passed, this line looks OK. + return true +} + +# Test that the pagination prompt is displayed unstyled. This is done +# by looking at the 'info files' output and selecting a width that +# will mean we should get a pagination prompt part way through a +# styled filename. +# +# Then, re-run 'info files' and check that for every pagination +# prompt, the previous line disables styling as expected. +proc test_pagination_prompt_styling {} { + with_ansi_styling_terminal { + clean_restart $::binfile + } + + if {![runto_main]} { + return + } + + # Set height so we actually get a pagination prompt. + gdb_test_no_output "set height 3" + + # Scan the 'info files' output and set DESIRED_WIDTH such that it + # will trigger pagination part-way through a styled filename. + set desired_width 0 + gdb_test_multiple "info files" "find good test width" { + -re "^info files\r\n" { + exp_continue + } + + -re "^$::pagination_prompt$" { + send_gdb "\n" + exp_continue + } + + -re "^$::gdb_prompt $" { + } + + -re "^((\\s+$::hex - $::hex is \[^\r\n\]+ in )\[^\r\n\]+)\r\n" { + if { $desired_width == 0 } { + set full_line $expect_out(1,string) + set inner_line $expect_out(2,string) + set desired_width [expr [string length $inner_line] + ([string length $full_line] - [string length $inner_line]) / 2] + } + exp_continue + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Now setup the screen width. + gdb_test_no_output "set width $desired_width" + + # Re-run 'info files'. Check that the content before any + # pagination prompt correctly disables styling. + set saw_bad_line false + set prev_line "" + gdb_test_multiple "info files" "check pagination prompt styling" { + -re "^info files\r\n" { + exp_continue + } + + -re "^$::pagination_prompt$" { + if { ![previous_line_is_ok $prev_line] } { + set saw_bad_line true + } + send_gdb "\n" + exp_continue + } + + -re "^(\[^\r\n\]+)$::pagination_prompt$" { + set prev_line $expect_out(1,string) + if { ![previous_line_is_ok $prev_line] } { + set saw_bad_line true + } + send_gdb "\n" + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_line } $gdb_test_name + } + + -re "^(\[^\r\n\]*)\r\n" { + set prev_line $expect_out(1,string) + exp_continue + } + } +} + +# Test that GDB can correctly restore the current style after a +# pagination prompt. +# +# Set the logging file to a garbage string based on LENGTH (is +# actually 2x LENGTH), then 'show logging file'. Press return at the +# pagination prompt, and check that the reset of the filename is +# styled correctly, and that GDB correctly switches back to the +# default style once the logging file has finished. +proc test_pagination_continue_styling_1 { length } { + with_ansi_styling_terminal { + clean_restart $::binfile + } + + set filename [string repeat "ax" $length] + + gdb_test_no_output "set logging file $filename" + + gdb_test_no_output "set height 3" + gdb_test_no_output "set width 80" + + set saw_bad_styling false + gdb_test_multiple "show logging file" "" { + -re "^show logging file\r\n" { + exp_continue + } + + -re "^The current logfile is \"\033\\\[32;49;22;27m(?:ax)+\033\\\[m" { + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m(?=--)" { + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+(?=--)" { + set saw_bad_styling true + exp_continue + } + + -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m\"\\.\r\n" { + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_styling } $gdb_test_name + } + + -re "^$::pagination_prompt$$" { + send_gdb "\n" + exp_continue + } + } +} + +# Wrapper around test_pagination_continue_styling_1, calls that +# function with different lengths. +proc test_pagination_continue_styling { } { + foreach_with_prefix length { 80 160 } { + test_pagination_continue_styling_1 $length + } +} + # Check to see if the Python styling of disassembler output is # expected or not, this styling requires Python support in GDB, and # the Python pygments module to be available. @@ -793,3 +1067,6 @@ test_colorsupport_truecolor test_colorsupport_truecolor_only test_enable_styling_warning +test_pagination_cmd_after_quit_styling +test_pagination_prompt_styling +test_pagination_continue_styling diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.c b/gdb/testsuite/gdb.base/user-namespace-attach.c new file mode 100644 index 0000000..684ce1c --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.c @@ -0,0 +1,35 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> + +volatile int spin_p = 1; + +int +main () +{ + alarm (60); + + printf ("pid = %lld\n", ((long long) getpid ())); + + while (spin_p) + sleep (1); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/user-namespace-attach.exp b/gdb/testsuite/gdb.base/user-namespace-attach.exp new file mode 100644 index 0000000..741093c --- /dev/null +++ b/gdb/testsuite/gdb.base/user-namespace-attach.exp @@ -0,0 +1,148 @@ +# Copyright 2025 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/>. + +# Check that GDB can attach to a process started using 'unshare'. The +# inferior is started in a separate mnt namespace. + +require can_spawn_for_attach + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile] == -1} { + return +} + +# This test relies (at least in some parts) on the sysroot being +# 'target:'. Grab the current sysroot now so we can skip those tests +# if the board file has changed the sysroot. +set sysroot "" +set test "show sysroot" +gdb_test_multiple $test $test { + -re -wrap "The current system root is \"(.*)\"\\." { + set sysroot $expect_out(1,string) + } +} + +# Start a process using 'unshare FLAGS', then attach to the process +# from GDB. Check that the attach worked as expected. +proc run_test { flags } { + + # If FLAGS contains '--mount' then a separate mnt namespace will + # be created, in which case the executable will have been read + # from the 'target:'. Otherwise, the executable will have been + # read from the local filesystem, and there will be no prefix. + # + # Of course, this only applies if the sysroot is 'target:', some + # boards change this, so skip these tests on those boards. + if { [lsearch -exact [split $flags " "] "--mount"] != -1 } { + if { $::sysroot ne "target:" } { + return + } + + set prefix "target:" + } else { + set prefix "" + } + + set unshare_cmd "unshare $flags" + + # Run '/bin/true' using UNSHARE_CMD. If the flags in UNSHARE_CMD + # aren't supported then this will fail, this means we shouldn't + # spawn the command with our test executable and try attaching. + # + # This will also fail if /bin/true isn't present, or doesn't work + # as we expect. But this should be fine for many targets. + set res [remote_exec target "$unshare_cmd /bin/true"] + if { [lindex $res 0] != 0 } { + unsupported "unshare flags not supported" + return + } + + set inferior_spawn_id \ + [spawn_wait_for_attach [list "$unshare_cmd $::binfile"]] + if { $inferior_spawn_id == -1 } { + unsupported "failed to spawn for attach" + return + } + + set inferior_pid [spawn_id_get_pid $inferior_spawn_id] + + clean_restart + + set saw_bad_warning false + gdb_test_multiple "attach $inferior_pid" "attach to inferior" { + -re "^attach $::decimal\r\n" { + exp_continue + } + + -re "^warning: \[^\r\n\]+: could not open as an executable file: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: \[^\r\n\]+: can't open to read symbols: \[^\r\n\]+\r\n" { + set saw_bad_warning true + exp_continue + } + + -re "^warning: Could not load vsyscall page because no executable was specified\r\n" { + # This warning is a secondary consequence of the above bad + # warnings, so don't count this as a bad warnings, ignore + # it instead. + exp_continue + } + + -re "^warning:\\s+$::decimal\\s*\[^\r\n\]+: No such file or directory\r\n" { + # This unrelated warning is seen when GDB stops in libc, + # and the source code for libc is not available. + exp_continue + } + + -re "^warning: \[^\r\n\]+\r\n" { + # If we ignore "other" warnings then, should the above + # warnings strings change we'll start ignoring the bad + # warnings, and the test will appear to pass. + # + # If you are seeing a warning here that really has nothing + # to do with the test failing, then the correct solution + # is to add a new regexp to specifically match _that_ + # warning, and ignore it. + set saw_bad_warning true + exp_continue + } + + -re "^$::gdb_prompt $" { + gdb_assert { !$saw_bad_warning } $gdb_test_name + } + + -re "^\[^\r\n\]*\r\n" { + exp_continue + } + } + + # Ensure GDB could access the executable. + set binfile_re [string_to_regexp $::binfile] + gdb_test "info inferiors" \ + "\r\n\\*\\s+$::decimal\\s+\[^\r\n\]+\\s+${prefix}${binfile_re}\\s*" +} + +set test_flags [list \ + "--mount --map-root-user" \ + "--user" \ + "--user --map-root-user"] + +foreach_with_prefix flags $test_flags { + run_test $flags +} diff --git a/gdb/testsuite/gdb.base/vfork-follow-parent.exp b/gdb/testsuite/gdb.base/vfork-follow-parent.exp index fca2993..8cb785d 100644 --- a/gdb/testsuite/gdb.base/vfork-follow-parent.exp +++ b/gdb/testsuite/gdb.base/vfork-follow-parent.exp @@ -19,6 +19,8 @@ # schedule-multiple on" or "set detach-on-fork on". Test these two resolution # methods. +require allow_fork_tests + standard_testfile .c vforked-prog.c set binfile ${testfile}-exit diff --git a/gdb/testsuite/gdb.base/watch-before-fork.exp b/gdb/testsuite/gdb.base/watch-before-fork.exp index 074cfbd..509561e 100644 --- a/gdb/testsuite/gdb.base/watch-before-fork.exp +++ b/gdb/testsuite/gdb.base/watch-before-fork.exp @@ -20,6 +20,8 @@ # This test uses "awatch". require allow_hw_watchpoint_access_tests +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile debug]} { diff --git a/gdb/testsuite/gdb.base/watch-vfork.exp b/gdb/testsuite/gdb.base/watch-vfork.exp index 1bc61bc..503727d 100644 --- a/gdb/testsuite/gdb.base/watch-vfork.exp +++ b/gdb/testsuite/gdb.base/watch-vfork.exp @@ -17,6 +17,8 @@ standard_testfile .c +require allow_fork_tests + if { [build_executable ${testfile}.exp ${testfile} $srcfile {debug}] } { untested "failed to compile" return -1 diff --git a/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp b/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp index b3892f3..fa63edb 100644 --- a/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp +++ b/gdb/testsuite/gdb.base/watchpoint-hw-attach.exp @@ -26,29 +26,18 @@ if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} { return -1 } -if ![runto_main] { - return -1 -} +set test_spawn_id [spawn_wait_for_attach $binfile] +set testpid [spawn_id_get_pid $test_spawn_id] -# Run to the point where mypid in the test program has been -# populated. -gdb_breakpoint [gdb_get_line_number "pidacquired"] -gdb_continue_to_breakpoint "pidacquired" - -# Get the PID of the test process. -set testpid [get_integer_valueof "mypid" 0] +gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach once" gdb_test "detach" "Detaching from program: .*, process $testpid\r\n\\\[Inferior $decimal \\(process $testpid\\) detached\\\]" -if {$testpid == ""} { - return -1 -} - # A clean restart is needed to force the hardware watchpoint setup # logic to run post attach rather than post inferior launch. clean_restart $binfile -gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach" +gdb_test "attach $testpid" "Attaching to program: .*, process $testpid.*" "attach twice" # Ensure the test program is in the top frame so the required # variables are in scope. @@ -62,3 +51,5 @@ gdb_test "watch watched_variable" \ gdb_test "continue" \ "continue.*Continuing.*\.Hardware watchpoint $decimal: watched_variable.*Old value = 0.*New value = 4.*watched_variable\\);" + +kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.btrace/multi-inferior.exp b/gdb/testsuite/gdb.btrace/multi-inferior.exp index ed2acb2..d87a941 100644 --- a/gdb/testsuite/gdb.btrace/multi-inferior.exp +++ b/gdb/testsuite/gdb.btrace/multi-inferior.exp @@ -24,6 +24,8 @@ require allow_btrace_tests +require allow_multi_inferior_tests + require !use_gdb_stub standard_testfile diff --git a/gdb/testsuite/gdb.cp/chained-calls.cc b/gdb/testsuite/gdb.cp/chained-calls.cc index 9d12c98..9358c71 100644 --- a/gdb/testsuite/gdb.cp/chained-calls.cc +++ b/gdb/testsuite/gdb.cp/chained-calls.cc @@ -23,6 +23,8 @@ public: S operator+ (const S &s); + int get (); + int a; }; @@ -41,6 +43,12 @@ S::operator+ (const S &s) return res; } +int +S::get () +{ + return a; +} + S f (int i) { @@ -162,6 +170,8 @@ public: U (type t); type get_type (); + int get (); + int a; char c; type tp[2]; @@ -191,6 +201,12 @@ U::get_type () } int +U::get () +{ + return a; +} + +int main () { int i = g(f(0)); @@ -198,6 +214,7 @@ main () B b = makeb (); C c; + int z = f (42).get (); return i + getb(b, 0); /* Break here */ } diff --git a/gdb/testsuite/gdb.cp/chained-calls.exp b/gdb/testsuite/gdb.cp/chained-calls.exp index 4f0597a..d34162c 100644 --- a/gdb/testsuite/gdb.cp/chained-calls.exp +++ b/gdb/testsuite/gdb.cp/chained-calls.exp @@ -42,3 +42,6 @@ gdb_test "p *c" ".* = {a = 5678}" "*c" gdb_test "p *c + *c" ".* = {a = 11356}" "*c + *c" gdb_test "p q(*c + *c)" ".* = {a = 11356}" "q(*c + *c)" gdb_test "p make_int().get_type ()" ".* = INT" "make_int().get_type ()" +gdb_test "p f(42).get()" " = 42" "f().get()" +gdb_test "ptype f(42).get()" "type = int" "ptype f().get()" +gdb_test "ptype make_int().get()" "type = int" "make_int().get()" diff --git a/gdb/testsuite/gdb.cp/cpexprs.exp.tcl b/gdb/testsuite/gdb.cp/cpexprs.exp.tcl index 1ac35af..5c3dfd6 100644 --- a/gdb/testsuite/gdb.cp/cpexprs.exp.tcl +++ b/gdb/testsuite/gdb.cp/cpexprs.exp.tcl @@ -28,17 +28,33 @@ proc test_breakpoint {func} { delete_breakpoints if { ! [gdb_breakpoint test_function] } { fail "set test_function breakpoint for $func" - } elseif { [gdb_test "continue" \ - "Continuing.\r\n\r\nBreakpoint $DEC+,.*test_function.*" \ - "continue to test_function for $func"] != 0 } { - } else { - gdb_breakpoint "$func" - set i [expr {[string last : $func] + 1}] - set efunc [string_to_regexp [string range $func $i end]] - gdb_test "continue" \ - "Continuing.\r\n\r\nBreakpoint $DEC+,.*$efunc.*" \ - "continue to $func" + return + } + + # Accept any input between "Continuing" and the breakpoint hit, as + # on Cygwin, we may see a "New Thread" notification. This is the + # Cygwin runtime spawning its own internal threads. + if { [gdb_test "continue" \ + "Continuing.\r\n.*Breakpoint $DEC+,.*test_function.*" \ + "continue to test_function for $func"] != 0 } { + return } + + # On some systems, the in-charge and not-in-charge dtors of a + # class may end up with the same address, so setting a breakpoint + # at a dtor like base::~base only finds one location. On other + # systems (e.g. Cygwin), the two dtors for the same class may have + # different addresses, so we find two locations for the + # breakpoint. Thus, expect that the breakpoint hit may or may not + # report a location number. + set bp_re "$DEC+(\.$DEC+)?" + + gdb_breakpoint "$func" + set i [expr {[string last : $func] + 1}] + set efunc [string_to_regexp [string range $func $i end]] + gdb_test "continue" \ + "Continuing.\r\n.*Breakpoint $bp_re,.*$efunc.*" \ + "continue to $func" } # Add a function to the list of tested functions diff --git a/gdb/testsuite/gdb.dap/attach.exp b/gdb/testsuite/gdb.dap/attach.exp index 37e867c..5e1f634 100644 --- a/gdb/testsuite/gdb.dap/attach.exp +++ b/gdb/testsuite/gdb.dap/attach.exp @@ -33,11 +33,11 @@ set attach_id [dap_attach $testpid $binfile] dap_check_request_and_response "configurationDone" configurationDone +dap_check_response "attach response" attach $attach_id + dap_wait_for_event_and_check "stopped" stopped \ "body reason" attach -dap_check_response "attach response" attach $attach_id - dap_shutdown true kill_wait_spawned_process $test_spawn_id diff --git a/gdb/testsuite/gdb.dap/log-message.exp b/gdb/testsuite/gdb.dap/log-message.exp index 421df14..cce367d 100644 --- a/gdb/testsuite/gdb.dap/log-message.exp +++ b/gdb/testsuite/gdb.dap/log-message.exp @@ -40,6 +40,15 @@ set obj [dap_check_request_and_response "set breakpoint" \ [list s $srcfile] $line]] set fn_bpno [dap_get_breakpoint_number $obj] +set eol {\n} +dap_wait_for_event_and_check "set breakpoint output, part 1" output \ + {body category} stdout \ + {body output} "No source file named log-message.c.$eol" + +dap_wait_for_event_and_check "set breakpoint output, part 2" output \ + {body category} stdout \ + {body output} "Breakpoint 1 (-source log-message.c -line $line) pending.$eol" + dap_check_request_and_response "configurationDone" configurationDone dap_check_response "launch response" launch $launch_id diff --git a/gdb/testsuite/gdb.dap/threads.c b/gdb/testsuite/gdb.dap/threads.c new file mode 100644 index 0000000..168f044 --- /dev/null +++ b/gdb/testsuite/gdb.dap/threads.c @@ -0,0 +1,67 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2019-2025 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/>. */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <pthread.h> + +#define NUM 2 + +static pthread_barrier_t threads_started_barrier; + +static void * +thread_function (void *arg) +{ + pthread_barrier_wait (&threads_started_barrier); + + while (1) + sleep (1); + + pthread_exit (NULL); +} + +static void +all_started (void) +{ +} + +int +main () +{ + pthread_t threads[NUM]; + long i; + + pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1); + + for (i = 1; i <= NUM; i++) + { + int res; + + res = pthread_create (&threads[i - 1], NULL, thread_function, NULL); + } + + pthread_barrier_wait (&threads_started_barrier); + + all_started (); + + printf ("sleeping\n"); + fflush (stdout); + sleep (180); + + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.dap/threads.exp b/gdb/testsuite/gdb.dap/threads.exp new file mode 100644 index 0000000..c91d107 --- /dev/null +++ b/gdb/testsuite/gdb.dap/threads.exp @@ -0,0 +1,81 @@ +# Copyright 2025 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/>. + +# Test DAP "threads" request. + +require allow_shlib_tests allow_dap_tests + +load_lib dap-support.exp + +standard_testfile + +set libname $testfile-solib +set srcfile_lib $srcdir/$subdir/$libname.c +set binfile_lib [standard_output_file $libname.so] + +if {[build_executable "failed to prepare" $testfile $srcfile \ + {debug pthreads}] == -1} { + return +} + +if {[dap_initialize] == ""} { + return +} + +set launch_id [dap_launch $testfile] + +set obj [dap_check_request_and_response "set breakpoint on all_started function" \ + setFunctionBreakpoints \ + {o breakpoints [a [o name [s all_started]]]}] +set fn_bpno [dap_get_breakpoint_number $obj] + +dap_check_request_and_response "configurationDone" configurationDone + +dap_check_response "launch response" launch $launch_id + +lassign [dap_wait_for_event_and_check "stopped at function breakpoint" \ + stopped \ + "body reason" breakpoint \ + "body hitBreakpointIds" $fn_bpno] \ + ignore \ + all_events + +# Verify that we saw the correct number of thread events. +set count 0 +foreach event $all_events { + if {[dict get $event type] == "event" + && [dict get $event event] == "thread" + && [dict get $event body reason] == "started"} { + incr count + } +} +gdb_assert {$count == 3} "correct number of thread events" + +dap_check_request_and_response "continue" continue \ + {o threadId [i 1]} + +# Make sure that the inferior has really re-started -- note that there +# is no "continue" event, because the "continue" request suppresses +# those. +dap_wait_for_event_and_check "output from inferior" output \ + {body output} "sleeping\\n" + +lassign [dap_check_request_and_response "threads request" threads] \ + response ignore + +gdb_assert {[llength [dict get $response body threads]] == 3} \ + "correct number of threads" + +dap_shutdown true diff --git a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp index 93f8f92..35bb401 100644 --- a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp +++ b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp @@ -46,7 +46,7 @@ set build_id_debug_file \ [standard_output_file [build_id_debug_filename_get $binfile]] # Get the BINFILE.debug filename. This is the file we should be -# moving to the BUILD_ID_DEBUG_FILE location, but we wont, we're going +# moving to the BUILD_ID_DEBUG_FILE location, but we won't, we're going # to move something else there instead. set debugfile [standard_output_file "${binfile}.debug"] diff --git a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp index 78fa252..dac4e6c 100644 --- a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp +++ b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp @@ -49,7 +49,7 @@ # # This obviously needs fixing, but is a separate problem from the one being # tested here, so this test deliberately checks the mapping using a file that -# is mmaped rather than loaded as a shared library, as such the file is in the +# is mmapped rather than loaded as a shared library, as such the file is in the # core-files list of mapped files, but is not in the shared library list. # # Despite this test living in the gdb.debuginfod/ directory, only the last @@ -260,7 +260,7 @@ proc load_core_file { testname { line_re "" } } { # We expect RES to be 2 (TCL_RETURN) or 1 (TCL_ERROR). If we get # here then somehow the 'catch' above finished without hitting # either of those cases, which is .... weird. - perror "unexepcted return value, code = $res, value = $string" + perror "unexpected return value, code = $res, value = $string" return -1 } } diff --git a/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp b/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp index 9f1842c..1008e46 100644 --- a/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp +++ b/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp @@ -89,7 +89,7 @@ if {[lindex $status 0] != 0} { } # Build the executable. This links against libfoo.so, which is -# poining at libfoo_1.so. Just to confuse things even more, this +# pointing at libfoo_1.so. Just to confuse things even more, this # executable uses dlopen to load libfoo_2.so. Weird! if { [build_executable "build executable" ${binfile} ${srcfile2} \ [list debug shlib=${library_filename} shlib_load]] == -1 } { diff --git a/gdb/testsuite/gdb.dwarf2/ada-array-bound.c b/gdb/testsuite/gdb.dwarf2/ada-array-bound.c new file mode 100644 index 0000000..5a7d397 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/ada-array-bound.c @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +/* The data used for the structure. */ + +unsigned char our_data[] = { 3, 7, 11, 13 }; + +/* Dummy main function. */ + +int +main() +{ + asm ("main_label: .globl main_label"); + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp b/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp new file mode 100644 index 0000000..f48df7b --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/ada-array-bound.exp @@ -0,0 +1,89 @@ +# Copyright 2025 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/>. + +# Test handling of an array type whose bound comes from the field of a +# structure. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile .c -debug.S + +# Set up the DWARF for the test. + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_Ada95} + {DW_AT_name $srcfile} + } { + declare_labels byte array disc struct + + byte: DW_TAG_base_type { + {DW_AT_byte_size 1 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_unsigned} + {DW_AT_name byte} + } + + array: DW_TAG_array_type { + {DW_AT_name array_type} + {DW_AT_type :$byte} + } { + DW_TAG_subrange_type { + {DW_AT_type :$byte} + {DW_AT_upper_bound :$disc} + } + } + + struct: DW_TAG_structure_type { + {DW_AT_name discriminated} + {DW_AT_byte_size 4 DW_FORM_sdata} + } { + disc: DW_TAG_member { + {DW_AT_name disc} + {DW_AT_type :$byte} + {DW_AT_data_member_location 0 DW_FORM_sdata} + } + DW_TAG_member { + {DW_AT_name nums} + {DW_AT_type :$array} + {DW_AT_data_member_location 1 DW_FORM_sdata} + } + } + + DW_TAG_variable { + {DW_AT_name "value"} + {DW_AT_type :$struct} + {DW_AT_external 1 DW_FORM_flag} + {DW_AT_location {DW_OP_addr [gdb_target_symbol "our_data"]} + SPECIAL_expr} + } + } + } +} + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} + +gdb_test_no_output "set language ada" +gdb_test "print value" \ + [string_to_regexp " = (disc => 3, nums => (7, 11, 13))"] diff --git a/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp b/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp index f2123fa..cb24b19 100644 --- a/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx-out-of-bounds.exp @@ -18,7 +18,13 @@ # Out of bounds index. set int_str_idx 1 +# With readnow, the dwarf error is printed during the file command, so skip +# the test. +require !readnow + +set prepare_for_testing_done 0 source $srcdir/$subdir/dw-form-strx.exp.tcl +require {expr $prepare_for_testing_done == 1} set re_dwarf_error \ [string_list_to_regexp \ diff --git a/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp index 9b62edf..3f739c4 100644 --- a/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp @@ -18,6 +18,8 @@ # Correct index. set int_str_idx 0 +set prepare_for_testing_done 0 source $srcdir/$subdir/dw-form-strx.exp.tcl +require {expr $prepare_for_testing_done == 1} gdb_test "ptype global_var" "type = int" diff --git a/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl index 15cfe27..bc5a654 100644 --- a/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl +++ b/gdb/testsuite/gdb.dwarf2/dw-form-strx.exp.tcl @@ -24,15 +24,15 @@ set asm_file [standard_output_file $srcfile2] # Debug info in the main file. Dwarf::assemble $asm_file { - declare_labels base_offset + declare_labels base_offset_cu1 - debug_str_offsets base_offset int + debug_str_offsets { base_offset base_offset_cu1 } int cu { version 5 } { DW_TAG_compile_unit { - {DW_AT_str_offsets_base $base_offset sec_offset} + {DW_AT_str_offsets_base $base_offset_cu1 sec_offset} } { declare_labels int4_type @@ -56,5 +56,9 @@ Dwarf::assemble $asm_file { if { [prepare_for_testing "failed to prepare" ${testfile} \ [list $srcfile $asm_file] {nodebug}] } { - return -1 + return } + +# Let includers know prepare_for_testing was done, without having to check +# source return status. +set prepare_for_testing_done 1 diff --git a/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S b/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S index c09c6db..06a93ac 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S +++ b/gdb/testsuite/gdb.dwarf2/dw2-modula2-self-type.S @@ -114,7 +114,11 @@ die221: .byte 0x0 - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .LASF1: .string "2.mod" .LASF0: diff --git a/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp b/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp index ea0fc03..6120878 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-ranges-psym-warning.exp @@ -117,7 +117,7 @@ if ![runto_main] { # the hole is there in the symbol table, but not the partial symbol table, # we run into: # (gdb) bt -# warning: (Internal error: pc 0x555555554619 in read in psymtab, \ +# warning: (Internal error: pc 0x555555554619 in read in psymtab, # but not in symtab.) # ... # (gdb) diff --git a/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S b/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S index cd999f4..551dda7 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S +++ b/gdb/testsuite/gdb.dwarf2/dw2-simple-locdesc.S @@ -160,7 +160,11 @@ d: .byte 0 .byte 0 .byte 0 - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .LASF2: .string "GNU C 4.7.0 20110727 (experimental)" .LASF0: diff --git a/gdb/testsuite/gdb.dwarf2/dw2-strp.S b/gdb/testsuite/gdb.dwarf2/dw2-strp.S index c7ede95..db3e64f 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-strp.S +++ b/gdb/testsuite/gdb.dwarf2/dw2-strp.S @@ -163,7 +163,11 @@ .byte 0x0 /* Terminator */ /* String table */ - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .Lproducer: .string "GNU C 3.3.3" .Lchar_str: diff --git a/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp new file mode 100644 index 0000000..f4e02da --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dynamic-bit-offset.exp @@ -0,0 +1,95 @@ +# Copyright 2025 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/>. + +# Test DW_AT_data_bit_offset with an expression. This is a DWARF +# extension, but expected to be in DWARF 6. See +# https://dwarfstd.org/issues/250501.1.html + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile ada-array-bound.c -debug.S + +# Set up the DWARF for the test. + +set asm_file [standard_output_file $srcfile2] +Dwarf::assemble $asm_file { + global srcdir subdir srcfile + + cu {} { + DW_TAG_compile_unit { + {DW_AT_language @DW_LANG_Ada95} + {DW_AT_name $srcfile} + } { + declare_labels byte array struct + + byte: DW_TAG_base_type { + {DW_AT_byte_size 1 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_unsigned} + {DW_AT_name byte} + } + + array: DW_TAG_array_type { + {DW_AT_name array_type} + {DW_AT_type :$byte} + } { + DW_TAG_subrange_type { + {DW_AT_type :$byte} + {DW_AT_upper_bound 3 DW_FORM_sdata} + } + } + + struct: DW_TAG_structure_type { + {DW_AT_name discriminated} + {DW_AT_byte_size 4 DW_FORM_sdata} + } { + DW_TAG_member { + {DW_AT_name disc} + {DW_AT_type :$byte} + {DW_AT_data_member_location 0 DW_FORM_sdata} + } + + # We know this is always at offset 1 but use an + # expression just to test this code path. This is a + # DWARF extension. See + # https://dwarfstd.org/issues/250501.1.html. + DW_TAG_member { + {DW_AT_name nums} + {DW_AT_type :$array} + {DW_AT_data_bit_offset {DW_OP_lit8} SPECIAL_expr} + } + } + + DW_TAG_variable { + {DW_AT_name "value"} + {DW_AT_type :$struct} + {DW_AT_external 1 DW_FORM_flag} + {DW_AT_location {DW_OP_addr [gdb_target_symbol "our_data"]} + SPECIAL_expr} + } + } + } +} + +if {[prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}]} { + return -1 +} + +gdb_test_no_output "set language ada" +gdb_test "print value" \ + [string_to_regexp " = (disc => 3, nums => (7, 11, 13))"] diff --git a/gdb/testsuite/gdb.dwarf2/fission-dw-form-strx.exp b/gdb/testsuite/gdb.dwarf2/fission-dw-form-strx.exp new file mode 100644 index 0000000..4f5867c --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/fission-dw-form-strx.exp @@ -0,0 +1,88 @@ +# Copyright 2025 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/>. + +# Check support for a DW_FORM_strx attribute in a dwo file. + +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +standard_testfile main.c -dw.S -dwo.S + +set main_asm_file [standard_output_file $srcfile2] +set dwo_asm_file [standard_output_file $srcfile3] + +# Debug info in the main file. +Dwarf::assemble $main_asm_file { + cu { + version 5 + dwo_id 0xF00D + } { + compile_unit { + {DW_AT_dwo_name ${::gdb_test_file_name}.dwo DW_FORM_strp} + } {} + } +} + +# Debug info in the DWO file. +Dwarf::assemble $dwo_asm_file { + debug_str_offsets { dwo 1 } int + + cu { + fission 1 + version 5 + dwo_id 0xF00D + } { + compile_unit {} { + declare_labels int4_type + + int4_type: DW_TAG_base_type { + {DW_AT_byte_size 4 DW_FORM_sdata} + {DW_AT_encoding @DW_ATE_signed} + {DW_AT_name 0 DW_FORM_strx_id} + } + + DW_TAG_variable { + {DW_AT_name global_var} + {DW_AT_type :$int4_type} + {DW_AT_location { + DW_OP_const1u 12 + DW_OP_stack_value + } SPECIAL_expr} + } + } + } +} + +# Build main file. +if { [build_executable "${testfile}.exp" $binfile \ + [list ${srcfile} ${main_asm_file}] {nodebug}] } { + return +} + +# Build DWO file. +set dwo_file [standard_output_file ${testfile}.dwo] +if { [gdb_compile_shlib $dwo_asm_file $dwo_file nodebug] != "" } { + return +} + +if { [is_remote host] } { + gdb_remote_download host $dwo_file +} + +clean_restart $binfile + +gdb_test "ptype global_var" "type = int" diff --git a/gdb/testsuite/gdb.dwarf2/pr11465.S b/gdb/testsuite/gdb.dwarf2/pr11465.S index fed98bc..f3f2c57 100644 --- a/gdb/testsuite/gdb.dwarf2/pr11465.S +++ b/gdb/testsuite/gdb.dwarf2/pr11465.S @@ -344,7 +344,11 @@ die149: .uleb128 0x16 /* DW_TAG_variable */ .byte 0x0 .byte 0x0 .byte 0x0 - .section .debug_str +#ifdef __arm__ + .section .debug_str,"MS",%progbits,1 +#else + .section .debug_str,"MS",@progbits,1 +#endif .LASF0: .string "_ZN1N1fE" .LASF7: diff --git a/gdb/testsuite/gdb.guile/scm-cmd.exp b/gdb/testsuite/gdb.guile/scm-cmd.exp index 9caca24..3709cb1 100644 --- a/gdb/testsuite/gdb.guile/scm-cmd.exp +++ b/gdb/testsuite/gdb.guile/scm-cmd.exp @@ -71,6 +71,65 @@ gdb_test_multiline "input subcommand" \ gdb_test "prefix-cmd subcmd ugh" "subcmd output, arg = ugh" "call subcmd" +# Create a sub-command using a partial, but still unique, prefix. + +gdb_test_multiline "sub-command using partial prefix" \ + "guile" "" \ + "(register-command! (make-command \"prefix subcmd2\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display (format #f \"subcmd2 output, arg = ~a\\n\" arg)))))" "" \ + "end" "" + +gdb_test "prefix-cmd subcmd2 ugh" "subcmd2 output, arg = ugh" "call subcmd2" + +# Now create a second prefix, similar to the first. + +gdb_test_multiline "create prefix-xxx prefix command" \ + "guile" "" \ + "(register-command! (make-command \"prefix-xxx\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:completer-class COMPLETE_NONE" "" \ + " #:prefix? #t))" "" \ + "end" "" + +# Now create a sub-command using an ambiguous prefix. + +gdb_test_multiline "sub-command using ambiguous partial prefix" \ + "guile" "" \ + "(register-command! (make-command \"prefix subcmd3\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display (format #f \"subcmd3 output, arg = ~a\\n\" arg)))))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'prefix' in position 1: \"prefix subcmd3\"" \ + "Error while executing Scheme code\\."] + +# Check for errors when creating a command with an unknown prefix. + +gdb_test_multiline "try to create 'unknown-prefix subcmd'" \ + "guile" "" \ + "(register-command! (make-command \"unknown-prefix subcmd\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"called unknown-prefix subcmd\"))))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'unknown-prefix' in position 1: \"unknown-prefix subcmd\"" \ + "Error while executing Scheme code\\."] + +gdb_test_multiline "try to create 'prefix-cmd unknown-prefix subcmd'" \ + "guile" "" \ + "(register-command! (make-command \"prefix-cmd unknown-prefix subcmd\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"called prefix-cmd unknown-prefix subcmd\"))))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'prefix-cmd unknown-prefix' in position 1: \"prefix-cmd unknown-prefix subcmd\"" \ + "Error while executing Scheme code\\."] + # Test a subcommand in an existing GDB prefix. gdb_test_multiline "input new subcommand" \ diff --git a/gdb/testsuite/gdb.guile/scm-frame.exp b/gdb/testsuite/gdb.guile/scm-frame.exp index b5ec73f..9a27c42 100644 --- a/gdb/testsuite/gdb.guile/scm-frame.exp +++ b/gdb/testsuite/gdb.guile/scm-frame.exp @@ -52,7 +52,7 @@ gdb_test "guile (print (frame-read-var bf1 \"b\"))" \ "\"bar\"" "test b" # Test the read-var function in another block other than the current -# block (in this case, the super block). Test thar read-var is reading +# block (in this case, the super block). Test that read-var is reading # the correct variables of i and f but they are the correct value and type. gdb_scm_test_silent_cmd "guile (define sb (block-superblock (frame-block bf1)))" \ "get superblock" diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index 8ab5d93..e35428a 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -67,9 +67,19 @@ with_test_prefix "test-param" { gdb_test_no_output "set print test-param off" gdb_test "show print test-param" "The state of the Test Parameter is off." "show parameter off" gdb_test "guile (print (parameter-value test-param))" "= #f" "parameter value, false" - gdb_test "help show print test-param" "Show the state of the boolean test-param.*" "show help" - gdb_test "help set print test-param" "Set the state of the boolean test-param.*" "set help" - gdb_test "help set print" "set print test-param -- Set the state of the boolean test-param.*" "general help" + gdb_test "help show print test-param" \ + [multi_line \ + "^Show the state of the boolean test-param\\." \ + "When enabled, test param does something useful\\. When disabled, does nothing\\."] \ + "show help" + gdb_test "help set print test-param" \ + [multi_line \ + "^Set the state of the boolean test-param\\." \ + "When enabled, test param does something useful\\. When disabled, does nothing\\."] \ + "set help" + gdb_test "help set print" \ + "set print test-param -- Set the state of the boolean test-param.*" \ + "general help" gdb_test "guile (print (parameter? test-param))" "= #t" gdb_test "guile (print (parameter? 42))" "= #f" @@ -314,9 +324,17 @@ with_test_prefix "test-undocumented-param" { gdb_test "show print test-undoc-param" "The state of the Test Parameter is on." "show parameter on" gdb_test_no_output "set print test-undoc-param off" gdb_test "show print test-undoc-param" "The state of the Test Parameter is off." "show parameter off" - gdb_test "help show print test-undoc-param" "This command is not documented." "show help" - gdb_test "help set print test-undoc-param" "This command is not documented." "set help" - gdb_test "help set print" "set print test-undoc-param -- This command is not documented.*" "general help" + gdb_test "help show print test-undoc-param" \ + [multi_line \ + "^Show the current value of 'print test-undoc-param'\\." \ + "This command is not documented\\."] \ + "show help" + gdb_test "help set print test-undoc-param" \ + [multi_line \ + "Set the current value of 'print test-undoc-param'\\." \ + "This command is not documented\\."] \ + "set help" + gdb_test "help set print" "set print test-undoc-param -- Set the current value of 'print test-undoc-param'\\..*" "general help" } # Test a parameter with a restricted range, where we need to notify the user @@ -379,13 +397,168 @@ gdb_test_no_output "guile (register-parameter! prev-ambig)" with_test_prefix "previously-ambiguous" { gdb_test "guile (print (parameter-value prev-ambig))" "= #f" "parameter value, false" - gdb_test "show print s" "Command is not documented is off." "show parameter off" + gdb_test "show print s" \ + "The current value of 'print s' is off\\." "show parameter off" gdb_test_no_output "set print s on" - gdb_test "show print s" "Command is not documented is on." "show parameter on" + gdb_test "show print s" \ + "The current value of 'print s' is on\\." "show parameter on" gdb_test "guile (print (parameter-value prev-ambig))" "= #t" "parameter value, true" - gdb_test "help show print s" "This command is not documented." "show help" - gdb_test "help set print s" "This command is not documented." "set help" - gdb_test "help set print" "set print s -- This command is not documented.*" "general help" + gdb_test "help show print s" \ + [multi_line \ + "^Show the current value of 'print s'\\." \ + "This command is not documented\\."] \ + "show help" + gdb_test "help set print s" \ + [multi_line \ + "Set the current value of 'print s'\\." \ + "This command is not documented\\."] \ + "set help" + gdb_test "help set print" \ + "set print s -- Set the current value of 'print s'\\..*" \ + "general help" +} + +gdb_test_multiline "create set/show foo1 prefix commands" \ + "guile" "" \ + "(register-command! (make-command \"set foo1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "(register-command! (make-command \"show foo1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "end" + +gdb_test_multiline "create set/show foo1 baz1 prefix commands" \ + "guile" "" \ + "(register-command! (make-command \"set foo1 baz1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "(register-command! (make-command \"show foo1 baz1\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "end" + +gdb_test_multiline "create 'foo bar' parameter" \ + "guile" "" \ + "(register-parameter! (make-parameter \"foo bar\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:parameter-type PARAM_BOOLEAN" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of 'foo bar' is ~a.\" value))" "" \ + " #:initial-value #t))" "" \ + "end" + +gdb_test "show foo1 bar" "^The state of 'foo bar' is on\\." "show parameter 'foo bar'" + +gdb_test_multiline "create set/show foo2 prefix commands" \ + "guile" "" \ + "(register-command! (make-command \"set foo2\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "(register-command! (make-command \"show foo2\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:prefix? #t))" "" \ + "end" "" + +gdb_test_multiline "create ambiguous 'foo baz' parameter" \ + "guile" "" \ + "(register-parameter! (make-parameter \"foo baz\"" "" \ + " #:command-class COMMAND_OBSCURE" "" \ + " #:parameter-type PARAM_BOOLEAN" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of 'foo baz' is ~a.\" value))" "" \ + " #:initial-value #t))" "" \ + "end" \ + [multi_line \ + "Out of range: could not find command prefix 'foo' in position 1: \"foo baz\"" \ + "Error while executing Scheme code."] + +with_test_prefix "empty doc string" { + gdb_test_multiline "empty doc string parameter" \ + "guile" "" \ + "(register-parameter! (make-parameter \"empty-doc-string\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_ZINTEGER" "" \ + " #:doc \"\"" "" \ + " #:set-doc \"Set doc string.\"" "" \ + " #:show-doc \"Show doc string.\"))" "" \ + "end" + + gdb_test "help set empty-doc-string" "^Set doc string\\." + gdb_test "help show empty-doc-string" "^Show doc string\\." +} + +with_test_prefix "set/show parameter" { + # This first set/show prefix command doesn't have an invoke + # method. As such, GDB installs the default invoke behaviour; set + # prints the full list of sub-commands, and show prints all the + # sub-command values. + gdb_test_multiline "Setup set/show parameter prefix with no invoke" \ + "guile" "" \ + "(register-command! (make-command \"set test-prefix\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE))" ""\ + "(register-command! (make-command \"show test-prefix\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE))" ""\ + "(register-parameter! (make-parameter \"test-prefix param-1\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_BOOLEAN))" "" \ + "(register-parameter! (make-parameter \"test-prefix param-2\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_UINTEGER))" "" \ + "(register-parameter! (make-parameter \"test-prefix param-3\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_STRING))" "" \ + "end" "" + + gdb_test "set test-prefix" \ + [multi_line \ + "List of \"set test-prefix\" subcommands:" \ + "" \ + "set test-prefix param-1 -- Set the current value of 'test-prefix param-1'." \ + "set test-prefix param-2 -- Set the current value of 'test-prefix param-2'." \ + "set test-prefix param-3 -- Set the current value of 'test-prefix param-3'." \ + "" \ + "Type \"help set test-prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] + + gdb_test "show test-prefix" \ + [multi_line \ + "test-prefix param-1: The current value of 'test-prefix param-1' is off\\." \ + "test-prefix param-2: The current value of 'test-prefix param-2' is 0\\." \ + "test-prefix param-3: The current value of 'test-prefix param-3' is \"\"\\."] + + # This next set/show prefix has an invoke method, which will be + # called instead of the default behaviour tested above. + gdb_test_multiline "Setup set/show parameter prefix with invoke" \ + "guile" "" \ + "(register-command! (make-command \"set test-prefix-2\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE" ""\ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"invoke -- set\\n\"))))" "" \ + "(register-command! (make-command \"show test-prefix-2\"" "" \ + " #:prefix? #t" "" \ + " #:command-class COMMAND_NONE" ""\ + " #:invoke (lambda (self arg from-tty)" "" \ + " (display \"invoke -- show\\n\"))))" "" \ + "(register-parameter! (make-parameter \"test-prefix-2 param-1\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_BOOLEAN))" "" \ + "(register-parameter! (make-parameter \"test-prefix-2 param-2\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_UINTEGER))" "" \ + "(register-parameter! (make-parameter \"test-prefix-2 param-3\"" "" \ + " #:command-class COMMAND_NONE" "" \ + " #:parameter-type PARAM_STRING))" "" \ + "end" "" + + gdb_test "set test-prefix-2" "^invoke -- set" + + gdb_test "show test-prefix-2" "^invoke -- show" } rename scm_param_test_maybe_no_output "" diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp index 69744dd..86b55bb 100644 --- a/gdb/testsuite/gdb.linespec/linespec.exp +++ b/gdb/testsuite/gdb.linespec/linespec.exp @@ -194,6 +194,12 @@ gdb_test "break lspec.h:$line" \ "Breakpoint \[0-9\]+ at $hex: file .*lspec.h, line $line." \ "set breakpoint in f1" +# This should only have a single location -- in no_multi_locs. +set line [gdb_get_line_number no_multi_locs] +gdb_test "break $line" \ + "Breakpoint \[0-9\]+ at $hex: file .*$srcfile, line $line." \ + "set breakpoint at no_multi_locs" + # # Multi-inferior tests. # diff --git a/gdb/testsuite/gdb.linespec/lspec.cc b/gdb/testsuite/gdb.linespec/lspec.cc index bb660fb..ab0a193 100644 --- a/gdb/testsuite/gdb.linespec/lspec.cc +++ b/gdb/testsuite/gdb.linespec/lspec.cc @@ -13,6 +13,8 @@ int body_elsewhere() #include "body.h" } +void no_multi_locs () { {int var = 0;} } + int main() { return dupname(0) + m(0) + n(0) + f1() + f2() + body_elsewhere(); diff --git a/gdb/testsuite/gdb.mi/interrupt-thread-group.exp b/gdb/testsuite/gdb.mi/interrupt-thread-group.exp index ff35109..869fb1c 100644 --- a/gdb/testsuite/gdb.mi/interrupt-thread-group.exp +++ b/gdb/testsuite/gdb.mi/interrupt-thread-group.exp @@ -54,7 +54,7 @@ mi_send_resuming_command "exec-continue --thread-group i1" \ # We can't run a second inferior on stub targets. We can still test with one # inferior and ensure that the command has the desired effect. -set use_second_inferior [expr {![use_gdb_stub]}] +set use_second_inferior [expr {![use_gdb_stub] && [allow_multi_inferior_tests]}] if { $use_second_inferior } { mi_gdb_test "-add-inferior" \ diff --git a/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp b/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp index 9897b2b..0a89a8a 100644 --- a/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp +++ b/gdb/testsuite/gdb.mi/mi-condbreak-throw.exp @@ -16,7 +16,7 @@ # Check that when GDB fails to evaluate the condition of a conditional # breakpoint we only get one *stopped notification. In this test case # the breakpoint condition fails due to throwing an uncaught C++ -# excpetion. +# exception. require allow_cplus_tests diff --git a/gdb/testsuite/gdb.mi/mi-multi-commands.exp b/gdb/testsuite/gdb.mi/mi-multi-commands.exp index 20b8d46..3bc63eb 100644 --- a/gdb/testsuite/gdb.mi/mi-multi-commands.exp +++ b/gdb/testsuite/gdb.mi/mi-multi-commands.exp @@ -90,7 +90,7 @@ proc run_test { args } { # looking for. However, due to the unpredictable # intermingling, it's much easier if we drop the ^ anchor. # However, with this gone dejagnu would sometimes match the - # second comand output before the first commands output. + # second command output before the first commands output. # # This approach just looks for the first command output, then, # once that has been found, we start looking for the second diff --git a/gdb/testsuite/gdb.mi/mi-var-display.exp b/gdb/testsuite/gdb.mi/mi-var-display.exp index 61b3894..5535368 100644 --- a/gdb/testsuite/gdb.mi/mi-var-display.exp +++ b/gdb/testsuite/gdb.mi/mi-var-display.exp @@ -96,7 +96,7 @@ mi_gdb_test "-var-evaluate-expression bar" \ # Desc: change value of bar mi_gdb_test "-var-assign bar 3" \ "\\^done,value=\"0x3\"" \ - "assing to variable bar" + "assign to variable bar" mi_gdb_test "-var-set-format bar decimal" \ "\\^done,format=\"decimal\",value=\"3\"" \ @@ -152,7 +152,7 @@ mi_gdb_test "-var-evaluate-expression foo" \ # Desc: change value of foo mi_gdb_test "-var-assign foo 3" \ "\\^done,value=\"03\"" \ - "assing to variable foo" + "assign to variable foo" mi_gdb_test "-var-set-format foo decimal" \ "\\^done,format=\"decimal\",value=\"3\"" \ diff --git a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp index 9198cfb..f85e108 100644 --- a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp +++ b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp @@ -40,6 +40,8 @@ standard_testfile # gdbserver modes are supported. require !use_gdb_stub +require allow_multi_inferior_tests + set compile_options "debug pthreads" if {[build_executable $testfile.exp $testfile ${srcfile} ${compile_options}] == -1} { untested "failed to compile" @@ -985,7 +987,7 @@ proc_with_prefix test_mi_stack_select_frame { mode } { # Now use the '-stack-select-frame' command with the --frame # option, this verifies that even when the frame GDB would - # swith to is the same as the frame specified with --frame, an + # switch to is the same as the frame specified with --frame, an # event is still sent to the CLI. set cli_re [make_cli_re $mode -1 -1 0] diff --git a/gdb/testsuite/gdb.multi/attach-no-multi-process.exp b/gdb/testsuite/gdb.multi/attach-no-multi-process.exp index 28aabaf..502f309 100644 --- a/gdb/testsuite/gdb.multi/attach-no-multi-process.exp +++ b/gdb/testsuite/gdb.multi/attach-no-multi-process.exp @@ -59,7 +59,10 @@ proc test {target_non_stop} { "switch to inferior 2" set res [gdbserver_start "--multi" ""] set gdbserver_gdbport [lindex $res 1] - gdb_target_cmd "extended-remote" $gdbserver_gdbport + if { [gdb_target_cmd_ext "extended-remote" $gdbserver_gdbport] == 2 } { + unsupported "non-stop RSP" + return + } # Start a program, then attach to it. set spawn_id_list [spawn_wait_for_attach [list $binfile]] diff --git a/gdb/testsuite/gdb.multi/attach-while-running.exp b/gdb/testsuite/gdb.multi/attach-while-running.exp index 5b63030..723ebb2 100644 --- a/gdb/testsuite/gdb.multi/attach-while-running.exp +++ b/gdb/testsuite/gdb.multi/attach-while-running.exp @@ -37,6 +37,7 @@ standard_testfile require can_spawn_for_attach +require allow_multi_inferior_tests if { [build_executable "failed to prepare" ${testfile} ${srcfile}] } { return @@ -49,7 +50,7 @@ proc do_test {} { } gdb_test -no-prompt-anchor "run &" - gdb_test "add-inferior" "Added inferior 2 on connection 1 .*" + gdb_test -no-prompt-anchor "add-inferior" "Added inferior 2 on connection 1 .*" gdb_test "inferior 2" "Switching to inferior 2 .*" set spawn_id [spawn_wait_for_attach $::binfile] diff --git a/gdb/testsuite/gdb.multi/bp-thread-specific.exp b/gdb/testsuite/gdb.multi/bp-thread-specific.exp index 32b7602..3fe4c20 100644 --- a/gdb/testsuite/gdb.multi/bp-thread-specific.exp +++ b/gdb/testsuite/gdb.multi/bp-thread-specific.exp @@ -19,6 +19,8 @@ # Also check that the correct thread-ids are used in the saved # breakpoints file. +require allow_multi_inferior_tests + # The plain remote target can't do multiple inferiors. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.multi/dummy-frame-restore.exp b/gdb/testsuite/gdb.multi/dummy-frame-restore.exp index 1a9d413..4119e3f 100644 --- a/gdb/testsuite/gdb.multi/dummy-frame-restore.exp +++ b/gdb/testsuite/gdb.multi/dummy-frame-restore.exp @@ -19,6 +19,8 @@ set executable ${testfile} # The plain remote target can't do multiple inferiors. require !use_gdb_stub +require allow_multi_inferior_tests + if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug}]} { return -1 } diff --git a/gdb/testsuite/gdb.multi/multi-arch.exp b/gdb/testsuite/gdb.multi/multi-arch.exp index d0ae511..1d41ba5 100644 --- a/gdb/testsuite/gdb.multi/multi-arch.exp +++ b/gdb/testsuite/gdb.multi/multi-arch.exp @@ -18,6 +18,8 @@ set testfile "multi-arch" +require allow_multi_inferior_tests + # The plain remote target can't do multiple inferiors. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.multi/multi-attach.exp b/gdb/testsuite/gdb.multi/multi-attach.exp index 2d702ee..210c8ca 100644 --- a/gdb/testsuite/gdb.multi/multi-attach.exp +++ b/gdb/testsuite/gdb.multi/multi-attach.exp @@ -17,6 +17,8 @@ # Test attaching to multiple threaded programs. +require allow_multi_inferior_tests + standard_testfile require can_spawn_for_attach diff --git a/gdb/testsuite/gdb.multi/multi-exit.exp b/gdb/testsuite/gdb.multi/multi-exit.exp index 71236c1..8393067 100644 --- a/gdb/testsuite/gdb.multi/multi-exit.exp +++ b/gdb/testsuite/gdb.multi/multi-exit.exp @@ -24,6 +24,8 @@ standard_testfile +require allow_multi_inferior_tests + require !use_gdb_stub if {[build_executable "failed to prepare" $testfile $srcfile]} { diff --git a/gdb/testsuite/gdb.multi/multi-kill.exp b/gdb/testsuite/gdb.multi/multi-kill.exp index 48a2534..7d66ab0 100644 --- a/gdb/testsuite/gdb.multi/multi-kill.exp +++ b/gdb/testsuite/gdb.multi/multi-kill.exp @@ -24,6 +24,8 @@ standard_testfile +require allow_multi_inferior_tests + require !use_gdb_stub if {[build_executable "failed to prepare" $testfile $srcfile {debug}]} { diff --git a/gdb/testsuite/gdb.multi/multi-re-run.exp b/gdb/testsuite/gdb.multi/multi-re-run.exp index 002de57..4caadea 100644 --- a/gdb/testsuite/gdb.multi/multi-re-run.exp +++ b/gdb/testsuite/gdb.multi/multi-re-run.exp @@ -20,6 +20,8 @@ # misbehave, including failing to load libthread_db.so. See PR # gdb/25410. +require allow_multi_inferior_tests + # Build two executables, with different symbols. set exec1 "multi-re-run-1" diff --git a/gdb/testsuite/gdb.multi/multi-target.exp.tcl b/gdb/testsuite/gdb.multi/multi-target.exp.tcl index 8c24602..dc88ca4 100644 --- a/gdb/testsuite/gdb.multi/multi-target.exp.tcl +++ b/gdb/testsuite/gdb.multi/multi-target.exp.tcl @@ -175,6 +175,10 @@ proc multi_target_prepare {} { return 0 } + if {![allow_multi_inferior_tests]} { + return 0 + } + # The plain remote target can't do multiple inferiors. if {[target_info gdb_protocol] != ""} { return 0 diff --git a/gdb/testsuite/gdb.multi/multi-term-settings.exp b/gdb/testsuite/gdb.multi/multi-term-settings.exp index 2f8b3b8..38322be 100644 --- a/gdb/testsuite/gdb.multi/multi-term-settings.exp +++ b/gdb/testsuite/gdb.multi/multi-term-settings.exp @@ -25,6 +25,8 @@ standard_testfile +require allow_multi_inferior_tests + require can_spawn_for_attach if [build_executable "failed to prepare" $testfile $srcfile {debug}] { diff --git a/gdb/testsuite/gdb.multi/start-inferior-specific.exp b/gdb/testsuite/gdb.multi/start-inferior-specific.exp index 819c1c3..74f984c 100644 --- a/gdb/testsuite/gdb.multi/start-inferior-specific.exp +++ b/gdb/testsuite/gdb.multi/start-inferior-specific.exp @@ -25,6 +25,8 @@ standard_testfile .c -other.c +require allow_multi_inferior_tests + require !use_gdb_stub set srcfile_other ${srcfile2} diff --git a/gdb/testsuite/gdb.multi/stop-all-on-exit.exp b/gdb/testsuite/gdb.multi/stop-all-on-exit.exp index b4ff09c..47071f3 100644 --- a/gdb/testsuite/gdb.multi/stop-all-on-exit.exp +++ b/gdb/testsuite/gdb.multi/stop-all-on-exit.exp @@ -18,6 +18,8 @@ # Test that in all-stop mode with multiple inferiors, GDB stops all # threads upon receiving an exit event from one of the inferiors. +require allow_multi_inferior_tests + # This is a test specific for a native target, where we use the # "-exec" argument to "add-inferior" and we explicitly don't do # "maint set target-non-stop on". diff --git a/gdb/testsuite/gdb.multi/tids-gid-reset.exp b/gdb/testsuite/gdb.multi/tids-gid-reset.exp index 6cc27eb..1785ac2 100644 --- a/gdb/testsuite/gdb.multi/tids-gid-reset.exp +++ b/gdb/testsuite/gdb.multi/tids-gid-reset.exp @@ -54,6 +54,8 @@ with_test_prefix "single-inferior" { # non-extended gdbserver is not supported. require !use_gdb_stub +require allow_multi_inferior_tests + # Test with multiple inferiors. This time, since we restart inferior # 1 while inferior 2 still has threads, then the new thread 1.1 should # end up with GID == 3, since we won't be able to reset the global diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp index b84f908..436a38a 100644 --- a/gdb/testsuite/gdb.multi/tids.exp +++ b/gdb/testsuite/gdb.multi/tids.exp @@ -124,6 +124,9 @@ with_test_prefix "single inferior" { gdb_test "print \$_inferior_thread_count" " = 1" } +# The rest of the tests require running multiple inferiors. +require allow_multi_inferior_tests + # "info threads" while there are multiple inferiors should show # qualified thread IDs. with_test_prefix "two inferiors" { @@ -290,7 +293,7 @@ with_test_prefix "two inferiors" { # Try both the convenience variable and the literal number. foreach thr {"\$thr" "20" "1.20" "\$inf.1" "30.1" } { set expected [string_to_regexp $thr] - gdb_test "info threads $thr" "No threads match '${expected}'." + gdb_test "info threads $thr" "No threads matched\\." # "info threads" works like a filter. If there's any other # valid thread in the list, there's no error. info_threads "$thr 1.1" "1.1" @@ -412,7 +415,7 @@ with_test_prefix "two inferiors" { # Check that we do parse the inferior number and don't confuse it. gdb_test "info threads 3.1" \ - "No threads match '3.1'\." + "No threads matched\\." } if { [allow_python_tests] } { diff --git a/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp b/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp index 70c3da9..3446296 100644 --- a/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp +++ b/gdb/testsuite/gdb.multi/watchpoint-multi-exit.exp @@ -17,6 +17,8 @@ # watchpoints don't end up with stale locations, preventing resumption # of other inferiors. +require allow_fork_tests + standard_testfile if {[build_executable "failed to build" $testfile $srcfile {debug}]} { diff --git a/gdb/testsuite/gdb.multi/watchpoint-multi.exp b/gdb/testsuite/gdb.multi/watchpoint-multi.exp index f448689..b0c8731 100644 --- a/gdb/testsuite/gdb.multi/watchpoint-multi.exp +++ b/gdb/testsuite/gdb.multi/watchpoint-multi.exp @@ -16,6 +16,8 @@ standard_testfile set executable ${testfile} +require allow_multi_inferior_tests + # Multiple inferiors are needed, therefore both native and extended gdbserver # modes are supported. Only non-extended gdbserver is not supported. require !use_gdb_stub diff --git a/gdb/testsuite/gdb.opt/break-on-_exit.exp b/gdb/testsuite/gdb.opt/break-on-_exit.exp index 2b94be8..9295fea 100644 --- a/gdb/testsuite/gdb.opt/break-on-_exit.exp +++ b/gdb/testsuite/gdb.opt/break-on-_exit.exp @@ -25,7 +25,7 @@ # for libc, then the breakpoint is set on the exec-local _exit@plt instead, # and that functionality will also not be used. # -# We may get the required setup in case of a libc with misssing separate +# We may get the required setup in case of a libc with missing separate # debuginfo, but we want the same effect if that debuginfo is installed. # # So, we use -readnever to read minimal symbols, but not non-miminal symbols. diff --git a/gdb/testsuite/gdb.pascal/integers.exp b/gdb/testsuite/gdb.pascal/integers.exp index 5c878c5..c9974a1 100644 --- a/gdb/testsuite/gdb.pascal/integers.exp +++ b/gdb/testsuite/gdb.pascal/integers.exp @@ -59,7 +59,7 @@ gdb_test "next" "l := k;" "next to 'l := k' line" gdb_test "print j" " = 2" # k should be equal to 3 gdb_test "print k" " = 3" -# But l shoud still be zero +# But l should still be zero if { $pascal_compiler_is_gpc } { setup_xfail *-*-* } diff --git a/gdb/testsuite/gdb.python/py-cmd.exp b/gdb/testsuite/gdb.python/py-cmd.exp index f76c176..5ac5712 100644 --- a/gdb/testsuite/gdb.python/py-cmd.exp +++ b/gdb/testsuite/gdb.python/py-cmd.exp @@ -328,4 +328,89 @@ proc_with_prefix test_command_redefining_itself {} { "call command redefining itself 2" } +# Try to create commands using unknown prefixes and check GDB gives an +# error. There's also a test in here for an ambiguous prefix, which +# gives the same error. +proc_with_prefix test_unknown_prefix {} { + clean_restart + + gdb_test_no_output "python gdb.Command('foo1', gdb.COMMAND_NONE, prefix=True)" + gdb_test_no_output "python gdb.Command('foo cmd', gdb.COMMAND_NONE)" + + foreach prefix { "xxx" "foo xxx" "foo1 xxx" } { + gdb_test "python gdb.Command('$prefix cmd', gdb.COMMAND_NONE)" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } + + gdb_test_no_output "python gdb.Command('foo2', gdb.COMMAND_NONE, prefix=True)" + + foreach prefix { "foo" "foo xxx" "foo1 xxx" "foo2 xxx" } { + gdb_test "python gdb.Command('$prefix cmd2', gdb.COMMAND_NONE)" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } +} + +# Check what happens if a command object is called without an 'invoke' +# method. +proc_with_prefix test_deleting_invoke_methods {} { + clean_restart + + gdb_test_multiline "create 'foo' prefix command" \ + "python" "" \ + "class test_prefix(gdb.Command):" "" \ + " def __init__ (self):" "" \ + " super().__init__ (\"foo\", gdb.COMMAND_USER, prefix=True)" "" \ + " def invoke (self, arg, from_tty):" "" \ + " print(\"In 'foo' invoke: %s\" % arg)" "" \ + "foo = test_prefix()" "" \ + "end" "" + + gdb_test_multiline "create 'foo bar' command" \ + "python" "" \ + "class test_cmd(gdb.Command):" "" \ + " def __init__ (self):" "" \ + " super().__init__ (\"foo bar\", gdb.COMMAND_USER)" "" \ + " def invoke (self, arg, from_tty):" "" \ + " print(\"In 'foo bar' invoke: %s\" % arg)" "" \ + "foo_bar = test_cmd()" "" \ + "end" "" + + gdb_test "foo def" "In 'foo' invoke: def" \ + "call 'foo' with an unknown sub-command" + + gdb_test "foo bar def" "In 'foo bar' invoke: def" \ + "call 'foo bar' with arguments" + + gdb_test_no_output "python del(foo_bar.__class__.invoke)" \ + "delete invoke from test_cmd class" + + with_test_prefix "after deleting test_cmd.invoke" { + gdb_test "foo def" "In 'foo' invoke: def" \ + "call 'foo' with an unknown sub-command" + + gdb_test "foo bar def" \ + "^Python command object missing 'invoke' method\\." \ + "call 'foo bar' with arguments" + } + + gdb_test_no_output "python del(foo.__class__.invoke)" \ + "delete invoke from test_prefix class" + + with_test_prefix "after deleting test_prefix.invoke" { + gdb_test "foo def" \ + "^Python command object missing 'invoke' method\\." \ + "call 'foo' with an unknown sub-command" + + gdb_test "foo bar def" \ + "^Python command object missing 'invoke' method\\." \ + "call 'foo bar' with arguments" + } +} + test_command_redefining_itself +test_unknown_prefix +test_deleting_invoke_methods diff --git a/gdb/testsuite/gdb.python/py-parameter-prefix.exp b/gdb/testsuite/gdb.python/py-parameter-prefix.exp new file mode 100644 index 0000000..eb09fe7 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-parameter-prefix.exp @@ -0,0 +1,382 @@ +# Copyright (C) 2025 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 file is part of the GDB testsuite. It tests +# gdb.ParameterPrefix. See each of the test procs for a full +# description of what is being tested. + +load_lib gdb-python.exp + +require allow_python_tests + +clean_restart + +# Helper proc to generate the output of 'show PREFIX' commands for the +# case where the prefix command doesn't handle unknown sub-commands. +# In this case GDB will list the value of every sub-command under +# PREFIX. +proc make_show_prefix_re { prefix } { + return "$prefix param-1:\\s+The current value of '$prefix param-1' is \"off\"\\." +} + +# Helper proc to generate the help text that describes all of the sub +# commands under PREFIX. The MODE is either 'set' or 'show'. This +# output will appear for 'help MODE PREFIX' and also for 'set PREFIX'. +proc make_sub_cmd_help_re { mode prefix } { + if { $mode == "set" } { + set word "Set" + } else { + set word "Show" + } + + return \ + [multi_line \ + "List of \"$mode $prefix\" subcommands:" \ + "" \ + "$mode $prefix param-1 -- $word the current value of '$prefix param-1'\\." \ + "" \ + "Type \"help $mode $prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] +} + +# Helper proc to generate the output of 'help MODE PREFIX', where MODE +# will be either 'set' or 'show'. The HELP_TEXT is the expected help +# text for this prefix command, this should not be a regexp, as this +# proc converts the text to a regexp. +# +# Return a single regexp which should match the output. +proc make_help_re { mode prefix help_text } { + set help_re [string_to_regexp $help_text] + + return \ + [multi_line \ + "$help_re" \ + "" \ + [make_sub_cmd_help_re $mode $prefix]] +} + +# Create gdb.ParameterPrefix without using a sub-class, both with, and +# without a doc string. For the doc string case, test single line, +# and multi-line doc strings. +proc_with_prefix test_basic_usage {} { + gdb_test_multiline "some basic ParameterPrefix usage" \ + "python" "" \ + "gdb.ParameterPrefix('prefix-1', gdb.COMMAND_NONE)" "" \ + "gdb.Parameter('prefix-1 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-1 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.ParameterPrefix('prefix-2', gdb.COMMAND_NONE," "" \ + " \"\"\"This is prefix-2 help string.\"\"\")" "" \ + "gdb.Parameter('prefix-2 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.ParameterPrefix('prefix-3', gdb.COMMAND_NONE," "" \ + " \"\"\"This is prefix-3 help string." "" \ + " " "" \ + " This help text spans multiple lines.\"\"\")" "" \ + "gdb.Parameter('prefix-3 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + foreach mode { "set" "show" } { + gdb_test "help $mode prefix-1" \ + [make_help_re $mode "prefix-1" \ + "This command is not documented."] + + gdb_test "help $mode prefix-2" \ + [make_help_re $mode "prefix-2" \ + "This is prefix-2 help string."] + + gdb_test "help $mode prefix-3" \ + [make_help_re $mode "prefix-3" \ + [multi_line \ + "This is prefix-3 help string." \ + "" \ + "This help text spans multiple lines."]] + + foreach prefix { prefix-1 prefix-2 prefix-3 } { + gdb_test "$mode $prefix xxx" \ + "^Undefined $mode $prefix command: \"xxx\"\\. Try \"help $mode $prefix\"\\." + } + } + + foreach prefix { prefix-1 prefix-2 prefix-3 } { + gdb_test "set $prefix" \ + [make_sub_cmd_help_re "set" $prefix] + + gdb_test "show $prefix" \ + [make_show_prefix_re $prefix] + } +} + +# Create a sub-class of gdb.ParameterPrefix, but don't do anything +# particularly interesting. Again test the with and without +# documentation string cases. +proc_with_prefix test_simple_sub_class {} { + gdb_test_multiline "some basic ParameterPrefix usage" \ + "python" "" \ + "class BasicParamPrefix(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefix('prefix-4')" "" \ + "gdb.Parameter('prefix-4 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithSingleLineDoc(gdb.ParameterPrefix):" "" \ + " \"\"\"This is a single line doc string.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefixWithSingleLineDoc('prefix-5')" "" \ + "gdb.Parameter('prefix-5 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithMultiLineDoc(gdb.ParameterPrefix):" "" \ + " \"\"\"This is a multi line doc string." "" \ + " " "" \ + " The rest of the doc string is here.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefixWithMultiLineDoc('prefix-6')" "" \ + "gdb.Parameter('prefix-6 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithDocParameter(gdb.ParameterPrefix):" "" \ + " \"\"\"This is an unsused doc string.\"\"\"" "" \ + " def __init__(self, name, doc):" "" \ + " super().__init__(name, gdb.COMMAND_NONE, doc)" "" \ + "BasicParamPrefixWithDocParameter('prefix-7'," "" \ + " \"\"\"The doc string text is here.\"\"\")" "" \ + "gdb.Parameter('prefix-7 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + foreach mode { "set" "show" } { + gdb_test "help $mode prefix-4" \ + [make_help_re $mode "prefix-4" \ + "This command is not documented."] + + gdb_test "help $mode prefix-5" \ + [make_help_re $mode "prefix-5" \ + "This is a single line doc string."] + + gdb_test "help $mode prefix-6" \ + [make_help_re $mode "prefix-6" \ + [multi_line \ + "This is a multi line doc string." \ + "" \ + "The rest of the doc string is here."]] + + gdb_test "help $mode prefix-7" \ + [make_help_re $mode "prefix-7" \ + "The doc string text is here."] + + foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } { + gdb_test "$mode $prefix xxx" \ + "^Undefined $mode $prefix command: \"xxx\"\\. Try \"help $mode $prefix\"\\." + } + } + + foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } { + gdb_test "set $prefix" \ + [make_sub_cmd_help_re "set" $prefix] + + gdb_test "show $prefix" \ + [make_show_prefix_re $prefix] + } +} + +# Create a sub-class of gdb.ParameterPrefix, and make use of +# 'invoke_set' and 'invoke_show'. Test that the invoke method is +# executed when expected, and that, by default, these invoke methods +# repeat when the user issues an empty command. +proc_with_prefix test_prefix_with_invoke {} { + gdb_test_multiline "ParameterPrefix with invoke_set" \ + "python" "" \ + "class PrefixWithInvokeSet(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set (a): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeSet('prefix-8')" "" \ + "gdb.Parameter('prefix-8 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithInvokeShow(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show (b): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeShow('prefix-9')" "" \ + "gdb.Parameter('prefix-9 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithBothInvoke(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set (c): \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show (d): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithBothInvoke('prefix-10')" "" \ + "gdb.Parameter('prefix-10 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + gdb_test "set prefix-8 xxx yyy" \ + "^invoke_set \\(a\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set \\(a\\): \"xxx yyy\" True" \ + "repeat set prefix-8 xxx yyy" + + gdb_test "show prefix-8 xxx yyy" \ + "^Undefined show prefix-8 command: \"xxx yyy\"\\. Try \"help show prefix-8\"\\." + + gdb_test "set prefix-9 xxx yyy" \ + "^Undefined set prefix-9 command: \"xxx yyy\"\\. Try \"help set prefix-9\"\\." + + gdb_test "show prefix-9 xxx yyy" \ + "^invoke_show \\(b\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_show \\(b\\): \"xxx yyy\" True" \ + "repeat show prefix-9 xxx yyy" + + gdb_test "set prefix-10 xxx yyy" \ + "^invoke_set \\(c\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set \\(c\\): \"xxx yyy\" True" \ + "repeat set prefix-10 xxx yyy" + + gdb_test "show prefix-10 xxx yyy" \ + "^invoke_show \\(d\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_show \\(d\\): \"xxx yyy\" True" \ + "repeat show prefix-10 xxx yyy" + + gdb_test "set prefix-8" \ + "^invoke_set \\(a\\): \"\" True" + + gdb_test "show prefix-8" \ + [make_show_prefix_re "prefix-8"] + + gdb_test "set prefix-9" \ + [make_sub_cmd_help_re "set" "prefix-9"] + + gdb_test "show prefix-9" \ + "^invoke_show \\(b\\): \"\" True" + + gdb_test "set prefix-10" \ + "^invoke_set \\(c\\): \"\" True" + + gdb_test "show prefix-10" \ + "^invoke_show \\(d\\): \"\" True" +} + +# Create ParameterPrefix sub-classes that make use of the +# dont_repeat() method. Check that the relevant set/show invoke +# callback doesn't repeat when an empty command is used. +proc_with_prefix test_dont_repeat {} { + gdb_test_multiline "ParameterPrefix with invoke_set and dont_repeat" \ + "python" "" \ + "class PrefixWithInvokeAndDoNotRepeatSet(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " self.dont_repeat()" "" \ + " print(f\"invoke_set: \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show: \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeAndDoNotRepeatSet('prefix-11')" "" \ + "gdb.Parameter('prefix-11 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithInvokeAndDoNotRepeatShow(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set: \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " self.dont_repeat()" "" \ + " print(f\"invoke_show: \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeAndDoNotRepeatShow('prefix-12')" "" \ + "gdb.Parameter('prefix-12 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + gdb_test "set prefix-11 xxx yyy" \ + "^invoke_set: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^" \ + "repeat set prefix-11 xxx yyy" + + gdb_test "show prefix-11 xxx yyy" \ + "^invoke_show: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "invoke_show: \"xxx yyy\" True" \ + "repeat show prefix-11 xxx yyy" + + gdb_test "set prefix-12 xxx yyy" \ + "^invoke_set: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set: \"xxx yyy\" True" \ + "repeat set prefix-12 xxx yyy" + + gdb_test "show prefix-12 xxx yyy" \ + "^invoke_show: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^" \ + "repeat show prefix-12 xxx yyy" +} + +# Create a parameter prefixm, and immediately add another prefix under +# the first. The important thing here is that the second prefix is +# created into an otherwise empty prefix as this triggered a bug at +# one point. +proc_with_prefix test_nested {} { + gdb_test_multiline "Create nested parameter prefixes" \ + "python" "" \ + "gdb.ParameterPrefix('prefix-13', gdb.COMMAND_NONE)" "" \ + "gdb.ParameterPrefix('prefix-13 prefix-14', gdb.COMMAND_NONE)" "" \ + "gdb.Parameter('prefix-13 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-13 param-2', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-13 prefix-14 param-3', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-13 prefix-14 param-4', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" "" + + gdb_test "show prefix-13 prefix-14" \ + [multi_line \ + "^prefix-13 prefix-14 param-3: The current value of 'prefix-13 prefix-14 param-3' is \"off\"\\." \ + "prefix-13 prefix-14 param-4: The current value of 'prefix-13 prefix-14 param-4' is \"off\"\\."] + + gdb_test "show prefix-13" \ + [multi_line \ + "^prefix-13 param-1: The current value of 'prefix-13 param-1' is \"off\"\\." \ + "prefix-13 param-2: The current value of 'prefix-13 param-2' is \"off\"\\." \ + "prefix-13 prefix-14 param-3: The current value of 'prefix-13 prefix-14 param-3' is \"off\"\\." \ + "prefix-13 prefix-14 param-4: The current value of 'prefix-13 prefix-14 param-4' is \"off\"\\."] + + gdb_test "set prefix-13 prefix-14" \ + [multi_line \ + "" \ + "set prefix-13 prefix-14 param-3 -- Set the current value of 'prefix-13 prefix-14 param-3'\\." \ + "set prefix-13 prefix-14 param-4 -- Set the current value of 'prefix-13 prefix-14 param-4'\\." \ + "" \ + ".*"] + + gdb_test "set prefix-13" \ + [multi_line \ + "" \ + "set prefix-13 param-1 -- Set the current value of 'prefix-13 param-1'\\." \ + "set prefix-13 param-2 -- Set the current value of 'prefix-13 param-2'\\." \ + "set prefix-13 prefix-14 -- This command is not documented\\." \ + "" \ + ".*"] +} + +test_basic_usage +test_simple_sub_class +test_prefix_with_invoke +test_dont_repeat +test_nested diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index c15bef1..214c570 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -346,6 +346,91 @@ proc_with_prefix test_really_undocumented_parameter { } { "test general help" } +# Test a parameter in which the __doc__ string is empty or None. +proc_with_prefix test_empty_doc_parameter {} { + gdb_test_multiline "empty __doc__ parameter" \ + "python" "" \ + "class EmptyDocParam(gdb.Parameter):" "" \ + " __doc__ = \"\"" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_empty_doc_param = EmptyDocParam('print test-empty-doc-param')" ""\ + "end" + + # Setting the __doc__ string to empty means GDB will completely + # elide it from the output. + gdb_test "help set print test-empty-doc-param" \ + "^Set the current value of 'print test-empty-doc-param'\\." + + gdb_test_multiline "None __doc__ parameter" \ + "python" "" \ + "class NoneDocParam(gdb.Parameter):" "" \ + " __doc__ = None" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_none_doc_param = NoneDocParam('print test-none-doc-param')" ""\ + "end" + + # Setting the __doc__ string to None, or anything else that isn't + # a string, causes GDB to use a default string instead. + gdb_test "help set print test-none-doc-param" \ + [multi_line \ + "^Set the current value of 'print test-none-doc-param'\\." \ + "This command is not documented\\."] +} + +# Test a parameter in which the set_doc/show_doc strings are either +# empty, or None. +proc_with_prefix test_empty_set_show_doc_parameter {} { + gdb_test_multiline "empty set/show doc parameter" \ + "python" "" \ + "class EmptySetShowParam(gdb.Parameter):" "" \ + " set_doc = \"\"" "" \ + " show_doc = \"\"" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_empty_set_show_param = EmptySetShowParam('print test-empty-set-show-param')" ""\ + "end" + + # Setting the set_doc/show_doc string to empty means GDB will use + # a suitable default string. + gdb_test "help set print test-empty-set-show-param" \ + [multi_line \ + "^Set the current value of 'print test-empty-set-show-param'\\." \ + "This command is not documented\\."] + + gdb_test "help show print test-empty-set-show-param" \ + [multi_line \ + "^Show the current value of 'print test-empty-set-show-param'\\." \ + "This command is not documented\\."] + + gdb_test_multiline "None set/show doc parameter" \ + "python" "" \ + "class NoneSetShowParam(gdb.Parameter):" "" \ + " set_doc = None" "" \ + " show_doc = None" "" \ + " def __init__(self, name):" "" \ + " super ().__init__(name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "test_none_set_show_param = NoneSetShowParam('print test-none-set-show-param')" ""\ + "end" + + # Setting the set_doc/show_doc string to None (or any non-string + # value) means GDB will use a suitable default string. + gdb_test "help set print test-none-set-show-param" \ + [multi_line \ + "^Set the current value of 'print test-none-set-show-param'\\." \ + "This command is not documented\\."] + + gdb_test "help show print test-none-set-show-param" \ + [multi_line \ + "^Show the current value of 'print test-none-set-show-param'\\." \ + "This command is not documented\\."] +} + # Test deprecated API. Do not use in your own implementations. proc_with_prefix test_deprecated_api_parameter { } { clean_restart @@ -669,6 +754,104 @@ proc_with_prefix test_ambiguous_parameter {} { "Parameter .* is ambiguous.*Error occurred in Python.*" gdb_test "python print(gdb.parameter('test-ambiguous-value-1a'))" \ "Could not find parameter.*Error occurred in Python.*" + + # Create command prefixs 'set foo1' and 'show foo1'. + gdb_test_no_output "python gdb.Command('set foo1', gdb.COMMAND_NONE, prefix=True)" + gdb_test_no_output "python gdb.Command('show foo1', gdb.COMMAND_NONE, prefix=True)" + + # Create a parameter under 'foo1', but use a truncated prefix. At + # this point though, the prefix is not ambiguous. + gdb_test_no_output "python gdb.Parameter('foo bar', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" + gdb_test "python print(gdb.parameter('foo1 bar'))" "False" + + # Create another prefix command, similar in name to the first. + gdb_test_no_output "python gdb.Command('set foo2', gdb.COMMAND_NONE, prefix=True)" + gdb_test_no_output "python gdb.Command('show foo2', gdb.COMMAND_NONE, prefix=True)" + + # An attempt to create a parameter using an ambiguous prefix will give an error. + gdb_test "python gdb.Parameter('foo baz', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix foo\\." \ + "Error occurred in Python: Could not find command prefix foo\\."] +} + +# Check that creating a gdb.Parameter with an unknown command prefix results in an error. +proc_with_prefix test_unknown_prefix {} { + gdb_test_multiline "create parameter" \ + "python" "" \ + "class UnknownPrefixParam(gdb.Parameter):" "" \ + " def __init__ (self, name):" "" \ + " super().__init__ (name, gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + " self.value = True" "" \ + "end" + + foreach prefix { "unknown-prefix" "style unknown-prefix" "style disassembler unknown-prefix"} { + gdb_test "python UnknownPrefixParam('$prefix new-param')" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: Could not find command prefix $prefix\\." \ + "Error occurred in Python: Could not find command prefix $prefix\\."] + } +} + +# Test the default behaviour of a set/show parameter prefix command. +proc_with_prefix test_set_show_parameters {} { + # This first set/show prefix command doesn't have an invoke + # method. As such, GDB installs the default invoke behaviour; set + # prints the full list of sub-commands, and show prints all the + # sub-command values. + gdb_test_multiline "Setup set/show parameter prefix with no invoke" \ + "python" "" \ + "class TestParamPrefix(gdb.Command):" "" \ + " \"\"\"TestParamPrefix documentation string.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE, prefix = True)" "" \ + "TestParamPrefix('set test-prefix')" "" \ + "TestParamPrefix('show test-prefix')" "" \ + "gdb.Parameter('test-prefix param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('test-prefix param-2', gdb.COMMAND_NONE, gdb.PARAM_INTEGER)" "" \ + "gdb.Parameter('test-prefix param-3', gdb.COMMAND_NONE, gdb.PARAM_STRING)" "" \ + "end" + + gdb_test "set test-prefix" \ + [multi_line \ + "List of \"set test-prefix\" subcommands:" \ + "" \ + "set test-prefix param-1 -- Set the current value of 'test-prefix param-1'." \ + "set test-prefix param-2 -- Set the current value of 'test-prefix param-2'." \ + "set test-prefix param-3 -- Set the current value of 'test-prefix param-3'." \ + "" \ + "Type \"help set test-prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] + + gdb_test "show test-prefix" \ + [multi_line \ + "test-prefix param-1: The current value of 'test-prefix param-1' is \"off\"\\." \ + "test-prefix param-2: The current value of 'test-prefix param-2' is \"0\"\\." \ + "test-prefix param-3: The current value of 'test-prefix param-3' is \"\"\\."] + + # This next set/show prefix has an invoke method, which will be + # called instead of the default behaviour tested above. + gdb_test_multiline "Setup set/show parameter prefix with invoke" \ + "python" "" \ + "class TestParamPrefix(gdb.Command):" "" \ + " \"\"\"TestParamPrefix documentation string.\"\"\"" "" \ + " def __init__(self, name, mode):" "" \ + " self._mode = mode" "" \ + " super().__init__(self._mode + ' ' + name, gdb.COMMAND_NONE, prefix = True)" "" \ + " def invoke(self, args, from_tty):" "" \ + " print('invoke -- ' + self._mode)" "" \ + "TestParamPrefix('test-prefix-2', 'set')" "" \ + "TestParamPrefix('test-prefix-2', 'show')" "" \ + "gdb.Parameter('test-prefix-2 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('test-prefix-2 param-2', gdb.COMMAND_NONE, gdb.PARAM_INTEGER)" "" \ + "gdb.Parameter('test-prefix-2 param-3', gdb.COMMAND_NONE, gdb.PARAM_STRING)" "" \ + "end" + + gdb_test "set test-prefix-2" "^invoke -- set" + + gdb_test "show test-prefix-2" "^invoke -- show" } test_directories @@ -679,11 +862,15 @@ test_color_parameter test_file_parameter test_undocumented_parameter test_really_undocumented_parameter +test_empty_doc_parameter +test_empty_set_show_doc_parameter test_deprecated_api_parameter test_gdb_parameter test_integer_parameter test_throwing_parameter test_language test_ambiguous_parameter +test_unknown_prefix +test_set_show_parameters rename py_param_test_maybe_no_output "" diff --git a/gdb/testsuite/gdb.python/py-source-styling-2.exp b/gdb/testsuite/gdb.python/py-source-styling-2.exp index b13ee1f..ebf7f32 100644 --- a/gdb/testsuite/gdb.python/py-source-styling-2.exp +++ b/gdb/testsuite/gdb.python/py-source-styling-2.exp @@ -32,7 +32,9 @@ if { [build_executable "failed to build" $testfile $srcfile $opts] == -1 } { return } -clean_restart +with_ansi_styling_terminal { + clean_restart +} gdb_test_no_output "maint set gnu-source-highlight enabled off" @@ -40,16 +42,14 @@ gdb_load $binfile require {gdb_py_module_available pygments} -with_ansi_styling_terminal { - gdb_test_no_output "set style enabled on" - - gdb_test_multiple "list $line_number" "Styling of c++ keyword try" { - -re -wrap " try\r\n.*" { - # Unstyled. - fail $gdb_test_name - } - -re -wrap "" { - pass $gdb_test_name - } +gdb_test_no_output "set style enabled on" + +gdb_test_multiple "list $line_number" "Styling of c++ keyword try" { + -re -wrap " try\r\n.*" { + # Unstyled. + fail $gdb_test_name + } + -re -wrap "" { + pass $gdb_test_name } } diff --git a/gdb/testsuite/gdb.python/py-warning.exp b/gdb/testsuite/gdb.python/py-warning.exp new file mode 100644 index 0000000..6b26a4e --- /dev/null +++ b/gdb/testsuite/gdb.python/py-warning.exp @@ -0,0 +1,63 @@ +# Copyright (C) 2025 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/>. + +# Test the gdb.warning() function. + +load_lib gdb-python.exp + +require allow_python_tests + +clean_restart + +# Basic usage. +gdb_test "python gdb.warning(\"some text\")" \ + "warning: some text" + +# Basic usage with named argument. +gdb_test "python gdb.warning(text=\"a warning message\")" \ + "warning: a warning message" + +# Make sure GDB prints format specifiers correctly. +gdb_test "python gdb.warning(\"%s %d %p\")" \ + "warning: %s %d %p" + +# Empty string gives an error. +gdb_test "python gdb.warning(\"\")" \ + [multi_line \ + "Python Exception <class 'ValueError'>: Empty text string passed to gdb\\.warning" \ + "Error occurred in Python: Empty text string passed to gdb\\.warning"] + +# Missing argument gives an error. +set re1 \ + [multi_line \ + [string_to_regexp \ + [concat \ + "Python Exception <class 'TypeError'>:" \ + "function missing required argument 'text' (pos 1)"]] \ + [string_to_regexp \ + [concat \ + "Error occurred in Python:" \ + "function missing required argument 'text' (pos 1)"]]] +set re2 \ + [multi_line \ + [string_to_regexp \ + [concat \ + "Python Exception <class 'TypeError'>:" \ + "Required argument 'text' (pos 1) not found"]] \ + [string_to_regexp \ + [concat \ + "Error occurred in Python:" \ + "Required argument 'text' (pos 1) not found"]]] +gdb_test "python gdb.warning()" $re1|$re2 diff --git a/gdb/testsuite/gdb.replay/connect.exp b/gdb/testsuite/gdb.replay/connect.exp index 5790d38..26b7aa3 100644 --- a/gdb/testsuite/gdb.replay/connect.exp +++ b/gdb/testsuite/gdb.replay/connect.exp @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. */ # -# Starts a communication with gdbsever setting the remotelog file. +# Starts a communication with gdbserver setting the remotelog file. # Modifies the remotelog with update_log proc, injects an error message # instead of the expected replay to the vMustReplyEmpty packet in order # to test GDB reacts to the error response properly. After the remotelog diff --git a/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp b/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp index 0ff56dd..00f58f8 100644 --- a/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp +++ b/gdb/testsuite/gdb.reverse/i386-avx-reverse.exp @@ -397,7 +397,7 @@ gdb_test_no_output "set \$ymm15.v2_int128 = {0x0, 0xcafeface}" "set ymm15 for vp if {[record_full_function "vzeroupper"] == true} { # Since vzeroupper needs to save 8 or 16 registers, let's check what was # actually recorded, instead of just undoing an instruction. Only - # really check the values of egisters 0, 1, 2 and 15 because those are + # really check the values of registers 0, 1, 2 and 15 because those are # the only ones we're setting. gdb_test "maint print record-instruction" \ [multi_line "Register ymm0h changed: 74565" \ diff --git a/gdb/testsuite/gdb.reverse/solib-precsave.exp b/gdb/testsuite/gdb.reverse/solib-precsave.exp index 277e33c..82b08cd 100644 --- a/gdb/testsuite/gdb.reverse/solib-precsave.exp +++ b/gdb/testsuite/gdb.reverse/solib-precsave.exp @@ -140,7 +140,7 @@ gdb_test_multiple "reverse-step" "reverse-step into solib function one" { pass $gdb_test_name } } -# Depending on wether the closing } has a line associated, we might have +# Depending on whether the closing } has a line associated, we might have # different acceptable results here gdb_test_multiple "reverse-step" "reverse-step within solib function one" { -re -wrap "return y;.*" { diff --git a/gdb/testsuite/gdb.reverse/solib-reverse.exp b/gdb/testsuite/gdb.reverse/solib-reverse.exp index 1e22e91..b2ef9b0 100644 --- a/gdb/testsuite/gdb.reverse/solib-reverse.exp +++ b/gdb/testsuite/gdb.reverse/solib-reverse.exp @@ -116,7 +116,7 @@ gdb_test_multiple "reverse-step" "reverse-step into solib function one" { pass $gdb_test_name } } -# Depending on wether the closing } has a line associated, we might have +# Depending on whether the closing } has a line associated, we might have # different acceptable results here gdb_test_multiple "reverse-step" "reverse-step within solib function one" { -re -wrap "return y;.*" { diff --git a/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.cpp b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.cpp new file mode 100644 index 0000000..d75bc76 --- /dev/null +++ b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.cpp @@ -0,0 +1,86 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +#ifdef DEVICE + +#include <hip/hip_runtime.h> + +constexpr unsigned int NUM_BREAKPOINT_HITS = 5; + +static __device__ void +break_here () +{ +} + +extern "C" __global__ void +kernel () +{ + for (int n = 0; n < NUM_BREAKPOINT_HITS; ++n) + break_here (); +} + +#else + +#include <hip/hip_runtime.h> +#include <unistd.h> + +constexpr unsigned int NUM_ITEMS_PER_BLOCK = 256; +constexpr unsigned int NUM_BLOCKS = 128; +constexpr unsigned int NUM_ITEMS = NUM_ITEMS_PER_BLOCK * NUM_BLOCKS; +constexpr unsigned int NUM_LOAD_UNLOADS = 5; + +#define CHECK(cmd) \ + { \ + hipError_t error = cmd; \ + if (error != hipSuccess) \ + { \ + fprintf (stderr, "error: '%s'(%d) at %s:%d\n", \ + hipGetErrorString (error), error, __FILE__, __LINE__); \ + exit (EXIT_FAILURE); \ + } \ + } + +int +main (int argc, const char **argv) +{ + if (argc != 2) + { + fprintf (stderr, "Usage: %s <hip_module_path>\n", argv[0]); + return 1; + } + + const auto module_path = argv[1]; + hipModule_t module; + CHECK (hipModuleLoad (&module, module_path)); + + /* Launch the kernel. */ + hipFunction_t function; + CHECK (hipModuleGetFunction (&function, module, "kernel")); + CHECK (hipModuleLaunchKernel (function, NUM_BLOCKS, 1, 1, + NUM_ITEMS_PER_BLOCK, 1, 1, 0, nullptr, nullptr, + nullptr)); + + /* Load and unload the module many times. */ + for (int i = 0; i < NUM_LOAD_UNLOADS; ++i) + { + hipModule_t dummy_module; + CHECK (hipModuleLoad (&dummy_module, module_path)); + CHECK (hipModuleUnload (dummy_module)); + } +} + +#endif diff --git a/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp new file mode 100644 index 0000000..3fe6a95 --- /dev/null +++ b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp @@ -0,0 +1,68 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# This file is part of GDB. + +# 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 test verifies what happens when a code object list update happens at the +# same time as some wave stop events are reported. It was added following a +# performance bug fix, where forward progress requirement disabled when +# pulling events from amd-dbgapi in amd_dbgapi_target_breakpoint::check_status. +# +# The test launches a kernel that hits a breakpoint with an always false +# condition a certain number of times. Meanwhile, the host loads and unloads +# a code object in a loop, causing check_status to be called. The hope is that +# check_status, when calling process_event_queue, will pull many WAVE_STOP +# events from the kernel hitting the breakpoint. +# +# Without the appropriate fix (of disabling forward progress requirement in +# check_status), GDB would hit the newly-added assert in process_event_queue, +# which verifies that forward progress requirement is disabled. Even without +# this assert, the test would likely time out (depending on the actual timeout +# value). + +load_lib rocm.exp +standard_testfile .cpp +require allow_hipcc_tests + +# Build the host executable. +if { [build_executable "failed to prepare" \ + $testfile $srcfile {debug hip}] == -1 } { + return -1 +} + +set hipmodule_path [standard_output_file ${testfile}.co] + +# Build the kernel object file. +if { [gdb_compile $srcdir/$subdir/$srcfile \ + $hipmodule_path object \ + { debug hip additional_flags=--genco additional_flags=-DDEVICE } ] != "" } { + return -1 +} + +proc do_test { } { + with_rocm_gpu_lock { + clean_restart $::binfile + gdb_test_no_output "set args $::hipmodule_path" "set args" + + if { ![runto_main] } { + return + } + + gdb_test "with breakpoint pending on -- break break_here if 0" + gdb_continue_to_end "continue to end" "continue" 1 + } +} + +do_test diff --git a/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp b/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp index 7588525..22d4b75 100644 --- a/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp +++ b/gdb/testsuite/gdb.rocm/fork-exec-gpu-to-non-gpu.exp @@ -21,6 +21,7 @@ load_lib rocm.exp require allow_hipcc_tests +require allow_fork_tests standard_testfile -execer.cpp -execee.cpp diff --git a/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp b/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp index a6bcf69..1386099 100644 --- a/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp +++ b/gdb/testsuite/gdb.rocm/fork-exec-non-gpu-to-gpu.exp @@ -20,6 +20,7 @@ load_lib rocm.exp require allow_hipcc_tests +require allow_fork_tests standard_testfile -execer.cpp -execee.cpp diff --git a/gdb/testsuite/gdb.rocm/precise-memory-fork.exp b/gdb/testsuite/gdb.rocm/precise-memory-fork.exp index d326c2e..23c1ebe 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory-fork.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory-fork.exp @@ -21,6 +21,7 @@ load_lib rocm.exp require allow_hipcc_tests +require allow_fork_tests standard_testfile .c diff --git a/gdb/testsuite/gdb.rocm/precise-memory.exp b/gdb/testsuite/gdb.rocm/precise-memory.exp index fbcb451..6711d80 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory.exp @@ -59,7 +59,7 @@ proc do_test { } { return } - # Get to the begining of the GPU kernel without precise memory enabled. + # Get to the beginning of the GPU kernel without precise memory enabled. with_test_prefix "goto gpu code" { gdb_test_no_output "set amdgpu precise-memory off" gdb_breakpoint "kernel" allow-pending diff --git a/gdb/testsuite/gdb.server/build-id-seqno.exp b/gdb/testsuite/gdb.server/build-id-seqno.exp index a508a44..8475ccc 100644 --- a/gdb/testsuite/gdb.server/build-id-seqno.exp +++ b/gdb/testsuite/gdb.server/build-id-seqno.exp @@ -90,13 +90,13 @@ proc load_binfile_check_debug_is_found { debuginfo_file testname } { with_test_prefix "$testname" { with_timeout_factor 5 { # Probing for .build-id based debug files on remote - # targets uses the vFile:stat packet by default, though + # targets uses the vFile:lstat packet by default, though # there is a work around that avoids this which can be # used if GDB is connected to an older gdbserver without # 'stat' support. # # Check the work around works by disabling use of the - # vFile:stat packet. + # vFile:lstat packet. foreach_with_prefix stat_pkt {auto off} { clean_restart @@ -105,7 +105,7 @@ proc load_binfile_check_debug_is_found { debuginfo_file testname } { gdb_test_no_output "set sysroot target:" - gdb_test "set remote hostio-stat-packet $stat_pkt" + gdb_test "set remote hostio-lstat-packet $stat_pkt" # Make sure we're disconnected, in case we're testing with an # extended-remote board, therefore already connected. diff --git a/gdb/testsuite/gdb.server/fileio-packets.exp b/gdb/testsuite/gdb.server/fileio-packets.exp new file mode 100644 index 0000000..9435efd --- /dev/null +++ b/gdb/testsuite/gdb.server/fileio-packets.exp @@ -0,0 +1,66 @@ +# Copyright 2025 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/>. + +# Test some remote file I/O. The associated Python script uses the +# Python API to create and send vFile:* packets to gdbserver to +# perform actions like 'stat'. The same action is then performed +# directly from Python (e.g. a 'stat' is performed), and the results, +# from gdbserver, and from the local syscall, are compared. + +load_lib gdb-python.exp +load_lib gdbserver-support.exp + +require allow_python_tests +require allow_gdbserver_tests +require {!is_remote host} +require {!is_remote target} + +standard_testfile + +clean_restart + +# Make sure we're disconnected, in case we're testing with an +# extended-remote board, therefore already connected. +gdb_test "disconnect" ".*" + +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] +gdb_test_no_output "source $pyfile" "source the script" + +# Start gdbserver, but always in extended-remote mode, and then +# connect to it from GDB. +set res [gdbserver_start "--multi --once" ""] +set gdbserver_protocol "extended-remote" +set gdbserver_gdbport [lindex $res 1] +gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport + +gdb_test_no_output "set python print-stack full" + +set test_file_1 [standard_output_file "test_file_1"] +remote_exec host "touch $test_file_1" + +set test_file_2 [standard_output_file "test_file_2"] +remote_exec host "ln -s $test_file_1 $test_file_2" + +gdb_test "python check_lstat(\"$test_file_1\")" "PASS" \ + "check remote lstat works on a normal file" + +gdb_test "python check_lstat(\"$test_file_2\")" "PASS" \ + "check remote lstat works on a symbolic link" + +gdb_test "python check_stat(\"$test_file_1\")" "PASS" \ + "check remote stat works on a normal file" + +gdb_test "python check_stat(\"$test_file_2\")" "PASS" \ + "check remote stat works on a symbolic link" diff --git a/gdb/testsuite/gdb.server/fileio-packets.py b/gdb/testsuite/gdb.server/fileio-packets.py new file mode 100644 index 0000000..f132e91 --- /dev/null +++ b/gdb/testsuite/gdb.server/fileio-packets.py @@ -0,0 +1,208 @@ +# Copyright (C) 2025 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/>. + +import os +import stat + + +# Hex encode INPUT_STRING in the same way that GDB does. Each +# character in INPUT_STRING is expanded to its two digit hex +# representation in the returned string. +# +# Only ASCII characters may appear in INPUT_STRING, this is more +# restrictive than GDB, but is good enough for testing. +def hex_encode(input_string): + byte_string = input_string.encode("ascii") + hex_string = byte_string.hex() + return hex_string + + +# Binary remote data packets can contain some escaped bytes. Decode +# the packet now. +def unescape_remote_data(buf): + escaped = False + res = bytearray() + for b in buf: + if escaped: + res.append(b ^ 0x20) + escaped = False + elif b == ord("}"): + escaped = True + else: + res.append(b) + res = bytes(res) + return res + + +# Decode the results of a remote stat like command from BUF. Returns +# None if BUF is not a valid stat result (e.g. if it indicates an +# error, or the buffer is too short). If BUF is valid then the fields +# are decoded according to the GDB remote protocol and placed into a +# dictionary, this dictionary is then returned. +def decode_stat_reply(buf, byteorder="big"): + + buf = unescape_remote_data(buf) + + if ( + buf[0] != ord("F") + or buf[1] != ord("4") + or buf[2] != ord("0") + or buf[3] != ord(";") + or len(buf) != 68 + ): + l = len(buf) + print(f"decode_stat_reply failed: {buf}\t(length = {l})") + return None + + # Discard the 'F40;' prefix. The rest is the 64 bytes of data to + # be decoded. + buf = buf[4:] + + st_dev = int.from_bytes(buf[0:4], byteorder=byteorder) + st_ino = int.from_bytes(buf[4:8], byteorder=byteorder) + st_mode = int.from_bytes(buf[8:12], byteorder=byteorder) + st_nlink = int.from_bytes(buf[12:16], byteorder=byteorder) + st_uid = int.from_bytes(buf[16:20], byteorder=byteorder) + st_gid = int.from_bytes(buf[20:24], byteorder=byteorder) + st_rdev = int.from_bytes(buf[24:28], byteorder=byteorder) + st_size = int.from_bytes(buf[28:36], byteorder=byteorder) + st_blksize = int.from_bytes(buf[36:44], byteorder=byteorder) + st_blocks = int.from_bytes(buf[44:52], byteorder=byteorder) + st_atime = int.from_bytes(buf[52:56], byteorder=byteorder) + st_mtime = int.from_bytes(buf[56:60], byteorder=byteorder) + st_ctime = int.from_bytes(buf[60:64], byteorder=byteorder) + + return { + "st_dev": st_dev, + "st_ino": st_ino, + "st_mode": st_mode, + "st_nlink": st_nlink, + "st_uid": st_uid, + "st_gid": st_gid, + "st_rdev": st_rdev, + "st_size": st_size, + "st_blksize": st_blksize, + "st_blocks": st_blocks, + "st_atime": st_atime, + "st_mtime": st_mtime, + "st_ctime": st_ctime, + } + + +# Perform an lstat of remote file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def remote_lstat(filename): + conn = gdb.selected_inferior().connection + if not isinstance(conn, gdb.RemoteTargetConnection): + raise gdb.GdbError("connection is the wrong type") + + filename_hex = hex_encode(filename) + reply = conn.send_packet("vFile:lstat:%s" % filename_hex) + + stat = decode_stat_reply(reply) + return stat + + +# Perform a stat of remote file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def remote_stat(filename): + conn = gdb.selected_inferior().connection + if not isinstance(conn, gdb.RemoteTargetConnection): + raise gdb.GdbError("connection is the wrong type") + + filename_hex = hex_encode(filename) + reply = conn.send_packet("vFile:stat:%s" % filename_hex) + + stat = decode_stat_reply(reply) + return stat + + +# Convert a stat_result object to a dictionary that should match the +# dictionary built from the remote protocol reply. +def stat_result_to_dict(res): + # GDB doesn't support the S_IFLNK flag for the remote protocol, so + # clear that flag in the local results. + if stat.S_ISLNK(res.st_mode): + st_mode = stat.S_IMODE(res.st_mode) + else: + st_mode = res.st_mode + + # GDB returns an integer for these fields, while Python returns a + # floating point value. Convert back to an integer to match GDB. + st_atime = int(res.st_atime) + st_mtime = int(res.st_mtime) + st_ctime = int(res.st_ctime) + + return { + "st_dev": res.st_dev, + "st_ino": res.st_ino, + "st_mode": st_mode, + "st_nlink": res.st_nlink, + "st_uid": res.st_uid, + "st_gid": res.st_gid, + "st_rdev": res.st_rdev, + "st_size": res.st_size, + "st_blksize": res.st_blksize, + "st_blocks": res.st_blocks, + "st_atime": st_atime, + "st_mtime": st_mtime, + "st_ctime": st_ctime, + } + + +# Perform an lstat of local file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def local_lstat(filename): + res = os.lstat(filename) + return stat_result_to_dict(res) + + +# Perform an lstat of local file FILENAME, and create a dictionary of +# the results, the keys are the fields of the stat structure. +def local_stat(filename): + res = os.stat(filename) + return stat_result_to_dict(res) + + +# Perform a remote lstat using GDB, and a local lstat using os.lstat. +# Compare the results to check they are the same. +# +# For this test to work correctly, gdbserver, and GDB (where this +# Python script is running), must see the same filesystem. +def check_lstat(filename): + s1 = remote_lstat(filename) + s2 = local_lstat(filename) + + print(f"remote = {s1}") + print(f"local = {s2}") + + assert s1 == s2 + print("PASS") + + +# Perform a remote stat using GDB, and a local stat using os.stat. +# Compare the results to check they are the same. +# +# For this test to work correctly, gdbserver, and GDB (where this +# Python script is running), must see the same filesystem. +def check_stat(filename): + s1 = remote_stat(filename) + s2 = local_stat(filename) + + print(f"remote = {s1}") + print(f"local = {s2}") + + assert s1 == s2 + print("PASS") diff --git a/gdb/testsuite/gdb.server/pread-offset-size.S b/gdb/testsuite/gdb.server/pread-offset-size.S new file mode 100644 index 0000000..6ca8cf0 --- /dev/null +++ b/gdb/testsuite/gdb.server/pread-offset-size.S @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 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/>. */ + +/* Here we are trying to create a large binary (> 2 GB), + 3742415472 bytes is about 3.5 gigabytes. */ + + .text + .globl _start +_start: + .skip 3742415472 + ret + .globl f + .type f, @function +f: + ret diff --git a/gdb/testsuite/gdb.server/pread-offset-size.exp b/gdb/testsuite/gdb.server/pread-offset-size.exp new file mode 100644 index 0000000..54e67c5 --- /dev/null +++ b/gdb/testsuite/gdb.server/pread-offset-size.exp @@ -0,0 +1,49 @@ +# Copyright (C) 2025 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/>. +# +# Check that GDBserver's vFile::pread implementation is able to access +# large files (> 2GB). + +load_lib gdbserver-support.exp + +require allow_gdbserver_tests + +standard_testfile .S + +if { [prepare_for_testing ${testfile}.exp $testfile \ + $srcfile {debug additional_flags=-nostdlib} ] } { + return -1 +} + +clean_restart + +gdb_test_no_output "set remote exec-file $binfile" \ + "set remote exec-file" + +# Make sure we're disconnected, in case we're testing with an +# extended-remote board, therefore already connected. +gdb_test "disconnect" ".*" + +set res [gdbserver_spawn ""] +set gdbserver_protocol [lindex $res 0] +set gdbserver_gdbport [lindex $res 1] + +gdb_test "target $gdbserver_protocol $gdbserver_gdbport" \ + "Remote debugging using .*" \ + "target $gdbserver_protocol" + +# If loading the large binary was successful, we should be able to +# place a breakpoint on f. +gdb_test "break f" "Breakpoint 1.*" diff --git a/gdb/testsuite/gdb.stabs/weird.def b/gdb/testsuite/gdb.stabs/weird.def index 179b126..f809963 100644 --- a/gdb/testsuite/gdb.stabs/weird.def +++ b/gdb/testsuite/gdb.stabs/weird.def @@ -294,7 +294,7 @@ attr69: # Using double quotes requires an escaping, as the stabs string # is a double quote delimited string. .stabs "constString2:c=s\"Double quote String2\"", N_LSYM,0,0, 0 -# Escaping sinlge quote with is easy +# Escaping single quote with is easy .stabs "constString3:c=s'String3 with embedded quote \' in the middle'", N_LSYM,0,0, 0 # Esaping double quotes is less clear... .stabs "constString4:c=s\"String4 with embedded quote \\" in the middle\"", N_LSYM,0,0, 0 diff --git a/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.exp b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.exp new file mode 100644 index 0000000..a05ce61 --- /dev/null +++ b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.exp @@ -0,0 +1,84 @@ +# Copyright 2025 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/>. + +# Test gdb_test_multiple -lbl, particularly with patterns that share a +# common prefix. + +standard_testfile + +clean_restart + +gdb_test_no_output "source ${srcdir}/${subdir}/$testfile.gdb" \ + "source gdb test script" + +set saw_prompt 0 +set saw_prefix 0 +set saw_command 0 +set saw_prefix_foo 0 +set saw_prefix_bar 0 + +# #1 - We need anchors so that the "prefix foo" pattern below does not +# match when the expect output buffer contains: +# +# "\r\nprefix xxx\r\n\prefix foo\r\n" +# +# #2 - We need an anchor on the prompt match as otherwise the prompt +# regexp would match: +# +# "\r\nmeant-to-be-matched-by-lbl-2\r\nprefix xxx\r\n(gdb) " +# +# This test would fail if -lbl did not force the built-in prompt match +# regexp to have an anchor as well, as without it, the built-in prompt +# regexp would have the exact same issue as #2 above. + +gdb_test_multiple "command" "" -lbl { + -re "^command(?=\r\n)" { + verbose -log <COMMAND> + incr saw_command + exp_continue + } + -re "^\r\nprefix foo(?=\r\n)" { + verbose -log <PREFIX-FOO> + incr saw_prefix_foo + exp_continue + } + -re "^\r\nprefix bar(?=\r\n)" { + verbose -log <PREFIX-BAR> + incr saw_prefix_bar + exp_continue + } + -re "^\r\nprefix \[^\r\n\]*(?=\r\n)" { + verbose -log <PREFIX> + incr saw_prefix + exp_continue + } + -re "^\r\n$gdb_prompt $" { + verbose -log <PROMPT> + incr saw_prompt + pass $gdb_test_name + } +} + +verbose -log "saw_command: $saw_command" +verbose -log "saw_prefix_foo: $saw_prefix_foo" +verbose -log "saw_prefix_bar: $saw_prefix_bar" +verbose -log "saw_prefix: $saw_prefix" +verbose -log "saw_prompt: $saw_prompt" + +gdb_assert {$saw_command == 1} +gdb_assert {$saw_prefix_foo == 1} +gdb_assert {$saw_prefix_bar == 1} +gdb_assert {$saw_prefix == 3} +gdb_assert {$saw_prompt == 1} diff --git a/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.gdb b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.gdb new file mode 100755 index 0000000..8c94dfa --- /dev/null +++ b/gdb/testsuite/gdb.testsuite/gdb_test_multiple-lbl.gdb @@ -0,0 +1,25 @@ +# Copyright 2025 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/>. + +define command + echo prefix xxx\n + echo meant-to-be-matched-by-lbl-1\n + echo prefix foo\n + echo prefix bar\n + echo meant-to-be-matched-by-lbl-2\n + echo prefix xxx\n + echo prefix xxx\n + echo meant-to-be-matched-by-lbl-3\n +end diff --git a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp index b41e3b2..fec31c3 100644 --- a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp +++ b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp @@ -74,42 +74,45 @@ proc test { non_stop } { delete_breakpoints # Start the second inferior. - with_test_prefix "second inferior" { - # With stub targets that do reload on run, if we let the new - # inferior share inferior 1's connection, runto would - # fail because GDB is already connected to something, like - # e.g. with --target_board=native-gdbserver: - # - # (gdb) kill - # ... - # (gdb) target remote localhost:2348 - # Already connected to a remote target. Disconnect? (y or n) - # - # Instead, start the inferior with no connection, and let - # gdb_load/runto spawn a new remote connection/gdbserver. - # - # OTOH, with extended-remote, we must let the new inferior - # reuse the current connection, so that runto below can - # issue the "run" command, and have the inferior run on the - # remote target. If we forced no connection, then "run" would - # either fail if "set auto-connect-native-target" is on, like - # the native-extended-gdbserver board enforces, or it would - # run the inferior on the native target, which isn't what is - # being tested. - # - # Since it's reload_on_run targets that need special care, we - # default to reusing the connection on most targets. - if [target_info exists gdb,do_reload_on_run] { - gdb_test "add-inferior -no-connection" "New inferior 2.*" - } else { - gdb_test "add-inferior" "New inferior 2.*" - } - gdb_test "inferior 2" "Switching to inferior 2 .*" - - gdb_load $binfile - - if ![runto setup_done] { - return -1 + if {[allow_multi_inferior_tests]} { + with_test_prefix "second inferior" { + # With stub targets that do reload on run, if we let the + # new inferior share inferior 1's connection, runto would + # fail because GDB is already connected to something, like + # e.g. with --target_board=native-gdbserver: + # + # (gdb) kill + # ... + # (gdb) target remote localhost:2348 + # Already connected to a remote target. Disconnect? (y or n) + # + # Instead, start the inferior with no connection, and let + # gdb_load/runto spawn a new remote connection/gdbserver. + # + # OTOH, with extended-remote, we must let the new inferior + # reuse the current connection, so that runto below can + # issue the "run" command, and have the inferior run on + # the remote target. If we forced no connection, then + # "run" would either fail if "set + # auto-connect-native-target" is on, like the + # native-extended-gdbserver board enforces, or it would + # run the inferior on the native target, which isn't what + # is being tested. + # + # Since it's reload_on_run targets that need special care, + # we default to reusing the connection on most targets. + if [target_info exists gdb,do_reload_on_run] { + gdb_test "add-inferior -no-connection" "New inferior 2.*" + } else { + gdb_test "add-inferior" "New inferior 2.*" + } + gdb_test "inferior 2" "Switching to inferior 2 .*" + + gdb_load $binfile + + if ![runto setup_done] { + return -1 + } } } @@ -158,13 +161,15 @@ proc test { non_stop } { verbose -log "xxxxx: iteration $iter" gdb_test -nopass "info threads" - if {$inf == 1} { - set inf 2 - } else { - set inf 1 - } + if {[allow_multi_inferior_tests]} { + if {$inf == 1} { + set inf 2 + } else { + set inf 1 + } - my_gdb_test "inferior $inf" ".*" "inferior $inf" + my_gdb_test "inferior $inf" ".*" "inferior $inf" + } my_gdb_test "print global_var = 555" " = 555" \ "write to global_var" diff --git a/gdb/testsuite/gdb.threads/current-lwp-dead.exp b/gdb/testsuite/gdb.threads/current-lwp-dead.exp index 7aa7ab9..c8364df 100644 --- a/gdb/testsuite/gdb.threads/current-lwp-dead.exp +++ b/gdb/testsuite/gdb.threads/current-lwp-dead.exp @@ -47,6 +47,6 @@ gdb_breakpoint $line gdb_continue_to_breakpoint "fn_return" ".*at-fn_return.*" # Confirm thread 2 is really gone. -gdb_test "info threads 2" "No threads match '2'\\." +gdb_test "info threads 2" "No threads matched\\." gdb_continue_to_end "" continue 1 diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp index e48b83c..8a1cb29 100644 --- a/gdb/testsuite/gdb.threads/detach-step-over.exp +++ b/gdb/testsuite/gdb.threads/detach-step-over.exp @@ -50,6 +50,8 @@ require can_spawn_for_attach +require allow_multi_inferior_tests + standard_testfile set bp_lineno [gdb_get_line_number "Set breakpoint here"] diff --git a/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp b/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp index 5245988..8ab540c 100644 --- a/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp +++ b/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp @@ -17,6 +17,8 @@ # another thread, in different combinations of "set follow-fork # parent/child", and other execution modes. +require allow_fork_tests + standard_testfile # Line where to stop the main thread. diff --git a/gdb/testsuite/gdb.threads/fork-child-threads.exp b/gdb/testsuite/gdb.threads/fork-child-threads.exp index abe9769..ba9dfc2 100644 --- a/gdb/testsuite/gdb.threads/fork-child-threads.exp +++ b/gdb/testsuite/gdb.threads/fork-child-threads.exp @@ -13,10 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Only GNU/Linux is known to support `set follow-fork-mode child'. -if { ! [istarget "*-*-linux*"] } { - return 0 -} +require allow_fork_tests standard_testfile diff --git a/gdb/testsuite/gdb.threads/fork-plus-threads.exp b/gdb/testsuite/gdb.threads/fork-plus-threads.exp index 3a5e66a..4ce88d3 100644 --- a/gdb/testsuite/gdb.threads/fork-plus-threads.exp +++ b/gdb/testsuite/gdb.threads/fork-plus-threads.exp @@ -20,6 +20,8 @@ # # See https://sourceware.org/bugzilla/show_bug.cgi?id=18600 +require allow_fork_tests + # In remote mode, we cannot continue debugging after all # inferiors have terminated, and this test requires that. if { [target_info exists gdb_protocol] diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp index d0a1ca1..538e1ca 100644 --- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp +++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp @@ -13,11 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Only GNU/Linux is known to support `set follow-fork-mode child'. -# -if { ! [istarget "*-*-linux*"] } { - return 0 -} +require allow_fork_tests standard_testfile diff --git a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp index 1f76898..c668a65 100644 --- a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp +++ b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp @@ -16,6 +16,8 @@ # This test verifies that several threads forking while another thread # is constantly stepping over a breakpoint is properly handled. +require allow_fork_tests + standard_testfile set linenum [gdb_get_line_number "set break here"] diff --git a/gdb/testsuite/gdb.threads/info-threads-options.c b/gdb/testsuite/gdb.threads/info-threads-options.c new file mode 100644 index 0000000..2c4cd85 --- /dev/null +++ b/gdb/testsuite/gdb.threads/info-threads-options.c @@ -0,0 +1,77 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022-2025 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/>. */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <pthread.h> + +#define NUM 4 + +static pthread_barrier_t threads_started_barrier; + +static void +stop_here () +{ +} + +static void +spin () +{ + while (1) + usleep (1); +} + +static void * +work (void *arg) +{ + int id = *(int *) arg; + + pthread_barrier_wait (&threads_started_barrier); + + if (id % 2 == 0) + stop_here (); + else + spin (); + + pthread_exit (NULL); +} + +int +main () +{ + /* Ensure we stop if GDB crashes and DejaGNU fails to kill us. */ + alarm (10); + + pthread_t threads[NUM]; + int ids[NUM]; + + pthread_barrier_init (&threads_started_barrier, NULL, NUM + 1); + + for (int i = 0; i < NUM; i++) + { + ids[i] = i; + pthread_create (&threads[i], NULL, work, &ids[i]); + } + + /* Wait until all threads are seen running. */ + pthread_barrier_wait (&threads_started_barrier); + + stop_here (); + + return 0; +} diff --git a/gdb/testsuite/gdb.threads/info-threads-options.exp b/gdb/testsuite/gdb.threads/info-threads-options.exp new file mode 100644 index 0000000..38e4e67 --- /dev/null +++ b/gdb/testsuite/gdb.threads/info-threads-options.exp @@ -0,0 +1,131 @@ +# Copyright (C) 2022-2025 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/>. + +# Test the filter flags of the "info threads" command. + +standard_testfile + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable debug] != "" } { + return -1 +} + +save_vars { GDBFLAGS } { + append GDBFLAGS " -ex \"set non-stop on\"" + clean_restart $binfile +} + +if ![runto_main] { + return -1 +} + +gdb_breakpoint "stop_here" +gdb_test_multiple "continue -a&" "" { + -re "Continuing.\r\n$gdb_prompt " { + pass $gdb_test_name + } +} + +set expected_hits 3 +set fill "\[^\r\n\]+" +set num_hits 0 +gdb_test_multiple "" "hit the breakpoint" -lbl { + -re "\r\nThread ${fill} hit Breakpoint ${decimal}," { + incr num_hits + if {$num_hits < $expected_hits} { + exp_continue + } + } +} +gdb_assert {$num_hits == $expected_hits} "expected threads hit the bp" + +# Count the number of running/stopped threads reported +# by the "info threads" command. We also capture thread ids +# for additional tests. +set running_tid "invalid" +set stopped_tid "invalid" + +set eol "(?=\r\n)" + +foreach_with_prefix flag {"" "-running" "-stopped" "-running -stopped"} { + set num_running 0 + set num_stopped 0 + gdb_test_multiple "info threads $flag" "info threads $flag" -lbl { + -re "Id${fill}Target Id${fill}Frame${fill}${eol}" { + exp_continue + } + -re "^\r\n. (${decimal})${fill}Thread ${fill}.running.${eol}" { + incr num_running + set running_tid $expect_out(1,string) + exp_continue + } + -re "^\r\n. (${decimal})${fill}Thread ${fill}stop_here ${fill}${eol}" { + incr num_stopped + set stopped_tid $expect_out(1,string) + exp_continue + } + -re "^\r\n$gdb_prompt $" { + pass $gdb_test_name + } + } + + if {$flag eq "-running"} { + gdb_assert {$num_running == 2} "num running" + gdb_assert {$num_stopped == 0} "num stopped" + } elseif {$flag eq "-stopped"} { + gdb_assert {$num_running == 0} "num running" + gdb_assert {$num_stopped == 3} "num stopped" + } else { + gdb_assert {$num_running == 2} "num running" + gdb_assert {$num_stopped == 3} "num stopped" + } +} + +verbose -log "running_tid=$running_tid, stopped_tid=$stopped_tid" + +# Test specifying thread ids. +gdb_test "info threads -running $stopped_tid" \ + "No threads matched\\." \ + "info thread -running for a stopped thread" +gdb_test "info threads -stopped $running_tid" \ + "No threads matched\\." \ + "info thread -stopped for a running thread" + +set ws "\[ \t\]+" +foreach tid "\"$running_tid\" \"$running_tid $stopped_tid\"" { + gdb_test "info threads -running $tid" \ + [multi_line \ + "${ws}Id${ws}Target Id${ws}Frame${ws}" \ + "${ws}${running_tid}${ws}Thread ${fill}.running."] \ + "info thread -running with [llength $tid] thread ids" +} + +foreach tid "\"$stopped_tid\" \"$stopped_tid $running_tid\"" { + gdb_test "info threads -stopped $tid" \ + [multi_line \ + "${ws}Id${ws}Target Id${ws}Frame${ws}" \ + "${ws}${stopped_tid}${ws}Thread ${fill} stop_here ${fill}"] \ + "info thread -stopped with [llength $tid] thread ids" +} + +gdb_test_multiple "info threads -stopped -running $stopped_tid $running_tid" \ + "filter flags and tids combined" { + -re -wrap ".*stop_here.*running.*" { + pass $gdb_test_name + } + -re -wrap ".*running.*stop_here.*" { + pass $gdb_test_name + } +} diff --git a/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp index bd81438..3a97127 100644 --- a/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp @@ -25,6 +25,8 @@ # 20.04.5 LTS with 32-bit kernel + 32-bit userland. It was NOT reproducible # using a circa 2023 Raspberry Pi OS w/ 64-bit kernel and 32-bit userland. +require allow_fork_tests + standard_testfile # Line where to stop the main thread. diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp index 183fda6..1cd6685 100644 --- a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp @@ -16,6 +16,8 @@ # Test doing a "next" on a thread during which forks or vforks happen in other # threads. +require allow_fork_tests + standard_testfile # Line where to stop the main thread. diff --git a/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp b/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp index e6e311e..29a011e 100644 --- a/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp +++ b/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp @@ -29,6 +29,8 @@ # parent thread from waitpid'ing it, preventing the main thread from joining # it, prevent it from writing the flag file, failing the test. +require allow_fork_tests + standard_testfile if { [is_remote target] } { diff --git a/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp b/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp index 8e77ab0..e627241 100644 --- a/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp +++ b/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp @@ -34,6 +34,8 @@ # event, and erroneously create a new inferior for it. Once fixed, the child # process' thread is hidden by whoever holds the pending fork event. +require allow_fork_tests + standard_testfile .c -touch-file.c set touch_file_bin $binfile-touch-file diff --git a/gdb/testsuite/gdb.threads/thread-bp-deleted.exp b/gdb/testsuite/gdb.threads/thread-bp-deleted.exp index 2eadd38..8cabb70 100644 --- a/gdb/testsuite/gdb.threads/thread-bp-deleted.exp +++ b/gdb/testsuite/gdb.threads/thread-bp-deleted.exp @@ -147,7 +147,7 @@ if {$is_remote} { exp_continue } - -re "No threads match '99'\\.\r\n$gdb_prompt $" { + -re "No threads matched\\.\r\n$gdb_prompt $" { if {!$saw_thread_exited && !$saw_bp_deleted && $attempt_count > 0} { sleep 1 incr attempt_count -1 diff --git a/gdb/testsuite/gdb.threads/thread-execl.c b/gdb/testsuite/gdb.threads/thread-execl.c index 403aa31..2d312d4 100644 --- a/gdb/testsuite/gdb.threads/thread-execl.c +++ b/gdb/testsuite/gdb.threads/thread-execl.c @@ -25,8 +25,9 @@ static const char *image; void * thread_execler (void *arg) { - /* Exec ourselves again. */ - if (execl (image, image, NULL) == -1) + /* Exec ourselves again. Pass an extra argument so that the + post-exec image knows to not re-exec yet again. */ + if (execl (image, image, "1", NULL) == -1) { perror ("execl"); abort (); @@ -40,6 +41,11 @@ main (int argc, char **argv) { pthread_t thread; + /* An extra argument means we're in the post-exec image, so we're + done. Don't re-exec again. */ + if (argc > 1) + exit (0); + image = argv[0]; pthread_create (&thread, NULL, thread_execler, NULL); diff --git a/gdb/testsuite/gdb.threads/threadapply.exp b/gdb/testsuite/gdb.threads/threadapply.exp index c53db79..9110617 100644 --- a/gdb/testsuite/gdb.threads/threadapply.exp +++ b/gdb/testsuite/gdb.threads/threadapply.exp @@ -224,6 +224,8 @@ proc kill_and_remove_inferior {thread_set} { # Test both "all" and a thread list, because those are implemented as # different commands in GDB. -foreach_with_prefix thread_set {"all" "1.1"} { - kill_and_remove_inferior $thread_set +if {[allow_multi_inferior_tests]} { + foreach_with_prefix thread_set {"all" "1.1"} { + kill_and_remove_inferior $thread_set + } } diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp b/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp index e23db0a..0b95a75 100644 --- a/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp +++ b/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp @@ -16,6 +16,8 @@ # Test following a vfork child that execs, when the vfork parent is a # threaded program, and it's a non-main thread that vforks. +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}]} { diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp b/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp index a6b7f49..ced52df 100644 --- a/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp +++ b/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp @@ -16,6 +16,8 @@ # Test following a vfork child that exits, when the vfork parent is a # threaded program, and it's a non-main thread that vforks. +require allow_fork_tests + standard_testfile if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}]} { diff --git a/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp b/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp index fd081b3..1f87427 100644 --- a/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp +++ b/gdb/testsuite/gdb.threads/vfork-multi-inferior.exp @@ -25,6 +25,10 @@ # To catch the bug, this test verifies that we can hit a breakpoint after a # vfork call, while a second inferior runs in the background. +require allow_fork_tests + +require allow_multi_inferior_tests + require !use_gdb_stub standard_testfile .c -sleep.c diff --git a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp index 2b9294d..61811ae 100644 --- a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp +++ b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp @@ -30,6 +30,8 @@ # breakpoints are removed, so the main thread would miss the breakpoint and run # until exit. +require allow_fork_tests + standard_testfile if { [build_executable "failed to prepare" ${testfile} ${srcfile} {debug pthreads}] } { diff --git a/gdb/testsuite/gdb.threads/watchpoint-fork.exp b/gdb/testsuite/gdb.threads/watchpoint-fork.exp index 376ca2a..8e9b1c3 100644 --- a/gdb/testsuite/gdb.threads/watchpoint-fork.exp +++ b/gdb/testsuite/gdb.threads/watchpoint-fork.exp @@ -21,6 +21,8 @@ # must be done before starting the test so as to not disrupt the execution # of the actual test. +require allow_fork_tests + set allow_hw_watchpoint_tests_p [allow_hw_watchpoint_tests] set testfile watchpoint-fork diff --git a/gdb/testsuite/gdb.trace/tspeed.exp b/gdb/testsuite/gdb.trace/tspeed.exp index 25862bf..be7f37e 100644 --- a/gdb/testsuite/gdb.trace/tspeed.exp +++ b/gdb/testsuite/gdb.trace/tspeed.exp @@ -17,7 +17,7 @@ load_lib "trace-support.exp" require allow_shlib_tests -# Do not run if gdbsever debug is enabled - the output file is many Gb. +# Do not run if gdbserver debug is enabled - the output file is many Gb. if [gdbserver_debug_enabled] { return 0 } diff --git a/gdb/testsuite/gdb.tui/esc-match.exp b/gdb/testsuite/gdb.tui/esc-match.exp new file mode 100644 index 0000000..db78ebe --- /dev/null +++ b/gdb/testsuite/gdb.tui/esc-match.exp @@ -0,0 +1,48 @@ +# Copyright 2025 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/>. + +# Test that the ANSI escape sequence matcher works +# character-by-character. + +load_lib gdb-python.exp +require allow_python_tests allow_tui_tests + +tuiterm_env + +Term::clean_restart 24 80 + +set remote_python_file [gdb_remote_download host \ + ${srcdir}/${subdir}/esc-match.py] +gdb_test_no_output "source ${remote_python_file}" \ + "source esc-match.py" + +if {![Term::enter_tui]} { + unsupported "TUI not supported" + return +} + +Term::command "python print_it()" + +Term::dump_screen + +set text [Term::get_all_lines] +# We should not see the control sequence here. +gdb_assert {![regexp -- "\\\[35;1mOUTPUT\\\[m" $text]} \ + "output visible without control sequences" + +# Also check the styling. +set text [Term::get_region 0 1 78 23 "\n" true] +gdb_assert {[regexp -- "<fg:magenta>.*OUTPUT" $text]} \ + "output is magenta" diff --git a/gdb/testsuite/gdb.tui/esc-match.py b/gdb/testsuite/gdb.tui/esc-match.py new file mode 100644 index 0000000..7816002 --- /dev/null +++ b/gdb/testsuite/gdb.tui/esc-match.py @@ -0,0 +1,26 @@ +# Copyright (C) 2025 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/>. + +import sys + +# Some text to print that includes styling. +OUT = "\033[35;1mOUTPUT\033[m" + + +def print_it(): + # Print to stderr avoids any buffering, showing the bug. + for c in OUT: + print(c, end="", file=sys.stderr) + print(file=sys.stderr) diff --git a/gdb/testsuite/gdb.tui/pr30056.exp b/gdb/testsuite/gdb.tui/pr30056.exp index 1123593..3403033 100644 --- a/gdb/testsuite/gdb.tui/pr30056.exp +++ b/gdb/testsuite/gdb.tui/pr30056.exp @@ -76,12 +76,12 @@ save_vars { env(LC_ALL) } { # open about this. kfail cli/30498 $test - # At this point we don't have a reponsive prompt. Send ^G to abort + # At this point we don't have a responsive prompt. Send ^G to abort # the i-search. send_gdb "\007" } - # We need a reponsive prompt here, to deal with the "monitor exit" + # We need a responsive prompt here, to deal with the "monitor exit" # that native-extended-gdbserver will send. Check that we have a # responsive prompt. Term::command "echo \\n" diff --git a/gdb/testsuite/gdb.tui/tui-focus.exp b/gdb/testsuite/gdb.tui/tui-focus.exp index 4c9d2a0..26e5060 100644 --- a/gdb/testsuite/gdb.tui/tui-focus.exp +++ b/gdb/testsuite/gdb.tui/tui-focus.exp @@ -73,7 +73,7 @@ foreach spec {{src true} {cmd true} {status true} {regs false} \ } } -# Use the Python TUI API to exercise some of the ambigous window name +# Use the Python TUI API to exercise some of the ambiguous window name # handling parts of the 'focus' command. Term::clean_restart 24 80 $binfile if {[allow_python_tests]} { diff --git a/gdb/testsuite/gdb.xml/bad-include.xml b/gdb/testsuite/gdb.xml/bad-include.xml index f7a2b72..cd9cce3 100644 --- a/gdb/testsuite/gdb.xml/bad-include.xml +++ b/gdb/testsuite/gdb.xml/bad-include.xml @@ -1 +1 @@ -<xi:include href="nonexistant.xml"/> +<xi:include href="nonexistent.xml"/> diff --git a/gdb/testsuite/gdb.xml/tdesc-xinclude.exp b/gdb/testsuite/gdb.xml/tdesc-xinclude.exp index 885d217..b934c80 100644 --- a/gdb/testsuite/gdb.xml/tdesc-xinclude.exp +++ b/gdb/testsuite/gdb.xml/tdesc-xinclude.exp @@ -43,7 +43,7 @@ set_arch "includes.xml" \ # This file contains a missing include. We should warn the user about # it. set_arch "bad-include.xml" \ - "warning:.*Could not load XML document \"nonexistant.xml\"$common_warn" + "warning:.*Could not load XML document \"nonexistent.xml\"$common_warn" # Make sure we detect infinite loops, eventually. set_arch "loop.xml" \ diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp index b347437..3a182c2 100644 --- a/gdb/testsuite/lib/dwarf.exp +++ b/gdb/testsuite/lib/dwarf.exp @@ -1072,7 +1072,10 @@ namespace eval Dwarf { } proc _section {name {flags ""} {type ""}} { - if {$flags == "" && $type == ""} { + if {$name == ".debug_str"} { + # Hard-code this because it's always desirable. + _emit " .section $name, \"MS\", %progbits, 1" + } elseif {$flags == "" && $type == ""} { _emit " .section $name" } elseif {$type == ""} { _emit " .section $name, \"$flags\"" @@ -3398,9 +3401,24 @@ namespace eval Dwarf { # Add the strings in ARGS to the .debug_str section, and create a # .debug_str_offsets section pointing to those strings. - # BASE_OFFSET is the label for DW_AT_str_offsets_base. - proc debug_str_offsets { base_offset args } { - _section .debug_str + # Current options are: + # dwo 0|1 - boolean indicating if the sections have the dwo suffix. + # default = 0 (no .dwo suffix) + # base_offset label + # - generate label, to be used in DW_AT_str_offsets_base. + # default = "" (don't generate a label). + proc debug_str_offsets { options args } { + parse_options { + { dwo 0 } + { base_offset "" } + } + + if { $dwo } { + _section .debug_str.dwo + } else { + _section .debug_str + } + set num 0 foreach arg $args { set str_label [_compute_label "str_${num}"] @@ -3412,12 +3430,18 @@ namespace eval Dwarf { declare_labels debug_str_offsets_start debug_str_offsets_end set initial_length "$debug_str_offsets_end - $debug_str_offsets_start" - _section .debug_str_offsets + if { $dwo } { + _section .debug_str_offsets.dwo + } else { + _section .debug_str_offsets + } _op .4byte $initial_length "Initial_length" debug_str_offsets_start: _op .2byte 0x5 "version" _op .2byte 0x0 "padding" - $base_offset: + if { $base_offset != "" } { + $base_offset: + } set num 0 foreach arg $args { set str_label [_compute_label "str_${num}"] diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 2a5d37c..777d64d 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -269,6 +269,13 @@ if ![info exists INTERNAL_GDBFLAGS] { } set INTERNAL_GDBFLAGS [append_gdb_data_directory_option $INTERNAL_GDBFLAGS] + + # Handle the case that "interactive-mode auto" reports off. + append INTERNAL_GDBFLAGS { -iex "set interactive-mode on"} + + if { [ishost "*-*-mingw*"] } { + append INTERNAL_GDBFLAGS { -iex "maint set console-translation-mode binary"} + } } # The variable gdb_prompt is a regexp which matches the gdb prompt. @@ -1026,7 +1033,10 @@ proc command_to_message { command } { # should not be anchored at the end of the buffer. This means that the # pattern can match even if there is stuff output after the prompt. Does not # have any effect if -prompt is specified. -# -lbl specifies that line-by-line matching will be used. +# -lbl specifies that line-by-line matching will be used. This means +# that lines from GDB not matched by any pattern will be consumed from +# the output buffer. This helps avoid buffer overflows and timeouts +# when testing verbose commands. # EXPECT_ARGUMENTS will be fed to expect in addition to the standard # patterns. Pattern elements will be evaluated in the caller's # context; action elements will be executed in the caller's context. @@ -1124,6 +1134,7 @@ proc gdb_test_multiple { command message args } { global any_spawn_id set line_by_line 0 + set lbl_anchor_re "" set prompt_regexp "" set prompt_anchor 1 for {set i 0} {$i < [llength $args]} {incr i} { @@ -1133,6 +1144,7 @@ proc gdb_test_multiple { command message args } { set prompt_regexp [lindex $args $i] } elseif { $arg == "-lbl" } { set line_by_line 1 + set lbl_anchor_re "^" } elseif { $arg == "-no-prompt-anchor" } { set prompt_anchor 0 } else { @@ -1391,7 +1403,7 @@ proc gdb_test_multiple { command message args } { fail "$errmsg" set result -1 } - -re "\r\n$prompt_regexp" { + -re "${lbl_anchor_re}\r\n$prompt_regexp" { if {![string match "" $message]} { fail "$message" } @@ -2301,7 +2313,8 @@ proc default_gdb_exit {} { } } - if { [is_remote host] && [board_info host exists fileid] } { + if { ([is_remote host] && [board_info host exists fileid]) + || [istarget *-*-mingw*] } { send_gdb "quit\n" gdb_expect 10 { -re "y or n" { @@ -2314,7 +2327,9 @@ proc default_gdb_exit {} { } if ![is_remote host] { - remote_close host + if {[catch { remote_close host } message]} { + warning "closing gdb failed with: $message" + } } unset gdb_spawn_id unset ::gdb_tty_name @@ -2577,6 +2592,17 @@ proc default_gdb_start { } { # Output with -q, and bracketed paste mode enabled, see above. verbose "GDB initialized." } + -re "^\033\\\[6n$gdb_prompt $" { + # With MSYS2 and TERM={xterm,ansi}, I get: + # + # builtin_spawn gdb -q ... + # ^[[6n(gdb) + # + # We set TERM to dumb by default to avoid this, but some + # test-cases set TERM to xterm or ansi, in which case we get this + # output. + verbose "GDB initialized." + } -re "$gdb_prompt $" { perror "GDB never initialized." unset gdb_spawn_id @@ -5087,6 +5113,40 @@ proc skip_inline_var_tests {} { return 0 } +# Return whether we allow running fork-related testcases. Targets +# that don't even have any concept of fork will just fail to compile +# the testcases and skip the tests that way if this returns true for +# them. Unix targets that do have a fork system call, but don't +# support intercepting forks will want to return false here, otherwise +# the testcases that exercise fork may hit a number of long cascading +# time out sequences. + +proc allow_fork_tests {} { + if {[istarget "*-*-cygwin*"] || [istarget "*-*-mingw*"]} { + return 0 + } + + return 1 +} + +# Return whether we allow running testcases that want to debug +# multiple inferiors with the same target. Not all targets support +# this. Note that some tests add a second inferior but never start +# it. Those tests should not be skipped due to this proc returning +# false. + +proc allow_multi_inferior_tests {} { + if {[istarget "*-*-cygwin*"] || [istarget "*-*-mingw*"]} { + return 0 + } + + if {[use_gdb_stub]} { + return 0 + } + + return 1 +} + # Return a 1 if we should run tests that require hardware breakpoints proc allow_hw_breakpoint_tests {} { @@ -6920,7 +6980,7 @@ proc kill_wait_spawned_process { proc_spawn_id } { proc spawn_id_get_pid { spawn_id } { set testpid [exp_pid -i $spawn_id] - if { [istarget "*-*-cygwin*"] } { + if { [istarget "*-*-cygwin*"] || [istarget "*-*-mingw*"] } { # testpid is the Cygwin PID, GDB uses the Windows PID, which # might be different due to the way fork/exec works. set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ] @@ -7489,6 +7549,22 @@ proc default_gdb_init { test_file_name } { setenv LC_CTYPE C setenv LANG C + # With MSYS2 and TERM={xterm,ansi}, I get: + # + # builtin_spawn gdb -q ... + # ^[[6n(gdb) + # + # While we're addressing this in default_gdb_start, this is not specific + # to gdb, other tools produce the same CSI sequence, and consequently we + # run into trouble in other places (like get_compiler_info). + # + # Set TERM to dumb to prevent the '^[[6n' from occurring. + # + # We could do this only for ishost *-*-mingw*, but that introduces + # inconsistency between platforms, with test-cases passing on one platform + # but failing on the other. So, we do this for all platforms. + setenv TERM dumb + # Don't let a .inputrc file or an existing setting of INPUTRC mess # up the test results. Certain tests (style tests and TUI tests) # want to set the terminal to a non-"dumb" value, and for those we @@ -9304,7 +9380,12 @@ proc core_find {binfile {deletefiles {}} {arg ""}} { file mkdir $coredir catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\"" # remote_exec host "${binfile}" - foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" { + set binfile_basename [file tail $binfile] + foreach i [list \ + ${coredir}/core \ + ${coredir}/core.coremaker.c \ + ${coredir}/${binfile_basename}.core \ + ${coredir}/${binfile_basename}.exe.core] { if [remote_file build exists $i] { remote_exec build "mv $i $destcore" set found 1 diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp index c285072..2389206 100644 --- a/gdb/testsuite/lib/gdbserver-support.exp +++ b/gdb/testsuite/lib/gdbserver-support.exp @@ -69,7 +69,7 @@ proc gdb_target_cmd_ext { targetname serialport {additional_text ""} } { } -re "Non-stop mode requested, but remote does not support non-stop.*$gdb_prompt $" { verbose "remote does not support non-stop" - return 1 + return 2 } -re "Remote MIPS debugging.*$additional_text.*$gdb_prompt" { verbose "Set target to $targetname" diff --git a/gdb/testsuite/make-check-all.sh b/gdb/testsuite/make-check-all.sh index c2fbadb..ab72574 100755 --- a/gdb/testsuite/make-check-all.sh +++ b/gdb/testsuite/make-check-all.sh @@ -192,7 +192,7 @@ do_tests () # Run make check. make $maketarget \ - RUNTESTFLAGS="${rtf[*]} ${tests[*]}" \ + RUNTESTFLAGS="${rtf[*]}" TESTS="${tests[*]}" \ 2>&1 \ | summary @@ -216,7 +216,7 @@ do_tests () cp gdb.sum gdb.log "$dir" # Record the 'make check' command to enable easy re-running. - echo "make $maketarget RUNTESTFLAGS=\"${rtf[*]} ${tests[*]}\"" \ + echo "make $maketarget RUNTESTFLAGS=\"${rtf[*]}\" TESTS=\"${tests[*]}\"" \ > "$dir/make-check.sh" fi } |