diff options
Diffstat (limited to 'gdb/testsuite')
242 files changed, 5804 insertions, 1626 deletions
diff --git a/gdb/testsuite/gdb.ada/return-small-char-array.exp b/gdb/testsuite/gdb.ada/return-small-char-array.exp new file mode 100644 index 0000000..75c781e --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array.exp @@ -0,0 +1,40 @@ +# 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/>. + +load_lib "ada.exp" + +require allow_ada_tests + +standard_ada_testfile proc + +if { [gdb_compile_ada $srcfile $binfile executable debug] != "" } { + return -1 +} + +clean_restart $testfile + +set bp_location [gdb_get_line_number "STOP" $testdir/proc.adb] +runto "proc.adb:$bp_location" + +gdb_test "print Value.Name(My_Value)" \ + { = "abcd"} + +# Step into the function. +gdb_test "step 2" \ + "return Of_Value;" + +# and finish. +gdb_test "finish" \ + { = "abcd"} diff --git a/gdb/testsuite/gdb.ada/return-small-char-array/proc.adb b/gdb/testsuite/gdb.ada/return-small-char-array/proc.adb new file mode 100644 index 0000000..b18d9fe --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array/proc.adb @@ -0,0 +1,22 @@ +-- 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/>. + +with Value; +procedure Proc is + My_Value : Value.T := "abcd"; +begin + null; -- STOP + My_Value := Value.Name(My_Value); +end; diff --git a/gdb/testsuite/gdb.ada/return-small-char-array/value.adb b/gdb/testsuite/gdb.ada/return-small-char-array/value.adb new file mode 100644 index 0000000..2dd9faa --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array/value.adb @@ -0,0 +1,21 @@ +-- 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/>. + +package body Value is + function Name (Of_Value : T) return T is + begin + return Of_Value; + end Name; +end Value; diff --git a/gdb/testsuite/gdb.ada/return-small-char-array/value.ads b/gdb/testsuite/gdb.ada/return-small-char-array/value.ads new file mode 100644 index 0000000..16b171e --- /dev/null +++ b/gdb/testsuite/gdb.ada/return-small-char-array/value.ads @@ -0,0 +1,20 @@ +-- 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/>. + +package Value is + type T is new String (1 .. 4); + + function Name (Of_Value : T) return T; +end; diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-core.c b/gdb/testsuite/gdb.arch/aarch64-gcs-core.c new file mode 100644 index 0000000..7767204 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.c @@ -0,0 +1,123 @@ +/* This test program 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 <stdio.h> +#include <stdlib.h> +#include <sys/auxv.h> +#include <linux/prctl.h> +#include <sys/syscall.h> + +/* Feature check for Guarded Control Stack. */ +#ifndef HWCAP_GCS +#define HWCAP_GCS (1UL << 32) +#endif + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#define PR_SET_SHADOW_STACK_STATUS 75 +#define PR_SHADOW_STACK_ENABLE (1UL << 0) +#endif + +/* We need to use a macro to call prctl because after GCS is enabled, it's not + possible to return from the function which enabled it. This is because the + return address of the calling function isn't on the GCS. */ +#define my_syscall2(num, arg1, arg2) \ + ({ \ + register long _num __asm__("x8") = (num); \ + register long _arg1 __asm__("x0") = (long)(arg1); \ + register long _arg2 __asm__("x1") = (long)(arg2); \ + register long _arg3 __asm__("x2") = 0; \ + register long _arg4 __asm__("x3") = 0; \ + register long _arg5 __asm__("x4") = 0; \ + \ + __asm__ volatile ("svc #0\n" \ + : "=r"(_arg1) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_num) \ + : "memory", "cc"); \ + _arg1; \ + }) + +#define get_gcspr(void) \ + ({ \ + unsigned long *gcspr; \ + \ + /* Get GCSPR_EL0. */ \ + asm volatile ("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \ + \ + gcspr; \ + }) + +/* Corrupt the return address to see if GDB will report a SIGSEGV with the + expected $_siginfo.si_code. */ +static void __attribute__ ((noinline)) +function (unsigned long *gcspr) +{ + /* x30 holds the return address. */ + register long x30 __asm__("x30") __attribute__ ((unused)); + + /* Print GCSPR to stdout so that the testcase can capture it. */ + printf ("%p\n", get_gcspr ()); + fflush (stdout); + + /* Cause a GCS exception. */ + x30 = 0xbadc0ffee; + __asm__ volatile ("ret\n"); +} + +int +main (void) +{ + if (!(getauxval (AT_HWCAP) & HWCAP_GCS)) + { + fprintf (stderr, "GCS support not found in AT_HWCAP\n"); + return EXIT_FAILURE; + } + + /* Force shadow stacks on, our tests *should* be fine with or + without libc support and with or without this having ended + up tagged for GCS and enabled by the dynamic linker. We + can't use the libc prctl() function since we can't return + from enabling the stack. Also lock GCS if not already + locked so we can test behaviour when it's locked. */ + unsigned long gcs_mode; + int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to read GCS state: %d\n", ret); + return EXIT_FAILURE; + } + + if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) + { + gcs_mode = PR_SHADOW_STACK_ENABLE; + ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to configure GCS: %d\n", ret); + return EXIT_FAILURE; + } + } + + unsigned long *gcspr = get_gcspr (); + + /* Pass gscpr to function just so it's used for something. */ + function (gcspr); /* Break here. */ + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp new file mode 100644 index 0000000..2261ac8 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp @@ -0,0 +1,113 @@ +# 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 reading and writing the core dump of a binary that uses a Guarded +# Control Stack. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return +} + +set linespec ${srcfile}:[gdb_get_line_number "Break here"] + +if ![runto $linespec] { + return +} + +# Obtain an OS-generated core file. Save test program output to +# ${binfile}.out. +set core_filename [core_find $binfile {} {} "${binfile}.out"] +set core_generated [expr {$core_filename != ""}] + +# Make sure GDB can read the given core file correctly. +proc check_core_file {core_filename saved_gcspr} { + global decimal hex + + # Load the core file. + if [gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by .*\\." \ + "Program terminated with signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ + "#0 function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \ + "$decimal.*__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "load core file"] { + return -1 + } + + # Check the value of GCSPR in the core file. + gdb_test "print/x \$gcspr" "\\$\[0-9\]+ = $saved_gcspr" \ + "gcspr contents from core file" +} + +if {!$core_generated} { + untested "unable to create or find corefile" +} + +if {$core_generated} { + clean_restart $binfile + + with_test_prefix "OS corefile" { + # Read GCSPR value from saved output of the test program. + set out_id [open ${binfile}.out "r"] + set gcspr_in_core [gets $out_id] + close $out_id + + check_core_file $core_filename $gcspr_in_core + } +} + +if ![gcore_cmd_available] { + unsupported "target does not support gcore command." + return +} + +clean_restart $binfile + +if ![runto $linespec] { + return +} + +# Continue until a crash. The line with the hex number is optional because +# it's printed by the test program, and doesn't appear in the Expect buffer +# when testing a remote target. +gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "($hex\r\n)?" \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ + "function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \ + {.*__asm__ volatile \("ret\\n"\);}] \ + "continue to SIGSEGV" + +set gcspr_in_gcore [get_valueof "/x" "\$gcspr" "*unknown*"] + +# Generate the gcore core file. +set gcore_filename [standard_output_file "${testfile}.gcore"] +set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"] + +gdb_assert { $gcore_generated } "gcore corefile created" +if {$gcore_generated} { + clean_restart $binfile + + with_test_prefix "gcore corefile" { + check_core_file $gcore_filename $gcspr_in_gcore + } +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c new file mode 100644 index 0000000..3d89535 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c @@ -0,0 +1,140 @@ +/* This test program 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 <stdio.h> +#include <stdlib.h> +#include <sys/auxv.h> +#include <sys/syscall.h> +#include <linux/prctl.h> + +/* Feature check for Guarded Control Stack. */ +#ifndef HWCAP_GCS +#define HWCAP_GCS (1UL << 32) +#endif + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#define PR_SET_SHADOW_STACK_STATUS 75 +#define PR_SHADOW_STACK_ENABLE (1UL << 0) +#endif + +/* We need to use a macro to call prctl because after GCS is enabled, it's not + possible to return from the function which enabled it. This is because the + return address of the calling function isn't on the GCS. */ +#define my_syscall2(num, arg1, arg2) \ + ({ \ + register long _num __asm__("x8") = (num); \ + register long _arg1 __asm__("x0") = (long)(arg1); \ + register long _arg2 __asm__("x1") = (long)(arg2); \ + register long _arg3 __asm__("x2") = 0; \ + register long _arg4 __asm__("x3") = 0; \ + register long _arg5 __asm__("x4") = 0; \ + \ + __asm__ volatile("svc #0\n" \ + : "=r"(_arg1) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_num) \ + : "memory", "cc"); \ + _arg1; \ + }) + +#define get_gcspr(void) \ + ({ \ + unsigned long *gcspr; \ + \ + /* Get GCSPR_EL0. */ \ + asm volatile("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \ + \ + gcspr; \ + }) + +static int __attribute__ ((noinline)) +function2 (void) +{ + return EXIT_SUCCESS; +} + +/* Put branch and link instructions being tested into their own functions so + that the program returns one level up in the stack after the displaced + stepped instruction. This tests that GDB doesn't leave the GCS out of sync + with the regular stack. */ + +static int __attribute__ ((noinline)) +function_bl (void) +{ + register int x0 __asm__("x0"); + + __asm__ ("bl function2\n" + : "=r"(x0) + : + : "x30"); + + return x0; +} + +static int __attribute__ ((noinline)) +function_blr (void) +{ + register int x0 __asm__("x0"); + + __asm__ ("blr %1\n" + : "=r"(x0) + : "r"(&function2) + : "x30"); + + return x0; +} + +int +main (void) +{ + if (!(getauxval (AT_HWCAP) & HWCAP_GCS)) + { + fprintf (stderr, "GCS support not found in AT_HWCAP\n"); + return EXIT_FAILURE; + } + + /* Force shadow stacks on, our tests *should* be fine with or + without libc support and with or without this having ended + up tagged for GCS and enabled by the dynamic linker. We + can't use the libc prctl() function since we can't return + from enabling the stack. */ + unsigned long gcs_mode; + int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to read GCS state: %d\n", ret); + return EXIT_FAILURE; + } + + if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) + { + gcs_mode = PR_SHADOW_STACK_ENABLE; + ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to configure GCS: %d\n", ret); + return EXIT_FAILURE; + } + } + + int ret1 = function_bl (); + int ret2 = function_blr (); + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (ret1 + ret2); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp new file mode 100644 index 0000000..2359d96 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp @@ -0,0 +1,86 @@ +# 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 displaced stepping in a program that uses a Guarded Control Stack. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +if ![runto_main] { + return +} + +gdb_test_no_output "set breakpoint auto-hw off" +gdb_test_no_output "set displaced-stepping on" + +# Get address of the branch and link instructions of interest. +set addr_bl 0 +set test "get address of bl instruction" +gdb_test_multiple "disassemble function_bl" $test -lbl { + -re "\r\n\\s+($hex) <\\+${decimal}>:\\s+bl\\s+${hex} <function2>(?=\r\n)" { + set addr_bl $expect_out(1,string) + exp_continue + } + -re -wrap "" { + gdb_assert { $addr_bl != 0 } $test + } +} + +set addr_blr 0 +set test "get address of blr instruction" +gdb_test_multiple "disassemble function_blr" $test -lbl { + -re "\r\n\\s+($hex) <\\+${decimal}>:\\s+blr\\s+x${decimal}(?=\r\n)" { + set addr_blr $expect_out(1,string) + exp_continue + } + -re -wrap "" { + gdb_assert { $addr_blr != 0 } $test + } +} + +if { $addr_bl == 0 || $addr_blr == 0 } { + return +} + +gdb_test "break *$addr_bl" \ + "Breakpoint $decimal at $hex: file .*aarch64-gcs-disp-step.c, line ${decimal}." \ + "set breakpoint at bl instruction" + +gdb_test "break *$addr_blr" \ + "Breakpoint $decimal at $hex: file .*aarch64-gcs-disp-step.c, line ${decimal}." \ + "set breakpoint at blr instruction" + +gdb_test "continue" \ + [multi_line \ + {Continuing\.} \ + "" \ + "Breakpoint $decimal, function_bl \\(\\) at .*aarch64-gcs-disp-step.c:${decimal}(?: \\\[GCS error\\\])?" \ + {[^\r\n]+"bl function2\\n"}] \ + "continue to breakpoint at bl" + +gdb_test "continue" \ + [multi_line \ + {Continuing\.} \ + "" \ + "Breakpoint $decimal, $hex in function_blr \\(\\) at .*aarch64-gcs-disp-step.c:${decimal}(?: \\\[GCS error\\\])?" \ + {[^\r\n]+"blr %1\\n"}] \ + "continue to breakpoint at blr" + +gdb_continue_to_end diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-return.c b/gdb/testsuite/gdb.arch/aarch64-gcs-return.c new file mode 100644 index 0000000..95518b6 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-return.c @@ -0,0 +1,105 @@ +/* This test program 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 <stdio.h> +#include <stdlib.h> +#include <sys/auxv.h> +#include <sys/syscall.h> +#include <linux/prctl.h> + +/* Feature check for Guarded Control Stack. */ +#ifndef HWCAP_GCS +#define HWCAP_GCS (1UL << 32) +#endif + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#define PR_SET_SHADOW_STACK_STATUS 75 +#define PR_SHADOW_STACK_ENABLE (1UL << 0) +#endif + +/* We need to use a macro to call prctl because after GCS is enabled, it's not + possible to return from the function which enabled it. This is because the + return address of the calling function isn't on the GCS. */ +#define my_syscall2(num, arg1, arg2) \ + ({ \ + register long _num __asm__("x8") = (num); \ + register long _arg1 __asm__("x0") = (long)(arg1); \ + register long _arg2 __asm__("x1") = (long)(arg2); \ + register long _arg3 __asm__("x2") = 0; \ + register long _arg4 __asm__("x3") = 0; \ + register long _arg5 __asm__("x4") = 0; \ + \ + __asm__ volatile("svc #0\n" \ + : "=r"(_arg1) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_num) \ + : "memory", "cc"); \ + _arg1; \ + }) + +static int __attribute__ ((noinline)) +call2 () +{ + return 42; /* Break call2. */ +} + +static int __attribute__ ((noinline)) +call1 () +{ + return call2 (); /* Break call1. */ +} + +int +main () +{ + if (!(getauxval (AT_HWCAP) & HWCAP_GCS)) + { + fprintf (stderr, "GCS support not found in AT_HWCAP\n"); + return EXIT_FAILURE; + } + + /* Force shadow stacks on, our tests *should* be fine with or + without libc support and with or without this having ended + up tagged for GCS and enabled by the dynamic linker. We + can't use the libc prctl() function since we can't return + from enabling the stack. Also lock GCS if not already + locked so we can test behaviour when it's locked. */ + unsigned long gcs_mode; + int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to read GCS state: %d\n", ret); + return EXIT_FAILURE; + } + + if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) + { + gcs_mode = PR_SHADOW_STACK_ENABLE; + ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to configure GCS: %d\n", ret); + return EXIT_FAILURE; + } + } + + call1 (); /* Break begin. */ + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-return.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-return.exp new file mode 100644 index 0000000..1d1c237 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-return.exp @@ -0,0 +1,129 @@ +# 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 the GDB return command in a program that uses a Guarded Control Stack. +# Based on the return tests in gdb.arch/amd64-shadow-stack-cmds.exp. +# Note that potential GCS violations often only occur after resuming normal +# execution. Therefore, it is important to test normal program +# completion after testing the return command. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +set begin_line [gdb_get_line_number "Break begin"] +set call1_line [gdb_get_line_number "Break call1"] +set call2_line [gdb_get_line_number "Break call2"] + +if ![runto ${begin_line}] { + return +} + +proc restart_and_run_infcall_call2 {} { + global binfile call2_line + clean_restart ${binfile} + if ![runto_main] { + return + } + set inside_infcall_str "The program being debugged stopped while in a function called from GDB" + gdb_breakpoint ${call2_line} + gdb_continue_to_breakpoint "Break call2" ".*Break call2.*" + gdb_test "call (int) call2()" \ + "Breakpoint \[0-9\]*, call2.*$inside_infcall_str.*" +} + +with_test_prefix "test inferior call and continue" { + gdb_breakpoint ${call1_line} + gdb_continue_to_breakpoint "Break call1" ".*Break call1.*" + + gdb_test "call (int) call2()" "= 42" + + gdb_continue_to_end +} + +with_test_prefix "test return inside an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "return" "\#0.*call2.*" \ + "Test GCS return inside an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} + +with_test_prefix "test return 'above' an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "frame 2" "call2 ().*" "move to frame 'above' inferior call" + + gdb_test "return" "\#0.*call1.*" \ + "Test GCS return 'above' an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} + +clean_restart ${binfile} +if ![runto ${begin_line}] { + return +} + +# Extract GCS pointer inside main, call1 and call2 function. +gdb_breakpoint ${call1_line} +gdb_breakpoint ${call2_line} +set gcspr_main [get_valueof /x "\$gcspr" 0 "get value of gcspr in main"] +gdb_continue_to_breakpoint "Break call1" ".*Break call1.*" +set gcspr_call1 [get_valueof /x "\$gcspr" 0 "get value of gcspr in call1"] +gdb_continue_to_breakpoint "Break call2" ".*Break call2.*" +set gcspr_call2 [get_valueof /x "\$gcspr" 0 "get value of gcspr in call2"] + +with_test_prefix "test frame level update" { + gdb_test "up" "call1.*" "move to frame 1" + gdb_test "print /x \$gcspr" "= $gcspr_call1" "check gcspr of frame 1" + gdb_test "up" "main.*" "move to frame 2" + gdb_test "print /x \$gcspr" "= $gcspr_main" "check gcspr of frame 2" + gdb_test "frame 0" "call2.*" "move to frame 0" + gdb_test "print /x \$gcspr" "= $gcspr_call2" "check gcspr of frame 0" +} + +with_test_prefix "test return from current frame" { + gdb_test "return (int) 1" "#0.*call1.*" \ + "Test GCS return from current frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} + +clean_restart ${binfile} +if ![runto_main] { + return +} + +with_test_prefix "test return from past frame" { + gdb_breakpoint ${call2_line} + gdb_continue_to_breakpoint "Break call2" ".*Break call2.*" + + gdb_test "frame 1" ".*in call1.*" + + gdb_test "return (int) 1" "#0.*main.*" \ + "Test GCS return from past frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-tdesc-without-linux.xml b/gdb/testsuite/gdb.arch/aarch64-gcs-tdesc-without-linux.xml new file mode 100644 index 0000000..056ab58 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-tdesc-without-linux.xml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>aarch64</architecture> + <feature name="org.gnu.gdb.aarch64.core"> + <flags id="cpsr_flags" size="4"> + <field name="SP" start="0" end="0" type="bool"/> + <field name="EL" start="2" end="3" type="uint32"/> + <field name="nRW" start="4" end="4" type="bool"/> + <field name="F" start="6" end="6" type="bool"/> + <field name="I" start="7" end="7" type="bool"/> + <field name="A" start="8" end="8" type="bool"/> + <field name="D" start="9" end="9" type="bool"/> + <field name="BTYPE" start="10" end="11" type="uint32"/> + <field name="SSBS" start="12" end="12" type="bool"/> + <field name="IL" start="20" end="20" type="bool"/> + <field name="SS" start="21" end="21" type="bool"/> + <field name="PAN" start="22" end="22" type="bool"/> + <field name="UAO" start="23" end="23" type="bool"/> + <field name="DIT" start="24" end="24" type="bool"/> + <field name="TCO" start="25" end="25" type="bool"/> + <field name="V" start="28" end="28" type="bool"/> + <field name="C" start="29" end="29" type="bool"/> + <field name="Z" start="30" end="30" type="bool"/> + <field name="N" start="31" end="31" type="bool"/> + </flags> + <reg name="x0" bitsize="64" type="int" regnum="0"/> + <reg name="x1" bitsize="64" type="int" regnum="1"/> + <reg name="x2" bitsize="64" type="int" regnum="2"/> + <reg name="x3" bitsize="64" type="int" regnum="3"/> + <reg name="x4" bitsize="64" type="int" regnum="4"/> + <reg name="x5" bitsize="64" type="int" regnum="5"/> + <reg name="x6" bitsize="64" type="int" regnum="6"/> + <reg name="x7" bitsize="64" type="int" regnum="7"/> + <reg name="x8" bitsize="64" type="int" regnum="8"/> + <reg name="x9" bitsize="64" type="int" regnum="9"/> + <reg name="x10" bitsize="64" type="int" regnum="10"/> + <reg name="x11" bitsize="64" type="int" regnum="11"/> + <reg name="x12" bitsize="64" type="int" regnum="12"/> + <reg name="x13" bitsize="64" type="int" regnum="13"/> + <reg name="x14" bitsize="64" type="int" regnum="14"/> + <reg name="x15" bitsize="64" type="int" regnum="15"/> + <reg name="x16" bitsize="64" type="int" regnum="16"/> + <reg name="x17" bitsize="64" type="int" regnum="17"/> + <reg name="x18" bitsize="64" type="int" regnum="18"/> + <reg name="x19" bitsize="64" type="int" regnum="19"/> + <reg name="x20" bitsize="64" type="int" regnum="20"/> + <reg name="x21" bitsize="64" type="int" regnum="21"/> + <reg name="x22" bitsize="64" type="int" regnum="22"/> + <reg name="x23" bitsize="64" type="int" regnum="23"/> + <reg name="x24" bitsize="64" type="int" regnum="24"/> + <reg name="x25" bitsize="64" type="int" regnum="25"/> + <reg name="x26" bitsize="64" type="int" regnum="26"/> + <reg name="x27" bitsize="64" type="int" regnum="27"/> + <reg name="x28" bitsize="64" type="int" regnum="28"/> + <reg name="x29" bitsize="64" type="int" regnum="29"/> + <reg name="x30" bitsize="64" type="int" regnum="30"/> + <reg name="sp" bitsize="64" type="data_ptr" regnum="31"/> + <reg name="pc" bitsize="64" type="code_ptr" regnum="32"/> + <reg name="cpsr" bitsize="32" type="cpsr_flags" regnum="33"/> + </feature> + <feature name="org.gnu.gdb.aarch64.gcs"> + <reg name="gcspr" bitsize="64" type="data_ptr" regnum="90" group="system"/> + </feature> +</target> diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.c b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.c new file mode 100644 index 0000000..10cf749 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.c @@ -0,0 +1,26 @@ +/* This test program 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 <stdio.h> + +int +main (void) +{ + printf ("Hello, world!\n"); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.exp new file mode 100644 index 0000000..f0508cd --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.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 GDB complains when given a target description with the GCS feature +# but not the GCS Linux feature. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +set xml_path "${srcdir}/${subdir}/aarch64-gcs-tdesc-without-linux.xml" + +gdb_test "set tdesc filename ${xml_path}" \ + "warning: Incomplete GCS support in the target: missing Linux part. GCS feature disabled." \ + "warn about incomplete GCS support" + +# We can't test a debugging session on a remote target because with the +# wrong tdesc, GDB expects a g packet reply with the wrong size. +if {[gdb_protocol_is_remote]} { + return +} + +if ![runto_main] { + return +} + +gdb_test "print \$gcspr" " = <unavailable>" "GCSPR is unavailable" + +# Now check that we can continue the debugging session normally. +gdb_test "next" + +gdb_continue_to_end diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs.c b/gdb/testsuite/gdb.arch/aarch64-gcs.c new file mode 100644 index 0000000..39519e4 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs.c @@ -0,0 +1,180 @@ +/* This test program 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 <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/auxv.h> +#include <sys/syscall.h> +#include <linux/prctl.h> + +/* Feature check for Guarded Control Stack. */ +#ifndef HWCAP_GCS +#define HWCAP_GCS (1UL << 32) +#endif + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#define PR_SET_SHADOW_STACK_STATUS 75 +#define PR_SHADOW_STACK_ENABLE (1UL << 0) +#endif + +/* We need to use a macro to call prctl because after GCS is enabled, it's not + possible to return from the function which enabled it. This is because the + return address of the calling function isn't on the GCS. */ +#define my_syscall2(num, arg1, arg2) \ + ({ \ + register long _num __asm__("x8") = (num); \ + register long _arg1 __asm__("x0") = (long)(arg1); \ + register long _arg2 __asm__("x1") = (long)(arg2); \ + register long _arg3 __asm__("x2") = 0; \ + register long _arg4 __asm__("x3") = 0; \ + register long _arg5 __asm__("x4") = 0; \ + \ + __asm__ volatile ("svc #0\n" \ + : "=r"(_arg1) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_num) \ + : "memory", "cc"); \ + _arg1; \ + }) + +#define get_gcspr(void) \ + ({ \ + unsigned long *gcspr; \ + \ + /* Get GCSPR_EL0. */ \ + asm volatile ("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \ + \ + gcspr; \ + }) + +static unsigned long *handler_gcspr = 0; + +static void +handler (int sig) +{ + handler_gcspr = get_gcspr (); +} + +static int __attribute__ ((unused)) +called_from_gdb (int val) +{ + return val + 1; +} + +/* Corrupt the return address to see if GDB will report a SIGSEGV with the + expected $_siginfo.si_code. */ +static void __attribute__ ((noinline)) +normal_function2 (void) +{ + /* x30 holds the return address. */ + register unsigned long x30 __asm__("x30") __attribute__ ((unused)); + + /* Cause a GCS exception. */ + x30 = 0xbadc0ffee; + /* Use explicit ret so that we can verify that a SIGSEGV was generated + exactly on the return instruction. */ + __asm__ volatile ("ret\n"); +} + +static inline void __attribute__ ((__always_inline__)) +inline_function2 (void) +{ + normal_function2 (); +} + +static void __attribute__ ((noinline)) +normal_function1 (void) +{ + inline_function2 (); +} + +/* First in a sequence of inline and normal functions, to test GDB + backtrace. */ +static inline void __attribute__ ((__always_inline__)) +inline_function1 (void) +{ + normal_function1 (); +} + +/* Trivial function, just so that GDB can test return with wrong GCSPR. */ +static void __attribute__ ((noinline)) +normal_function0 (void) +{ + /* Use explicit ret so that we can verify that a SIGSEGV was generated + exactly on the return instruction. */ + __asm__ volatile ("ret\n"); +} + +int +main (void) +{ + if (!(getauxval (AT_HWCAP) & HWCAP_GCS)) + { + fprintf (stderr, "GCS support not found in AT_HWCAP\n"); + return EXIT_FAILURE; + } + + /* Force shadow stacks on, our tests *should* be fine with or + without libc support and with or without this having ended + up tagged for GCS and enabled by the dynamic linker. We + can't use the libc prctl() function since we can't return + from enabling the stack. Also lock GCS if not already + locked so we can test behaviour when it's locked. */ + unsigned long gcs_mode; + int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to read GCS state: %d\n", ret); + return EXIT_FAILURE; + } + + if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) + { + gcs_mode = PR_SHADOW_STACK_ENABLE; + ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to configure GCS: %d\n", ret); + return EXIT_FAILURE; + } + } + + /* Regular function call. */ + normal_function0 (); + + /* This is used by GDB. */ + __attribute__((unused)) unsigned long *gcspr = get_gcspr (); + + struct sigaction act = { 0 }; + + act.sa_handler = &handler; /* Break here. */ + if (sigaction (SIGUSR1, &act, NULL) == -1) + { + perror ("sigaction"); + exit (EXIT_FAILURE); + } + + raise (SIGUSR1); + +/* Call sequence of inline and normal functions, to test GDB backtrace. */ + inline_function1 (); + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs.exp b/gdb/testsuite/gdb.arch/aarch64-gcs.exp new file mode 100644 index 0000000..ad73b41 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs.exp @@ -0,0 +1,98 @@ +# 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 a binary that uses a Guarded Control Stack. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +set linespec ${srcfile}:[gdb_get_line_number "Break here"] + +if ![runto ${linespec}] { + return +} + +gdb_test "print \$gcs_features_enabled" \ + [string_to_regexp { = [ PR_SHADOW_STACK_ENABLE ]}] \ + "GCS is enabled" + +gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "GDB knows about gcspr" +gdb_test "print \$gcspr == gcspr" ". = 1" "GDB has the correct gcspr value" +gdb_test_no_output "set \$gcspr_in_main = \$gcspr" \ + "save gcspr value in main for later" + +# If the inferior function call fails, we don't want the tests following it +# to be affected. +gdb_test_no_output "set unwindonsignal on" +gdb_test "print called_from_gdb (41)" ". = 42" "call inferior function" + +gdb_test "break handler" "Breakpoint \[0-9\]+ .*aarch64-gcs.c, line \[0-9\]+\\." +gdb_test "handle SIGUSR1 nostop" \ + ".*\r\nSIGUSR1\\s+No\\s+Yes\\s+Yes\\s+User defined signal 1" \ + "let the inferior receive SIGUSR1 uninterrupted" +gdb_test "continue" \ + ".*\r\nBreakpoint \[0-9\]+, handler \\(sig=10\\) at .*aarch64-gcs.c.*handler_gcspr = get_gcspr \\(\\);" \ + "continue to signal handler" + +gdb_test_no_output "set \$gcspr_in_handler = \$gcspr" \ + "save gcspr value in handler for later" +# Select the frame above the <signal handler called> frame, which makes GDB +# unwind the gcspr from the signal frame GCS context. +gdb_test "frame 2" "#2 ($hex in )?\\S+ \\(.*\\) (at|from) \\S+.*" \ + "reached frame 2" +gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "gcspr in frame level 2" +gdb_test "print \$gcspr == \$gcspr_in_handler + 8" ". = 1" \ + "gcspr unwound from signal context is correct" + +gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ + "normal_function2 \\(\\) at .*aarch64-gcs.c:$decimal" \ + "${decimal}\\s+__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "continue to SIGSEGV" + +gdb_test "print \$_siginfo.si_code" ". = 10" \ + "test value of si_code when GCS SIGSEGV happens" +# The GCS grows down, and there are two real frames until main. +gdb_test "print \$gcspr == \$gcspr_in_main - 16" ". = 1" \ + "test value of gcspr when GCS SIGSEGV happens" + +# Test writing to GCSPR. +clean_restart ${binfile} +if ![runto normal_function0] { + return +} + +gdb_test_no_output "set \$gcspr = 0xbadc0ffee" "set bogus gcspr value" +# Continue to make sure that the value was actually written to the register. +# The SIGSEGV isn't a GCS error because the problem isn't that the GCS entry +# doesn't match the return address, but rather that that GCSPR is pointing +# to an invalid address. +gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Program received signal SIGSEGV, Segmentation fault\\." \ + "normal_function0 \\(\\) at .*aarch64-gcs.c:$decimal" \ + "${decimal}\\s+__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "continue after bad gcspr" diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp new file mode 100644 index 0000000..c819cbc --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp @@ -0,0 +1,143 @@ +# Copyright 2024-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 shadow stack enabling for frame level update, the return and the +# call commands. +# As potential CET violations often only occur after resuming normal +# execution, test normal program continuation after each return or call +# commands. + +require allow_ssp_tests + +standard_testfile amd64-shadow-stack.c + +# Restart GDB an run until breakpoint in call2. + +proc restart_and_run_infcall_call2 {} { + global binfile + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + set inside_infcall_str "The program being debugged stopped while in a function called from GDB" + gdb_breakpoint [ gdb_get_line_number "break call2" ] + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + gdb_test "call (int) call2()" \ + "Breakpoint \[0-9\]*, call2.*$inside_infcall_str.*" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug additional_flags="-fcf-protection=return"}] } { + return -1 + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + + with_test_prefix "test inferior call and continue" { + gdb_breakpoint [ gdb_get_line_number "break call1" ] + gdb_continue_to_breakpoint "break call1" ".*break call1.*" + + gdb_test "call (int) call2()" "= 42" + + gdb_continue_to_end + } + + with_test_prefix "test return inside an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "return" "\#0.*call2.*" \ + "Test shadow stack return inside an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end + } + + with_test_prefix "test return 'above' an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "frame 2" "call2 ().*" "move to frame 'above' inferior call" + + gdb_test "return" "\#0.*call1.*" \ + "Test shadow stack return 'above' an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + + set call1_line [ gdb_get_line_number "break call1" ] + set call2_line [ gdb_get_line_number "break call2" ] + + # Extract shadow stack pointer inside main, call1 and call2 function. + gdb_breakpoint $call1_line + gdb_breakpoint $call2_line + set ssp_main [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in main"] + gdb_continue_to_breakpoint "break call1" ".*break call1.*" + set ssp_call1 [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in call1"] + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + set ssp_call2 [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in call2"] + + with_test_prefix "test frame level update" { + gdb_test "up" "call1.*" "move to frame 1" + gdb_test "print /x \$pl3_ssp" "= $ssp_call1" "check pl3_ssp of frame 1" + gdb_test "up" "main.*" "move to frame 2" + gdb_test "print /x \$pl3_ssp" "= $ssp_main" "check pl3_ssp of frame 2" + gdb_test "frame 0" "call2.*" "move to frame 0" + gdb_test "print /x \$pl3_ssp" "= $ssp_call2" "check pl3_ssp of frame 0" + } + + with_test_prefix "test return from current frame" { + gdb_test "return (int) 1" "#0.*call1.*" \ + "Test shadow stack return from current frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + # Potential CET violations often only occur after resuming normal execution. + # Therefore, it is important to test normal program continuation after + # testing the return command. + gdb_continue_to_end + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + + with_test_prefix "test return from past frame" { + gdb_breakpoint $call2_line + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + + gdb_test "frame 1" ".*in call1.*" + + gdb_test "return (int) 1" "#0.*main.*" \ + "Test shadow stack return from past frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + # Potential CET violations often only occur after resuming normal execution. + # Therefore, it is important to test normal program continuation after + # testing the return command. + gdb_continue_to_end + } +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c new file mode 100644 index 0000000..5e84793 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c @@ -0,0 +1,46 @@ +/* This test program 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 <stdio.h> + +/* Call the return instruction before function epilogue to trigger a + control-flow exception. */ +void +function () +{ + unsigned long ssp; + #ifndef __ILP32__ + asm volatile ("xor %0, %0; rdsspq %0" : "=r" (ssp)); + #else + asm volatile ("xor %0, %0; rdsspd %0" : "=r" (ssp)); + #endif + + /* Print ssp to stdout so that the testcase can capture it. */ + printf ("%p\n", (void *) ssp); + fflush (stdout); + + /* Manually cause a control-flow exception by executing a return + instruction before function epilogue, so the address atop the stack + is not the return instruction. */ + __asm__ volatile ("ret\n"); +} + +int +main (void) +{ + function (); /* Break here. */ +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp new file mode 100644 index 0000000..a45cd06 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp @@ -0,0 +1,119 @@ +# Copyright 2024-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 shadow stack pointer note in core dumps. +# Based on the corefile tests in gdb.arch/aarch64-gcs-core.exp. + +require allow_ssp_tests + +standard_testfile + +# Make sure GDB can read the given core file correctly. + +proc check_core_file {core_filename saved_pl3_ssp} { + global decimal + + # Load the core file. + if [gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by .*\\." \ + "Program terminated with signal SIGSEGV, Segmentation fault.*" \ + "#0 function \\(\\) at .*amd64-shadow-stack-corefile.c:$decimal" \ + "$decimal.*__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "load core file"] { + return + } + + # Check the value of ssp in the core file. + gdb_test "print/x \$pl3_ssp" "\\$\[0-9\]+ = $saved_pl3_ssp" \ + "pl3_ssp contents from core file $saved_pl3_ssp" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + {debug additional_flags="-fcf-protection=return"}] } { + return + } + + set linespec ${srcfile}:[gdb_get_line_number "Break here"] + + if ![runto $linespec] { + return + } + + # Obtain an OS-generated core file. Save test program output to + # ${binfile}.out. + set core_filename [core_find $binfile {} {} "${binfile}.out"] + set core_generated [expr {$core_filename != ""}] + + if {!$core_generated} { + untested "unable to create or find corefile" + } + + # Load the core file and check the value of the shadow stack pointer. + if {$core_generated} { + clean_restart $binfile + + with_test_prefix "OS corefile" { + # Read ssp value from saved output of the test program. + set out_id [open ${binfile}.out "r"] + set ssp_in_gcore [gets $out_id] + close $out_id + check_core_file $core_filename $ssp_in_gcore + } + } + + if ![gcore_cmd_available] { + unsupported "target does not support gcore command." + return + } + + clean_restart $binfile + + if ![runto $linespec] { + return + } + + # Continue until a crash. The line with the hex number is optional because + # it's printed by the test program, and doesn't appear in the Expect buffer + # when testing a remote target. + + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "($hex\r\n)?" \ + "Program received signal SIGSEGV, Segmentation fault.*" \ + "function \\(\\) at .*amd64-shadow-stack-corefile.c:$decimal" \ + {.*__asm__ volatile \("ret\\n"\);}] \ + "continue to SIGSEGV" + + set ssp_in_gcore [get_valueof "/x" "\$pl3_ssp" "*unknown*"] + + # Generate the gcore core file. + set gcore_filename [standard_output_file "${testfile}.gcore"] + set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"] + + gdb_assert { $gcore_generated } "gcore corefile created" + if { $gcore_generated } { + clean_restart $binfile + + with_test_prefix "gcore corefile" { + check_core_file $gcore_filename $ssp_in_gcore + } + } +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp new file mode 100644 index 0000000..e4efa00 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.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 continue from call instructions with shadow stack and displaced +# stepping being enabled. + +require allow_ssp_tests support_displaced_stepping + +standard_testfile amd64-shadow-stack.c + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + additional_flags="-fcf-protection=return"] } { + return + } + + # Enable displaced stepping. + gdb_test_no_output "set displaced-stepping on" + gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*" + + if { ![runto_main] } { + return + } + + # Get the address of the call to the call1 function. + set call1_addr -1 + gdb_test_multiple "disassemble main" "" { + -re -wrap "($hex) <\\+($decimal)>:\\s*call\\s*0x.*<call1>.*" { + set call1_addr $expect_out(1,string) + pass $gdb_test_name + } + } + + if { $call1_addr == -1 } { + return + } + + # Get the address of the call to the call2 function. + set call2_addr -1 + gdb_test_multiple "disassemble call1" "" { + -re -wrap "($hex) <\\+($decimal)>:\\s*call\\s*0x.*<call2>.*" { + set call2_addr $expect_out(1,string) + pass $gdb_test_name + } + } + + if { $call2_addr == -1 } { + return + } + + gdb_test "break *$call1_addr" \ + "Breakpoint $decimal at $hex.*" \ + "break at the address of the call1 instruction" + + gdb_test "break *$call2_addr" \ + "Breakpoint $decimal at $hex.*" \ + "break at the address of the call2 instruction" + + gdb_test "continue" \ + "Breakpoint $decimal, $call1_addr in main ().*" \ + "continue until call1 instruction" + + # Test continue from breakpoint at call1 and call2 instructions. + gdb_test "continue" \ + "Breakpoint $decimal, $call2_addr in call1 ().*" \ + "continue from call1 instruction" + + gdb_continue_to_end "continue from call2 instruction" +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c new file mode 100644 index 0000000..4a1ca1e --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c @@ -0,0 +1,40 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024-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/>. */ + +static int +call2 () +{ + return 42; /* break call2. */ +} + +static int +call1 () +{ + return call2 (); /* break call1. */ +} + +int +main () +{ + /* Depending on instruction generation we might end up in the call + instruction of call1 function after "runto_main". Avoid this by + adding a nop instruction, to simplify the testing in + amd64-shadow-stack-disp-step.exp. */ + asm ("nop"); + call1 (); /* break main. */ + return 0; +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack.exp new file mode 100644 index 0000000..a72334a --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.exp @@ -0,0 +1,71 @@ +# Copyright 2024-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 accessing the shadow stack pointer register. + +require allow_ssp_tests + +standard_testfile + +# Write PL3_SSP register with invalid shadow stack pointer value. +proc write_invalid_ssp {} { + gdb_test "print /x \$pl3_ssp = 0x12345678" "= 0x12345678" "set pl3_ssp value" + gdb_test "print /x \$pl3_ssp" "= 0x12345678" "read pl3_ssp value after setting" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + additional_flags="-fcf-protection=return"] } { + return + } + + if {![runto_main]} { + return + } + + with_test_prefix "invalid ssp" { + write_invalid_ssp + + # Continue until SIGSEV to test that the value is written back to HW. + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Program received signal SIGSEGV, Segmentation fault\\." \ + "$hex in main \\(\\)"] \ + "continue to SIGSEGV" + } + + clean_restart ${binfile} + if { ![runto_main] } { + return + } + + with_test_prefix "restore original ssp" { + # Read PL3_SSP register. + set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read pl3_ssp value"] + + write_invalid_ssp + + # Restore original value. + gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main" "restore original value" + + # Now we should not see a SIGSEV, since the original value is restored. + gdb_continue_to_end + } +} diff --git a/gdb/testsuite/gdb.base/a2-run.exp b/gdb/testsuite/gdb.base/a2-run.exp index 4e95e56..966e12a 100644 --- a/gdb/testsuite/gdb.base/a2-run.exp +++ b/gdb/testsuite/gdb.base/a2-run.exp @@ -18,6 +18,8 @@ # Can't do this test without stdio support. require {!gdb_skip_stdio_test "a2run.exp"} +set have_startup_shell [have_startup_shell] + # # test running programs # @@ -166,9 +168,8 @@ gdb_run_cmd setup_xfail "arm-*-coff" gdb_test_stdio "" "720" "" "run \"$testfile\" again after setting args" -# GOAL: Test that shell is being used with "run". For remote debugging -# targets, there is no guarantee that a "shell" (whatever that is) is used. -if {![is_remote target]} { +# GOAL: Test that shell is being used with "run". +if { $have_startup_shell == 1 } { gdb_test_stdio "run `echo 8`" \ "40320" "" "run \"$testfile\" with shell" } diff --git a/gdb/testsuite/gdb.base/annotate-symlink.exp b/gdb/testsuite/gdb.base/annotate-symlink.exp index f7f6f18..a5d431e 100644 --- a/gdb/testsuite/gdb.base/annotate-symlink.exp +++ b/gdb/testsuite/gdb.base/annotate-symlink.exp @@ -20,7 +20,7 @@ standard_testfile realname-expand.c realname-expand-real.c require {!is_remote host} set srcdirabs [file join [pwd] $srcdir] -set srcfilelink [standard_output_file realname-expand-link.c] +set srcfilelink [build_standard_output_file realname-expand-link.c] remote_exec build "ln -sf ${srcdirabs}/${subdir}/${srcfile2} $srcfilelink" diff --git a/gdb/testsuite/gdb.base/args.exp b/gdb/testsuite/gdb.base/args.exp index 33952e4..7b62a75 100644 --- a/gdb/testsuite/gdb.base/args.exp +++ b/gdb/testsuite/gdb.base/args.exp @@ -21,6 +21,7 @@ require {!target_info exists noargs} # This test requires starting new inferior processes, skip it if the target # board is a stub. require !use_gdb_stub +require {expr [have_startup_shell] != -1} standard_testfile diff --git a/gdb/testsuite/gdb.base/attach.c b/gdb/testsuite/gdb.base/attach.c index b3c5498..5133dd0 100644 --- a/gdb/testsuite/gdb.base/attach.c +++ b/gdb/testsuite/gdb.base/attach.c @@ -5,7 +5,7 @@ exit unless/until gdb sets the variable to non-zero.) */ #include <stdio.h> -#include <unistd.h> +#include "gdb_watchdog.h" int bidule = 0; volatile int should_exit = 0; @@ -14,7 +14,7 @@ int main () { int local_i = 0; - alarm (60); + gdb_watchdog (60); while (! should_exit) { diff --git a/gdb/testsuite/gdb.base/backtrace.exp b/gdb/testsuite/gdb.base/backtrace.exp index 35784b4..3020666 100644 --- a/gdb/testsuite/gdb.base/backtrace.exp +++ b/gdb/testsuite/gdb.base/backtrace.exp @@ -17,11 +17,7 @@ standard_testfile -set flags {} -lappend flags debug -lappend_include_file flags $srcdir/lib/attributes.h - -if { [prepare_for_testing "failed to prepare" $testfile $srcfile $flags] } { +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { return -1 } diff --git a/gdb/testsuite/gdb.base/break-interp.exp b/gdb/testsuite/gdb.base/break-interp.exp index 649cc86..04d8c55 100644 --- a/gdb/testsuite/gdb.base/break-interp.exp +++ b/gdb/testsuite/gdb.base/break-interp.exp @@ -218,6 +218,7 @@ proc test_core {file displacement} { set corefile [core_find $file {} "segv"] if {$corefile == ""} { + untested "unable to create or find corefile" return } diff --git a/gdb/testsuite/gdb.base/color-prompt.exp b/gdb/testsuite/gdb.base/color-prompt.exp new file mode 100644 index 0000000..c037185 --- /dev/null +++ b/gdb/testsuite/gdb.base/color-prompt.exp @@ -0,0 +1,29 @@ +# 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 using a prompt with color in CLI. + +# Using tuiterm requires setting TERM on host. +require {!is_remote host} + +# We use a tuiterm, which allows us to determine cursor position. +tuiterm_env +Term::clean_restart 8 80 + +# We start with an empty screen, to generate a visible prompt. +Term::gen_prompt + +set tui 0 +source $srcdir/$subdir/../gdb.tui/color-prompt.exp.tcl diff --git a/gdb/testsuite/gdb.base/corefile.exp b/gdb/testsuite/gdb.base/corefile.exp index da1fdf3..fd8d1d1 100644 --- a/gdb/testsuite/gdb.base/corefile.exp +++ b/gdb/testsuite/gdb.base/corefile.exp @@ -31,6 +31,7 @@ if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} { # mmapped data in core file" test. set corefile [core_find $binfile {}] if {$corefile == ""} { + untested "unable to create or find corefile" return 0 } diff --git a/gdb/testsuite/gdb.base/corefile2.exp b/gdb/testsuite/gdb.base/corefile2.exp index 392705b..d35ba1a 100644 --- a/gdb/testsuite/gdb.base/corefile2.exp +++ b/gdb/testsuite/gdb.base/corefile2.exp @@ -40,6 +40,7 @@ if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} { set corefile [core_find $binfile {}] if {$corefile == ""} { + untested "unable to create or find corefile" return 0 } diff --git a/gdb/testsuite/gdb.base/corefile3.exp b/gdb/testsuite/gdb.base/corefile3.exp index 57b2300..ef391d1 100644 --- a/gdb/testsuite/gdb.base/corefile3.exp +++ b/gdb/testsuite/gdb.base/corefile3.exp @@ -34,6 +34,7 @@ if {[build_executable $testfile.exp $testfile $srcfile] == -1} { set corefile [core_find $binfile {}] if {$corefile == ""} { + untested "unable to create or find corefile" return } diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 01e3cc1..b5e64c2 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -759,7 +759,6 @@ set show_conv_list \ {$_probe_arg10 = <error: No frame selected>} \ {$_probe_arg11 = <error: No frame selected>} \ {$_cimag = <internal function _cimag>} \ - {$_colorsupport = "monochrome"} \ {$_creal = <internal function _creal>} \ {$_isvoid = <internal function _isvoid>} \ {$_shell = <internal function _shell>} \ @@ -767,11 +766,9 @@ set show_conv_list \ {$_gdb_maint_setting = <internal function _gdb_maint_setting>} \ {$_gdb_setting_str = <internal function _gdb_setting_str>} \ {$_gdb_setting = <internal function _gdb_setting>} \ - {$_gdb_major = 17} \ - {$_gdb_minor = 1} \ {$_shell_exitsignal = void} \ {$_shell_exitcode = 0} \ - {$_active_linker_namespaces = 1} \ + {$_linker_namespace_count = 0} \ {$_linker_namespace = <error: No registers.>}\ } if [allow_python_tests] { @@ -788,10 +785,52 @@ if [allow_python_tests] { {$_any_caller_matches = <internal function _any_caller_matches>} \ } } -gdb_test_list_exact "show convenience" "show convenience" \ - "\[^\r\n\]+\[\r\n\]+" \ - "\[^\r\n\]+" \ - $show_conv_list + +set lines [gdb_get_lines_no_pass "show convenience"] +set matches 0 +set all_found 1 +foreach s $show_conv_list { + if {  $lines] } { + verbose -log "didn't match: '$s'" + set all_found 0 + break + } + incr matches +} + +set re_var [string_to_regexp {$_colorsupport}] +if { [is_remote host] } { + set re_val {[^\r\n]+} +} else { + set re_val [string_to_regexp {"monochrome"}] +} +if { [regexp "$re_var = $re_val" $lines] } { + incr matches +} else { + set all_found 0 +} + +set re_vars \ + [list \ + [string_to_regexp {$_gdb_major}] \ + [string_to_regexp {$_gdb_minor}]] +foreach re_var $re_vars { + if { [regexp "$re_var = $decimal" $lines] } { + incr matches + } else { + set all_found 0 + } +} + +if { [regexp [string_to_regexp {$_tlb = void}] $lines] } { + incr matches +} else { + # Convenience variable _tlb is added only if support for windows targets + # is enabled. Don't complain if it's missing. +} + +gdb_assert { $all_found && $matches == [count_newlines $lines] } \ + "show convenience" #test show directories gdb_test "show directories" "Source directories searched: .cdir\[:;\].cwd" diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp index 4d3e8eb..bfce900 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -38,16 +38,80 @@ if { [build_executable "failed to build" $testfile $srcfile \ return } +# Return a list of shared libraries extract from the "info sharedlibrary" +# command. Each item in the list is itself a list with the following items: +# +# - "from" address +# - "to" address +# - namespace ID +# - name (file path) + +proc get_info_shared {} { + set from_re "($::hex)\\s+" + set to_re "($::hex)\\s+" + set ns_re "(?:($::decimal)\\s+)?" + set syms_read_re "(Yes( \\(\\*\\))?|No)\\s+" + set name_re "(\[^\r\n\]+)" + set libs {} + + gdb_test_multiple "info sharedlibrary" "" { + -re {From\s+To\s+(Linker NS\s+)?Syms Read\s+Shared Object Library\r\n} { + exp_continue + } + + -re "^${from_re}${to_re}${ns_re}${syms_read_re}${name_re}\r\n" { + set from $expect_out(1,string) + set to $expect_out(2,string) + set ns $expect_out(3,string) + set name $expect_out(4,string) + + lappend libs [list $from $to $ns $name] + exp_continue + } + + -re {^\(\*\): Shared library is missing debugging information\.\r\n} { + exp_continue + } + + -re "^$::gdb_prompt " { + pass $gdb_test_name + } + } + + return $libs +} + +# Verify that "info sharedlibrary" does not contain duplicate entries. + +proc check_no_duplicates {} { + with_test_prefix "check no duplicates" { + set libs [get_info_shared] + array set seen {} + set seen_duplicate 0 + + foreach lib $libs { + if {[info exists seen($lib)]} { + verbose -log "already seen: $lib" + set seen_duplicate 1 + } + + set seen($lib) 1 + } + + gdb_assert {!$seen_duplicate} "no duplicates" + } +} + # Run the command "info sharedlibrary" and get the first namespace # for the so proc get_first_so_ns {} { set ns -1 set lib_regexp [string_to_regexp ${::binfile_lib}] gdb_test_multiple "info sharedlibrary $::so_name" "get SO namespace" -lbl { - -re "\r\nFrom\\s+To\\s+\(NS\\s+\)?Syms\\s+Read\\s+Shared Object Library(?=\r\n)" { + -re "\r\nFrom\\s+To\\s+\(Linker NS\\s+\)?Syms\\s+Read\\s+Shared Object Library(?=\r\n)" { exp_continue } - -re "\r\n$::hex\\s+$::hex\\s+\\\[\\\[($::decimal)\\\]\\\]\\s+\[^\r\n]+${lib_regexp}(?=\r\n)" { + -re "\r\n$::hex\\s+$::hex\\s+($::decimal)\\s+\[^\r\n]+${lib_regexp}(?=\r\n)" { if {$ns == -1} { set ns $expect_out(1,string) } @@ -78,9 +142,11 @@ proc test_info_shared {} { # Next, test that we *do* print a namespace column after loading SOs. gdb_test "info sharedlibrary" \ - "From\\s+To\\s+NS\\s+Syms\\s+Read\\s+Shared Object Library.*" \ + "From\\s+To\\s+Linker NS\\s+Syms\\s+Read\\s+Shared Object Library.*" \ "after loading everything" + check_no_duplicates + gdb_assert {[get_first_so_ns] == 1} "before closing any library" gdb_test "next" ".*second dlclose.*" "close first library" @@ -107,11 +173,13 @@ proc test_info_shared {} { # Run all tests related to the linkage namespaces convenience # variables, _active_namespaces and _current_namespaces. +# Also tests that the namespace ID is only printed at the correct +# times. proc_with_prefix test_conv_vars {} { clean_restart $::binfile - gdb_test "print \$_active_linker_namespaces" "1" \ - "1 namespace before starting inferior" + gdb_test "print \$_linker_namespace_count" "0" \ + "0 namespace before starting inferior" gdb_test "print \$_linker_namespace" "No registers." \ "No current namespace before starting inferior" @@ -119,7 +187,7 @@ proc_with_prefix test_conv_vars {} { return } - gdb_test "print \$_active_linker_namespaces" "1" \ + gdb_test "print \$_linker_namespace_count" "1" \ "Before activating namespaces" gdb_test "print \$_linker_namespace" ".* = 0" \ "Still in the default namespace" @@ -141,12 +209,12 @@ proc_with_prefix test_conv_vars {} { "print namespace of selected frame" gdb_continue_to_breakpoint "first dlclose" - gdb_test "print \$_active_linker_namespaces" "4" "all SOs loaded" + gdb_test "print \$_linker_namespace_count" "4" "all SOs loaded" gdb_test "next" ".*second dlclose.*" "close one SO" - gdb_test "print \$_active_linker_namespaces" "3" "one SOs unloaded" + gdb_test "print \$_linker_namespace_count" "3" "one SOs unloaded" gdb_test "next" ".*third dlclose.*" "close another SO" - gdb_test "print \$_active_linker_namespaces" "2" "two SOs unloaded" + gdb_test "print \$_linker_namespace_count" "2" "two SOs unloaded" # Restarting GDB so that we can test setting a breakpoint # using the convenience variable, while a proper bp syntax @@ -186,50 +254,44 @@ proc test_info_linker_namespaces {} { # First, test printing a single namespace, and ensure all of # them are correct, using both syntaxes. - set found_all_libs false - gdb_test_multiple "info linker-namespaces \[\[0\]\]" "print namespace 0" -lbl { - -re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[0\\\]\\\]" { - # Some systems may add libc and libm to every loaded namespace, - # others may load only one or neither, because the SO doesn't - # actually use either library. The best we can do is check if - # we found the dynamic linker, and up to 2 more libraries. - set libs $expect_out(1,string) - set found_all_libs [expr $libs - 1 <= 2] - exp_continue - } - -re "^\r\n$::gdb_prompt $" { - gdb_assert $found_all_libs "the correct number of libraries was reported" - } - -re "(^\r\n)?\[^\r\n\]+(?=\r\n)" { - exp_continue + set n_libraries 999 + + gdb_test_multiple "info linker-namespaces \[\[0\]\]" "print namespace 0" { + -re -wrap "($::decimal) librar(?:y|ies) loaded in linker namespace 0:.*" { + set n_libraries $expect_out(1,string) } } + + # Some systems may add libc and libm to every loaded namespace, + # others may load only one or neither, because the SO doesn't + # actually use either library. The best we can do is check if + # we found the dynamic linker, and up to 2 more libraries. + gdb_assert {$n_libraries <= 3} "the correct number of libraries was reported" + + set binfile_lib_re [string_to_regexp $::binfile_lib] + foreach_with_prefix ns {1 2 3} { set found_test_so false - set found_all_libs false - gdb_test_multiple "info linker-namespaces $ns" "print namespace $ns" -lbl { - -re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[$ns\\\]\\\]" { - set libs $expect_out(1,string) - # Some systems may add libc and libm to every loaded namespace, - # others may load only one or neither, because the SO doesn't - # actually use either library. The best we can do is check if - # we found the dynamic linker, the test SO, and maybe up to 2 - # more libraries. - set found_all_libs [expr $libs - 2 <= 2] + set n_libraries 999 + + gdb_test_multiple "info linker-namespaces $ns" "print namespace $ns" { + -re ".*($::decimal) librar(?:y|ies) loaded in linker namespace $ns:\r\n" { + set n_libraries $expect_out(1,string) exp_continue } - -re "^\r\n\[^\r\n\]+${::binfile_lib}\[^\r\n\]*(?=\r\n)" { + + -re -wrap "${binfile_lib_re}.*" { set found_test_so true - exp_continue - } - -re "^\r\n$::gdb_prompt $" { - gdb_assert $found_test_so "this testfle's SO was reported" - gdb_assert $found_all_libs "the correct number of libraries was reported" - } - -re "(^\r\n)?\[^\r\n\]+(?=\r\n)" { - exp_continue } } + + # Some systems may add libc and libm to every loaded namespace, + # others may load only one or neither, because the SO doesn't + # actually use either library. The best we can do is check if + # we found the dynamic linker, the test SO, and maybe up to 2 + # more libraries. + gdb_assert {$n_libraries <= 4} "the correct number of libraries was reported" + gdb_assert {$found_test_so} "this testfile's SO was reported" } # These patterns are simpler, and purposefully glob multiple lines. @@ -237,14 +299,15 @@ proc test_info_linker_namespaces {} { # without worrying about the libraries printed, since that was tested # above. gdb_test "info linker-namespaces" \ - [multi_line "There are 4 linker namespaces loaded" \ - "There are $::decimal libraries loaded in linker namespace ..0.." \ + [multi_line "There are 4 linker namespaces loaded\\." \ + "" \ + "$::decimal librar(y|ies) loaded in linker namespace 0:" \ ".*" \ - "There are $::decimal libraries loaded in linker namespace ..1.." \ + "$::decimal librar(y|ies) loaded in linker namespace 1:" \ ".*" \ - "There are $::decimal libraries loaded in linker namespace ..2.." \ + "$::decimal librar(y|ies) loaded in linker namespace 2:" \ ".*" \ - "There are $::decimal libraries loaded in linker namespace ..3.." \ + "$::decimal librar(y|ies) loaded in linker namespace 3:" \ ".*" ] "print namespaces with no argument" } diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp index da17002..54fb4c4 100644 --- a/gdb/testsuite/gdb.base/dlmopen.exp +++ b/gdb/testsuite/gdb.base/dlmopen.exp @@ -95,9 +95,19 @@ if { $dyln_name eq "" } { return } +# If the dynamic linker path contains a symlink, some instances show the real +# path instead of the original path. Accept both. +lassign [remote_exec target realpath "$dyln_name"] realpath_ret dyln_realpath_name + +if { $realpath_ret == 0 } { + set dyln_realpath_name [string trim $dyln_realpath_name] +} else { + set dyln_realpath_name "not-a-valid-path" +} + # Return true if FILENAME is the dynamic linker. Otherwise return false. proc is_dyln { filename } { - return [expr {$filename eq $::dyln_name}] + return [expr {$filename eq $::dyln_name || $filename eq $::dyln_realpath_name}] } # Check that 'info shared' show NUM occurrences of DSO. @@ -106,7 +116,7 @@ proc check_dso_count { dso num } { set count 0 gdb_test_multiple "info shared" "info shared" { - -re "$hex $hex \(\[\[$::decimal\]\]\\s+\)\?Yes \[^\r\n\]*$dso\r\n" { + -re "$hex $hex \($::decimal\\s+\)\?Yes \[^\r\n\]*$dso\r\n" { # use longer form so debug remote does not interfere set count [expr $count + 1] exp_continue @@ -233,7 +243,7 @@ proc get_dyld_info {} { set dyld_count 0 set dyld_start_addr "" gdb_test_multiple "info sharedlibrary" "" { - -re "From\\s+To\\s+\(NS\\s+\)?Syms\\s+Read\\s+Shared Object Library\r\n" { + -re "From\\s+To\\s+\(Linker NS\\s+\)?Syms\\s+Read\\s+Shared Object Library\r\n" { exp_continue } -re "^($::hex)\\s+$::hex\\s+\(\#$::decimal\\s+\)?\[^/\]+(/\[^\r\n\]+)\r\n" { @@ -358,15 +368,19 @@ proc_with_prefix test_solib_unmap_events { } { # dynamic linker as pending when some instances of the library were # unloaded, despite there really only being one copy of the dynamic # linker actually loaded into the inferior's address space. - gdb_test_multiple "info breakpoints $bpnum" "check b/p status" { - -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+<PENDING>\\s+\\*$::hex\\s*\r\n\\s+stop only if \\(0\\)" { - fail $gdb_test_name - } - - -re -wrap "$bpnum\\s+breakpoint\\s+keep\\s+y\\s+$::hex\\s*\[^\r\n\]+\r\n\\s+stop only if \\(0\\)" { - pass $gdb_test_name - } - } + set hs {[^\r\n]} + set re_pass \ + [multi_line \ + "" \ + [join \ + [list \ + "$bpnum" "breakpoint" "keep" "y" "$::hex$hs+"] \ + {\s+}] \ + [string cat \ + {\s+} \ + [string_to_regexp "stop only if (0)"] \ + ([string_to_regexp " (target evals)"])?]] + gdb_test "info breakpoints $bpnum" $re_pass "check b/p status" # With all the dlclose calls now complete, we should be back to a # single copy of the dynamic linker. diff --git a/gdb/testsuite/gdb.base/dump.c b/gdb/testsuite/gdb.base/dump.c index bdcafbf..14b66b1 100644 --- a/gdb/testsuite/gdb.base/dump.c +++ b/gdb/testsuite/gdb.base/dump.c @@ -35,7 +35,7 @@ main() for (i = 0; i < ARRSIZE; i++) intarray[i] = i+1; - intstruct.a = 12 * 1; + intstruct.a = (12 * 1) << 16; intstruct.b = 12 * 2; intstruct.c = 12 * 3; intstruct.d = 12 * 4; diff --git a/gdb/testsuite/gdb.base/dump.exp b/gdb/testsuite/gdb.base/dump.exp index a55e5b0..7c84056 100644 --- a/gdb/testsuite/gdb.base/dump.exp +++ b/gdb/testsuite/gdb.base/dump.exp @@ -107,14 +107,7 @@ set endian [get_endianness] # Now generate some dump files. proc make_dump_file { command msg } { - global gdb_prompt - - gdb_test_multiple "${command}" "$msg" { - -re ".*\[Ee\]rror.*$gdb_prompt $" { fail $msg } - -re ".*\[Ww\]arning.*$gdb_prompt $" { fail $msg } - -re ".*\[Uu\]ndefined .*$gdb_prompt $" { fail $msg } - -re ".*$gdb_prompt $" { pass $msg } - } + gdb_test_no_output "${command}" "$msg" } make_dump_file "dump val [set intarr1.bin] intarray" \ diff --git a/gdb/testsuite/gdb.base/exprs.exp b/gdb/testsuite/gdb.base/exprs.exp index f703c18..81f6f19 100644 --- a/gdb/testsuite/gdb.base/exprs.exp +++ b/gdb/testsuite/gdb.base/exprs.exp @@ -285,11 +285,14 @@ gdb_test "print v_short + " \ gdb_test "print v_short =}{= 3" \ "A syntax error in expression, near `\\}\\{= 3'\\." +set hs {[^\r\n]} +set re_debug [string cat $hs* {[Ss]hift} $hs*] + gdb_test_no_output "set debug parse 1" set saw_start 0 set saw_val 0 gdb_test_multiple "print 23" "print with debugging" -lbl { - -re "\r\nStarting parse(?=\r\n)" { + -re "\r\n${re_debug}(?=\r\n)" { set saw_start 1 exp_continue } diff --git a/gdb/testsuite/gdb.base/fullname.exp b/gdb/testsuite/gdb.base/fullname.exp index 430d0c4..ce617d5 100644 --- a/gdb/testsuite/gdb.base/fullname.exp +++ b/gdb/testsuite/gdb.base/fullname.exp @@ -65,7 +65,7 @@ if { [gdb_breakpoint [standard_output_file tmp-${srcfile}]:${line} {no-message}] } # Build the test executable using a relative path. -if { [gdb_compile [relative_filename [pwd] [standard_output_file tmp-${srcfile}]] \ +if { [gdb_compile [relative_filename [pwd] [build_standard_output_file tmp-${srcfile}]] \ "${binfile}" executable {debug}] != "" } { return -1 } diff --git a/gdb/testsuite/gdb.base/infcall-failure-2.exp b/gdb/testsuite/gdb.base/infcall-failure-2.exp new file mode 100644 index 0000000..2a7d784 --- /dev/null +++ b/gdb/testsuite/gdb.base/infcall-failure-2.exp @@ -0,0 +1,37 @@ +# 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/>. + +standard_testfile infcall-failure.c + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] == -1 } { + return +} + +if { ![runto_main] } { + return +} + +if { ![gdb_breakpoint "*0x1" message] } { + return +} + +gdb_test "p foo ()" \ + [multi_line \ + [string_to_regexp "Command aborted."] \ + ".*" ] + +# Check that gdb is still responsive. Regression test for PR gdb/33068. +gdb_test "p 1 + 1" \ + " = 2" diff --git a/gdb/testsuite/gdb.base/inferior-args.exp b/gdb/testsuite/gdb.base/inferior-args.exp index 9406c78..dfa1215 100644 --- a/gdb/testsuite/gdb.base/inferior-args.exp +++ b/gdb/testsuite/gdb.base/inferior-args.exp @@ -17,6 +17,7 @@ # This does not work on boards that don't support inferior arguments. require {!target_info exists noargs} +require {expr [have_startup_shell] != -1} standard_testfile .c diff --git a/gdb/testsuite/gdb.base/info_sources_2.exp b/gdb/testsuite/gdb.base/info_sources_2.exp index c469049..39b594b 100644 --- a/gdb/testsuite/gdb.base/info_sources_2.exp +++ b/gdb/testsuite/gdb.base/info_sources_2.exp @@ -72,13 +72,18 @@ proc run_info_sources { extra_args args } { set objfile_name "" set source_files {} set files {} + # Note below we sanitize paths so we can compare against the + # host_file_normalize'd paths later. Note we sanitize, but + # don't normalize here, as the latter would turn a relative + # path into an absolute path, and this testcase wants to make + # sure that GDB prints the absolute path. gdb_test_multiple $cmd "" { -re "${command_regex}\r\n" { exp_continue } -re "^(\[^\r\n\]+):\r\n" { - set objfile_name $expect_out(1,string) + set objfile_name [host_file_sanitize $expect_out(1,string)] if { $is_remote_target } { set objfile_name [file tail $objfile_name] } @@ -101,7 +106,7 @@ proc run_info_sources { extra_args args } { } -re "^(\[^,\r\n\]+), " { - set f $expect_out(1,string) + set f [host_file_sanitize $expect_out(1,string)] lappend files $f exp_continue } @@ -111,7 +116,7 @@ proc run_info_sources { extra_args args } { return } - set f $expect_out(1,string) + set f [host_file_sanitize $expect_out(1,string)] lappend files $f set info_sources($objfile_name) $files set $objfile_name "" @@ -133,7 +138,7 @@ proc run_info_sources { extra_args args } { } # Figure out the path for SOURCEFILE that we're looking for. - set sourcepath [file normalize ${srcdir}/${subdir}/${sourcefile}] + set sourcepath [host_file_normalize ${srcdir}/${subdir}/${sourcefile}] if { $is_remote_target } { set objfile [file tail $objfile] @@ -156,32 +161,34 @@ proc run_info_sources { extra_args args } { # The actual tests. +set host_binfile [host_file_normalize $binfile$EXEEXT] + run_info_sources "" \ - ${binfile} ${srcfile} \ - ${binfile} ${testfile}-header.h \ + ${host_binfile} ${srcfile} \ + ${host_binfile} ${testfile}-header.h \ ${solib_name} ${srcfile2} \ ${solib_name} ${testfile}-header.h run_info_sources "-basename info_sources_2" \ - ${binfile} ${srcfile} \ - ${binfile} ${testfile}-header.h \ + ${host_binfile} ${srcfile} \ + ${host_binfile} ${testfile}-header.h \ ${solib_name} ${srcfile2} \ ${solib_name} ${testfile}-header.h run_info_sources "-basename \\.c" \ - ${binfile} ${srcfile} \ - ${binfile} !${testfile}-header.h \ + ${host_binfile} ${srcfile} \ + ${host_binfile} !${testfile}-header.h \ ${solib_name} ${srcfile2} \ ${solib_name} !${testfile}-header.h run_info_sources "-basename -- -test\\.c" \ - ${binfile} ${srcfile} \ - ${binfile} !${testfile}-header.h \ + ${host_binfile} ${srcfile} \ + ${host_binfile} !${testfile}-header.h \ ${solib_name} !${srcfile2} \ ${solib_name} !${testfile}-header.h run_info_sources "-basename -- -lib\\.c" \ - ${binfile} !${srcfile} \ - ${binfile} !${testfile}-header.h \ + ${host_binfile} !${srcfile} \ + ${host_binfile} !${testfile}-header.h \ ${solib_name} ${srcfile2} \ ${solib_name} !${testfile}-header.h diff --git a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py index bc4a673..4ab7257 100644 --- a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py +++ b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py @@ -65,6 +65,10 @@ class TestUnwinder(Unwinder): for reg in pending_frame.architecture().registers("general"): val = pending_frame.read_register(reg) + # Having unavailable registers leads to a fall back to the standard + # unwinders. Don't add unavailable registers to avoid this. + if str(val) == "<unavailable>": + continue unwinder.add_saved_register(reg, val) return unwinder diff --git a/gdb/testsuite/gdb.base/macro-source-path.exp b/gdb/testsuite/gdb.base/macro-source-path.exp index 47ad789..47d99aa 100644 --- a/gdb/testsuite/gdb.base/macro-source-path.exp +++ b/gdb/testsuite/gdb.base/macro-source-path.exp @@ -33,7 +33,7 @@ require {!is_remote host} # Set the current working directory to $out/cwd, so that we can test compiling # using relative paths. -set out_dir [standard_output_file ""] +set out_dir [build_standard_output_file ""] file mkdir $out_dir/cwd file mkdir $out_dir/other file copy -force $srcdir/$subdir/$srcfile $out_dir/cwd @@ -53,7 +53,7 @@ proc test { src name } { return } - clean_restart $binfile + clean_restart [host_file_normalize $binfile] if { ![runto_main] } { return diff --git a/gdb/testsuite/gdb.base/many-headers.exp b/gdb/testsuite/gdb.base/many-headers.exp index f46b980..5e022da 100644 --- a/gdb/testsuite/gdb.base/many-headers.exp +++ b/gdb/testsuite/gdb.base/many-headers.exp @@ -33,6 +33,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile debug]} { # Generate core file. set corefile [core_find $binfile] if {$corefile == ""} { + untested "unable to create or find corefile" return 0 } diff --git a/gdb/testsuite/gdb.base/readline-ask.exp b/gdb/testsuite/gdb.base/readline-ask.exp index 3f98e13..2948970 100644 --- a/gdb/testsuite/gdb.base/readline-ask.exp +++ b/gdb/testsuite/gdb.base/readline-ask.exp @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +require {!is_remote host} + standard_testfile .c set inputrc ${srcdir}/${subdir}/${testfile}.inputrc diff --git a/gdb/testsuite/gdb.base/readline.exp b/gdb/testsuite/gdb.base/readline.exp index 198d686..9b87790 100644 --- a/gdb/testsuite/gdb.base/readline.exp +++ b/gdb/testsuite/gdb.base/readline.exp @@ -21,6 +21,8 @@ # Tests for readline operations. # +require {!is_remote host} + # This function is used to test operate-and-get-next. # NAME is the name of the test. # ARGS is a list of alternating commands and expected results. diff --git a/gdb/testsuite/gdb.base/source-dir.exp b/gdb/testsuite/gdb.base/source-dir.exp index b2bf78c..3b0c5dd 100644 --- a/gdb/testsuite/gdb.base/source-dir.exp +++ b/gdb/testsuite/gdb.base/source-dir.exp @@ -62,7 +62,7 @@ proc test_truncated_comp_dir {} { # /some/path/to/gdb/build/testsuite/ # We are going to copy the source file out of the source tree into # a location like this: - # /some/path/to/gdb/build/testsuite/output/gdb.base/soure-dir/ + # /some/path/to/gdb/build/testsuite/output/gdb.base/source-dir/ # # We will then switch to this directory and compile the source # file, however, we will ask GCC to remove this prefix from the @@ -87,11 +87,12 @@ proc test_truncated_comp_dir {} { return } - set working_dir [standard_output_file ""] + set working_dir [build_standard_output_file ""] with_cwd $working_dir { - set strip_dir [file normalize "${working_dir}/../.."] + set strip_dir [build_file_normalize "${working_dir}/../.."] + set h_strip_dir [host_file_normalize $strip_dir] - set new_srcfile [standard_output_file ${srcfile}] + set new_srcfile [build_standard_output_file ${srcfile}] set fd [open "$new_srcfile" w] puts $fd "int main () @@ -100,8 +101,17 @@ proc test_truncated_comp_dir {} { }" close $fd + # We ask GCC to remove both the build and host views of the + # path, because we don't know which one GCC uses. E.g., we're + # testing on MSYS2 with an MSYS2 cross-compiler that targets + # MinGW, then the path GCC uses is a Unix path. If OTOH we're + # testing on MSYS2 with a native Windows compiler, then the + # path GCC uses is a Windows path. set options \ - "debug additional_flags=-fdebug-prefix-map=${strip_dir}=" + [list \ + "debug" \ + "additional_flags=-fdebug-prefix-map=${strip_dir}=" \ + "additional_flags=-fdebug-prefix-map=${h_strip_dir}="] if { [gdb_compile "${srcfile}" "${binfile}" \ executable ${options}] != "" } { untested "failed to compile" @@ -133,9 +143,9 @@ proc test_truncated_comp_dir {} { "Does not include preprocessor macro info." ] \ "info source before setting directory search list" - gdb_test "dir $strip_dir" \ + gdb_test "dir $h_strip_dir" \ [search_dir_list [list \ - "$strip_dir" \ + "$h_strip_dir" \ "\\\$cdir" \ "\\\$cwd"]] \ "setup source path search directory" @@ -146,17 +156,23 @@ proc test_truncated_comp_dir {} { "4\[ \t\]+return 0;" \ "5\[ \t\]+\\}" ] - gdb_test "info source" \ - [multi_line \ - "Current source file is ${srcfile}" \ - "Compilation directory is \[^\n\r\]+" \ - "Located in ${new_srcfile}" \ - "Contains 5 lines." \ - "Source language is c." \ - "Producer is \[^\n\r\]+" \ - "\[^\n\r\]+" \ - "\[^\n\r\]+" ] \ - "info source after setting directory search list" + set re [multi_line \ + "Current source file is ${srcfile}" \ + "Compilation directory is \[^\n\r\]+" \ + "Located in (\[^\n\r\]+)" \ + "Contains 5 lines." \ + "Source language is c." \ + "Producer is \[^\n\r\]+" \ + "\[^\n\r\]+" \ + "\[^\n\r\]+"] + set test "info source after setting directory search list" + gdb_test_multiple "info source" $test { + -re -wrap "$re" { + set host_new_srcfile [host_file_normalize $new_srcfile] + set host_location [host_file_sanitize $expect_out(1,string)] + gdb_assert {$host_new_srcfile eq $host_location} $gdb_test_name + } + } } proc test_change_search_directory_with_empty_dirname {} { diff --git a/gdb/testsuite/gdb.base/source-search.c b/gdb/testsuite/gdb.base/source-search.c new file mode 100644 index 0000000..2320c5c --- /dev/null +++ b/gdb/testsuite/gdb.base/source-search.c @@ -0,0 +1,127 @@ +/* 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/>. */ + +int +main (void) +{ + /* Line 21 */ + /* Line 22 */ + /* Line 23 */ + /* Line 24 */ + /* Line 25 */ + /* Line 26 */ + /* Line 27 */ + /* Line 28 */ + /* Line 29 */ + /* Line 30 */ + /* Line 31 */ + /* Line 32 */ + /* Line 33 */ + /* Line 34 */ + /* Line 35 */ + /* Line 36 */ + /* Line 37 */ + /* Line 38 */ + /* Line 39 */ + /* Line 40 */ + /* Line 41 */ + /* Line 42 */ + /* Line 43 */ + /* Line 44 */ + /* Line 45 */ + /* Line 46 */ + /* Line 47 */ + /* Line 48 */ + /* Line 49 */ + /* Line 50 */ + /* Line 51 */ + /* Line 52 */ + /* Line 53 */ + /* Line 54 */ + /* Line 55 */ + /* Line 56 */ + /* Line 57 */ + /* Line 58 */ + /* Line 59 */ + /* Line 60 */ + /* Line 61 */ + /* Line 62 */ + /* Line 63 */ + /* Line 64 */ + /* Line 65 */ + /* Line 66 */ + /* Line 67 */ + /* Line 68 */ + /* Line 69 */ + /* Line 70 */ + /* Line 71 */ + /* Line 72 */ + /* Line 73 */ + /* Line 74 */ + /* Line 75 */ + /* Line 76 */ + /* Line 77 */ + /* Line 78 */ + /* Line 79 */ + /* Line 80 */ + /* Line 81 */ + /* Line 82 */ + /* Line 83 */ + /* Line 84 */ + /* Line 85 */ + /* Line 86 */ + /* Line 87 */ + /* Line 88 */ + /* Line 89 */ + /* Line 90 */ + /* Line 91 */ + /* Line 92 */ + /* Line 93 */ + /* Line 94 */ + /* Line 95 */ + /* Line 96 */ + /* Line 97 */ + /* Line 98 */ + /* Line 99 */ + /* Line 100 */ + /* Line 101 */ + /* Line 102 */ + /* Line 103 */ + /* Line 104 */ + /* Line 105 */ + /* Line 106 */ + /* Line 107 */ + /* Line 108 */ + /* Line 109 */ + /* Line 110 */ + /* Line 111 */ + /* Line 112 */ + /* Line 113 */ + /* Line 114 */ + /* Line 115 */ + /* Line 116 */ + /* Line 117 */ + /* Line 118 */ + /* Line 119 */ + /* Line 120 */ + /* Line 121 */ + /* Line 122 */ + /* Line 123 */ + /* Line 124 */ + /* Line 125 */ + return 0; +} /* Last line. */ diff --git a/gdb/testsuite/gdb.base/source-search.exp b/gdb/testsuite/gdb.base/source-search.exp new file mode 100644 index 0000000..559c500 --- /dev/null +++ b/gdb/testsuite/gdb.base/source-search.exp @@ -0,0 +1,106 @@ +# 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 'forward-search' and 'reverse-search' commands. This test +# relies on some hard-coded line numbers relating to the source file. +# We could switch to using gdb_get_line_number, but it doesn't feel +# like that would add much value; just don't change the source file. + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return +} + +gdb_test "forward-search This testcase is part" \ + "1\\s+/\\* This testcase is part of GDB, the GNU debugger\\." \ + "search for first line of the file" + +gdb_test "forward-search This testcase is part" \ + "Expression not found" \ + "repeated search doesn't find the same first line" + +# The 'reverse-search' command starts searching from the line before +# the last line displayed. So in this case, the reverse search starts +# from line 0, i.e. nothing is searched. +gdb_test "reverse-search This testcase is part" \ + "Expression not found" \ + "reverse search doesn't find the first line either" + +# List some source lines, and then perform some forward-searches. The +# searches start from the first line after the last line displayed. +gdb_test "list 20" ".*" \ + "list source code ahead of a forward-search" +gdb_test "forward-search Line 2" \ + "25\\s+/\\* Line 25 \\*/" \ + "first forward-search after a list" +gdb_test "forward-search Line 2" \ + "26\\s+/\\* Line 26 \\*/" \ + "second forward-search after a list" +gdb_test "forward-search Line 2" \ + "27\\s+/\\* Line 27 \\*/" \ + "third forward-search after a list" + +# Now reverse-search from where we got too. +gdb_test "reverse-search Line 2" \ + "26\\s+/\\* Line 26 \\*/" \ + "first reverse-search for 'Line 2'" +gdb_test "reverse-search Line 2" \ + "25\\s+/\\* Line 25 \\*/" \ + "second reverse-search for 'Line 2'" +gdb_test "reverse-search Line 2" \ + "24\\s+/\\* Line 24 \\*/" \ + "third reverse-search for 'Line 2'" + +# List some source lines, and then perform a reverse-search. The +# search starts frm the first line before the last line displayed. +gdb_test "list 20" ".*" \ + "list source code ahead of a reverse-search" +gdb_test "reverse-search Line 2" \ + "23\\s+/\\* Line 23 \\*/" \ + "reverse-search after a list" + +# List the last lines of the file, then reverse search for the last +# line. As reverse-search starts on the line before the last line +# displayed, this will fail to find the last line. +gdb_test "list 127" +gdb_test "reverse-search Last line" \ + "Expression not found" \ + "reverse search for the last line fails" + +# List some lines from the middle of the file. Then try an invalid +# 'list' command. Finally, check searches pick up from the middle of +# the file where the first 'list' successfully completed. +foreach_with_prefix search_direction { forward reverse } { + foreach_with_prefix bad_list { out-of-range backwards } { + gdb_test "list 50" + + if { $bad_list eq "out-of-range" } { + gdb_test "list 1000" \ + "Line number 995 out of range; \[^\r\n\]+ has 127 lines\\." + } else { + gdb_test_no_output "list 60,50" + } + + if { $search_direction eq "forward" } { + set line 55 + } else { + set line 53 + } + + gdb_test "${search_direction}-search Line" \ + "$line\\s+/\\* Line $line \\*/" + } +} diff --git a/gdb/testsuite/gdb.base/startup-with-shell.exp b/gdb/testsuite/gdb.base/startup-with-shell.exp index 80dfdf3..9c016b4 100644 --- a/gdb/testsuite/gdb.base/startup-with-shell.exp +++ b/gdb/testsuite/gdb.base/startup-with-shell.exp @@ -22,6 +22,8 @@ require !use_gdb_stub # (via dejagnu) yet. require {!is_remote target} +require {expr [have_startup_shell] != -1} + standard_testfile if { [build_executable "failed to prepare" $testfile $srcfile debug] } { diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index a6c18d3..6b1b08e 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +require {!is_remote host} + load_lib gdb-python.exp # Test CLI output styling. diff --git a/gdb/testsuite/gdb.base/tls-common.exp.tcl b/gdb/testsuite/gdb.base/tls-common.exp.tcl index 7aa7f46..fc212a9 100644 --- a/gdb/testsuite/gdb.base/tls-common.exp.tcl +++ b/gdb/testsuite/gdb.base/tls-common.exp.tcl @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-dlobj-lib.c b/gdb/testsuite/gdb.base/tls-dlobj-lib.c index c69bab7..e82a064 100644 --- a/gdb/testsuite/gdb.base/tls-dlobj-lib.c +++ b/gdb/testsuite/gdb.base/tls-dlobj-lib.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-dlobj.c b/gdb/testsuite/gdb.base/tls-dlobj.c index 322bdda..a93f4a7 100644 --- a/gdb/testsuite/gdb.base/tls-dlobj.c +++ b/gdb/testsuite/gdb.base/tls-dlobj.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-dlobj.exp b/gdb/testsuite/gdb.base/tls-dlobj.exp index 02f2ff8..32e869e 100644 --- a/gdb/testsuite/gdb.base/tls-dlobj.exp +++ b/gdb/testsuite/gdb.base/tls-dlobj.exp @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj.c b/gdb/testsuite/gdb.base/tls-multiobj.c index 10e67da..dd4aadb 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj.c +++ b/gdb/testsuite/gdb.base/tls-multiobj.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj.exp b/gdb/testsuite/gdb.base/tls-multiobj.exp index 97acb33..2a52610 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj.exp +++ b/gdb/testsuite/gdb.base/tls-multiobj.exp @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj1.c b/gdb/testsuite/gdb.base/tls-multiobj1.c index 86e7222..6207173 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj1.c +++ b/gdb/testsuite/gdb.base/tls-multiobj1.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj2.c b/gdb/testsuite/gdb.base/tls-multiobj2.c index cea0709..9ff8b67 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj2.c +++ b/gdb/testsuite/gdb.base/tls-multiobj2.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-multiobj3.c b/gdb/testsuite/gdb.base/tls-multiobj3.c index bb0f239..3594eba0 100644 --- a/gdb/testsuite/gdb.base/tls-multiobj3.c +++ b/gdb/testsuite/gdb.base/tls-multiobj3.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-nothreads.c b/gdb/testsuite/gdb.base/tls-nothreads.c index b3aaa33..cac20f8 100644 --- a/gdb/testsuite/gdb.base/tls-nothreads.c +++ b/gdb/testsuite/gdb.base/tls-nothreads.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2024 Free Software Foundation, Inc. + Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/tls-nothreads.exp b/gdb/testsuite/gdb.base/tls-nothreads.exp index 92a5cd9..105e686 100644 --- a/gdb/testsuite/gdb.base/tls-nothreads.exp +++ b/gdb/testsuite/gdb.base/tls-nothreads.exp @@ -1,4 +1,4 @@ -# Copyright 2024 Free Software Foundation, Inc. +# Copyright 2024-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 diff --git a/gdb/testsuite/gdb.base/watchpoint-unaligned.c b/gdb/testsuite/gdb.base/watchpoint-unaligned.c index d3c1349..ca2fa45 100644 --- a/gdb/testsuite/gdb.base/watchpoint-unaligned.c +++ b/gdb/testsuite/gdb.base/watchpoint-unaligned.c @@ -18,6 +18,8 @@ #include <stdint.h> #include <assert.h> +static volatile int volatile_dummy; + static int again; static volatile struct @@ -53,6 +55,40 @@ write_size8twice (void) data.u.size8twice[offset] = first; data.u.size8twice[offset + 1] = second; #endif + + /* Setting a breakpoint on an instruction after an instruction triggering a + watchpoint makes it ambiguous which one will be reported. + Insert a dummy instruction in between to make sure the watchpoint gets + reported. */ + volatile_dummy = 1; + + return; /* write_size8twice_return */ +} + +static void +read_size8twice (void) +{ + static uint64_t volatile first; + static uint64_t volatile second; + +#ifdef __aarch64__ + volatile void *p = &data.u.size8twice[offset]; + asm volatile ("ldp %0, %1, [%2]" + : "=r" (first), "=r" (second) /* output */ + : "r" (p) /* input */ + : /* clobber */); +#else + first = data.u.size8twice[offset]; + second = data.u.size8twice[offset + 1]; +#endif + + /* Setting a breakpoint on an instruction after an instruction triggering a + watchpoint makes it ambiguous which one will be reported. + Insert a dummy instruction inbetween to make sure the watchpoint gets + reported. */ + volatile_dummy = 1; + + return; /* read_size8twice_return */ } int @@ -63,6 +99,7 @@ main (void) assert (sizeof (data) == 8 + 3 * 8); write_size8twice (); + read_size8twice (); while (size) { diff --git a/gdb/testsuite/gdb.base/watchpoint-unaligned.exp b/gdb/testsuite/gdb.base/watchpoint-unaligned.exp index 9220402..85b1eb7 100644 --- a/gdb/testsuite/gdb.base/watchpoint-unaligned.exp +++ b/gdb/testsuite/gdb.base/watchpoint-unaligned.exp @@ -151,6 +151,63 @@ foreach_with_prefix wpcount {4 7} { gdb_assert $got_hit $test } +proc size8twice { function cmd offset index } { + clean_restart $::testfile + + if { ![runto $function] } { + return -1 + } + + # Set offset in the inferior. + gdb_test_no_output "set var offset = $offset" + + # Set a breakpoint. + set bp_src_string "${function}_return" + set bp_loc [gdb_get_line_number $bp_src_string] + gdb_breakpoint $bp_loc \ + "Breakpoint $::decimal at $::hex" "$bp_src_string" + + # Set a hardware watchpoint. + set watch_index [expr $offset + $index] + set test "$cmd data.u.size8twice\[$watch_index\]" + set wpnum 0 + gdb_test_multiple $test "" { + -re -wrap "Hardware (read )?watchpoint ($::decimal): .*" { + set wpnum $expect_out(2,string) + pass $gdb_test_name + } + -re -wrap "Watchpoint ($::decimal): .*" { + if {[istarget "arm*-*-*"]} { + untested $gdb_test_name + } else { + fail $gdb_test_name + } + } + } + + if { ! $wpnum } { + # No hardware watchpoint, we're done. + return 0 + } + + # Try to trigger the hardware watchpoint. + set got_hit 0 + gdb_test_multiple "continue" "" { + -re -wrap "\r\nCould not insert hardware watchpoint .*" { + } + -re -wrap "Hardware (read )?watchpoint $wpnum:.*(New value|Value) = .*" { + set got_hit 1 + send_gdb "continue\n" + exp_continue + } + -re -wrap " $bp_src_string .*" { + } + } + gdb_assert { $got_hit } + + return $got_hit +} + # We've got an array with 3 8-byte elements. Do a store of 16 bytes, # to: # - elements 0 and 1 (offset == 0), and @@ -158,49 +215,21 @@ foreach_with_prefix wpcount {4 7} { # For each case, check setting a watchpoint at: # - the first written element (index == 0), and # - the second element (index == 1). -foreach_with_prefix offset { 0 1 } { - foreach_with_prefix index { 0 1 } { - - clean_restart $binfile - - if ![runto_main] { - return -1 - } - - gdb_test_no_output "set var offset = $offset" - gdb_breakpoint [gdb_get_line_number "final_return"] \ - "Breakpoint $decimal at $hex" "final_return" - set watch_index [expr $offset + $index] - set test "watch data.u.size8twice\[$watch_index\]" - set wpnum 0 - gdb_test_multiple $test $test { - -re "Hardware watchpoint (\[0-9\]+): .*\r\n$gdb_prompt $" { - set wpnum $expect_out(1,string) - pass $gdb_test_name - } - -re "Watchpoint (\[0-9\]+): .*\r\n$gdb_prompt $" { - if {[istarget "arm*-*-*"]} { - untested $gdb_test_name - } else { - fail $gdb_test_name - } +foreach_with_prefix fun { write_size8twice read_size8twice } { + if { $fun == "write_size8twice" } { + set cmd "watch" + } else { + set cmd "rwatch" + } + foreach_with_prefix offset { 0 1 } { + foreach_with_prefix index { 0 1 } { + set res [size8twice $fun $cmd $offset $index] + if { $res != 1 } { + break } } - if {$wpnum} { - set test "continue" - set got_hit 0 - gdb_test_multiple $test $test { - -re "\r\nCould not insert hardware watchpoint .*\r\n$gdb_prompt $" { - } - -re "Hardware watchpoint $wpnum:.*New value = .*\r\n$gdb_prompt $" { - set got_hit 1 - send_gdb "continue\n" - exp_continue - } - -re " final_return .*\r\n$gdb_prompt $" { - } - } - gdb_assert $got_hit "size8twice write" + if { $res != 1 } { + break } } } diff --git a/gdb/testsuite/gdb.cp/method-ref-return.cc b/gdb/testsuite/gdb.cp/method-ref-return.cc new file mode 100644 index 0000000..4169bfe --- /dev/null +++ b/gdb/testsuite/gdb.cp/method-ref-return.cc @@ -0,0 +1,42 @@ +/* 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/>. */ + +/* Test that we can access class method/data member via reference. */ + +struct foo +{ + foo () : m_a (42) {} + int get_a () const { return m_a; } + int m_a; +}; + +struct bar +{ + bar () : m_foo () {} + const foo &get_foo () const { return m_foo; } + foo m_foo; +}; + +int +main (int argc, char *argv[]) +{ + bar b; + const foo &ref = b.get_foo (); + int ret = ref.m_a; // breakpoint here + ret += ref.get_a (); + return ret; +}
\ No newline at end of file diff --git a/gdb/testsuite/gdb.cp/method-ref-return.exp b/gdb/testsuite/gdb.cp/method-ref-return.exp new file mode 100644 index 0000000..1ac5ac9 --- /dev/null +++ b/gdb/testsuite/gdb.cp/method-ref-return.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/>. + +# Test calling methods and accessing members via reference. + +require allow_cplus_tests + +standard_testfile .cc + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +# Set a breakpoint after the bar object is created and the reference is obtained. +gdb_breakpoint [gdb_get_line_number "breakpoint here"] +gdb_continue_to_breakpoint "after reference assignment" + +# Test that we can call the method through reference and get the expected result. +gdb_test_multiple "print b.get_foo()" "print method call returning reference" { + -re "\\$\[0-9\]+ = \\(const foo &\\) @$hex: \\{m_a = 42\\}\r\n$gdb_prompt $" { + pass $gdb_test_name + } + -re "Could not validate memory tag: Value can't be converted to integer\\." { + fail "$gdb_test_name" + } +} + +# Test accessing the member through the reference. +gdb_test "print b.get_foo ().m_a" \ + "\\$\[0-9\]+ = 42" \ + "print member access through reference" + +# Test calling method on the referenced object. +gdb_test "print b.get_foo ().get_a()" \ + "\\$\[0-9\]+ = 42" \ + "print method call on referenced object" + +# Test that the stored reference works correctly. +gdb_test_multiple "print ref" "print stored reference" { + -re "\\$\[0-9\]+ = \\(const foo &\\) @$hex: \\{m_a = 42\\}\r\n$gdb_prompt $" { + pass $gdb_test_name + } + -re "Could not validate memory tag: Value can't be converted to integer\\." { + fail "$gdb_test_name" + } +} + +gdb_test "print ref.m_a" \ + "\\$\[0-9\]+ = 42" \ + "print member through stored reference" + +gdb_test "print ref.get_a()" \ + "\\$\[0-9\]+ = 42" \ + "print method call through stored reference"
\ No newline at end of file diff --git a/gdb/testsuite/gdb.dap/scopes.c b/gdb/testsuite/gdb.dap/scopes.c index d8929f1..2a1d76c 100644 --- a/gdb/testsuite/gdb.dap/scopes.c +++ b/gdb/testsuite/gdb.dap/scopes.c @@ -27,6 +27,8 @@ int main () static int scalar = 23; + void *ptr = (void *) &scalar; + { const char *inner = "inner block"; diff --git a/gdb/testsuite/gdb.dap/scopes.exp b/gdb/testsuite/gdb.dap/scopes.exp index 59d344b..e4e5c28 100644 --- a/gdb/testsuite/gdb.dap/scopes.exp +++ b/gdb/testsuite/gdb.dap/scopes.exp @@ -25,123 +25,166 @@ if {[build_executable ${testfile}.exp $testfile] == -1} { return } -if {[dap_initialize] == ""} { - return -} +save_vars { ::env(GLIBC_TUNABLES) } { + + # If x86 shadow stack is supported we need to configure GLIBC_TUNABLES + # such that the feature is enabled and the register pl3_ssp is + # available. Otherwise the request to fetch all registers will fail + # with "message": "value is not available". + if { [allow_ssp_tests] } { + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + } -set launch_id [dap_launch $testfile] + if {[dap_initialize] == ""} { + return + } -set line [gdb_get_line_number "BREAK"] -set obj [dap_check_request_and_response "set breakpoint by line number" \ - setBreakpoints \ - [format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \ - [list s $srcfile] $line]] -set line_bpno [dap_get_breakpoint_number $obj] + set launch_id [dap_launch $testfile] -dap_check_request_and_response "configurationDone" configurationDone + set line [gdb_get_line_number "BREAK"] + set obj [dap_check_request_and_response "set breakpoint by line number" \ + setBreakpoints \ + [format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \ + [list s $srcfile] $line]] + set line_bpno [dap_get_breakpoint_number $obj] -dap_check_response "launch response" launch $launch_id + dap_check_request_and_response "configurationDone" configurationDone -dap_wait_for_event_and_check "inferior started" thread "body reason" started + dap_check_response "launch response" launch $launch_id -dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ - "body reason" breakpoint \ - "body hitBreakpointIds" $line_bpno + dap_wait_for_event_and_check "inferior started" thread "body reason" started -set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \ - {o threadId [i 1]}] \ - 0] -set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id] + dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ + "body reason" breakpoint \ + "body hitBreakpointIds" $line_bpno -set scopes [dap_check_request_and_response "get scopes" scopes \ + set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \ + {o threadId [i 1]}] \ + 0] + set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id] + + set scopes [dap_check_request_and_response "get scopes" scopes \ [format {o frameId [i %d]} $frame_id]] -set scopes [dict get [lindex $scopes 0] body scopes] + set scopes [dict get [lindex $scopes 0] body scopes] -# Request the scopes twice, and verify that the results are identical. -# GDB previously had a bug where it would return new scopes each time. -set scopes2 [dap_check_request_and_response "get scopes again" scopes \ + # Request the scopes twice, and verify that the results are identical. + # GDB previously had a bug where it would return new scopes each time. + set scopes2 [dap_check_request_and_response "get scopes again" scopes \ [format {o frameId [i %d]} $frame_id]] -set scopes2 [dict get [lindex $scopes2 0] body scopes] -gdb_assert {$scopes2 == $scopes} "identical scopes requests yield same body" - -gdb_assert {[llength $scopes] == 2} "two scopes" - -lassign $scopes scope reg_scope -gdb_assert {[dict get $scope name] == "Locals"} "scope is locals" -gdb_assert {[dict get $scope presentationHint] == "locals"} \ - "locals presentation hint" -gdb_assert {[dict get $scope namedVariables] == 3} "three vars in scope" - -gdb_assert {[dict get $reg_scope name] == "Registers"} \ - "second scope is registers" -gdb_assert {[dict get $reg_scope presentationHint] == "registers"} \ - "registers presentation hint" -gdb_assert {[dict get $reg_scope namedVariables] > 0} "at least one register" - -set num [dict get $scope variablesReference] -# Send two requests and combine them, to verify that using a range -# works. -set refs1 [lindex [dap_check_request_and_response "fetch variables 0,1" \ - "variables" \ - [format {o variablesReference [i %d] count [i 2]} \ - $num]] \ - 0] -set refs2 [lindex [dap_check_request_and_response "fetch variables 2" \ - "variables" \ - [format {o variablesReference [i %d] \ - start [i 2] count [i 1]} \ - $num]] \ - 0] - -set vars [concat [dict get $refs1 body variables] \ - [dict get $refs2 body variables]] -foreach var $vars { - set name [dict get $var name] - - if {$name != "dei"} { - gdb_assert {[dict get $var variablesReference] == 0} \ - "$name has no structure" - } - - switch $name { - "inner" { - gdb_assert {[string match "*inner block*" [dict get $var value]]} \ - "check value of inner" + set scopes2 [dict get [lindex $scopes2 0] body scopes] + gdb_assert {$scopes2 == $scopes} "identical scopes requests yield same body" + + gdb_assert {[llength $scopes] == 2} "two scopes" + + lassign $scopes scope reg_scope + gdb_assert {[dict get $scope name] == "Locals"} "scope is locals" + gdb_assert {[dict get $scope presentationHint] == "locals"} \ + "locals presentation hint" + set count [dict get $scope namedVariables] + gdb_assert {$count == 4} "four vars in scope" + + gdb_assert {[dict get $reg_scope name] == "Registers"} \ + "second scope is registers" + gdb_assert {[dict get $reg_scope presentationHint] == "registers"} \ + "registers presentation hint" + gdb_assert {[dict get $reg_scope namedVariables] > 0} "at least one register" + + set num [dict get $scope variablesReference] + # Send two requests and combine them, to verify that using a range + # works. + set refs1 [lindex [dap_check_request_and_response "fetch variables 0,1" \ + "variables" \ + [format {o variablesReference [i %d] count [i 2]} \ + $num]] \ + 0] + set refs2 [lindex [dap_check_request_and_response "fetch variables 2" \ + "variables" \ + [format {o variablesReference [i %d] \ + start [i 2] count [i %d]} \ + $num [expr {$count - 2}]]] \ + 0] + + set vars [concat [dict get $refs1 body variables] \ + [dict get $refs2 body variables]] + foreach var $vars { + set name [dict get $var name] + + if {$name != "dei"} { + gdb_assert {[dict get $var variablesReference] == 0} \ + "$name has no structure" } - "dei" { - gdb_assert {[dict get $var value] == ""} "check value of dei" - set dei_ref [dict get $var variablesReference] - } - "scalar" { - gdb_assert {[dict get $var value] == 23} "check value of scalar" + + switch $name { + "inner" { + gdb_assert {[string match "*inner block*" [dict get $var value]]} \ + "check value of inner" + } + "dei" { + gdb_assert {[dict get $var value] == ""} "check value of dei" + set dei_ref [dict get $var variablesReference] + } + "scalar" { + gdb_assert {[dict get $var value] == 23} "check value of scalar" + } + "ptr" { + gdb_assert {[dict get $var memoryReference] != ""} \ + "check memoryReference of ptr" + } + default { + fail "unknown variable $name" + } } - default { - fail "unknown variable $name" + } + + set refs [lindex [dap_check_request_and_response "fetch contents of dei" \ + "variables" \ + [format {o variablesReference [i %d]} $dei_ref]] \ + 0] + set deivals [dict get $refs body variables] + gdb_assert {[llength $deivals] == 2} "dei has two members" + + # Request more children than exist. See PR dap/33228. + set seq [dap_send_request variables \ + [format {o variablesReference [i %d] count [i 100]} $dei_ref]] + lassign [dap_read_response variables $seq] response ignore + gdb_assert {[dict get $response success] == "false"} \ + "variables with invalid count" + + set num [dict get $reg_scope variablesReference] + lassign [dap_check_request_and_response "fetch all registers" \ + "variables" \ + [format {o variablesReference [i %d] count [i %d]} $num\ + [dict get $reg_scope namedVariables]]] \ + val events + + # If any register has children, try to fetch those as well. This is a + # regression test for part of PR dap/33228. + foreach var [dict get $val body variables] { + set regvar [dict get $var variablesReference] + if {$regvar > 0} { + # If variablesReference is non-zero, then there must be either + # named or indexed children. + if {[dict exists $var namedVariables]} { + set n [dict get $var namedVariables] + } else { + set n [dict get $var indexedVariables] + } + + dap_check_request_and_response "fetch register children for $regvar" \ + "variables" \ + [format {o variablesReference [i %d] count [i %d]} $regvar $n] } } -} -set refs [lindex [dap_check_request_and_response "fetch contents of dei" \ - "variables" \ - [format {o variablesReference [i %d]} $dei_ref]] \ - 0] -set deivals [dict get $refs body variables] -gdb_assert {[llength $deivals] == 2} "dei has two members" - -set num [dict get $reg_scope variablesReference] -# The request succeeding is sufficient. -set val [dap_check_request_and_response "fetch first register" \ - "variables" \ - [format {o variablesReference [i %d] count [i 1]} $num]] - -set num [dict get $scope variablesReference] -set refs [lindex [dap_check_request_and_response "set variable scalar" \ - "setVariable" \ - [format {o variablesReference [i %d] name [s scalar] \ - value [s 32]} \ + set num [dict get $scope variablesReference] + set refs [lindex [dap_check_request_and_response "set variable scalar" \ + "setVariable" \ + [format {o variablesReference [i %d] name [s scalar] \ + value [s 32]} \ $num]] \ - 0] -gdb_assert { [dict get $refs body value] == 32 } \ - "setting variable yields updated value" + 0] + gdb_assert { [dict get $refs body value] == 32 } \ + "setting variable yields updated value" -dap_shutdown + dap_shutdown +} diff --git a/gdb/testsuite/gdb.dwarf2/backward-spec-inter-cu.exp b/gdb/testsuite/gdb.dwarf2/backward-spec-inter-cu.exp index b62f928..1876849 100644 --- a/gdb/testsuite/gdb.dwarf2/backward-spec-inter-cu.exp +++ b/gdb/testsuite/gdb.dwarf2/backward-spec-inter-cu.exp @@ -20,6 +20,7 @@ load_lib dwarf.exp # This test can only be run on targets which support DWARF-2 and use gas. require dwarf2_support +require !readnow standard_testfile main.c -debug.S @@ -98,6 +99,11 @@ foreach_with_prefix worker_threads $worker_threads_list { gdb_load $binfile + set index [have_index $binfile] + if { ![string eq $index ""] } { + return + } + gdb_test "pipe maint print objfiles | grep ns::v" \ "$ws+qualified:$ws+ns::v" \ "v has parent ns" diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c index ccfb150..258ebfa 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.c @@ -33,11 +33,28 @@ bar_helper (void) asm ("foobar_entry_label: .globl foobar_entry_label"); } +__attribute__ ((noinline)) +void +barso_helper (void) +{ + asm ("barso_helper_label: .globl barso_helper_label"); + I++; + J++; + asm ("fooso_entry_label: .globl fooso_entry_label"); + J++; + K++; + asm ("foobarso_entry_label: .globl foobarso_entry_label"); +} + int main (void) { asm ("main_label: .globl main_label"); bar_helper (); + I = 0; + J = 0; + K = 0; + barso_helper (); return 0; } diff --git a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp index bd22560..bba4cfb 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-entry-points.exp @@ -36,6 +36,7 @@ Dwarf::assemble $asm_file { get_func_info main get_func_info bar_helper + get_func_info barso_helper set int_size [get_sizeof "int" 4] @@ -135,8 +136,8 @@ Dwarf::assemble $asm_file { {decl_file 1 data1} {decl_line $bar_line data1} {external 1 flag} - {low_pc $bar_helper_start addr} - {high_pc "$bar_helper_start + $bar_helper_len" addr} + {low_pc $barso_helper_start addr} + {high_pc "$barso_helper_start + $barso_helper_len" addr} } { formal_parameter { {name I} @@ -152,7 +153,7 @@ Dwarf::assemble $asm_file { {name fooso} {decl_file 1 data1} {decl_line $foo_line data1} - {low_pc foo_entry_label addr} + {low_pc fooso_entry_label addr} } { formal_parameter { {name J} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-linkage-name-trust.exp b/gdb/testsuite/gdb.dwarf2/dw2-linkage-name-trust.exp index 01eab48..7627e8e 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-linkage-name-trust.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-linkage-name-trust.exp @@ -27,7 +27,7 @@ standard_testfile .S set executable ${testfile} if {[prepare_for_testing_full "failed to prepare" \ - [list $testfile c++ $testfile-main.cc {c++ debug} \ + [list $testfile c++ $testfile-main.cc {c++ nodebug} \ $srcfile {}]]} { return -1 } diff --git a/gdb/testsuite/gdb.dwarf2/forward-spec-inter-cu.exp b/gdb/testsuite/gdb.dwarf2/forward-spec-inter-cu.exp index e92e582..26d0af6 100644 --- a/gdb/testsuite/gdb.dwarf2/forward-spec-inter-cu.exp +++ b/gdb/testsuite/gdb.dwarf2/forward-spec-inter-cu.exp @@ -20,6 +20,7 @@ load_lib dwarf.exp # This test can only be run on targets which support DWARF-2 and use gas. require dwarf2_support +require !readnow standard_testfile main.c -debug.S @@ -98,6 +99,11 @@ foreach_with_prefix worker_threads $worker_threads_list { gdb_load $binfile + set index [have_index $binfile] + if { ![string eq $index ""] } { + return + } + gdb_test "pipe maint print objfiles | grep ns::v" \ "$ws+qualified:$ws+ns::v" \ "v has parent ns" diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp index c0c2635..d4407d7 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp @@ -18,6 +18,10 @@ # Generate binaries imitating different ways source file paths can be passed to # compilers. Test printing macros from those binaries. +load_lib dwarf.exp + +require dwarf2_support + # The do_test proc comes from macro-source-path.exp.tcl. source $srcdir/$subdir/macro-source-path.exp.tcl diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp index 0b3239e..23a40ba 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp @@ -18,6 +18,10 @@ # Generate binaries imitating different ways source file paths can be passed to # compilers. Test printing macros from those binaries. +load_lib dwarf.exp + +require dwarf2_support + # The do_test proc comes from macro-source-path.exp.tcl. source $srcdir/$subdir/macro-source-path.exp.tcl diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp index 940f997..99f7857 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp @@ -18,6 +18,10 @@ # Generate binaries imitating different ways source file paths can be passed to # compilers. Test printing macros from those binaries. +load_lib dwarf.exp + +require dwarf2_support + # The do_test proc comes from macro-source-path.exp.tcl. source $srcdir/$subdir/macro-source-path.exp.tcl diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp index dea0308..569b409 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp @@ -18,6 +18,10 @@ # Generate binaries imitating different ways source file paths can be passed to # compilers. Test printing macros from those binaries. +load_lib dwarf.exp + +require dwarf2_support + # The do_test proc comes from macro-source-path.exp.tcl. source $srcdir/$subdir/macro-source-path.exp.tcl diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp index 98a278e..0517d29 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp @@ -18,6 +18,10 @@ # Generate binaries imitating different ways source file paths can be passed to # compilers. Test printing macros from those binaries. +load_lib dwarf.exp + +require dwarf2_support + # The do_test proc comes from macro-source-path.exp.tcl. source $srcdir/$subdir/macro-source-path.exp.tcl diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path.exp.tcl b/gdb/testsuite/gdb.dwarf2/macro-source-path.exp.tcl index ecaf685..6254286 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path.exp.tcl +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path.exp.tcl @@ -21,10 +21,6 @@ # The entry points for this test are in the various # gdb.dwarf2/macro-source-path-*.exp files. -load_lib dwarf.exp - -require dwarf2_support - standard_testfile macro-source-path.c lassign [function_range main $srcdir/$subdir/$srcfile] \ diff --git a/gdb/testsuite/gdb.gdb/index-file.exp b/gdb/testsuite/gdb.gdb/index-file.exp index 2252b79..a9af211 100644 --- a/gdb/testsuite/gdb.gdb/index-file.exp +++ b/gdb/testsuite/gdb.gdb/index-file.exp @@ -30,6 +30,13 @@ if { $filename eq "" } { return -1 } +# If FILENAME is a libtool wrapper, then we need to get the path of the real +# executable. +set filename [selftest_libtool_get_real_gdb_executable $filename] +if { $filename eq "" } { + return -1 +} + with_timeout_factor $timeout_factor { # Start GDB, load FILENAME. clean_restart $filename diff --git a/gdb/testsuite/gdb.gdb/python-helper.exp b/gdb/testsuite/gdb.gdb/python-helper.exp index 8126740..33243c9 100644 --- a/gdb/testsuite/gdb.gdb/python-helper.exp +++ b/gdb/testsuite/gdb.gdb/python-helper.exp @@ -291,4 +291,4 @@ proc test_python_helper {} { } # Use the self-test framework to run the test. -do_self_tests captured_main test_python_helper +do_self_tests test_python_helper diff --git a/gdb/testsuite/gdb.gdb/selftest.exp b/gdb/testsuite/gdb.gdb/selftest.exp index 1cf9265..f5cea1d 100644 --- a/gdb/testsuite/gdb.gdb/selftest.exp +++ b/gdb/testsuite/gdb.gdb/selftest.exp @@ -167,5 +167,5 @@ proc test_with_self { } { save_vars { INTERNAL_GDBFLAGS } { set INTERNAL_GDBFLAGS [string map {"-q" ""} $INTERNAL_GDBFLAGS] - do_self_tests captured_main test_with_self + do_self_tests test_with_self } diff --git a/gdb/testsuite/gdb.guile/scm-color.exp b/gdb/testsuite/gdb.guile/scm-color.exp index 578f712..4c6a9c2 100644 --- a/gdb/testsuite/gdb.guile/scm-color.exp +++ b/gdb/testsuite/gdb.guile/scm-color.exp @@ -19,6 +19,7 @@ load_lib gdb-guile.exp require allow_guile_tests +require {!is_remote host} # Start GDB with styling support. with_ansi_styling_terminal { diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index e35428a..acd78b9 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -39,7 +39,8 @@ if { [is_remote host] } { gdb_test "guile (print (parameter-value \"directories\"))" \ "\\\$cdir.\\\$cwd" } else { - set escaped_directory [string_to_regexp "$srcdir/$subdir"] + set directory [host_file_normalize "$::srcdir/$::subdir"] + set escaped_directory [string_to_regexp $directory] gdb_test "guile (print (parameter-value \"directories\"))" \ "$escaped_directory.\\\$cdir.\\\$cwd" } @@ -565,47 +566,50 @@ rename scm_param_test_maybe_no_output "" # Test a color parameter. -with_ansi_styling_terminal { - # This enables 256 colors support and disables colors approximation. - setenv TERM xterm-256color - setenv COLORTERM truecolor - - # Start with a fresh gdb. - gdb_exit - gdb_start - gdb_reinitialize_dir $srcdir/$subdir - - gdb_install_guile_utils - gdb_install_guile_module - - # We use "." here instead of ":" so that this works on win32 too. - set escaped_directory [string_to_regexp "$srcdir/$subdir"] - - gdb_test_multiline "color gdb parameter" \ - "guile" "" \ - "(define test-color-param" "" \ - " (make-parameter \"print test-color-param\"" "" \ - " #:command-class COMMAND_DATA" "" \ - " #:parameter-type PARAM_COLOR" "" \ - " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \ - " #:show-doc \"Show the state of the test-color-param.\"" "" \ - " #:set-doc \"Set the state of the test-color-param.\"" "" \ - " #:show-func (lambda (self value)" "" \ - " (format #f \"The state of the test-color-param is ~a.\" value))" "" \ - " #:initial-value (make-color \"green\")))" "" \ - "(register-parameter! test-color-param)" "" \ - "end" - - with_test_prefix "test-color-param" { - with_test_prefix "initial-value" { - gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)" - gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value" - gdb_test_no_output "set print test-color-param 255" - } - with_test_prefix "new-value" { - gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value" - gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)" - gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter" +if { ![is_remote host] } { + with_ansi_styling_terminal { + + # This enables 256 colors support and disables colors approximation. + setenv TERM xterm-256color + setenv COLORTERM truecolor + + # Start with a fresh gdb. + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + + gdb_install_guile_utils + gdb_install_guile_module + + # We use "." here instead of ":" so that this works on win32 too. + set escaped_directory [string_to_regexp "$srcdir/$subdir"] + + gdb_test_multiline "color gdb parameter" \ + "guile" "" \ + "(define test-color-param" "" \ + " (make-parameter \"print test-color-param\"" "" \ + " #:command-class COMMAND_DATA" "" \ + " #:parameter-type PARAM_COLOR" "" \ + " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \ + " #:show-doc \"Show the state of the test-color-param.\"" "" \ + " #:set-doc \"Set the state of the test-color-param.\"" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of the test-color-param is ~a.\" value))" "" \ + " #:initial-value (make-color \"green\")))" "" \ + "(register-parameter! test-color-param)" "" \ + "end" + + with_test_prefix "test-color-param" { + with_test_prefix "initial-value" { + gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color green COLORSPACE_ANSI_8COLOR>" "color parameter value (green)" + gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value" + gdb_test_no_output "set print test-color-param 255" + } + with_test_prefix "new-value" { + gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value" + gdb_test "guile (print (parameter-value test-color-param))" "= #<gdb:color 255 COLORSPACE_XTERM_256COLOR>" "color parameter value (255)" + gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter" + } } } } diff --git a/gdb/testsuite/gdb.mi/mi-corefile.exp b/gdb/testsuite/gdb.mi/mi-corefile.exp index 3f0e720..b491486 100644 --- a/gdb/testsuite/gdb.mi/mi-corefile.exp +++ b/gdb/testsuite/gdb.mi/mi-corefile.exp @@ -29,6 +29,7 @@ if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} { set corefile [core_find $binfile {}] if {$corefile == ""} { + untested "unable to create or find corefile" return 0 } diff --git a/gdb/testsuite/gdb.mi/mi-dlmopen.exp b/gdb/testsuite/gdb.mi/mi-dlmopen.exp index c0208eb..936027f 100644 --- a/gdb/testsuite/gdb.mi/mi-dlmopen.exp +++ b/gdb/testsuite/gdb.mi/mi-dlmopen.exp @@ -65,9 +65,19 @@ if { $dyln_name eq "" } { set bp_main [gdb_get_line_number "bp.main" $srcfile] set bp_loaded [gdb_get_line_number "bp.loaded" $srcfile] +# If the dynamic linker path contains a symlink, some instances show the real +# path instead of the original path. Accept both. +lassign [remote_exec target realpath "$dyln_name"] realpath_ret dyln_realpath_name + +if { $realpath_ret == 0 } { + set dyln_realpath_name [string trim $dyln_realpath_name] +} else { + set dyln_realpath_name "not-a-valid-path" +} + # Return true if FILENAME is the dynamic linker. Otherwise return false. proc is_dyln { filename } { - return [expr {$filename eq $::dyln_name}] + return [expr {$filename eq $::dyln_name || $filename eq $::dyln_realpath_name}] } # Run 'info sharedlibrary' and count the number of mappings that look @@ -81,7 +91,7 @@ proc get_dyld_info {} { set dyld_count 0 set dyld_start_addr "" gdb_test_multiple "info sharedlibrary" "" { - -re "~\"From\\s+To(\\s+NS)?\\s+Syms\\s+Read\\s+Shared Object Library\\\\n\"\r\n" { + -re "~\"From\\s+To(\\s+Linker NS)?\\s+Syms\\s+Read\\s+Shared Object Library\\\\n\"\r\n" { exp_continue } -re "^~\"($::hex)\\s+${::hex}(\\s+$::decimal)?\\s+\[^/\]+(/\[^\r\n\]+)\\\\n\"\r\n" { diff --git a/gdb/testsuite/gdb.mi/mi-exec-run.exp b/gdb/testsuite/gdb.mi/mi-exec-run.exp index a7a61b8..43406cf 100644 --- a/gdb/testsuite/gdb.mi/mi-exec-run.exp +++ b/gdb/testsuite/gdb.mi/mi-exec-run.exp @@ -30,6 +30,8 @@ set MIFLAGS "-i=mi" # cannot use it, then there is no point in running this testcase. require !use_gdb_stub +set have_startup_shell [have_startup_shell] + standard_testfile mi-start.c if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { @@ -172,6 +174,9 @@ remote_exec target "chmod \"a-x\" $binfile.nox" foreach_with_prefix inferior-tty {"main" "separate"} { foreach_with_prefix mi {"main" "separate"} { foreach_with_prefix force-fail {0 1} { + if { ${force-fail} && $have_startup_shell == -1 } { + continue + } test ${inferior-tty} ${mi} ${force-fail} } } diff --git a/gdb/testsuite/gdb.python/py-color-pagination.exp b/gdb/testsuite/gdb.python/py-color-pagination.exp new file mode 100644 index 0000000..e7a9e4f --- /dev/null +++ b/gdb/testsuite/gdb.python/py-color-pagination.exp @@ -0,0 +1,137 @@ +# 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.Color and how this +# interacts with GDB's pagination system. + +load_lib gdb-python.exp + +require allow_python_tests +require {!is_remote host} + +standard_testfile + +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +set str "<[string repeat - 78]>" + +# These define all the default attributes for a style: background +# color, intensity, italics, and underlined. +set other_attr ";49;22;23;24;27" + +# These colors set the foreground color only. Everything else is the +# default. +set black "(?:\033\\\[30${other_attr}m)" +set red "(?:\033\\\[31${other_attr}m)" +set green "(?:\033\\\[32${other_attr}m)" +set yellow "(?:\033\\\[33${other_attr}m)" +set blue "(?:\033\\\[34${other_attr}m)" +set magenta "(?:\033\\\[35${other_attr}m)" +set cyan "(?:\033\\\[36${other_attr}m)" +set white "(?:\033\\\[37${other_attr}m)" + +set any_color "(?:${black}|${red}|${green}|${yellow}|${blue}|${magenta}|${cyan}|${white})" + +# Run the command 'TYPE-fill MODE' which fills the screen with output and +# triggers the pagination prompt. Check that styling is applied correctly +# to the output. +proc test_pagination { type mode } { + + # Start with a fresh GDB, but enable color support. + with_ansi_styling_terminal { + clean_restart + } + + gdb_test_no_output "source $::pyfile" "source the script" + + gdb_test_no_output "set width 80" + gdb_test_no_output "set height 15" + + set saw_bad_color_handling false + set expected_restore_color "" + set last_color "" + gdb_test_multiple "$type-fill $mode" "" { + -re "^$type-fill $mode\r\n" { + exp_continue + } + + -re "^(${::any_color})(${::any_color})$::str" { + # After a continuation prompt GDB will restore the previous + # color, and then we immediately switch to a new color. + set restored_color $expect_out(1,string) + if { $restored_color ne $expected_restore_color } { + set saw_bad_color_handling true + } + set last_color $expect_out(2,string) + exp_continue + } + + -re "^(${::any_color})$::str" { + # This pattern matches printing STR in all cases that are not + # immediately after a pagination prompt. In this case there is + # a single escape sequence to set the color. + set last_color $expect_out(1,string) + exp_continue + } + + -re "^\033\\\[${::decimal}m$::str" { + # This catches the case where the color's escape sequence has + # not been converted back into a full style. This indicates + # something went wrong in the pager_file::puts function. + set saw_bad_color_handling true + exp_continue + } + + -re "^\033\\\[m$::pagination_prompt$" { + # After a pagination prompt we expect GDB to restore the last + # color. + set expected_restore_color $last_color + + # Send '\n' to view more output. + send_gdb "\n" + exp_continue + } + + -re "^$::pagination_prompt$" { + # After a pagination prompt we expect GDB to restore the last + # color. + set expected_restore_color $last_color + + # If we didn't see a color reset sequence before the pagination + # prompt, then the prompt will have been printed in the wrong + # color, this is a GDB bug. + set saw_bad_color_handling true + + # Send '\n' to view more output. + send_gdb "\n" + exp_continue + } + + -re "^\r\n" { + # The matches the newline sent to the continuation prompt. + exp_continue + } + + -re "^\033\\\[m\r\n$::gdb_prompt $" { + gdb_assert { !$saw_bad_color_handling } $gdb_test_name + } + } +} + +foreach_with_prefix type { color } { + foreach_with_prefix mode { write print } { + test_pagination $type $mode + } +} diff --git a/gdb/testsuite/gdb.python/py-color-pagination.py b/gdb/testsuite/gdb.python/py-color-pagination.py new file mode 100644 index 0000000..efd501e --- /dev/null +++ b/gdb/testsuite/gdb.python/py-color-pagination.py @@ -0,0 +1,46 @@ +# 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 gdb + +basic_colors = ["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"] + + +def write(mode, text): + if mode == "write": + gdb.write(text) + else: + print(text, end="") + + +class ColorTester(gdb.Command): + def __init__(self): + super().__init__("color-fill", gdb.COMMAND_USER) + + def invoke(self, args, from_tty): + mode = args + str = "<" + "-" * 78 + ">" + for i in range(0, 20): + for color_name in basic_colors: + c = gdb.Color(color_name) + write(mode, c.escape_sequence(True)) + write(mode, str) + + default = gdb.Color("none") + write(mode, default.escape_sequence(True)) + write(mode, "\n") + + +ColorTester() diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp index 2601cf3..08089e5 100644 --- a/gdb/testsuite/gdb.python/py-color.exp +++ b/gdb/testsuite/gdb.python/py-color.exp @@ -18,6 +18,7 @@ load_lib gdb-python.exp require allow_python_tests +require {!is_remote host} # Start with a fresh GDB, but enable color support. with_ansi_styling_terminal { diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp index 114a606..2463185 100644 --- a/gdb/testsuite/gdb.python/py-format-string.exp +++ b/gdb/testsuite/gdb.python/py-format-string.exp @@ -1202,7 +1202,9 @@ with_test_prefix "format_string" { set current_lang "c" prepare_gdb "${binfile}" test_all_common - test_styling + if { ![is_remote host] } { + test_styling + } } } } diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index 214c570..30a477b 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -38,13 +38,17 @@ proc_with_prefix test_directories { } { # doesn't set search directories on remote host. set directories ".*\\\$cdir.\\\$cwd" } else { - set escaped_directory [string_to_regexp "$::srcdir/$::subdir"] + set directory [host_file_normalize "$::srcdir/$::subdir"] + set escaped_directory [string_to_regexp $directory] set directories "$escaped_directory.\\\$cdir.\\\$cwd" } gdb_test "python print (gdb.parameter ('directories'))" $directories } proc_with_prefix test_data_directory { } { + # Proc assumes local host. + require {!is_remote host} + clean_restart # Check we can correctly read the data-directory parameter. First, @@ -187,6 +191,8 @@ proc_with_prefix test_enum_parameter { } { # Test an color parameter. proc_with_prefix test_color_parameter { } { + require {!is_remote host} + global env with_ansi_styling_terminal { # This enables 256 colors support and disables colors approximation. diff --git a/gdb/testsuite/gdb.python/py-startup-opt.exp b/gdb/testsuite/gdb.python/py-startup-opt.exp index 7410706..929c64d 100644 --- a/gdb/testsuite/gdb.python/py-startup-opt.exp +++ b/gdb/testsuite/gdb.python/py-startup-opt.exp @@ -17,6 +17,7 @@ # initialized. require allow_python_tests +require {!is_remote host} # Return a list containing two directory paths for newly created home # directories. diff --git a/gdb/testsuite/gdb.python/py-styled-execute.exp b/gdb/testsuite/gdb.python/py-styled-execute.exp index 0b27c63..198dab5 100644 --- a/gdb/testsuite/gdb.python/py-styled-execute.exp +++ b/gdb/testsuite/gdb.python/py-styled-execute.exp @@ -17,6 +17,7 @@ # on the value of the third argument passed to gdb.execute. require allow_python_tests +require {!is_remote host} load_lib gdb-python.exp diff --git a/gdb/testsuite/gdb.replay/connect.exp b/gdb/testsuite/gdb.replay/connect.exp index 26b7aa3..b25c372 100644 --- a/gdb/testsuite/gdb.replay/connect.exp +++ b/gdb/testsuite/gdb.replay/connect.exp @@ -70,9 +70,8 @@ proc_with_prefix record_initial_logfile {} { # Connect to gdbreply using the global REMOTELOG. Runs to a breakpoint # in main. proc_with_prefix replay_without_error {} { - global binfile global remotelog - clean_restart $binfile + clean_restart $::testfile # Make sure we're disconnected, in case we're testing with an # extended-remote board, therefore already connected. gdb_test "disconnect" ".*" @@ -97,7 +96,6 @@ proc_with_prefix replay_without_error {} { # copy of REMOTELOG. Attempt to connect to the remote and expect to see # the error reported by GDB. proc_with_prefix replay_with_mustreplyempty_error {} { - global binfile global remotelog global testfile set newline E.errtext @@ -107,7 +105,7 @@ proc_with_prefix replay_with_mustreplyempty_error {} { # the vMustReplayEmty packet to an error. update_log $remotelog $output_file "vMustReplyEmpty" $newline - clean_restart $binfile + clean_restart $::testfile # Make sure we're disconnected, in case we're testing with an # extended-remote board, therefore already connected. gdb_test "disconnect" ".*" diff --git a/gdb/testsuite/gdb.reverse/sigall-precsave.exp b/gdb/testsuite/gdb.reverse/sigall-precsave.exp index 64869c5..79838a0 100644 --- a/gdb/testsuite/gdb.reverse/sigall-precsave.exp +++ b/gdb/testsuite/gdb.reverse/sigall-precsave.exp @@ -138,7 +138,7 @@ proc test_one_sig_reverse {prevsig} { } } -clean_restart $binfile +clean_restart $::testfile runto gen_ABRT diff --git a/gdb/testsuite/gdb.reverse/sigall-reverse.exp b/gdb/testsuite/gdb.reverse/sigall-reverse.exp index b72e098..82547d8 100644 --- a/gdb/testsuite/gdb.reverse/sigall-reverse.exp +++ b/gdb/testsuite/gdb.reverse/sigall-reverse.exp @@ -144,7 +144,7 @@ proc test_one_sig_reverse {prevsig} { } } -clean_restart $binfile +clean_restart $::testfile runto gen_ABRT diff --git a/gdb/testsuite/gdb.reverse/solib-precsave.exp b/gdb/testsuite/gdb.reverse/solib-precsave.exp index 82b08cd..9636167 100644 --- a/gdb/testsuite/gdb.reverse/solib-precsave.exp +++ b/gdb/testsuite/gdb.reverse/solib-precsave.exp @@ -60,7 +60,7 @@ if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile} executable \ # and is no longer attempted. Instead, the test does not make assumptions # about whether the debug info is present or not. -clean_restart $binfile +clean_restart $::testfile gdb_load_shlib $library1 gdb_load_shlib $library2 diff --git a/gdb/testsuite/gdb.reverse/solib-reverse.exp b/gdb/testsuite/gdb.reverse/solib-reverse.exp index b2ef9b0..4efd19f 100644 --- a/gdb/testsuite/gdb.reverse/solib-reverse.exp +++ b/gdb/testsuite/gdb.reverse/solib-reverse.exp @@ -52,7 +52,7 @@ if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile} executable $exec_opt # and is no longer attempted. Instead, the test does not make assumptions # about whether the debug info is present or not. -clean_restart $binfile +clean_restart $::testfile gdb_load_shlib $library1 gdb_load_shlib $library2 diff --git a/gdb/testsuite/gdb.reverse/step-precsave.exp b/gdb/testsuite/gdb.reverse/step-precsave.exp index b49c21b..9a0127a 100644 --- a/gdb/testsuite/gdb.reverse/step-precsave.exp +++ b/gdb/testsuite/gdb.reverse/step-precsave.exp @@ -69,7 +69,7 @@ with_timeout_factor 10 { gdb_test "kill" "" "kill process, prepare to debug log file" \ "Kill the program being debugged\\? \\(y or n\\) " "y" -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test "record restore $precsave" \ "Restored records from core file .*" \ 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 index 3fe6a95..e994884 100644 --- a/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp +++ b/gdb/testsuite/gdb.rocm/code-object-load-while-breakpoint-hit.exp @@ -53,7 +53,8 @@ if { [gdb_compile $srcdir/$subdir/$srcfile \ proc do_test { } { with_rocm_gpu_lock { - clean_restart $::binfile + clean_restart + gdb_load $::binfile gdb_test_no_output "set args $::hipmodule_path" "set args" if { ![runto_main] } { diff --git a/gdb/testsuite/gdb.rocm/displaced-stepping.exp b/gdb/testsuite/gdb.rocm/displaced-stepping.exp index cd50fec..9e8abd4 100644 --- a/gdb/testsuite/gdb.rocm/displaced-stepping.exp +++ b/gdb/testsuite/gdb.rocm/displaced-stepping.exp @@ -28,7 +28,8 @@ if {[build_executable "failed to prepare" $testfile $srcfile {hip}]} { } proc do_test {} { - clean_restart $::binfile + clean_restart + gdb_load $::binfile with_rocm_gpu_lock { if ![runto_main] { 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 22d4b75..dfd1092 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 @@ -54,7 +54,8 @@ proc do_test { detach-on-fork follow-fork-mode fork_func } { } with_rocm_gpu_lock { - clean_restart ${::binfile}-execer-${fork_func} + clean_restart + gdb_load ${::binfile}-execer-${fork_func} gdb_test_no_output "set detach-on-fork ${detach-on-fork}" gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}" 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 1386099..b14e2c7 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 @@ -53,7 +53,8 @@ proc do_test { detach-on-fork follow-fork-mode fork_func } { } with_rocm_gpu_lock { - clean_restart ${::binfile}-execer-${fork_func} + clean_restart + gdb_load ${::binfile}-execer-${fork_func} gdb_test_no_output "set detach-on-fork ${detach-on-fork}" gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}" diff --git a/gdb/testsuite/gdb.rocm/mi-attach.cpp b/gdb/testsuite/gdb.rocm/mi-attach.cpp index da7659d..441d460 100644 --- a/gdb/testsuite/gdb.rocm/mi-attach.cpp +++ b/gdb/testsuite/gdb.rocm/mi-attach.cpp @@ -15,8 +15,8 @@ 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 <hip/hip_runtime.h> +#include "gdb_watchdog.h" __global__ void kern () @@ -30,7 +30,7 @@ main () { /* This program will run outside of GDB, make sure that if anything goes wrong it eventually gets killed. */ - alarm (30); + gdb_watchdog (30); kern<<<1, 1>>> (); return hipDeviceSynchronize () != hipSuccess; diff --git a/gdb/testsuite/gdb.rocm/multi-inferior-gpu.exp b/gdb/testsuite/gdb.rocm/multi-inferior-gpu.exp index 4f55432..0ed11e8 100644 --- a/gdb/testsuite/gdb.rocm/multi-inferior-gpu.exp +++ b/gdb/testsuite/gdb.rocm/multi-inferior-gpu.exp @@ -28,7 +28,8 @@ if {[build_executable "failed to prepare" $testfile $srcfile {debug hip}]} { } proc do_test {} { - clean_restart $::binfile + clean_restart + gdb_load $::binfile gdb_test_no_output "set non-stop on" gdb_test_no_output "set detach-on-fork off" gdb_test_no_output "set follow-fork parent" diff --git a/gdb/testsuite/gdb.rocm/precise-memory-exec.exp b/gdb/testsuite/gdb.rocm/precise-memory-exec.exp index 506488c..76be078 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory-exec.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory-exec.exp @@ -29,7 +29,8 @@ if {[build_executable "failed to prepare $testfile" $testfile $srcfile {debug}]} } proc do_test { follow-exec-mode } { - clean_restart $::binfile + clean_restart + gdb_load $::binfile with_rocm_gpu_lock { if ![runto_main] { diff --git a/gdb/testsuite/gdb.rocm/precise-memory-warning-sigsegv.exp b/gdb/testsuite/gdb.rocm/precise-memory-warning-sigsegv.exp index f855719..da0a95a 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory-warning-sigsegv.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory-warning-sigsegv.exp @@ -29,7 +29,8 @@ if {[build_executable "failed to prepare" $testfile $srcfile {debug hip}]} { } proc do_test { } { - clean_restart $::binfile + clean_restart + gdb_load $::binfile with_rocm_gpu_lock { if ![runto_main] { diff --git a/gdb/testsuite/gdb.rocm/precise-memory.exp b/gdb/testsuite/gdb.rocm/precise-memory.exp index 6711d80..8f00559 100644 --- a/gdb/testsuite/gdb.rocm/precise-memory.exp +++ b/gdb/testsuite/gdb.rocm/precise-memory.exp @@ -28,7 +28,8 @@ if {[build_executable "failed to prepare" $testfile $srcfile {debug hip}]} { } proc do_test { } { - clean_restart $::binfile + clean_restart + gdb_load $::binfile with_rocm_gpu_lock { if ![runto_main] { diff --git a/gdb/testsuite/gdb.rocm/simple.exp b/gdb/testsuite/gdb.rocm/simple.exp index bc90a0a..8f6ff3e 100644 --- a/gdb/testsuite/gdb.rocm/simple.exp +++ b/gdb/testsuite/gdb.rocm/simple.exp @@ -27,7 +27,8 @@ if {[build_executable "failed to prepare" $testfile $srcfile {debug hip}]} { } proc do_test {} { - clean_restart $::binfile + clean_restart + gdb_load $::binfile with_rocm_gpu_lock { if ![runto_main] { diff --git a/gdb/testsuite/gdb.server/bkpt-other-inferior.exp b/gdb/testsuite/gdb.server/bkpt-other-inferior.exp index 893bd72..453be14 100644 --- a/gdb/testsuite/gdb.server/bkpt-other-inferior.exp +++ b/gdb/testsuite/gdb.server/bkpt-other-inferior.exp @@ -23,7 +23,7 @@ standard_testfile server.c require allow_gdbserver_tests -if { [prepare_for_testing "failed to prepare" ${binfile} "${srcfile}" \ +if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ {debug pthreads}] } { return } diff --git a/gdb/testsuite/gdb.server/connect-stopped-target.exp b/gdb/testsuite/gdb.server/connect-stopped-target.exp index 021f063..603782c 100644 --- a/gdb/testsuite/gdb.server/connect-stopped-target.exp +++ b/gdb/testsuite/gdb.server/connect-stopped-target.exp @@ -34,7 +34,7 @@ proc do_test {nonstop} { global gdb_prompt global hex - clean_restart $binfile + clean_restart $::testfile # 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/connect-without-multi-process.exp b/gdb/testsuite/gdb.server/connect-without-multi-process.exp index 1a7246c..f47e57e 100644 --- a/gdb/testsuite/gdb.server/connect-without-multi-process.exp +++ b/gdb/testsuite/gdb.server/connect-without-multi-process.exp @@ -38,7 +38,7 @@ proc do_test {multiprocess} { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart $binfile + clean_restart $::testfile } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/exit-multiple-threads.exp b/gdb/testsuite/gdb.server/exit-multiple-threads.exp index 73e4c32..aae7842 100644 --- a/gdb/testsuite/gdb.server/exit-multiple-threads.exp +++ b/gdb/testsuite/gdb.server/exit-multiple-threads.exp @@ -45,7 +45,8 @@ proc prepare_for_test { executable target_executable disable_multi_process } { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart ${executable} + clean_restart + gdb_load $executable } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/ext-attach.exp b/gdb/testsuite/gdb.server/ext-attach.exp index bda3ae9..6af2ede 100644 --- a/gdb/testsuite/gdb.server/ext-attach.exp +++ b/gdb/testsuite/gdb.server/ext-attach.exp @@ -45,7 +45,7 @@ proc run_test { target_async target_non_stop to_disable } { set ::GDBFLAGS "$::GDBFLAGS -ex \"set sysroot\"" } - clean_restart $::binfile + clean_restart $::testfile } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp index 2286454..f4ff546 100644 --- a/gdb/testsuite/gdb.server/ext-run.exp +++ b/gdb/testsuite/gdb.server/ext-run.exp @@ -37,7 +37,7 @@ save_vars { GDBFLAGS } { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart $binfile + clean_restart $::testfile } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/extended-remote-restart.exp b/gdb/testsuite/gdb.server/extended-remote-restart.exp index df722a1..b3c8c72 100644 --- a/gdb/testsuite/gdb.server/extended-remote-restart.exp +++ b/gdb/testsuite/gdb.server/extended-remote-restart.exp @@ -58,7 +58,7 @@ proc test_reload { do_kill_p follow_child_p } { global decimal global binfile - clean_restart ${binfile} + clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.server/monitor-exit-quit.exp b/gdb/testsuite/gdb.server/monitor-exit-quit.exp index ce63560..f308c0f 100644 --- a/gdb/testsuite/gdb.server/monitor-exit-quit.exp +++ b/gdb/testsuite/gdb.server/monitor-exit-quit.exp @@ -34,7 +34,7 @@ save_vars { GDBFLAGS } { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart $binfile + clean_restart $::testfile } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/non-existing-program.exp b/gdb/testsuite/gdb.server/non-existing-program.exp index 7119723..ec9c044 100644 --- a/gdb/testsuite/gdb.server/non-existing-program.exp +++ b/gdb/testsuite/gdb.server/non-existing-program.exp @@ -34,6 +34,8 @@ if { $gdbserver == "" } { # to spawn the program before opening the connection. set spawn_id [remote_spawn target "$gdbserver stdio non-existing-program"] +set eol {[\r\n]} + set msg "gdbserver exits cleanly" set saw_exiting 0 expect { @@ -51,7 +53,7 @@ expect { exp_continue } # This is what we get on Windows. - -re "Error creating process\r\n\r\nExiting\r\n" { + -re "Error creating process.*$eol+Exiting$eol+" { set saw_exiting 1 exp_continue } diff --git a/gdb/testsuite/gdb.server/server-kill.exp b/gdb/testsuite/gdb.server/server-kill.exp index 0a759ae..a9fcabb 100644 --- a/gdb/testsuite/gdb.server/server-kill.exp +++ b/gdb/testsuite/gdb.server/server-kill.exp @@ -43,7 +43,7 @@ proc prepare {} { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart $binfile + clean_restart $::testfile } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/server-pipe.exp b/gdb/testsuite/gdb.server/server-pipe.exp index d786946..20ca0b0 100644 --- a/gdb/testsuite/gdb.server/server-pipe.exp +++ b/gdb/testsuite/gdb.server/server-pipe.exp @@ -50,7 +50,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile debug]} { # the contents of the gdb.TargetConnection.details string. proc do_test { target } { global timeout - clean_restart ${::binfile} + clean_restart ${::testfile} # 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/server-run.exp b/gdb/testsuite/gdb.server/server-run.exp index 6c9db98..53b3278 100644 --- a/gdb/testsuite/gdb.server/server-run.exp +++ b/gdb/testsuite/gdb.server/server-run.exp @@ -34,7 +34,7 @@ save_vars { GDBFLAGS } { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart $binfile + clean_restart $::testfile } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/stop-reply-no-thread-multi.exp b/gdb/testsuite/gdb.server/stop-reply-no-thread-multi.exp index 42608c4..9ae0092 100644 --- a/gdb/testsuite/gdb.server/stop-reply-no-thread-multi.exp +++ b/gdb/testsuite/gdb.server/stop-reply-no-thread-multi.exp @@ -54,7 +54,7 @@ proc run_test { target_non_stop disable_feature } { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart ${binfile} + clean_restart ${::testfile} } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.server/stop-reply-no-thread.exp b/gdb/testsuite/gdb.server/stop-reply-no-thread.exp index 38402e8..b111dd9 100644 --- a/gdb/testsuite/gdb.server/stop-reply-no-thread.exp +++ b/gdb/testsuite/gdb.server/stop-reply-no-thread.exp @@ -42,7 +42,7 @@ proc run_test { disable_feature target_nonstop } { set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\"" } - clean_restart ${binfile} + clean_restart ${::testfile} } # Make sure we're disconnected, in case we're testing with an diff --git a/gdb/testsuite/gdb.testsuite/gdb-caching-proc-consistency.exp b/gdb/testsuite/gdb.testsuite/gdb-caching-proc-consistency.exp index 0957dbd..4676a41 100644 --- a/gdb/testsuite/gdb.testsuite/gdb-caching-proc-consistency.exp +++ b/gdb/testsuite/gdb.testsuite/gdb-caching-proc-consistency.exp @@ -95,7 +95,8 @@ proc test_file { file } { } if { $setup_gdb } { - clean_restart $obj + clean_restart + gdb_load $obj } test_proc $procname diff --git a/gdb/testsuite/gdb.testsuite/mount-point-map.exp b/gdb/testsuite/gdb.testsuite/mount-point-map.exp new file mode 100644 index 0000000..9e462bb --- /dev/null +++ b/gdb/testsuite/gdb.testsuite/mount-point-map.exp @@ -0,0 +1,49 @@ +# 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/>. + +set unix_to_win { + /bin C:/msys64/usr/bin + /c C: + / C:/msys64 +} + +# Test that FROM is normalized to TO. + +proc test {from to} { + set got [host_file_normalize_mingw $from $::unix_to_win] + verbose -log "input: $from" + verbose -log "expected: $to" + verbose -log "got: $got" + gdb_assert {$got == $to} $from +} + +# Drive letters always get a '/' suffix, other Windows file names do +# not. +test "/" "C:/msys64" +test "/c" "C:/" +test "/bin" "C:/msys64/usr/bin" + +# A file name that already starts with a drive letter. +test "C:/msys64" "C:/msys64" + +# A subdir/subfile under each mount. +test "/foo" "C:/msys64/foo" +test "/c/foo" "C:/foo" +test "/bin/foo" "C:/msys64/usr/bin/foo" + +# Test slash normalization. +test "//" "C:/msys64" +test "/c///foo//bar//" "C:/foo/bar" +# We don't currently handle UNC paths. +test "//server///" "C:/msys64/server" 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 fec31c3..6846b38 100644 --- a/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp +++ b/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.exp @@ -51,7 +51,7 @@ proc test { non_stop } { save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"set non-stop $non_stop\"" - clean_restart ${binfile} + clean_restart ${::testfile} } if ![runto setup_done] { diff --git a/gdb/testsuite/gdb.threads/async.exp b/gdb/testsuite/gdb.threads/async.exp index b1e562a..6347333 100644 --- a/gdb/testsuite/gdb.threads/async.exp +++ b/gdb/testsuite/gdb.threads/async.exp @@ -32,7 +32,7 @@ proc test_current_thread {expected_thr} { global gdb_prompt global binfile - clean_restart $binfile + clean_restart $::testfile if {![runto "all_started"]} { return diff --git a/gdb/testsuite/gdb.threads/attach-non-stop.exp b/gdb/testsuite/gdb.threads/attach-non-stop.exp index 9404edd..b8da5b1 100644 --- a/gdb/testsuite/gdb.threads/attach-non-stop.exp +++ b/gdb/testsuite/gdb.threads/attach-non-stop.exp @@ -37,7 +37,7 @@ proc test {target_non_stop non_stop cmd} { save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"maint set target-non-stop $target_non_stop\"" append GDBFLAGS " -ex \"set non-stop $non_stop\"" - clean_restart $binfile + clean_restart $::testfile } set test_spawn_id [spawn_wait_for_attach $binfile] diff --git a/gdb/testsuite/gdb.threads/attach-stopped.exp b/gdb/testsuite/gdb.threads/attach-stopped.exp index e628adf..a3926d9 100644 --- a/gdb/testsuite/gdb.threads/attach-stopped.exp +++ b/gdb/testsuite/gdb.threads/attach-stopped.exp @@ -43,7 +43,7 @@ proc corefunc { threadtype } { # Stop the program remote_exec build "kill -s STOP ${testpid}" - clean_restart $binfile + clean_restart $::testfile # Verify that we can attach to the stopped process. diff --git a/gdb/testsuite/gdb.threads/bp_in_thread.exp b/gdb/testsuite/gdb.threads/bp_in_thread.exp index c63f179..5180c18 100644 --- a/gdb/testsuite/gdb.threads/bp_in_thread.exp +++ b/gdb/testsuite/gdb.threads/bp_in_thread.exp @@ -24,7 +24,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart $binfile +clean_restart $::testfile runto_main diff --git a/gdb/testsuite/gdb.threads/break-while-running.exp b/gdb/testsuite/gdb.threads/break-while-running.exp index fbc2b59..fb7994d 100644 --- a/gdb/testsuite/gdb.threads/break-while-running.exp +++ b/gdb/testsuite/gdb.threads/break-while-running.exp @@ -39,7 +39,7 @@ proc test { update_thread_list always_inserted non_stop } { global gdb_prompt global decimal - clean_restart $binfile + clean_restart $::testfile gdb_test_no_output "set non-stop $non_stop" gdb_test_no_output "set breakpoint always-inserted $always_inserted" diff --git a/gdb/testsuite/gdb.threads/check-libthread-db.exp b/gdb/testsuite/gdb.threads/check-libthread-db.exp index b97ab49..6d63185 100644 --- a/gdb/testsuite/gdb.threads/check-libthread-db.exp +++ b/gdb/testsuite/gdb.threads/check-libthread-db.exp @@ -40,7 +40,7 @@ set initial_thread_re "($thread_re1|$thread_re2)" with_test_prefix "user-initiated check" { # User-initiated check with libthread_db not loaded. - clean_restart ${binfile} + clean_restart ${::testfile} gdb_test "maint show check-libthread-db" \ "Whether to check libthread_db at load time is off." @@ -85,7 +85,7 @@ with_test_prefix "automated load-time check" { # Automated load-time check with NPTL possibly uninitialized. with_test_prefix "libpthread.so possibly not initialized" { - clean_restart ${binfile} + clean_restart ${::testfile} gdb_test_no_output "maint set check-libthread-db 1" gdb_test_no_output "set debug libthread-db 1" @@ -104,7 +104,7 @@ with_test_prefix "automated load-time check" { # Automated load-time check with NPTL fully operational. if { [can_spawn_for_attach] } { with_test_prefix "libpthread.so fully initialized" { - clean_restart ${binfile} + clean_restart ${::testfile} gdb_test_no_output "maint set check-libthread-db 1" gdb_test_no_output "set debug libthread-db 1" diff --git a/gdb/testsuite/gdb.threads/corethreads.exp b/gdb/testsuite/gdb.threads/corethreads.exp index 3b50ae3..0011dc3 100644 --- a/gdb/testsuite/gdb.threads/corethreads.exp +++ b/gdb/testsuite/gdb.threads/corethreads.exp @@ -29,6 +29,7 @@ if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executa set corefile [core_find $binfile] if {$corefile == ""} { + untested "unable to create or find corefile" return 0 } diff --git a/gdb/testsuite/gdb.threads/detach-step-over.exp b/gdb/testsuite/gdb.threads/detach-step-over.exp index 8a1cb29..1767464 100644 --- a/gdb/testsuite/gdb.threads/detach-step-over.exp +++ b/gdb/testsuite/gdb.threads/detach-step-over.exp @@ -66,7 +66,7 @@ proc start_gdb_for_test {condition_eval target_non_stop non_stop displaced} { append ::GDBFLAGS " -ex \"set non-stop $non_stop\"" append ::GDBFLAGS " -ex \"set displaced $displaced\"" append ::GDBFLAGS " -ex \"set schedule-multiple on\"" - clean_restart $::binfile + clean_restart $::testfile } gdb_test_no_output "set breakpoint condition-evaluation $condition_eval" diff --git a/gdb/testsuite/gdb.threads/execl.exp b/gdb/testsuite/gdb.threads/execl.exp index 502d387..a42dce9 100644 --- a/gdb/testsuite/gdb.threads/execl.exp +++ b/gdb/testsuite/gdb.threads/execl.exp @@ -31,7 +31,7 @@ if {[gdb_compile "${srcdir}/${subdir}/${srcfile1}" "${binfile1}" executable {deb return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if { [is_remote target] } { gdb_remote_download target $binfile1 } diff --git a/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp b/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp index 8ab540c..29ec34c 100644 --- a/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp +++ b/gdb/testsuite/gdb.threads/foll-fork-other-thread.exp @@ -46,7 +46,7 @@ proc do_test { fork_func follow target-non-stop non-stop displaced-stepping } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" - clean_restart ${::binfile}-${fork_func} + clean_restart ${::testfile}-${fork_func} } gdb_test_no_output "set displaced-stepping ${displaced-stepping}" diff --git a/gdb/testsuite/gdb.threads/fork-child-threads.exp b/gdb/testsuite/gdb.threads/fork-child-threads.exp index ba9dfc2..d1b413c 100644 --- a/gdb/testsuite/gdb.threads/fork-child-threads.exp +++ b/gdb/testsuite/gdb.threads/fork-child-threads.exp @@ -21,7 +21,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp index 538e1ca..e0e3625 100644 --- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp +++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp @@ -21,7 +21,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 0 @@ -75,7 +75,7 @@ gdb_test_multiple "info threads" "$test" { # Start over, but this time, don't switch away from the fork event thread. -clean_restart $binfile +clean_restart $::testfile if {![runto_main]} { return 0 } diff --git a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp index c668a65..d65fe83d 100644 --- a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp +++ b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp @@ -37,7 +37,7 @@ proc probe_displaced_stepping_support {} { global binfile gdb_prompt with_test_prefix "probe displaced-stepping support" { - clean_restart $binfile + clean_restart $::testfile gdb_test_no_output "set displaced on" if {![runto_main]} { @@ -76,7 +76,7 @@ proc do_test { cond_bp_target detach_on_fork displaced } { save_vars { GDBFLAGS } { set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop on\""] - clean_restart $binfile + clean_restart $::testfile } if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/hand-call-in-threads.exp b/gdb/testsuite/gdb.threads/hand-call-in-threads.exp index d1be1d0..fd934f5 100644 --- a/gdb/testsuite/gdb.threads/hand-call-in-threads.exp +++ b/gdb/testsuite/gdb.threads/hand-call-in-threads.exp @@ -44,7 +44,7 @@ proc get_dummy_frame_number { } { return "" } -clean_restart ${binfile} +clean_restart ${::testfile} if { ![runto_main] } { return 0 diff --git a/gdb/testsuite/gdb.threads/inf-thr-count.exp b/gdb/testsuite/gdb.threads/inf-thr-count.exp index 61533ab..5272f5c 100644 --- a/gdb/testsuite/gdb.threads/inf-thr-count.exp +++ b/gdb/testsuite/gdb.threads/inf-thr-count.exp @@ -44,7 +44,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile \ # the inferior while it is running. save_vars {GDBFLAGS} { append GDBFLAGS { -ex "set non-stop on"} - clean_restart $binfile + clean_restart $::testfile } if ![runto_main] { diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp index 62a183c..35a1fe5 100644 --- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp @@ -53,7 +53,7 @@ proc start_gdb_and_runto_main { target_async target_non_stop } { append ::GDBFLAGS \ " -ex \"maintenance set target-async ${target_async}\"" - clean_restart ${::binfile} + clean_restart ${::testfile} } if { ![runto_main] } { diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp index 0f068c6..ed98998 100644 --- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp @@ -42,7 +42,7 @@ proc start_gdb_and_runto_main { target_async target_non_stop } { append ::GDBFLAGS \ " -ex \"maintenance set target-async ${target_async}\"" - clean_restart ${::binfile} + clean_restart ${::testfile} } if { ![runto_main] } { diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp index c404a7d..bc12fb4 100644 --- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp @@ -38,7 +38,7 @@ proc start_gdb_and_runto_main { target_async target_non_stop } { append ::GDBFLAGS \ " -ex \"maintenance set target-async ${target_async}\"" - clean_restart ${::binfile} + clean_restart ${::testfile} } if { ![runto_main] } { diff --git a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp index 9dbaa4f..03c6959 100644 --- a/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp +++ b/gdb/testsuite/gdb.threads/infcall-from-bp-cond-timeout.exp @@ -52,7 +52,7 @@ proc run_test { target_async target_non_stop non_stop other_thread_bp unwind } { append ::GDBFLAGS " -ex \"maint non-stop $non_stop\"" append ::GDBFLAGS " -ex \"maintenance set target-async ${target_async}\"" - clean_restart ${::binfile} + clean_restart ${::testfile} } if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/info-threads-options.exp b/gdb/testsuite/gdb.threads/info-threads-options.exp index 38e4e67..e6c68e2 100644 --- a/gdb/testsuite/gdb.threads/info-threads-options.exp +++ b/gdb/testsuite/gdb.threads/info-threads-options.exp @@ -24,7 +24,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"set non-stop on\"" - clean_restart $binfile + clean_restart $::testfile } if ![runto_main] { diff --git a/gdb/testsuite/gdb.threads/interrupt-while-step-over.exp b/gdb/testsuite/gdb.threads/interrupt-while-step-over.exp index 05587eb..44e4d64 100644 --- a/gdb/testsuite/gdb.threads/interrupt-while-step-over.exp +++ b/gdb/testsuite/gdb.threads/interrupt-while-step-over.exp @@ -169,7 +169,7 @@ proc testdriver {displaced} { save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"set non-stop on\"" - clean_restart $binfile + clean_restart $::testfile } gdb_test_no_output "set displaced-stepping $displaced" diff --git a/gdb/testsuite/gdb.threads/interrupted-hand-call.exp b/gdb/testsuite/gdb.threads/interrupted-hand-call.exp index 3a2bc63..15c48b1 100644 --- a/gdb/testsuite/gdb.threads/interrupted-hand-call.exp +++ b/gdb/testsuite/gdb.threads/interrupted-hand-call.exp @@ -28,7 +28,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if { ![runto_main] } { return 0 diff --git a/gdb/testsuite/gdb.threads/killed.exp b/gdb/testsuite/gdb.threads/killed.exp index b1cec80b0..1fa83af 100644 --- a/gdb/testsuite/gdb.threads/killed.exp +++ b/gdb/testsuite/gdb.threads/killed.exp @@ -62,7 +62,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_run_cmd gdb_test "" "" "run program to completion" diff --git a/gdb/testsuite/gdb.threads/leader-exit-attach.exp b/gdb/testsuite/gdb.threads/leader-exit-attach.exp index 641d6b5..2df4a18 100644 --- a/gdb/testsuite/gdb.threads/leader-exit-attach.exp +++ b/gdb/testsuite/gdb.threads/leader-exit-attach.exp @@ -31,7 +31,7 @@ set testpid [spawn_id_get_pid $test_spawn_id] # Wait a bit for the leader thread to exit, before attaching. sleep 2 -clean_restart ${binfile} +clean_restart ${::testfile} # Save this early as we may not be able to talk with GDBserver anymore # when we need to check it. diff --git a/gdb/testsuite/gdb.threads/linux-dp.exp b/gdb/testsuite/gdb.threads/linux-dp.exp index 1652f78..35cc255 100644 --- a/gdb/testsuite/gdb.threads/linux-dp.exp +++ b/gdb/testsuite/gdb.threads/linux-dp.exp @@ -44,7 +44,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" runto_main diff --git a/gdb/testsuite/gdb.threads/local-watch-wrong-thread.exp b/gdb/testsuite/gdb.threads/local-watch-wrong-thread.exp index 3006b83..a63f0be 100644 --- a/gdb/testsuite/gdb.threads/local-watch-wrong-thread.exp +++ b/gdb/testsuite/gdb.threads/local-watch-wrong-thread.exp @@ -28,7 +28,7 @@ if {[gdb_compile_pthreads \ return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set can-use-hw-watchpoints 1" "" diff --git a/gdb/testsuite/gdb.threads/main-thread-exit-during-detach.exp b/gdb/testsuite/gdb.threads/main-thread-exit-during-detach.exp index 20e7bc4..1ce0194 100644 --- a/gdb/testsuite/gdb.threads/main-thread-exit-during-detach.exp +++ b/gdb/testsuite/gdb.threads/main-thread-exit-during-detach.exp @@ -50,7 +50,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile \ proc run_test { spawn_inferior } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"set non-stop on\"" - clean_restart $::binfile + clean_restart $::testfile } # Setup the inferior. When complete the main thread (#1) will diff --git a/gdb/testsuite/gdb.threads/manythreads.exp b/gdb/testsuite/gdb.threads/manythreads.exp index ae51c5a..2cd296f 100644 --- a/gdb/testsuite/gdb.threads/manythreads.exp +++ b/gdb/testsuite/gdb.threads/manythreads.exp @@ -31,7 +31,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" runto_main diff --git a/gdb/testsuite/gdb.threads/multi-create.exp b/gdb/testsuite/gdb.threads/multi-create.exp index 966d44d..cb86aac 100644 --- a/gdb/testsuite/gdb.threads/multi-create.exp +++ b/gdb/testsuite/gdb.threads/multi-create.exp @@ -21,7 +21,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} runto_main # Run to the beginning of create_function several times. Make sure diff --git a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp index 1aa9253..2694ce5 100644 --- a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp +++ b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp @@ -23,7 +23,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ return -1 } -clean_restart "${binfile}" +clean_restart "${::testfile}" if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/next-bp-other-thread.exp b/gdb/testsuite/gdb.threads/next-bp-other-thread.exp index cf8d687..6b696dd 100644 --- a/gdb/testsuite/gdb.threads/next-bp-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-bp-other-thread.exp @@ -28,7 +28,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile \ # Test all "set scheduler-locking" variants. foreach schedlock {"off" "step" "on" } { with_test_prefix "schedlock=$schedlock" { - clean_restart $binfile + clean_restart $::testfile if ![runto_main] { continue 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 3a97127..82e85a6 100644 --- a/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-fork-exec-other-thread.exp @@ -67,7 +67,7 @@ proc do_test { fork_func target-non-stop non-stop displaced-stepping } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" - clean_restart ${::binfile}-${fork_func} + clean_restart ${::testfile}-${fork_func} } gdb_test_no_output "set displaced-stepping ${displaced-stepping}" diff --git a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp index 1cd6685..9349091 100644 --- a/gdb/testsuite/gdb.threads/next-fork-other-thread.exp +++ b/gdb/testsuite/gdb.threads/next-fork-other-thread.exp @@ -58,7 +58,7 @@ proc do_test { fork_func target-non-stop non-stop displaced-stepping } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" - clean_restart ${::binfile}-${fork_func} + clean_restart ${::testfile}-${fork_func} } gdb_test_no_output "set displaced-stepping ${displaced-stepping}" 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 29a011e..9cc4978 100644 --- a/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp +++ b/gdb/testsuite/gdb.threads/pending-fork-event-detach-ns.exp @@ -52,7 +52,7 @@ proc do_test { } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"set non-stop on\"" - clean_restart $::binfile + clean_restart $::testfile } if { ![runto break_here_first] } { diff --git a/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp b/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp index e627241..fa86488 100644 --- a/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp +++ b/gdb/testsuite/gdb.threads/pending-fork-event-detach.exp @@ -93,7 +93,8 @@ proc do_test { target-non-stop who_forks fork_function stop_mode } { save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" - clean_restart $this_binfile + clean_restart + gdb_load $this_binfile } if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/pending-step.exp b/gdb/testsuite/gdb.threads/pending-step.exp index 1c2422e..d31f879 100644 --- a/gdb/testsuite/gdb.threads/pending-step.exp +++ b/gdb/testsuite/gdb.threads/pending-step.exp @@ -54,7 +54,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/print-threads.exp b/gdb/testsuite/gdb.threads/print-threads.exp index 51a14b2..3d2dc14 100644 --- a/gdb/testsuite/gdb.threads/print-threads.exp +++ b/gdb/testsuite/gdb.threads/print-threads.exp @@ -32,7 +32,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab # Now we can proceed with the real testing. -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" #gdb_test_no_output "set print address off" diff --git a/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp b/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp index 776c08e..6c6a4b1 100644 --- a/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp +++ b/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp @@ -43,6 +43,7 @@ # threads are reaped. We test that as well. standard_testfile +set testfile_base $testfile # Test that GDBserver exits. @@ -214,7 +215,7 @@ proc test_detach {multi_process cmd} { with_test_prefix "detach" { global binfile - clean_restart ${binfile} + clean_restart ${::testfile} if ![runto_main] { return -1 @@ -242,7 +243,7 @@ proc test_detach_watch {wp multi_process cmd} { with_test_prefix "watchpoint:$wp" { global binfile decimal - clean_restart ${binfile} + clean_restart ${::testfile} if ![runto_main] { return -1 @@ -290,7 +291,7 @@ proc test_detach_killed_outside {multi_process cmd} { with_test_prefix "killed outside" { global binfile - clean_restart ${binfile} + clean_restart ${::testfile} if ![runto_main] { return -1 @@ -334,14 +335,15 @@ proc do_test {multi_process cmd} { return } - set binfile [standard_output_file ${testfile}-$multi_process-$cmd] + set testfile $::testfile_base-$multi_process-$cmd + set binfile [standard_output_file $testfile] set options {debug pthreads} if {$multi_process} { lappend options "additional_flags=-DMULTIPROCESS" } if {[build_executable "failed to build" \ - $testfile-$multi_process-$cmd $srcfile $options] == -1} { + $testfile $srcfile $options] == -1} { return -1 } diff --git a/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp b/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp index 26dc8cc..a990dc9 100644 --- a/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp +++ b/gdb/testsuite/gdb.threads/process-dies-while-handling-bp.exp @@ -42,7 +42,7 @@ proc do_test { non_stop cond_bp_target } { save_vars { GDBFLAGS } { set GDBFLAGS [concat $GDBFLAGS " -ex \"set non-stop $non_stop\""] - clean_restart $binfile + clean_restart $::testfile } if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/pthread_cond_wait.exp b/gdb/testsuite/gdb.threads/pthread_cond_wait.exp index c1be1cd..e914db0 100644 --- a/gdb/testsuite/gdb.threads/pthread_cond_wait.exp +++ b/gdb/testsuite/gdb.threads/pthread_cond_wait.exp @@ -25,7 +25,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} runto_main gdb_test "break break_me" \ diff --git a/gdb/testsuite/gdb.threads/pthreads.exp b/gdb/testsuite/gdb.threads/pthreads.exp index 0437e74..04ae91e2 100644 --- a/gdb/testsuite/gdb.threads/pthreads.exp +++ b/gdb/testsuite/gdb.threads/pthreads.exp @@ -34,7 +34,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" #gdb_test_no_output "set print address off" diff --git a/gdb/testsuite/gdb.threads/queue-signal.exp b/gdb/testsuite/gdb.threads/queue-signal.exp index f791ffa..34abc3b 100644 --- a/gdb/testsuite/gdb.threads/queue-signal.exp +++ b/gdb/testsuite/gdb.threads/queue-signal.exp @@ -20,7 +20,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if ![runto_main] { return 0 diff --git a/gdb/testsuite/gdb.threads/schedlock-new-thread.exp b/gdb/testsuite/gdb.threads/schedlock-new-thread.exp index c398137..5fda4b8 100644 --- a/gdb/testsuite/gdb.threads/schedlock-new-thread.exp +++ b/gdb/testsuite/gdb.threads/schedlock-new-thread.exp @@ -31,7 +31,7 @@ proc test {non-stop schedlock} { save_vars ::GDBFLAGS { append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" set sl [expr $schedlock == "on" ? 1 : 0] - clean_restart $::binfile-$sl + clean_restart $::testfile-$sl } set linenum1 [gdb_get_line_number "set break 1 here"] diff --git a/gdb/testsuite/gdb.threads/schedlock-thread-exit.exp b/gdb/testsuite/gdb.threads/schedlock-thread-exit.exp index 434b058..137f652 100644 --- a/gdb/testsuite/gdb.threads/schedlock-thread-exit.exp +++ b/gdb/testsuite/gdb.threads/schedlock-thread-exit.exp @@ -28,7 +28,7 @@ if { [build_executable "failed to prepare" ${testfile} ${srcfile} \ } proc do_test { } { - clean_restart $::binfile + clean_restart $::testfile # One of the launched threads will report a stop on thread_func. Some # others will also stop on thread_func and have a pending status. diff --git a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp index 2586800..6d344e3 100644 --- a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp +++ b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp @@ -35,7 +35,7 @@ proc test { step_over } { global srcfile binfile tdlabel_re with_test_prefix "step-over $step_over" { - clean_restart ${binfile} + clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp index 73f88c1..acb018b 100644 --- a/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp +++ b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp @@ -32,7 +32,7 @@ proc test { schedlock } { global srcfile binfile tdlabel_re with_test_prefix "schedlock $schedlock" { - clean_restart ${binfile} + clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp index 7445cad..9de0908 100644 --- a/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp +++ b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp @@ -29,7 +29,7 @@ proc test { command } { global srcfile binfile tdlabel_re with_test_prefix "$command" { - clean_restart ${binfile} + clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/signal-sigtrap.exp b/gdb/testsuite/gdb.threads/signal-sigtrap.exp index 8154ddf..849d628 100644 --- a/gdb/testsuite/gdb.threads/signal-sigtrap.exp +++ b/gdb/testsuite/gdb.threads/signal-sigtrap.exp @@ -32,7 +32,7 @@ proc test { sigtrap_thread } { global srcfile binfile tdlabel_re with_test_prefix "sigtrap thread $sigtrap_thread" { - clean_restart ${binfile} + clean_restart ${::testfile} if {![runto "thread_function"]} { return 0 diff --git a/gdb/testsuite/gdb.threads/sigthread.exp b/gdb/testsuite/gdb.threads/sigthread.exp index 9d2f9b5..dea8eb8 100644 --- a/gdb/testsuite/gdb.threads/sigthread.exp +++ b/gdb/testsuite/gdb.threads/sigthread.exp @@ -24,7 +24,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/staticthreads.exp b/gdb/testsuite/gdb.threads/staticthreads.exp index 0374666..bbe11ab 100644 --- a/gdb/testsuite/gdb.threads/staticthreads.exp +++ b/gdb/testsuite/gdb.threads/staticthreads.exp @@ -34,7 +34,7 @@ foreach_with_prefix have_tls { "-DHAVE_TLS" "" } { } } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" @@ -94,7 +94,7 @@ gdb_test_multiple "quit" "$test" { pass "$test" } } -clean_restart ${binfile} +clean_restart ${::testfile} if { "$have_tls" != "" } { diff --git a/gdb/testsuite/gdb.threads/step-N-all-progress.exp b/gdb/testsuite/gdb.threads/step-N-all-progress.exp index c874d79..031e36a 100644 --- a/gdb/testsuite/gdb.threads/step-N-all-progress.exp +++ b/gdb/testsuite/gdb.threads/step-N-all-progress.exp @@ -31,7 +31,7 @@ proc test {non-stop target-non-stop} { save_vars ::GDBFLAGS { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" - clean_restart $::binfile + clean_restart $::testfile } if { ![runto_main] } { diff --git a/gdb/testsuite/gdb.threads/step-over-exec.exp b/gdb/testsuite/gdb.threads/step-over-exec.exp index 7c553f2..b06f3a4 100644 --- a/gdb/testsuite/gdb.threads/step-over-exec.exp +++ b/gdb/testsuite/gdb.threads/step-over-exec.exp @@ -71,7 +71,8 @@ proc do_test { execr_thread different_text_segments displaced_stepping } { return -1 } - clean_restart ${execr_binfile} + clean_restart + gdb_load $execr_binfile gdb_test_no_output "set displaced-stepping $displaced_stepping" diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp b/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp index cf10bdc..fdd2b27 100644 --- a/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp +++ b/gdb/testsuite/gdb.threads/step-over-thread-exit-while-stop-all-threads.exp @@ -29,7 +29,7 @@ if { [build_executable "failed to prepare" $testfile \ proc test {displaced-stepping target-non-stop} { save_vars ::GDBFLAGS { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" - clean_restart $::binfile + clean_restart $::testfile } gdb_test_no_output "set displaced-stepping ${displaced-stepping}" diff --git a/gdb/testsuite/gdb.threads/step-over-thread-exit.exp b/gdb/testsuite/gdb.threads/step-over-thread-exit.exp index 31037a7..8ed2b21 100644 --- a/gdb/testsuite/gdb.threads/step-over-thread-exit.exp +++ b/gdb/testsuite/gdb.threads/step-over-thread-exit.exp @@ -55,7 +55,7 @@ proc test {step_over_mode non-stop target-non-stop schedlock cmd ns_stop_all} { save_vars ::GDBFLAGS { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" - clean_restart $::binfile + clean_restart $::testfile } if { $step_over_mode == "none" } { diff --git a/gdb/testsuite/gdb.threads/stepi-over-clone.exp b/gdb/testsuite/gdb.threads/stepi-over-clone.exp index 5da123e..d6f1680 100644 --- a/gdb/testsuite/gdb.threads/stepi-over-clone.exp +++ b/gdb/testsuite/gdb.threads/stepi-over-clone.exp @@ -106,7 +106,7 @@ proc test {non_stop displaced third_thread} { save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"set non-stop $non_stop\"" append GDBFLAGS " -ex \"set displaced $displaced\"" - clean_restart $binfile + clean_restart $::testfile } runto_main diff --git a/gdb/testsuite/gdb.threads/switch-threads.exp b/gdb/testsuite/gdb.threads/switch-threads.exp index d43603c..1f67a45 100644 --- a/gdb/testsuite/gdb.threads/switch-threads.exp +++ b/gdb/testsuite/gdb.threads/switch-threads.exp @@ -29,7 +29,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} runto_main diff --git a/gdb/testsuite/gdb.threads/thread-bp-deleted.exp b/gdb/testsuite/gdb.threads/thread-bp-deleted.exp index 8cabb70..8001229 100644 --- a/gdb/testsuite/gdb.threads/thread-bp-deleted.exp +++ b/gdb/testsuite/gdb.threads/thread-bp-deleted.exp @@ -38,7 +38,7 @@ set is_remote \ # This test requires background execution, which relies on non-stop mode. save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"maint set target-non-stop on\"" - clean_restart ${binfile} + clean_restart ${::testfile} } if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/thread-execl.exp b/gdb/testsuite/gdb.threads/thread-execl.exp index 04ba518..13a6ef4 100644 --- a/gdb/testsuite/gdb.threads/thread-execl.exp +++ b/gdb/testsuite/gdb.threads/thread-execl.exp @@ -35,7 +35,7 @@ proc do_test { schedlock } { set prefix "schedlock $schedlock" } with_test_prefix "$prefix" { - clean_restart ${binfile} + clean_restart ${::testfile} if {$schedlock == "non-stop"} { gdb_test_no_output "set non-stop 1" diff --git a/gdb/testsuite/gdb.threads/thread-find.exp b/gdb/testsuite/gdb.threads/thread-find.exp index 456f7d3..171b94b 100644 --- a/gdb/testsuite/gdb.threads/thread-find.exp +++ b/gdb/testsuite/gdb.threads/thread-find.exp @@ -21,7 +21,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" runto_main diff --git a/gdb/testsuite/gdb.threads/thread-specific-bp.exp b/gdb/testsuite/gdb.threads/thread-specific-bp.exp index c4858f2..8f48b61 100644 --- a/gdb/testsuite/gdb.threads/thread-specific-bp.exp +++ b/gdb/testsuite/gdb.threads/thread-specific-bp.exp @@ -118,7 +118,7 @@ proc check_thread_specific_breakpoint {non_stop} { foreach_with_prefix non_stop {on off} { save_vars { GDBFLAGS } { append GDBFLAGS " -ex \"set non-stop $non_stop\"" - clean_restart $binfile + clean_restart $::testfile } check_thread_specific_breakpoint $non_stop diff --git a/gdb/testsuite/gdb.threads/thread-specific.exp b/gdb/testsuite/gdb.threads/thread-specific.exp index bf9c63b..d1e6f4d 100644 --- a/gdb/testsuite/gdb.threads/thread-specific.exp +++ b/gdb/testsuite/gdb.threads/thread-specific.exp @@ -62,7 +62,7 @@ proc get_thread_list { } { return $thr_list } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set print sevenbit-strings" gdb_test_no_output "set width 0" diff --git a/gdb/testsuite/gdb.threads/thread-unwindonsignal.exp b/gdb/testsuite/gdb.threads/thread-unwindonsignal.exp index 5f4ac1f..dc74714 100644 --- a/gdb/testsuite/gdb.threads/thread-unwindonsignal.exp +++ b/gdb/testsuite/gdb.threads/thread-unwindonsignal.exp @@ -28,7 +28,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if { ![runto_main] } { return 0 diff --git a/gdb/testsuite/gdb.threads/thread_check.exp b/gdb/testsuite/gdb.threads/thread_check.exp index ee5f35a..9f92066 100644 --- a/gdb/testsuite/gdb.threads/thread_check.exp +++ b/gdb/testsuite/gdb.threads/thread_check.exp @@ -39,7 +39,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 1 diff --git a/gdb/testsuite/gdb.threads/threadapply.exp b/gdb/testsuite/gdb.threads/threadapply.exp index 9110617..34de561 100644 --- a/gdb/testsuite/gdb.threads/threadapply.exp +++ b/gdb/testsuite/gdb.threads/threadapply.exp @@ -25,7 +25,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} # # Run to `main' where we begin our tests. @@ -73,7 +73,7 @@ proc thr_apply_detach {thread_set} { global binfile global break_line - clean_restart ${binfile} + clean_restart ${::testfile} if ![runto_main] { return -1 @@ -112,7 +112,7 @@ proc kill_and_remove_inferior {thread_set} { set any "\[^\r\n\]*" set ws "\[ \t\]\+" - clean_restart ${binfile} + clean_restart ${::testfile} with_test_prefix "start inferior 1" { runto_main diff --git a/gdb/testsuite/gdb.threads/threadcrash.exp b/gdb/testsuite/gdb.threads/threadcrash.exp index 15d2a20..76d2c6c 100644 --- a/gdb/testsuite/gdb.threads/threadcrash.exp +++ b/gdb/testsuite/gdb.threads/threadcrash.exp @@ -237,7 +237,7 @@ proc_with_prefix test_corefile {} { proc_with_prefix test_gcore {} { - clean_restart "$::binfile" + clean_restart "$::testfile" gdb_test "handle SIGUSR1 nostop print pass" \ ".*SIGUSR1.*No.*Yes.*Yes.*User defined signal 1" \ @@ -275,7 +275,7 @@ if [prepare_for_testing "failed to prepare" $testfile $srcfile \ return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set backtrace limit unlimited" diff --git a/gdb/testsuite/gdb.threads/threxit-hop-specific.exp b/gdb/testsuite/gdb.threads/threxit-hop-specific.exp index ce2df7c..b55e80c 100644 --- a/gdb/testsuite/gdb.threads/threxit-hop-specific.exp +++ b/gdb/testsuite/gdb.threads/threxit-hop-specific.exp @@ -23,7 +23,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} runto_main diff --git a/gdb/testsuite/gdb.threads/tls-core.exp b/gdb/testsuite/gdb.threads/tls-core.exp index 96b1c6a..587ae61 100644 --- a/gdb/testsuite/gdb.threads/tls-core.exp +++ b/gdb/testsuite/gdb.threads/tls-core.exp @@ -27,7 +27,7 @@ set core_supported [expr {$corefile != ""}] # Generate a core file with "gcore". -clean_restart ${binfile} +clean_restart ${::testfile} runto thread_proc @@ -43,7 +43,7 @@ proc tls_core_test {supported corefile} { upvar host_triplet host_triplet upvar binfile binfile - clean_restart ${binfile} + clean_restart ${::testfile} set test "load core file" if {$supported} { diff --git a/gdb/testsuite/gdb.threads/tls-nodebug-pie.exp b/gdb/testsuite/gdb.threads/tls-nodebug-pie.exp index 01abcfa..44c12f5 100644 --- a/gdb/testsuite/gdb.threads/tls-nodebug-pie.exp +++ b/gdb/testsuite/gdb.threads/tls-nodebug-pie.exp @@ -20,7 +20,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 0 } diff --git a/gdb/testsuite/gdb.threads/tls-nodebug.exp b/gdb/testsuite/gdb.threads/tls-nodebug.exp index ebfa752..971f26c 100644 --- a/gdb/testsuite/gdb.threads/tls-nodebug.exp +++ b/gdb/testsuite/gdb.threads/tls-nodebug.exp @@ -26,7 +26,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} if {![runto_main]} { return 0 } diff --git a/gdb/testsuite/gdb.threads/tls-shared.exp b/gdb/testsuite/gdb.threads/tls-shared.exp index 35596bc..b7fa965 100644 --- a/gdb/testsuite/gdb.threads/tls-shared.exp +++ b/gdb/testsuite/gdb.threads/tls-shared.exp @@ -29,7 +29,7 @@ if { [gdb_compile_shlib_pthreads ${srcdir}/${subdir}/${srcfile_lib} ${binfile_li } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_load_shlib ${binfile_lib} if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/tls-so_extern.exp b/gdb/testsuite/gdb.threads/tls-so_extern.exp index 3cef672..a0aa5f8 100644 --- a/gdb/testsuite/gdb.threads/tls-so_extern.exp +++ b/gdb/testsuite/gdb.threads/tls-so_extern.exp @@ -28,7 +28,7 @@ if { [gdb_compile_shlib_pthreads ${srcdir}/${subdir}/${srcfile_lib} ${binfile_li } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_load_shlib ${binfile_lib} if {![runto_main]} { diff --git a/gdb/testsuite/gdb.threads/tls.exp b/gdb/testsuite/gdb.threads/tls.exp index 73fada7..4b43387 100644 --- a/gdb/testsuite/gdb.threads/tls.exp +++ b/gdb/testsuite/gdb.threads/tls.exp @@ -153,7 +153,7 @@ proc check_thread_stack {number spin_threads spin_threads_level} { } } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_multiple "print a_thread_local" "" { -re -wrap "Cannot find thread-local variables on this target" { diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp b/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp index 0b95a75..7c2b309 100644 --- a/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp +++ b/gdb/testsuite/gdb.threads/vfork-follow-child-exec.exp @@ -30,7 +30,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}]} proc test_vfork {detach} { global binfile - clean_restart $binfile + clean_restart $::testfile if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp b/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp index ced52df..a5e7475 100644 --- a/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp +++ b/gdb/testsuite/gdb.threads/vfork-follow-child-exit.exp @@ -30,7 +30,7 @@ if {[build_executable "failed to prepare" $testfile $srcfile {debug pthreads}]} proc test_vfork {detach} { global binfile - clean_restart $binfile + clean_restart $::testfile if {![runto_main]} { return 0 diff --git a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp index 61811ae..fce974b 100644 --- a/gdb/testsuite/gdb.threads/vfork-multi-thread.exp +++ b/gdb/testsuite/gdb.threads/vfork-multi-thread.exp @@ -59,7 +59,7 @@ proc do_test { target-non-stop non-stop follow-fork-mode detach-on-fork schedule save_vars { ::GDBFLAGS } { append ::GDBFLAGS " -ex \"maintenance set target-non-stop ${target-non-stop}\"" append ::GDBFLAGS " -ex \"set non-stop ${non-stop}\"" - clean_restart ${::binfile} + clean_restart ${::testfile} } gdb_test_no_output "set follow-fork-mode ${follow-fork-mode}" diff --git a/gdb/testsuite/gdb.threads/watchthreads.exp b/gdb/testsuite/gdb.threads/watchthreads.exp index 49fc762..f3ec7f4 100644 --- a/gdb/testsuite/gdb.threads/watchthreads.exp +++ b/gdb/testsuite/gdb.threads/watchthreads.exp @@ -31,7 +31,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart $binfile +clean_restart $::testfile gdb_test_no_output "set can-use-hw-watchpoints 1" "" # diff --git a/gdb/testsuite/gdb.threads/watchthreads2.exp b/gdb/testsuite/gdb.threads/watchthreads2.exp index 2426be4..19a60e4 100644 --- a/gdb/testsuite/gdb.threads/watchthreads2.exp +++ b/gdb/testsuite/gdb.threads/watchthreads2.exp @@ -31,7 +31,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} gdb_test_no_output "set can-use-hw-watchpoints 1" "" diff --git a/gdb/testsuite/gdb.threads/wp-replication.exp b/gdb/testsuite/gdb.threads/wp-replication.exp index 68f5eb0..8a5f95d 100644 --- a/gdb/testsuite/gdb.threads/wp-replication.exp +++ b/gdb/testsuite/gdb.threads/wp-replication.exp @@ -34,7 +34,7 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab return -1 } -clean_restart ${binfile} +clean_restart ${::testfile} # Force hardware watchpoints to be used. gdb_test_no_output "set can-use-hw-watchpoints 1" "" diff --git a/gdb/testsuite/gdb.trace/ax.exp b/gdb/testsuite/gdb.trace/ax.exp index 3380cdf..cc40853 100644 --- a/gdb/testsuite/gdb.trace/ax.exp +++ b/gdb/testsuite/gdb.trace/ax.exp @@ -30,7 +30,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ return -1 } -clean_restart $binfile +clean_restart $testfile runto_main gdb_test "maint agent 12" ".*const8 12.*pop.*end.*" diff --git a/gdb/testsuite/gdb.trace/basic-libipa.exp b/gdb/testsuite/gdb.trace/basic-libipa.exp index e7612ac..27be96b 100644 --- a/gdb/testsuite/gdb.trace/basic-libipa.exp +++ b/gdb/testsuite/gdb.trace/basic-libipa.exp @@ -42,7 +42,7 @@ save_vars { env(ASAN_OPTIONS) } { # LD_PRELOAD. append_environment_default ASAN_OPTIONS verify_asan_link_order 0 - clean_restart $binfile + clean_restart $testfile } if {![runto_main]} { diff --git a/gdb/testsuite/gdb.trace/entry-values.exp b/gdb/testsuite/gdb.trace/entry-values.exp index f6bcf66..161496a 100644 --- a/gdb/testsuite/gdb.trace/entry-values.exp +++ b/gdb/testsuite/gdb.trace/entry-values.exp @@ -29,7 +29,7 @@ if {[gdb_compile [list ${binfile}1.o] \ return -1 } -clean_restart ${binfile}1 +clean_restart ${testfile}1 set returned_from_foo "" @@ -193,7 +193,7 @@ gdb_test_sequence "bt" "bt, 2" { # Restart GDB and trace. -clean_restart $binfile +clean_restart $testfile load_lib "trace-support.exp" diff --git a/gdb/testsuite/gdb.trace/ftrace-lock.exp b/gdb/testsuite/gdb.trace/ftrace-lock.exp index 36a7566..8c9d4aa 100644 --- a/gdb/testsuite/gdb.trace/ftrace-lock.exp +++ b/gdb/testsuite/gdb.trace/ftrace-lock.exp @@ -32,7 +32,7 @@ set options [list debug pthreads [gdb_target_symbol_prefix_flags] \ additional_flags=-DNUM_THREADS=$NUM_THREADS] with_test_prefix "runtime trace support check" { - if { [prepare_for_testing "prepare for testing" ${binfile}-check \ + if { [prepare_for_testing "prepare for testing" ${testfile}-check \ $srcfile $options] } { return } @@ -55,7 +55,7 @@ set remote_libipa [gdb_load_shlib $libipa] lappend options shlib=$libipa if { [prepare_for_testing "prepare for testing with libipa" \ - $binfile $srcfile $options] } { + $testfile $srcfile $options] } { return } diff --git a/gdb/testsuite/gdb.trace/packetlen.exp b/gdb/testsuite/gdb.trace/packetlen.exp index cf9fcc4..52a34b9 100644 --- a/gdb/testsuite/gdb.trace/packetlen.exp +++ b/gdb/testsuite/gdb.trace/packetlen.exp @@ -25,7 +25,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ return -1 } -clean_restart $binfile +clean_restart $testfile gdb_test "tstop" ".*" "" gdb_test "tfind none" ".*" "" runto_main diff --git a/gdb/testsuite/gdb.trace/passc-dyn.exp b/gdb/testsuite/gdb.trace/passc-dyn.exp index 0a67df1..b4ec45a 100644 --- a/gdb/testsuite/gdb.trace/passc-dyn.exp +++ b/gdb/testsuite/gdb.trace/passc-dyn.exp @@ -25,7 +25,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ return -1 } -clean_restart $binfile +clean_restart $testfile runto_main if {![gdb_target_supports_trace]} { diff --git a/gdb/testsuite/gdb.trace/report.exp b/gdb/testsuite/gdb.trace/report.exp index 45857a1..f2a04f7 100644 --- a/gdb/testsuite/gdb.trace/report.exp +++ b/gdb/testsuite/gdb.trace/report.exp @@ -24,7 +24,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ untested "failed to compile" return -1 } -clean_restart $binfile +clean_restart $testfile runto_main if {![gdb_target_supports_trace]} { diff --git a/gdb/testsuite/gdb.trace/tfile.exp b/gdb/testsuite/gdb.trace/tfile.exp index 020f4d4..4d156f7 100644 --- a/gdb/testsuite/gdb.trace/tfile.exp +++ b/gdb/testsuite/gdb.trace/tfile.exp @@ -61,7 +61,7 @@ if {!$purely_local} { remote_download host [remote_upload target tfile-error.tf] tfile-error.tf } -clean_restart $binfile +clean_restart $testfile # Program has presumably exited, now target a trace file it created. @@ -121,7 +121,7 @@ gdb_test "info registers" "The program has no registers now\." \ # Now start afresh, using only a trace file. -clean_restart $binfile +clean_restart $testfile gdb_test "target tfile $tfile_error" "Created tracepoint.*" \ "target tfile [file tail $tfile_error]" diff --git a/gdb/testsuite/gdb.trace/tfind.exp b/gdb/testsuite/gdb.trace/tfind.exp index ae73206..92386e4 100644 --- a/gdb/testsuite/gdb.trace/tfind.exp +++ b/gdb/testsuite/gdb.trace/tfind.exp @@ -27,7 +27,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" "$binfile" \ return -1 } -clean_restart $binfile +clean_restart $testfile # 6.2 test help tstart gdb_test "help tstart" \ diff --git a/gdb/testsuite/gdb.trace/trace-mt.exp b/gdb/testsuite/gdb.trace/trace-mt.exp index 0c6e4e5..092dc6d 100644 --- a/gdb/testsuite/gdb.trace/trace-mt.exp +++ b/gdb/testsuite/gdb.trace/trace-mt.exp @@ -22,7 +22,7 @@ set additional_flags [gdb_target_symbol_prefix_flags] require gdb_trace_common_supports_arch with_test_prefix "runtime trace support check" { - if { [prepare_for_testing "prepare for testing" ${binfile} $srcfile \ + if { [prepare_for_testing "prepare for testing" $testfile $srcfile \ [list debug pthreads $additional_flags]] } { return } @@ -37,13 +37,13 @@ with_test_prefix "runtime trace support check" { } } -proc step_over_tracepoint { binfile trace_type } \ +proc step_over_tracepoint { testfile trace_type } \ {with_test_prefix "step over $trace_type" \ { global hex # Start with a fresh gdb. - clean_restart $binfile + clean_restart $testfile # Make sure inferior is running in all-stop mode. gdb_test_no_output "set non-stop 0" @@ -63,13 +63,13 @@ proc step_over_tracepoint { binfile trace_type } \ # Set breakpoint and tracepoint at the same address. -proc break_trace_same_addr { binfile trace_type option } \ +proc break_trace_same_addr { testfile trace_type option } \ {with_test_prefix "$trace_type $option" \ { global hex # Start with a fresh gdb. - clean_restart $binfile + clean_restart $testfile if ![runto_main] { return -1 } @@ -96,10 +96,10 @@ proc break_trace_same_addr { binfile trace_type option } \ }} foreach break_always_inserted { "on" "off" } { - break_trace_same_addr $binfile "trace" ${break_always_inserted} + break_trace_same_addr $testfile "trace" ${break_always_inserted} } -step_over_tracepoint $binfile "trace" +step_over_tracepoint $testfile "trace" require allow_shlib_tests @@ -108,8 +108,8 @@ set libipa [get_in_proc_agent] set remote_libipa [gdb_load_shlib $libipa] # Compile test case again with IPA. -set binfile_ipa ${binfile}-ipa -if { [prepare_for_testing "prepare for testing" $binfile_ipa $srcfile \ +set testfile_ipa $testfile-ipa +if { [prepare_for_testing "prepare for testing" $testfile_ipa $srcfile \ [list debug pthreads $additional_flags shlib=$libipa]] } { return } @@ -122,8 +122,8 @@ if { [gdb_test "info sharedlibrary" ".*${remote_libipa}.*" "IPA loaded"] != 0 } untested "could not find IPA lib loaded" } else { foreach break_always_inserted { "on" "off" } { - break_trace_same_addr $binfile_ipa "ftrace" ${break_always_inserted} + break_trace_same_addr $testfile_ipa "ftrace" ${break_always_inserted} } - step_over_tracepoint $binfile_ipa "ftrace" + step_over_tracepoint $testfile_ipa "ftrace" } diff --git a/gdb/testsuite/gdb.trace/tsv.exp b/gdb/testsuite/gdb.trace/tsv.exp index 96c7c35..837633d 100644 --- a/gdb/testsuite/gdb.trace/tsv.exp +++ b/gdb/testsuite/gdb.trace/tsv.exp @@ -23,7 +23,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ return -1 } -clean_restart $binfile +clean_restart $testfile # PR gdb/21352: Command tsave does not support -r argument gdb_test "tsave -r" "Argument required \\\(file in which to save trace data\\\)\." \ @@ -193,7 +193,7 @@ gdb_test_multiple "target ctf ${tracefile}.ctf" "" { } # Restart. -clean_restart ${binfile} +clean_restart $testfile if {![runto_main]} { return diff --git a/gdb/testsuite/gdb.trace/while-dyn.exp b/gdb/testsuite/gdb.trace/while-dyn.exp index 53a8e54..3940ff6 100644 --- a/gdb/testsuite/gdb.trace/while-dyn.exp +++ b/gdb/testsuite/gdb.trace/while-dyn.exp @@ -26,7 +26,7 @@ if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ return -1 } -clean_restart $binfile +clean_restart $testfile runto_main if {![gdb_target_supports_trace]} { diff --git a/gdb/testsuite/gdb.tui/basic.exp b/gdb/testsuite/gdb.tui/basic.exp index 35c99bd..8ecc91a 100644 --- a/gdb/testsuite/gdb.tui/basic.exp +++ b/gdb/testsuite/gdb.tui/basic.exp @@ -112,5 +112,9 @@ set re_noattr "\[^<\]" set status_window_line 15 set status [Term::get_line_with_attrs $status_window_line] -gdb_assert { [regexp "^<reverse:1>$re_noattr*<reverse:0>$" $status] == 1} \ +verbose -log "status line: '$status'" + +# The status line uses standout, which may translate to different attributes +# depending on the terminal settings. Just check for at least one attribute. +gdb_assert { [regexp "^<.*>(exec|extended-r)" $status] == 1 } \ "status window: reverse" diff --git a/gdb/testsuite/gdb.tui/color-prompt.exp b/gdb/testsuite/gdb.tui/color-prompt.exp index a95b24a..af6e467 100644 --- a/gdb/testsuite/gdb.tui/color-prompt.exp +++ b/gdb/testsuite/gdb.tui/color-prompt.exp @@ -15,18 +15,16 @@ # Check using a prompt with color in TUI. +require allow_tui_tests + tuiterm_env Term::clean_restart 24 80 -# Set colored prompt. if {![Term::enter_tui]} { unsupported "TUI not supported" return } -Term::command "set prompt \\033\[31m(gdb) \\033\[0m" - -set line [Term::get_line_with_attrs $Term::_cur_row] -gdb_assert { [regexp "^<fg:red>$gdb_prompt <fg:default> *$" $line] } \ - "prompt with color" +set tui 1 +source $srcdir/$subdir/color-prompt.exp.tcl diff --git a/gdb/testsuite/gdb.tui/color-prompt.exp.tcl b/gdb/testsuite/gdb.tui/color-prompt.exp.tcl new file mode 100644 index 0000000..e6f4d3b --- /dev/null +++ b/gdb/testsuite/gdb.tui/color-prompt.exp.tcl @@ -0,0 +1,80 @@ +# 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 using a prompt with color in TUI ($tui == 0) or CLI ($tui == 0). + +set csi [string cat {\033} "\["] +set rl_prompt_start_ignore {\001} +set rl_prompt_end_ignore {\002} + +foreach_with_prefix rl_prompt_start_end_ignore { 0 1 } { + set color_on [string cat $csi 31m] + set color_off [string cat $csi 0m] + + if { $rl_prompt_start_end_ignore } { + set color_on \ + [string cat \ + $rl_prompt_start_ignore \ + $color_on \ + $rl_prompt_end_ignore] + set color_off \ + [string cat \ + $rl_prompt_start_ignore \ + $color_off \ + $rl_prompt_end_ignore] + } + + # Set prompt with color. + set prompt "${color_on}(gdb) $color_off" + Term::command "set prompt $prompt" + + # Check the color. + set line [Term::get_line_with_attrs $Term::_cur_row] + gdb_assert { [regexp "^<fg:red>$gdb_prompt <fg:default> *$" $line] } \ + "prompt with color" + + # Type a string. + set cmd "some long command" + send_gdb $cmd + Term::wait_for_line ^[string_to_regexp "(gdb) $cmd"] 23 + + # Send ^A, aka C-a, trigger beginning-of-line. + send_gdb "\001" + if { $tui || $rl_prompt_start_end_ignore } { + Term::wait_for_line ^[string_to_regexp "(gdb) $cmd"] 6 + } else { + # Without the markers, readline may get the cursor position wrong, so + # match less strict. + Term::wait_for_line ^[string_to_regexp "(gdb) $cmd"] + } + Term::dump_screen + + # Type something else to flush out the effect of the ^A. + set prefix "A" + send_gdb $prefix + if { $tui || $rl_prompt_start_end_ignore } { + Term::wait_for_line ^[string_to_regexp "(gdb) $prefix$cmd"] 7 + } else { + # Without the markers, readline may get the cursor position wrong, so + # match less strict. + Term::wait_for_line [string_to_regexp "$prefix"] + } + + # Abort command line editing, and regenerate prompt. + send_gdb "\003" + + # Reset prompt to default prompt. + Term::command "set prompt (gdb) " +} diff --git a/gdb/testsuite/gdb.tui/compact-source.exp b/gdb/testsuite/gdb.tui/compact-source.exp index b050159..33d8827 100644 --- a/gdb/testsuite/gdb.tui/compact-source.exp +++ b/gdb/testsuite/gdb.tui/compact-source.exp @@ -41,7 +41,7 @@ if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} { return -1 } -Term::clean_restart 24 80 $binfile +Term::clean_restart 24 80 $testfile gdb_test_no_output "maint set tui-left-margin-verbose on" gdb_test_no_output "set tui compact-source on" diff --git a/gdb/testsuite/gdb.tui/gdb.tcl b/gdb/testsuite/gdb.tui/gdb.tcl new file mode 100755 index 0000000..ca207ed --- /dev/null +++ b/gdb/testsuite/gdb.tui/gdb.tcl @@ -0,0 +1,20 @@ +#!/usr/bin/env tclsh + +# 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/>. + +puts "foo\033(%5" + +gets stdin diff --git a/gdb/testsuite/gdb.tui/main-2.exp b/gdb/testsuite/gdb.tui/main-2.exp index 71ad03b..2bc6b8d 100644 --- a/gdb/testsuite/gdb.tui/main-2.exp +++ b/gdb/testsuite/gdb.tui/main-2.exp @@ -26,7 +26,7 @@ if { [build_executable "failed to prepare" $testfile $srcfile ] == -1} { return -1 } -Term::clean_restart 24 80 $binfile +Term::clean_restart 24 80 $testfile if {![runto_main]} { perror "test suppressed" @@ -41,7 +41,7 @@ if {![Term::enter_tui]} { set line " return 0;" set nr [gdb_get_line_number $line] -set screen_line [Term::get_string_with_attrs 6 1 79] +set screen_line [Term::get_string_with_attrs 6 11 79] verbose -log "screen line 6: '$screen_line'" -gdb_assert { [regexp "$nr <reverse:1>$line<reverse:0>" $screen_line] } \ +gdb_assert { [regexp "<reverse:1>$line<reverse:0>" $screen_line] } \ "highlighted line in middle of source window" diff --git a/gdb/testsuite/gdb.tui/main.exp b/gdb/testsuite/gdb.tui/main.exp index d6960c7..e49da35 100644 --- a/gdb/testsuite/gdb.tui/main.exp +++ b/gdb/testsuite/gdb.tui/main.exp @@ -43,7 +43,10 @@ if {![Term::enter_tui]} { } send_gdb "file [standard_output_file $testfile]\n" -gdb_assert { [Term::wait_for "Reading symbols from"] } "file command" +# Matching the output is difficult because it may or may not wrap. Simply +# match the resulting prompt. +gdb_assert { [Term::wait_for ""] } "file command" + Term::check_contents "show main after file" \ [string_to_regexp "|___[format %06d $nr]_$line"] diff --git a/gdb/testsuite/gdb.tui/new-layout.exp b/gdb/testsuite/gdb.tui/new-layout.exp index f517997..914e10c 100644 --- a/gdb/testsuite/gdb.tui/new-layout.exp +++ b/gdb/testsuite/gdb.tui/new-layout.exp @@ -150,4 +150,7 @@ Term::check_box "before cmd_only: src box in src layout" 0 0 80 15 Term::command "layout cmd_only" Term::command "layout src" + +# Flush out and check the resulting src box. +Term::command "print 1" Term::check_box "after cmd_only: src box in src layout" 0 0 80 15 diff --git a/gdb/testsuite/gdb.tui/pr30056.exp b/gdb/testsuite/gdb.tui/pr30056.exp index 3403033..dd3b25c 100644 --- a/gdb/testsuite/gdb.tui/pr30056.exp +++ b/gdb/testsuite/gdb.tui/pr30056.exp @@ -72,9 +72,8 @@ save_vars { env(LC_ALL) } { # Send ^C to clear the command line. send_gdb "\003" } else { - # Sending ^C currently doesn't abort the i-search. PR cli/30498 is - # open about this. - kfail cli/30498 $test + # Sending ^C currently doesn't abort the i-search. + fail $test # At this point we don't have a responsive prompt. Send ^G to abort # the i-search. diff --git a/gdb/testsuite/gdb.tui/source-search.c b/gdb/testsuite/gdb.tui/source-search.c new file mode 100644 index 0000000..2320c5c --- /dev/null +++ b/gdb/testsuite/gdb.tui/source-search.c @@ -0,0 +1,127 @@ +/* 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/>. */ + +int +main (void) +{ + /* Line 21 */ + /* Line 22 */ + /* Line 23 */ + /* Line 24 */ + /* Line 25 */ + /* Line 26 */ + /* Line 27 */ + /* Line 28 */ + /* Line 29 */ + /* Line 30 */ + /* Line 31 */ + /* Line 32 */ + /* Line 33 */ + /* Line 34 */ + /* Line 35 */ + /* Line 36 */ + /* Line 37 */ + /* Line 38 */ + /* Line 39 */ + /* Line 40 */ + /* Line 41 */ + /* Line 42 */ + /* Line 43 */ + /* Line 44 */ + /* Line 45 */ + /* Line 46 */ + /* Line 47 */ + /* Line 48 */ + /* Line 49 */ + /* Line 50 */ + /* Line 51 */ + /* Line 52 */ + /* Line 53 */ + /* Line 54 */ + /* Line 55 */ + /* Line 56 */ + /* Line 57 */ + /* Line 58 */ + /* Line 59 */ + /* Line 60 */ + /* Line 61 */ + /* Line 62 */ + /* Line 63 */ + /* Line 64 */ + /* Line 65 */ + /* Line 66 */ + /* Line 67 */ + /* Line 68 */ + /* Line 69 */ + /* Line 70 */ + /* Line 71 */ + /* Line 72 */ + /* Line 73 */ + /* Line 74 */ + /* Line 75 */ + /* Line 76 */ + /* Line 77 */ + /* Line 78 */ + /* Line 79 */ + /* Line 80 */ + /* Line 81 */ + /* Line 82 */ + /* Line 83 */ + /* Line 84 */ + /* Line 85 */ + /* Line 86 */ + /* Line 87 */ + /* Line 88 */ + /* Line 89 */ + /* Line 90 */ + /* Line 91 */ + /* Line 92 */ + /* Line 93 */ + /* Line 94 */ + /* Line 95 */ + /* Line 96 */ + /* Line 97 */ + /* Line 98 */ + /* Line 99 */ + /* Line 100 */ + /* Line 101 */ + /* Line 102 */ + /* Line 103 */ + /* Line 104 */ + /* Line 105 */ + /* Line 106 */ + /* Line 107 */ + /* Line 108 */ + /* Line 109 */ + /* Line 110 */ + /* Line 111 */ + /* Line 112 */ + /* Line 113 */ + /* Line 114 */ + /* Line 115 */ + /* Line 116 */ + /* Line 117 */ + /* Line 118 */ + /* Line 119 */ + /* Line 120 */ + /* Line 121 */ + /* Line 122 */ + /* Line 123 */ + /* Line 124 */ + /* Line 125 */ + return 0; +} /* Last line. */ diff --git a/gdb/testsuite/gdb.tui/source-search.exp b/gdb/testsuite/gdb.tui/source-search.exp new file mode 100644 index 0000000..41bf121 --- /dev/null +++ b/gdb/testsuite/gdb.tui/source-search.exp @@ -0,0 +1,72 @@ +# 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 forward-search and reverse-search within the TUI src window. + +tuiterm_env + +standard_testfile .c + +if {[build_executable "failed to build" ${testfile} ${srcfile}] == -1} { + return +} + +Term::clean_restart 24 80 $testfile +if {![Term::enter_tui]} { + unsupported "TUI not supported" + return +} + +proc check_src_window { testname first_line } { + set last_line [expr $first_line + 12] + Term::check_box_contents $testname 0 0 80 15 \ + "^\\s+${first_line}\\s+.*\\s+${last_line}\\s+/\\* Line ${last_line} \\*/\\s+$" + +} + +# Initialise the src window so that it is displaying known contents. +# Check that the expected contents are displayed. +Term::command "list 20" +set first_line 15 +check_src_window "initial src contents" $first_line + +# Search forward. Searches are from the last line displayed, so this +# will move the next source line onto the screen each time. +for { set i 1 } { $i < 4 } { incr i } { + incr first_line + Term::command "forward-search Line" + check_src_window "src windows after forward-search $i" $first_line +} + +# Reverse search. Like forward-search, but move backward through the +# source. +for { set i 1 } { $i < 3 } { incr i } { + incr first_line -1 + Term::command "reverse-search Line" + check_src_window "src windows after reverse-search $i" $first_line +} + +# Until there are no matching lines left. +Term::command "reverse-search Line" +gdb_assert {[regexp -- "^Expression not found\\s+$" [Term::get_line 22]]} \ + "check start of source was reached" + +Term::command "forward-search Last line" +Term::check_box_contents "forward-search to end of file" 0 0 80 15 \ + "^\\s+122\\s+.*/\\* Last line\\. \\*/\\s+$" + +Term::command "reverse-search This testcase is part" +Term::check_box_contents "reverse-search to start of file" 0 0 80 15 \ + "^\\s+1\\s+.*\\s+13\\s+GNU General Public License for more details\\.\\s+$" diff --git a/gdb/testsuite/gdb.tui/tui-disasm-long-lines.exp b/gdb/testsuite/gdb.tui/tui-disasm-long-lines.exp index 35f990d..7a9841f 100644 --- a/gdb/testsuite/gdb.tui/tui-disasm-long-lines.exp +++ b/gdb/testsuite/gdb.tui/tui-disasm-long-lines.exp @@ -35,7 +35,7 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "$binfile" \ } } -Term::clean_restart 24 80 $binfile +Term::clean_restart 24 80 $testfile if {![Term::prepare_for_tui]} { unsupported "TUI not supported" return diff --git a/gdb/testsuite/gdb.tui/tui-disasm-styling.exp b/gdb/testsuite/gdb.tui/tui-disasm-styling.exp index 513d787..6859744 100644 --- a/gdb/testsuite/gdb.tui/tui-disasm-styling.exp +++ b/gdb/testsuite/gdb.tui/tui-disasm-styling.exp @@ -37,12 +37,15 @@ proc check_asm_output { expect_styled testname } { $testname } -Term::clean_restart 24 80 $binfile +Term::clean_restart 24 80 $testfile if {![Term::enter_tui]} { unsupported "TUI not supported" return } +# Proc enter_tui switches off styling, re-enable it. +Term::command "set style enabled on" + Term::command "layout asm" Term::check_box "asm box" 0 0 80 15 diff --git a/gdb/testsuite/gdb.tui/tui-focus.exp b/gdb/testsuite/gdb.tui/tui-focus.exp index 26e5060..50a3315 100644 --- a/gdb/testsuite/gdb.tui/tui-focus.exp +++ b/gdb/testsuite/gdb.tui/tui-focus.exp @@ -36,7 +36,7 @@ foreach spec {{src true} {cmd true} {status true} {regs false} \ lassign $spec window valid_p with_test_prefix "window=$window" { - Term::clean_restart 24 80 $binfile + Term::clean_restart 24 80 $testfile if {![Term::prepare_for_tui]} { unsupported "TUI not supported" return @@ -75,7 +75,7 @@ foreach spec {{src true} {cmd true} {status true} {regs false} \ # 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 +Term::clean_restart 24 80 $testfile if {[allow_python_tests]} { # Create a very simple tui window. gdb_py_test_silent_cmd \ diff --git a/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp b/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp index 51623e8..47482a2 100644 --- a/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp +++ b/gdb/testsuite/gdb.tui/tui-layout-asm-short-prog.exp @@ -20,9 +20,13 @@ tuiterm_env standard_testfile tui-layout-asm-short-prog.S -if {[build_executable "failed to prepare" ${testfile} ${srcfile} \ - {debug additional_flags=-nostdlib \ - additional_flags=-nostartfiles}] == -1} { +set opts {} +lappend opts debug +lappend opts additional_flags=-static +lappend opts additional_flags=-nostdlib +lappend opts additional_flags=-nostartfiles + +if { [build_executable "failed to prepare" $testfile $srcfile $opts] == -1 } { return -1 } diff --git a/gdb/testsuite/gdb.tui/tui-layout.exp b/gdb/testsuite/gdb.tui/tui-layout.exp index 7bb0ea1..f604871 100644 --- a/gdb/testsuite/gdb.tui/tui-layout.exp +++ b/gdb/testsuite/gdb.tui/tui-layout.exp @@ -40,7 +40,7 @@ if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { # happens to be in after a call to clean_restart. proc test_layout_or_focus {layout_name terminal execution} { - global binfile gdb_prompt + global gdb_prompt set dumb_terminal [string equal $terminal "dumb"] @@ -48,9 +48,9 @@ proc test_layout_or_focus {layout_name terminal execution} { save_vars { env(TERM) } { setenv TERM $terminal if {$dumb_terminal} { - clean_restart $binfile + clean_restart $::testfile } else { - Term::clean_restart 24 80 $binfile + Term::clean_restart 24 80 $::testfile if {![Term::prepare_for_tui]} { unsupported "TUI not supported" return diff --git a/gdb/testsuite/gdb.tui/tui-missing-src.exp b/gdb/testsuite/gdb.tui/tui-missing-src.exp index f29ec8c..aca417c 100644 --- a/gdb/testsuite/gdb.tui/tui-missing-src.exp +++ b/gdb/testsuite/gdb.tui/tui-missing-src.exp @@ -64,10 +64,8 @@ f2 (int x) close $fd # Step 3: Compile the source files. -if { [gdb_compile "${srcfiles}" "${binfile}" \ - executable {debug additional_flags=-O0}] != "" } { - untested "failed to compile" - return -1 +if { [build_executable "failed to prepare" $testfile $srcfiles] == -1 } { + return } # Step 4: Remove the main.c file. diff --git a/gdb/testsuite/gdb.tui/tui-mode-switch.exp b/gdb/testsuite/gdb.tui/tui-mode-switch.exp new file mode 100644 index 0000000..c605962 --- /dev/null +++ b/gdb/testsuite/gdb.tui/tui-mode-switch.exp @@ -0,0 +1,57 @@ +require allow_tui_tests + +tuiterm_env + +if { [ishost *-*-*bsd*] } { + # We need support for alternate screen, and xterm doesn't have it. + set term xterm-clear +} else { + set term xterm +} + +Term::with_term $term { + Term::clean_restart 12 40 +} + +if {![Term::prepare_for_tui]} { + unsupported "TUI not supported" + return 0 +} + +# Generate prompt. +Term::gen_prompt + +# Move to last line. +for { set i 1 } { $i <= 11 } { incr i } { + send_gdb "\n" + Term::wait_for "" +} + +# Type "foo". +send_gdb "foo" +set line { 0 11 40 1 } +gdb_assert { [Term::wait_for_region_contents {*}$line "^$gdb_prompt foo"] } \ + "type foo" + +# Enter TUI. +send_gdb "\030\001" +gdb_assert { [Term::wait_for ""] } "enter TUI" + +# Exit TUI. +send_gdb "\030\001" +gdb_assert { [Term::wait_for ""] } "exit TUI" + +# Type b. +send_gdb "b" +gdb_assert { [Term::wait_for_region_contents {*}$line "^$gdb_prompt b"] } \ + "type b" + +# Check that we don't see "boo". +gdb_assert { ![Term::check_region_contents_p {*}$line "^$gdb_prompt boo"] } \ + "no boo" +Term::dump_screen + +# We need an empty prompt here, to deal with the "monitor exit" that +# native-extended-gdbserver will send. Send a backspace. +send_gdb "\010" +Term::wait_for "" diff --git a/gdb/testsuite/gdb.tui/tuiterm-2.exp b/gdb/testsuite/gdb.tui/tuiterm-2.exp index 5992271..a451834 100644 --- a/gdb/testsuite/gdb.tui/tuiterm-2.exp +++ b/gdb/testsuite/gdb.tui/tuiterm-2.exp @@ -106,6 +106,76 @@ with_override Term::accept_gdb_output test_accept_gdb_output { } gdb_assert { [Term::wait_for ""] } } + + with_test_prefix "Term::wait_for 2" { + Term::_setup 4 20 + set send_cnt 0 + set expect_send {} + set action_cnt 0 + set actions { + { + Term::_move_cursor 0 0 + + Term::_insert "${::border}(gdb) " + set pos $Term::_cur_col + + Term::_insert "foo" + + Term::_move_cursor 19 0 + Term::_insert "$::border" + + Term::_move_cursor $pos 0 + } + { + Term::_move_cursor 0 1 + + Term::_insert "${::border}(gdb) " + set pos $Term::_cur_col + + Term::_move_cursor 19 1 + Term::_insert "$::border" + + Term::_move_cursor $pos 1 + } + } + + # Wait for a prompt. + gdb_assert { [Term::wait_for ""] } + + # The first action sets the cursor after the prompt on the + # first line. The second action sets the cursor after the + # prompt on the second line. Check that wait_for returns + # after the second action, not the first. + gdb_assert { $Term::_cur_row == 1 } + } } } } + +with_test_prefix "Unrecognized escape sequence" { + spawn $srcdir/$subdir/gdb.tcl + switch_gdb_spawn_id $spawn_id + + Term::_setup 4 20 + + save_vars timeout { + set timeout 1 + + set line { 0 0 20 1 } + + # Parse "foo". + gdb_assert { [Term::wait_for_region_contents \ + {*}$line \ + [string_to_regexp "foo"]] } \ + "foo" + + # Parse "\033(%5". + gdb_assert { ![Term::accept_gdb_output 0] } \ + "fail to parse escape sequence" + gdb_assert { [Term::wait_for_region_contents \ + {*}$line \ + [string_to_regexp "^\[(%5"]] } \ + "echoed escape sequence" + } + Term::dump_screen +} diff --git a/gdb/testsuite/gdb.tui/tuiterm.exp b/gdb/testsuite/gdb.tui/tuiterm.exp index 6cd65f3..ed9478a 100644 --- a/gdb/testsuite/gdb.tui/tuiterm.exp +++ b/gdb/testsuite/gdb.tui/tuiterm.exp @@ -653,13 +653,21 @@ proc test_cursor_backward_tabulation { } { } proc test_repeat { } { - Term::_move_cursor 2 1 - set Term::_last_char X + Term::_move_cursor 0 1 + + Term::_insert "xxX" + gdb_assert { $Term::_last_char == "X" } + check "insert" { + "abcdefgh" + "xxXlmnop" + "qrstuvwx" + "yz01234 " + } 3 1 - Term::_csi_b 3 + Term::_csi_b 2 check "repeat" { "abcdefgh" - "ijXXXnop" + "xxXXXnop" "qrstuvwx" "yz01234 " } 5 1 diff --git a/gdb/testsuite/lib/ada.exp b/gdb/testsuite/lib/ada.exp index 37bed85..50067eb 100644 --- a/gdb/testsuite/lib/ada.exp +++ b/gdb/testsuite/lib/ada.exp @@ -220,7 +220,8 @@ proc gnat_runtime_has_debug_info_1 { shared } { return 0 } - clean_restart $dst + clean_restart + gdb_load $dst if { ! [runto "GNAT_Debug_Info_Test"] } { return 0 diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 98691df..1652a76 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -2252,6 +2252,209 @@ proc gdb_assert { condition {message ""} } { return $res } +# Comparison command for "lsort -command". Sorts two strings by +# descending file name length. + +proc compare_length_desc {a b} { + expr {[string length $b] - [string length $a]} +} + +# Fill in and return the global cache for Windows <=> Unix mount point +# mappings, for Windows. +# +# Calling external processes on MSYS2/Cygwin is expensive so instead +# of calling "cygpath -ua $FILENAME" or "cygpath -ma $FILENAME" for +# every file name, we extract the Windows and Unix file names of each +# mount point using the 'mount' command output, and cache the +# mappings, for both directions. + +gdb_caching_proc get_mount_point_map {} { + array set win_to_unix {} + array set unix_to_win {} + + # The 'mount' command provides all mappings. The general format + # is: 'WindowsFileName on UnixFileName type ...' + # + # For example: + # 'C:/msys64 on / type ntfs (binary,auto)' + # 'C: on /c type ntfs (binary,posix=0,user,noumount,auto)' + set mount_output [exec mount] + + foreach line [split $mount_output \n] { + if {[regexp {^(.+) on (.+) type } $line -> win_filename unix_filename]} { + set win_to_unix($win_filename) $unix_filename + set unix_to_win($unix_filename) $win_filename + } + } + + # Sort each mapping's keys by descending file name length, + # otherwise we wouldn't be able to look for '/foo' in '/' (for + # example). + + set sorted_win {} + foreach k [lsort -command compare_length_desc [array names win_to_unix]] { + lappend sorted_win $k $win_to_unix($k) + } + + set sorted_unix {} + foreach k [lsort -command compare_length_desc [array names unix_to_win]] { + lappend sorted_unix $k $unix_to_win($k) + } + + # Return both sorted lists: {win => unix} {unix => win} + return [list $sorted_win $sorted_unix] +} + +# Normalize backward slashes to forward slashes. + +proc normalize_slashes {filename} { + return [string map {\\ /} $filename] +} + +# Sanitize a host file name, without making it absolute or resolving +# symlinks. On native Windows, this normalizes slashes to forward +# slashes, and makes sure that if the file name starts with a drive +# letter, it is upper case. On other systems, it just returns the +# file name unmodified. + +proc host_file_sanitize {filename} { + if {[ishost *-*-mingw*]} { + set filename [normalize_slashes $filename] + + # If the file name starts with a drive letter, uppercase it. + if {[regexp {^([a-zA-Z]):(/.*)?} $filename -> drive rest]} { + set filename "[string toupper $drive]:$rest" + } + } + + return $filename +} + +# Normalize a file name for the build machine. If running native +# Windows GDB, this converts a Windows file name to the corresponding +# Unix filename, per the mount table. For example, this replaces +# 'c:/foo' with '/c/foo' (on MSYS2) or '/cygdrive/c/foo' (on Cygwin). +# On other systems, it just wraps "file normalize". + +proc build_file_normalize {filename} { + if {[ishost *-*-mingw*]} { + set filename [host_file_sanitize $filename] + + # Handle Windows => Unix mount point conversion. We assume + # there are no symlinks to resolve, which is a reasonable + # assumption for native Windows testing. + + # Get Windows => Unix map. + lassign [get_mount_point_map] win_to_unix _ + + foreach {win_filename unix_filename} $win_to_unix { + if {[string equal -length [string length $win_filename] \ + $win_filename $filename]} { + set rest [string range $filename \ + [string length $win_filename] end] + return "${unix_filename}$rest" + } + } + } + + return [file normalize $filename] +} + +# Normalize a file name for the host machine and native Windows GDB. +# This converts a Unix file name to a Windows filename, +# per the mount table. E.g., '/c/foo' (on MSYS2) or '/cygdrive/c/foo' +# (on Cygwin) is converted to 'c:/foo'. + +proc host_file_normalize_mingw {filename unix_to_win} { + set filename [host_file_sanitize $filename] + + # If the file name already starts with a drive letter (e.g., + # C:/foo), we're done. Don't let it fallthrough to "file + # normalize", which would misinterpret it as a relative file + # name. + if {[regexp {^[A-Z]:/} $filename]} { + return $filename + } + + # Collapse all repeated forward slashes. + set filename [regsub -all {//+} $filename {/}] + + # Strip trailing slash, except for root. + if {$filename ne "/" && [string match */ $filename]} { + set filename [string range $filename 0 end-1] + } + + foreach {unix_filename win_filename} $unix_to_win { + set mount_len [string length $unix_filename] + if {[string equal -length $mount_len $unix_filename $filename]} { + if {$unix_filename eq "/"} { + if {$filename eq "/"} { + return "$win_filename" + } else { + return "$win_filename$filename" + } + } elseif {[string length $filename] == $mount_len} { + # Like "cygpath -ma" if the file name resolves to a + # drive letter, append a slash, to make it unambiguous + # that we resolved to the root of the drive and not + # the drive's current directory. + if {[string match {[A-Za-z]:} $win_filename]} { + return "$win_filename/" + } else { + return "$win_filename" + } + } elseif {[string index $filename $mount_len] eq "/"} { + set rest [string range $filename $mount_len end] + return "$win_filename$rest" + } + } + } + + return [file normalize $filename] +} + +# Normalize a file name for the host machine. If running native +# Windows GDB, this converts a Unix file name to a Windows filename, +# per the mount table. E.g., '/c/foo' (on MSYS2) or '/cygdrive/c/foo' +# (on Cygwin) is converted to 'c:/foo'. + +proc host_file_normalize {filename} { + if {[ishost *-*-mingw*]} { + # Get Unix => Windows map. + lassign [get_mount_point_map] _ unix_to_win + return [host_file_normalize_mingw $filename $unix_to_win] + } + + return [file normalize $filename] +} + +# Wrapper around "file join" that handles host-specific details. +# +# For Cygwin/MSYS2's Tcl, file names that start with a drive letter +# are not considered absolute file names, thus 'file join "c:/" "d:/"' +# returns "c:/d:". This procedure thus detects absolute Windows-style +# file names, and treats them as absolute, bypassing "file join". + +proc host_file_join {args} { + if {[isbuild *-*-mingw*]} { + set result "" + foreach filename $args { + set filename [host_file_sanitize $filename] + + # If the file name starts with drive letter and colon + # (e.g., "C:/"), treat it as absolute. + if {[regexp {^[A-Z]:/} $filename]} { + set result $filename + } else { + set result [file join $result $filename] + } + } + return $result + } else { + return [file join {*}$args] + } +} + proc gdb_reinitialize_dir { subdir } { global gdb_prompt @@ -2266,7 +2469,8 @@ proc gdb_reinitialize_dir { subdir } { } gdb_expect 60 { -re "Source directories searched.*$gdb_prompt $" { - send_gdb "dir $subdir\n" + set dir [host_file_normalize $subdir] + send_gdb "dir $dir\n" gdb_expect 60 { -re "Source directories searched.*$gdb_prompt $" { verbose "Dir set to $subdir" @@ -4294,6 +4498,76 @@ gdb_caching_proc allow_tsx_tests {} { return $allow_tsx_tests } +# Run a test on the target to check if it supports x86 shadow stack. Return 1 +# if shadow stack is enabled, 0 otherwise. + +gdb_caching_proc allow_ssp_tests {} { + global srcdir subdir gdb_prompt hex + + set me "allow_ssp_tests" + + if { ![istarget i?86-*-*] && ![istarget x86_64-*-* ] } { + verbose "$me: target known to not support shadow stack." + return 0 + } + + # There is no need to check the actual HW in addition to ptrace support. + # We need both checks and ptrace will tell us about the HW state. + set compile_flags "{additional_flags=-fcf-protection=return}" + set src { int main() { return 0; } } + if {![gdb_simple_compile $me $src executable $compile_flags]} { + return 0 + } + + save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + # No error message, compilation succeeded so now run it via gdb. + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + gdb_load $obj + if {![runto_main]} { + remote_file build delete $obj + return 0 + } + set shadow_stack_disabled_re "(<unavailable>)" + if {[istarget *-*-linux*]} { + # Starting with v6.6, the Linux kernel supports CET shadow stack. + # Dependent on the target we can see a nullptr or "<unavailable>" + # when shadow stack is supported by HW and the Linux kernel but + # not enabled for the current thread (for example due to a lack + # of compiler or glibc support for -fcf-protection). + set shadow_stack_disabled_re "$shadow_stack_disabled_re|(.*0x0)" + } + + set allow_ssp_tests 0 + gdb_test_multiple "print \$pl3_ssp" "test shadow stack support" { + -re -wrap "(.*$hex)((?!(.*0x0)).)" { + verbose -log "$me: Shadow stack support detected." + set allow_ssp_tests 1 + } + -re -wrap $shadow_stack_disabled_re { + # In case shadow stack is not enabled (for example due to a + # lack of compiler or glibc support for -fcf-protection). + verbose -log "$me: Shadow stack is not enabled." + } + -re -wrap "void" { + # In case we don't have hardware or kernel support. + verbose -log "$me: No shadow stack support." + } + } + + gdb_exit + } + + remote_file build delete $obj + + verbose "$me: returning $allow_ssp_tests" 2 + return $allow_ssp_tests +} + # Run a test on the target to see if it supports avx512bf16. Return 1 if so, # 0 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. @@ -4436,7 +4710,8 @@ gdb_caching_proc allow_lam_tests {} { # No error message, compilation succeeded so now run it via gdb. set allow_lam_tests 0 - clean_restart $obj + clean_restart + gdb_load $obj gdb_run_cmd gdb_expect { -re ".*$inferior_exited_re with code.*${gdb_prompt} $" { @@ -4779,7 +5054,8 @@ gdb_caching_proc aarch64_initialize_sve_information { } { return [array get supported_vl] } - clean_restart $test_exec + clean_restart + gdb_load $test_exec if {![runto_main]} { return [array get supported_vl] @@ -4936,7 +5212,8 @@ gdb_caching_proc aarch64_initialize_sme_information { } { return [array get supported_svl] } - clean_restart $test_exec + clean_restart + gdb_load $test_exec if {![runto_main]} { return [array get supported_svl] @@ -5030,7 +5307,8 @@ gdb_caching_proc allow_aarch64_mops_tests {} { } # Compilation succeeded so now run it via gdb. - clean_restart $obj + clean_restart + gdb_load $obj gdb_run_cmd gdb_expect { -re ".*$inferior_exited_re with code 01.*${gdb_prompt} $" { @@ -5053,6 +5331,57 @@ gdb_caching_proc allow_aarch64_mops_tests {} { return $allow_mops_tests } +# Run a test on the target to see if it supports AArch64 GCS extensions. +# Return 1 if so, 0 if it does not. Note this causes a restart of GDB. + +gdb_caching_proc allow_aarch64_gcs_tests {} { + global srcdir subdir gdb_prompt inferior_exited_re + + set me "allow_aarch64_gcs_tests" + + if { ![is_aarch64_target]} { + return 0 + } + + # Compile a program that tests the GCS feature. + set src { + #include <stdbool.h> + #include <sys/auxv.h> + + /* Feature check for Guarded Control Stack. */ + #ifndef HWCAP_GCS + #define HWCAP_GCS (1UL << 32) + #endif + + int main (void) { + bool gcs_supported = getauxval (AT_HWCAP) & HWCAP_GCS; + + /* Return success if GCS is supported. */ + return !gcs_supported; + } + } + + if {![gdb_simple_compile $me $src executable]} { + return 0 + } + + # Compilation succeeded so now run it via gdb. + set allow_gcs_tests 0 + clean_restart $obj + gdb_run_cmd + gdb_expect { + -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { + verbose -log "\n$me: gcs support detected" + set allow_gcs_tests 1 + } + } + gdb_exit + remote_file build delete $obj + + verbose "$me: returning $allow_gcs_tests" 2 + return $allow_gcs_tests +} + # A helper that compiles a test case to see if __int128 is supported. proc gdb_int128_helper {lang} { return [gdb_can_simple_compile "i128-for-$lang" { @@ -6311,6 +6640,9 @@ proc gdb_compile {source dest type options} { } } + # Automatically handle includes in testsuite/lib/. + auto_lappend_include_files options $source + cond_wrap [expr $pie != -1 || $nopie != -1] \ with_PIE_multilib_flags_filtered { set result [target_compile $source $dest $type $options] @@ -6846,7 +7178,20 @@ gdb_caching_proc can_spawn_for_attach {} { set me "can_spawn_for_attach" set src { - #include <unistd.h> + #ifdef _WIN32 + # include <windows.h> + #else + # include <unistd.h> + #endif + + #ifdef _WIN32 + unsigned + sleep (unsigned seconds) + { + Sleep (seconds * 1000); + return 0; + } + #endif int main (void) @@ -7457,13 +7802,13 @@ proc clean_standard_output_dir {} { } # Directory containing the standard output files. - set standard_output_dir [file normalize [standard_output_file ""]] + set standard_output_dir [build_standard_output_file ""] # Ensure that standard_output_dir is clean, or only contains # gdb.log / gdb.sum. set log_file_info [split [log_file -info]] set log_file [file normalize [lindex $log_file_info end]] - if { $log_file == [file normalize [standard_output_file gdb.log]] } { + if { $log_file == [file normalize [build_standard_output_file gdb.log]] } { # Dir already contains active gdb.log. Don't remove the dir, but # check that it's clean otherwise. set res [glob -directory $standard_output_dir -tails *] @@ -7718,22 +8063,39 @@ proc make_gdb_parallel_path { args } { } # Turn BASENAME into a full file name in the standard output -# directory. It is ok if BASENAME is the empty string; in this case -# the directory is returned. +# directory, as seen from the build machine. I.e., as seen from the +# system driving DejaGnu. (E.g., if DejaGnu is being driven by MSYS2 +# to test native Windows GDB, the "build" file names should be file +# names TCL understands, i.e., Unix file names.) It is OK if BASENAME +# is the empty string; in this case the directory is returned. -proc standard_output_file {basename} { +proc build_standard_output_file {basename} { global objdir subdir gdb_test_file_name set dir [make_gdb_parallel_path outputs $subdir $gdb_test_file_name] file mkdir $dir - # If running on MinGW, replace /c/foo with c:/foo - if { [ishost *-*-mingw*] } { - set dir [exec sh -c "cd ${dir} && pwd -W"] - } return [file join $dir $basename] } -# Turn BASENAME into a file name on host. +# Turn BASENAME into a full file name in the standard output +# directory, as seen from a non-remote host. I.e., assuming the build +# and the host share the filesystem. E.g., if DejaGnu is being driven +# by MSYS2 to test native Windows GDB, the "host" file names should be +# file names GDB understands, i.e., Windows file names. It is OK if +# BASENAME is the empty string; in this case the directory is +# returned. + +proc standard_output_file {basename} { + global objdir subdir gdb_test_file_name + + set dir [make_gdb_parallel_path outputs $subdir $gdb_test_file_name] + file mkdir $dir + set dir [host_file_normalize $dir] + return [host_file_join $dir $basename] +} + +# Like standard_output_file, but handles remote hosts. Turn BASENAME +# into a file name on (potentially remote) host. proc host_standard_output_file { basename } { if { [is_remote host] } { @@ -9237,7 +9599,8 @@ gdb_caching_proc target_endianness {} { return 0 } - clean_restart $obj + clean_restart + gdb_load $obj if ![runto_main] { return 0 } @@ -9363,7 +9726,13 @@ proc remove_core {pid {test ""}} { } } -proc core_find {binfile {deletefiles {}} {arg ""}} { +# Runs ${binfile} expecting it to crash and generate a core file. +# If DELETEFILES is provided, remove these files after running the program. +# If ARG is provided, pass it as a command line argument to the program. +# If OUTPUT_FILE is provided, save the program output to it. +# Returns the name of the core dump, or empty string if not found. + +proc core_find {binfile {deletefiles {}} {arg ""} {output_file "/dev/null"}} { global objdir subdir set destcore "$binfile.core" @@ -9385,7 +9754,7 @@ proc core_find {binfile {deletefiles {}} {arg ""}} { set found 0 set coredir [standard_output_file coredir.[getpid]] file mkdir $coredir - catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\"" + catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >${output_file} 2>&1\"" # remote_exec host "${binfile}" set binfile_basename [file tail $binfile] foreach i [list \ @@ -9476,7 +9845,8 @@ gdb_caching_proc target_supports_scheduler_locking {} { return 0 } - clean_restart $obj + clean_restart + gdb_load $obj if ![runto_main] { return 0 } @@ -10971,7 +11341,8 @@ gdb_caching_proc have_epilogue_line_info {} { return False } - clean_restart $obj + clean_restart + gdb_load $obj gdb_test_multiple "info line 6" "epilogue test" { -re -wrap ".*starts at address.*and ends at.*" { @@ -11114,6 +11485,66 @@ proc lappend_include_file { flags file } { } } +# Helper for auto_lappend_include_files that handles one source file, +# and tracks the list of already-visited files. + +proc auto_lappend_include_files_1 {flags source {visited {}}} { + upvar $flags up_flags + upvar $visited up_visited + global srcdir + + set ext [string tolower [file extension $source]] + if {$ext ni {".c" ".cpp" ".cc" ".h" ".s"}} { + return + } + + if {[catch {open $source r} fh err]} { + error "Failed to open file '$source': $err" + } + set contents [read $fh] + close $fh + + lappend up_visited $source + + # Match lines like: + # #include "gdb_foo.h" + set re "^\\s*#include\\s+\"(.*)\"" + + foreach line [split $contents "\n"] { + if {[regexp $re $line -> basename]} { + set lib_file "$srcdir/lib/$basename" + + # If already processed, skip. + if {[lsearch -exact $up_visited $lib_file] != -1} { + continue + } + + if {![file exists $lib_file]} { + continue + } + + # Append to include list, and recurse into the included + # file. + lappend_include_file up_flags $lib_file + auto_lappend_include_files_1 up_flags $lib_file up_visited + } + } +} + +# Automatically handle includes under gdb/testsuite/lib/. +# +# For each source file in SOURCES, look for #include directives +# including files that live in testsuite/lib/. For each such included +# file, call lappend_include_file for it. + +proc auto_lappend_include_files {flags sources} { + upvar $flags up_flags + set visited {} + foreach src $sources { + auto_lappend_include_files_1 up_flags $src visited + } +} + # Return a list of supported host locales. gdb_caching_proc host_locales { } { @@ -11225,5 +11656,46 @@ gdb_caching_proc have_builtin_trap {} { } executable] } +# Return 1 if there is a startup shell. Return -1 if there's no startup shell. +# Return -1 otherwise. + +gdb_caching_proc have_startup_shell {} { + if { [is_remote target] } { + # For remote debugging targets, there is no guarantee that a "shell" + # is used. + return -1 + } + + + gdb_exit + gdb_start + + set re_on \ + [string_to_regexp "Use of shell to start subprocesses is on."] + set re_off \ + [string_to_regexp "Use of shell to start subprocesses is off."] + set re_cmd_unsupported \ + [string_to_regexp \ + {Undefined show command: "startup-with-shell". Try "help show".}] + + set supported -1 + gdb_test_multiple "show startup-with-shell" "" { + -re -wrap $re_on { + set supported 1 + } + -re -wrap $re_off { + set supported 0 + } + -re -wrap $re_cmd_unsupported { + } + -re -wrap "" { + } + } + + gdb_exit + + return $supported +} + # Always load compatibility stuff. load_lib future.exp diff --git a/gdb/testsuite/lib/gdb_watchdog.h b/gdb/testsuite/lib/gdb_watchdog.h new file mode 100644 index 0000000..15d63e7 --- /dev/null +++ b/gdb/testsuite/lib/gdb_watchdog.h @@ -0,0 +1,75 @@ +/* This file 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/>. */ + +/* Set a watchdog that aborts the testcase after a timeout. */ + +#ifndef GDB_WATCHDOG_H +#define GDB_WATCHDOG_H + +/* Forward declaration to make sure the definitions have the right + prototype, at least in C. */ +static void gdb_watchdog (unsigned int seconds); + +static const char _gdb_watchdog_msg[] + = "gdb_watchdog: timeout expired - aborting test\n"; + +#ifdef _WIN32 +#include <windows.h> +#include <stdlib.h> +#include <stdio.h> + +static VOID CALLBACK +_gdb_watchdog_timer_routine (PVOID lpParam, BOOLEAN TimerOrWaitFired) +{ + fputs (_gdb_watchdog_msg, stderr); + abort (); +} + +static void +gdb_watchdog (unsigned int seconds) +{ + HANDLE timer; + + if (!CreateTimerQueueTimer (&timer, NULL, + _gdb_watchdog_timer_routine, NULL, + seconds * 1000, 0, 0)) + abort (); +} + +#else /* POSIX systems */ + +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> + +static void +_gdb_sigalrm_handler (int signo) +{ + write (2, _gdb_watchdog_msg, sizeof (_gdb_watchdog_msg) - 1); + abort (); +} + +static void +gdb_watchdog (unsigned int seconds) +{ + signal (SIGALRM, _gdb_sigalrm_handler); + alarm (seconds); +} + +#endif + +#endif /* GDB_WATCHDOG_H */ diff --git a/gdb/testsuite/lib/selftest-support.exp b/gdb/testsuite/lib/selftest-support.exp index e037664..af8ec6f 100644 --- a/gdb/testsuite/lib/selftest-support.exp +++ b/gdb/testsuite/lib/selftest-support.exp @@ -16,7 +16,7 @@ # Find a pathname to a file that we would execute if the shell was asked # to run $arg using the current PATH. -proc find_gdb { arg } { +proc _selftest_find_gdb { arg } { # If the arg directly specifies an existing executable file, then # simply use it. @@ -36,65 +36,163 @@ proc find_gdb { arg } { return $arg } -# A helper proc that sets up for self-testing. -# EXECUTABLE is the gdb to use. -# FUNCTION is the function to break in, either captured_main -# or captured_command_loop. -# Return 0 in case of success, -1 in case of failure, and -2 in case of -# skipping the test-case. +# Return true if the GDB under test is installed (as opposed to a GDB in its +# build directory). -proc selftest_setup { executable function } { - global gdb_prompt - global INTERNAL_GDBFLAGS +proc _selftest_gdb_is_installed {} { + # If GDB_DATA_DIRECTORY is empty, assume that it is an installed GDB. It is + # not a perfectly accurate check, but should be good enough. + return [expr {"$::GDB_DATA_DIRECTORY" == ""}] +} + +# Return true if the libtool binary is present on the host. + +proc _selftest_has_libtool {} { + lassign [remote_exec host "sh -c \"command -v libtool\""] status output + return [expr {$status == 0}] +} + +# If GDB is executed from a build tree, run libtool to obtain the real +# executable path for EXECUTABLE, which may be a libtool wrapper. Return +# the path on success. On failure, issue an UNTESTED test result and return +# an empty string. +# +# If GDB is executed from an installed location, return EXECUTABLE unchanged. +# +# If libtool is not present on the host system, return EXECUTABLE unchanged. +# The test might still work, because the GDB binary is not always a libtool +# wrapper. + +proc selftest_libtool_get_real_gdb_executable { executable } { + if [_selftest_gdb_is_installed] { + return $executable + } - # load yourself into the debugger + if ![_selftest_has_libtool] { + return $executable + } + + lassign [remote_exec host libtool "--mode=execute echo -n $executable"] \ + status executable + + if { $status != 0 } { + untested "failed to run libtool" + return "" + } - global gdb_file_cmd_debug_info - set gdb_file_cmd_debug_info "unset" + return $executable +} + +# Return true if EXECUTABLE has debug info. +# +# If it doesn't, or if it's not possible to determine, issue an UNTESTED test +# result and return false. + +proc _selftest_check_executable_debug_info { executable } { + set ::gdb_file_cmd_debug_info "unset" + set result true + + # On Cygwin (at least), gdb/gdb.exe is a libtool wrapper (which happens to + # be a PE executable). The real binary is gdb/.libs/gdb.exe. If we load + # gdb/gdb.exe, we won't see any debug info and conclude that we can't run + # the test. Obtain the real executable path using libtool. + # + # At the time of writing, we don't see a libtool wrapper generated on Linux. + # But if there was one, it would be a shell script, and it would not be + # possible to load it in gdb. This conversion would therefore also be + # necessary. + # + # If testing against an installed GDB, then there won't be a libtool + # wrapper, no need to convert. + set executable [selftest_libtool_get_real_gdb_executable $executable] + + if { $executable == "" } { + # selftest_libtool_get_real_gdb_executable already records an UNTESTED + # on failure. + return false + } - set result [gdb_load $executable] + gdb_start - if {$result != 0} { - return -1 + if {[gdb_load $executable] != 0} { + untested "failed to load executable when checking for debug info" + set result false } - if {$gdb_file_cmd_debug_info != "debug"} { + if {$::gdb_file_cmd_debug_info != "debug"} { untested "no debug information, skipping testcase." - return -2 + set result false } - # Set a breakpoint at $function. + gdb_exit + + return $result +} + +# A helper proc that sets up for self-testing. +# +# Assumes that the inferior GDB is already loaded in the top-level GDB. +# +# Return 0 in case of success, -1 in case of failure, and -2 in case of +# skipping the test-case. + +proc _selftest_setup { } { + global gdb_prompt + global INTERNAL_GDBFLAGS + + # Set a breakpoint at main + set function main if { [gdb_breakpoint $function "no-message"] != 1 } { untested "Cannot set breakpoint at $function, skipping testcase." return -2 } + # Debugging on Windows shows random threads starting and exiting, + # interfering with the tests. Disable them, since they are not useful here. + gdb_test_no_output "set print thread-events off" + # run yourself set description "run until breakpoint at $function" + set re_hs {[^\r\n]+} + set re_args [string cat \ + [string_to_regexp "("] \ + $re_hs \ + [string_to_regexp ")"]] + set re_pass \ + [multi_line \ + "Starting program: $re_hs" \ + ".*" \ + [string cat "Breakpoint $::decimal, $function $re_args at" \ + " ${re_hs}gdb.c:$re_hs"] \ + ".*"] + set re_xfail \ + [multi_line \ + "Starting program: $re_hs" \ + ".*" \ + "Breakpoint $::decimal, $function $re_args$re_hs" \ + ".*"] gdb_test_multiple "run $INTERNAL_GDBFLAGS" "$description" { - -re "Starting program.*Breakpoint \[0-9\]+,.*$function \\(.*\\).* at .*main.c:.*$gdb_prompt $" { - pass "$description" - } - -re "Starting program.*Breakpoint \[0-9\]+,.*$function \\(.*\\).*$gdb_prompt $" { - xfail "$description (line numbers scrambled?)" - } - -re "vfork: No more processes.*$gdb_prompt $" { - fail "$description (out of virtual memory)" - return -1 - } - -re ".*$gdb_prompt $" { - fail "$description" - return -1 - } + -re -wrap $re_pass { + pass $description + } + -re -wrap $re_xfail { + xfail "$description (line numbers scrambled?)" + } + -re -wrap "vfork: No more processes.*" { + fail "$description (out of virtual memory)" + return -1 + } + -re -wrap "" { + fail $description + return -1 + } } return 0 } -# Prepare for running a self-test by moving the GDB executable to a -# location where we can use it as the inferior. Return the filename -# of the new location. +# Return the location of the gdb executable to test. # # If the current testing setup is not suitable for running a # self-test, then return an empty string. @@ -114,52 +212,54 @@ proc selftest_prepare {} { # ... or with a stub-like server? I.e., gdbserver + "target # remote"? In that case we won't be able to pass command line - # arguments to GDB, and selftest_setup wants to do exactly that. + # arguments to GDB, and _selftest_setup wants to do exactly that. if [use_gdb_stub] { return } - # Run the test with self. Copy the file executable file in case - # this OS doesn't like to edit its own text space. - - set gdb_fullpath [find_gdb $::GDB] - - if {[is_remote host]} { - set xgdb x$::tool - } else { - set xgdb [standard_output_file x$::tool] - } - - # Remove any old copy lying around. - remote_file host delete $xgdb - - set filename [remote_download host $gdb_fullpath $xgdb] - - return $filename + return [_selftest_find_gdb $::GDB] } # A simple way to run some self-tests. -proc do_self_tests {function body} { +proc do_self_tests {body} { set file [selftest_prepare] if { $file eq "" } { return } - gdb_start + # Check if the gdb executable has debug info. + if { ![_selftest_check_executable_debug_info $file] } { + return + } + + # FILE might be a libtool wrapper. In order to debug the real thing, pass + # FILE on the command-line of the top-level gdb, and run under + # `libtool --mode=execute. libtool will replace FILE with the path to the + # real executable and set any path required for it to find its dependent + # libraries. + # + # If testing against an installed GDB, there won't be a libtool wrapper. + save_vars { ::GDB ::GDBFLAGS } { + if { ![_selftest_gdb_is_installed] && [_selftest_has_libtool] } { + set ::GDB "libtool --mode=execute $::GDB" + } + + set ::GDBFLAGS "$::GDBFLAGS $file" + gdb_start + } # When debugging GDB with GDB, some operations can take a relatively long # time, especially if the build is non-optimized. Bump the timeout for the # duration of the test. with_timeout_factor 10 { - set result [selftest_setup $file $function] + set result [_selftest_setup] if {$result == 0} { set result [uplevel $body] } } gdb_exit - catch "remote_file host delete $file" if {$result == -1} { warning "Couldn't test self" diff --git a/gdb/testsuite/lib/tuiterm.exp b/gdb/testsuite/lib/tuiterm.exp index b83b8af..e1af223 100644 --- a/gdb/testsuite/lib/tuiterm.exp +++ b/gdb/testsuite/lib/tuiterm.exp @@ -33,1331 +33,1690 @@ namespace eval Term { variable _resize_count - proc _log { what } { - verbose "+++ $what" - } + variable _TERM + set _TERM "" - # Call BODY, then log WHAT along with the original and new cursor position. - proc _log_cur { what body } { - variable _cur_row - variable _cur_col + variable _alternate + variable _alternate_setup + set _alternate 0 + set _alternate_setup 0 +} - set orig_cur_row $_cur_row - set orig_cur_col $_cur_col +proc Term::_log { what } { + verbose "+++ $what" +} - set code [catch {uplevel $body} result] +# Call BODY, then log WHAT along with the original and new cursor position. +proc Term::_log_cur { what body } { + variable _cur_row + variable _cur_col - _log "$what, cursor: ($orig_cur_row, $orig_cur_col) -> ($_cur_row, $_cur_col)" + set orig_cur_row $_cur_row + set orig_cur_col $_cur_col - if { $code == 1 } { - global errorInfo errorCode - return -code $code -errorinfo $errorInfo -errorcode $errorCode $result - } else { - return -code $code $result - } - } + set code [catch {uplevel $body} result] - # If ARG is empty, return DEF: otherwise ARG. This is useful for - # defaulting arguments in CSIs. - proc _default {arg def} { - if {$arg == ""} { - return $def - } - return $arg + _log "$what, cursor: ($orig_cur_row, $orig_cur_col) -> ($_cur_row, $_cur_col)" + + if { $code == 1 } { + global errorInfo errorCode + return -code $code -errorinfo $errorInfo -errorcode $errorCode $result + } else { + return -code $code $result } +} - # Erase in the line Y from SX to just before EX. - proc _clear_in_line {sx ex y} { - variable _attrs - variable _chars - set lattr [array get _attrs] - while {$sx < $ex} { - set _chars($sx,$y) [list " " $lattr] - incr sx - } +# If ARG is empty, return DEF: otherwise ARG. This is useful for +# defaulting arguments in CSIs. +proc Term::_default {arg def} { + if {$arg == ""} { + return $def } + return $arg +} - # Erase the lines from SY to just before EY. - proc _clear_lines {sy ey} { - variable _cols - while {$sy < $ey} { - _clear_in_line 0 $_cols $sy - incr sy - } +# Erase in the line Y from SX to just before EX. +proc Term::_clear_in_line {sx ex y} { + variable _attrs + variable _chars + set lattr [array get _attrs] + while {$sx < $ex} { + set _chars($sx,$y) [list " " $lattr] + incr sx } +} - # Beep. - proc _ctl_0x07 {} { +# Erase the lines from SY to just before EY. +proc Term::_clear_lines {sy ey} { + variable _cols + while {$sy < $ey} { + _clear_in_line 0 $_cols $sy + incr sy } +} + +# Beep. +proc Term::_ctl_0x07 {} { +} + +# Return 1 if tuiterm has the bw/auto_left_margin enabled. +proc Term::_have_bw {} { + return [expr \ + [string equal $Term::_TERM "ansiw"] \ + || [string equal $Term::_TERM "ansis"]] +} - # Return 1 if tuiterm has the bw/auto_left_margin enabled. - proc _have_bw {} { - return [string equal $Term::_TERM "ansiw"] +# Backspace. +proc Term::_ctl_0x08 { {bw -1} } { + if { $bw == -1 } { + set bw [_have_bw] } + _log_cur "Backspace, bw == $bw" { + variable _cur_col + variable _cur_row + variable _cols - # Backspace. - proc _ctl_0x08 { {bw -1} } { - if { $bw == -1 } { - set bw [_have_bw] + if { $_cur_col > 0 } { + # No wrapping needed. + incr _cur_col -1 + return } - _log_cur "Backspace, bw == $bw" { - variable _cur_col - variable _cur_row - variable _cols - if { $_cur_col > 0 } { - # No wrapping needed. - incr _cur_col -1 - return - } + if { ! $bw } { + # Wrapping not enabled. + return + } - if { ! $bw } { - # Wrapping not enabled. - return - } + if { $_cur_row == 0 } { + # Can't wrap. + return + } + + # Wrap to previous line. + set _cur_col [expr $_cols - 1] + incr _cur_row -1 + } +} + +# Linefeed. +proc Term::_ctl_0x0a {} { + _log_cur "Line feed" { + variable _cur_row + variable _rows + variable _cols + variable _chars - if { $_cur_row == 0 } { - # Can't wrap. - return + incr _cur_row 1 + while {$_cur_row >= $_rows} { + # Scroll the display contents. We scroll one line at + # a time here; as _cur_row was only increased by one, + # a single line scroll should be enough to put the + # cursor back on the screen. But we wrap the + # scrolling inside a while loop just to be on the safe + # side. + for {set y 0} {$y < [expr $_rows - 1]} {incr y} { + set next_y [expr $y + 1] + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$y) $_chars($x,$next_y) + } } - # Wrap to previous line. - set _cur_col [expr $_cols - 1] incr _cur_row -1 } } +} - # Linefeed. - proc _ctl_0x0a {} { - _log_cur "Line feed" { - variable _cur_row - variable _rows - variable _cols - variable _chars - - incr _cur_row 1 - while {$_cur_row >= $_rows} { - # Scroll the display contents. We scroll one line at - # a time here; as _cur_row was only increased by one, - # a single line scroll should be enough to put the - # cursor back on the screen. But we wrap the - # scrolling inside a while loop just to be on the safe - # side. - for {set y 0} {$y < [expr $_rows - 1]} {incr y} { - set next_y [expr $y + 1] - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$y) $_chars($x,$next_y) - } - } +# Carriage return. +proc Term::_ctl_0x0d {} { + _log_cur "Carriage return" { + variable _cur_col - incr _cur_row -1 - } - } + set _cur_col 0 } +} - # Carriage return. - proc _ctl_0x0d {} { - _log_cur "Carriage return" { - variable _cur_col +# Designate G0 Character Set, USASCII (ESC ( B) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html (see "ESC ( C", case C = B) +proc Term::_esc_0x28_B {} { + _log "ignored: G0: USASCII" +} - set _cur_col 0 - } - } +# Designate G0 Character Set, DEC Special Character and Line Drawing Set (ESC ( 0) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html (see "ESC ( C", case C = 0) +proc Term::_esc_0x28_0 {} { + _log "ignored: G0: DEC Special Character and Line Drawing Set" +} + +# DECKPAM (Application Keypad, ESC =) +# +# https://vt100.net/docs/vt510-rm/DECKPAM.html +proc Term::_esc_0x3d {} { + _log "ignored: Application Keypad" +} - # Insert Character. - # - # https://vt100.net/docs/vt510-rm/ICH.html - proc _csi_@ {args} { - set n [_default [lindex $args 0] 1] +# DECKPNM (Normal Keypad, ESC >) +# +# https://vt100.net/docs/vt510-rm/DECKPNM.html +proc Term::_esc_0x3e {} { + _log "ignored: Normal Keypad" +} - _log_cur "Insert Character ($n)" { - variable _cur_col - variable _cur_row - variable _cols - variable _chars +# Insert Character. +# +# https://vt100.net/docs/vt510-rm/ICH.html +proc Term::_csi_@ {args} { + set n [_default [lindex $args 0] 1] - # Move characters right of the cursor right by N positions, - # starting with the rightmost one. - for {set in_col [expr $_cols - $n - 1]} {$in_col >= $_cur_col} {incr in_col -1} { - set out_col [expr $in_col + $n] - set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) - } + _log_cur "Insert Character ($n)" { + variable _cur_col + variable _cur_row + variable _cols + variable _chars - # Write N blank spaces starting from the cursor. - _clear_in_line $_cur_col [expr $_cur_col + $n] $_cur_row + # Move characters right of the cursor right by N positions, + # starting with the rightmost one. + for {set in_col [expr $_cols - $n - 1]} {$in_col >= $_cur_col} {incr in_col -1} { + set out_col [expr $in_col + $n] + set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) } - } - # Horizontal Position Absolute. - # - # https://vt100.net/docs/vt510-rm/HPA.html - proc _csi_` {args} { - # Same as Cursor Horizontal Absolute. - return [Term::_csi_G {*}$args] + # Write N blank spaces starting from the cursor. + _clear_in_line $_cur_col [expr $_cur_col + $n] $_cur_row } +} + +# Horizontal Position Absolute. +# +# https://vt100.net/docs/vt510-rm/HPA.html +proc Term::_csi_` {args} { + # Same as Cursor Horizontal Absolute. + return [Term::_csi_G {*}$args] +} - # Cursor Up. - # - # https://vt100.net/docs/vt510-rm/CUU.html - proc _csi_A {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Up. +# +# https://vt100.net/docs/vt510-rm/CUU.html +proc Term::_csi_A {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Up ($arg)" { - variable _cur_row + _log_cur "Cursor Up ($arg)" { + variable _cur_row - set _cur_row [expr {max ($_cur_row - $arg, 0)}] - } + set _cur_row [expr {max ($_cur_row - $arg, 0)}] } +} - # Cursor Down. - # - # https://vt100.net/docs/vt510-rm/CUD.html - proc _csi_B {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Down. +# +# https://vt100.net/docs/vt510-rm/CUD.html +proc Term::_csi_B {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Down ($arg)" { - variable _cur_row - variable _rows + _log_cur "Cursor Down ($arg)" { + variable _cur_row + variable _rows - set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] - } + set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] } +} - # Cursor Forward. - # - # https://vt100.net/docs/vt510-rm/CUF.html - proc _csi_C {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Forward. +# +# https://vt100.net/docs/vt510-rm/CUF.html +proc Term::_csi_C {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Forward ($arg)" { - variable _cur_col - variable _cols + _log_cur "Cursor Forward ($arg)" { + variable _cur_col + variable _cols - set _cur_col [expr {min ($_cur_col + $arg, $_cols - 1)}] - } + set _cur_col [expr {min ($_cur_col + $arg, $_cols - 1)}] } +} - # Cursor Backward. - # - # https://vt100.net/docs/vt510-rm/CUB.html - proc _csi_D {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Backward. +# +# https://vt100.net/docs/vt510-rm/CUB.html +proc Term::_csi_D {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Backward ($arg)" { - variable _cur_col + _log_cur "Cursor Backward ($arg)" { + variable _cur_col - set _cur_col [expr {max ($_cur_col - $arg, 0)}] - } + set _cur_col [expr {max ($_cur_col - $arg, 0)}] } +} - # Cursor Next Line. - # - # https://vt100.net/docs/vt510-rm/CNL.html - proc _csi_E {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Next Line. +# +# https://vt100.net/docs/vt510-rm/CNL.html +proc Term::_csi_E {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Next Line ($arg)" { - variable _cur_col - variable _cur_row - variable _rows + _log_cur "Cursor Next Line ($arg)" { + variable _cur_col + variable _cur_row + variable _rows - set _cur_col 0 - set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] - } + set _cur_col 0 + set _cur_row [expr {min ($_cur_row + $arg, $_rows - 1)}] } +} - # Cursor Previous Line. - # - # https://vt100.net/docs/vt510-rm/CPL.html - proc _csi_F {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Previous Line. +# +# https://vt100.net/docs/vt510-rm/CPL.html +proc Term::_csi_F {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Previous Line ($arg)" { - variable _cur_col - variable _cur_row - variable _rows + _log_cur "Cursor Previous Line ($arg)" { + variable _cur_col + variable _cur_row + variable _rows - set _cur_col 0 - set _cur_row [expr {max ($_cur_row - $arg, 0)}] - } + set _cur_col 0 + set _cur_row [expr {max ($_cur_row - $arg, 0)}] } +} - # Cursor Horizontal Absolute. - # - # https://vt100.net/docs/vt510-rm/CHA.html - proc _csi_G {args} { - set arg [_default [lindex $args 0] 1] +# Cursor Horizontal Absolute. +# +# https://vt100.net/docs/vt510-rm/CHA.html +proc Term::_csi_G {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Cursor Horizontal Absolute ($arg)" { - variable _cur_col - variable _cols + _log_cur "Cursor Horizontal Absolute ($arg)" { + variable _cur_col + variable _cols - set _cur_col [expr {min ($arg, $_cols)} - 1] - } + set _cur_col [expr {min ($arg, $_cols)} - 1] } +} - # Cursor Position. - # - # https://vt100.net/docs/vt510-rm/CUP.html - proc _csi_H {args} { - set row [_default [lindex $args 0] 1] - set col [_default [lindex $args 1] 1] +# Cursor Position. +# +# https://vt100.net/docs/vt510-rm/CUP.html +proc Term::_csi_H {args} { + set row [_default [lindex $args 0] 1] + set col [_default [lindex $args 1] 1] - _log_cur "Cursor Position ($row, $col)" { - variable _cur_col - variable _cur_row + _log_cur "Cursor Position ($row, $col)" { + variable _cur_col + variable _cur_row - set _cur_row [expr {$row - 1}] - set _cur_col [expr {$col - 1}] - } + set _cur_row [expr {$row - 1}] + set _cur_col [expr {$col - 1}] } +} - # Cursor Horizontal Forward Tabulation. - # - # https://vt100.net/docs/vt510-rm/CHT.html - proc _csi_I {args} { - set n [_default [lindex $args 0] 1] +# Cursor Horizontal Forward Tabulation. +# +# https://vt100.net/docs/vt510-rm/CHT.html +proc Term::_csi_I {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Cursor Horizontal Forward Tabulation ($n)" { - variable _cur_col - variable _cols + _log_cur "Cursor Horizontal Forward Tabulation ($n)" { + variable _cur_col + variable _cols - incr _cur_col [expr {$n * 8 - $_cur_col % 8}] - if {$_cur_col >= $_cols} { - set _cur_col [expr {$_cols - 1}] - } + incr _cur_col [expr {$n * 8 - $_cur_col % 8}] + if {$_cur_col >= $_cols} { + set _cur_col [expr {$_cols - 1}] } } +} - # Erase in Display. - # - # https://vt100.net/docs/vt510-rm/ED.html - proc _csi_J {args} { - set arg [_default [lindex $args 0] 0] - - _log_cur "Erase in Display ($arg)" { - variable _cur_col - variable _cur_row - variable _rows - variable _cols - - if {$arg == 0} { - # Cursor (inclusive) to end of display. - _clear_in_line $_cur_col $_cols $_cur_row - _clear_lines [expr {$_cur_row + 1}] $_rows - } elseif {$arg == 1} { - # Beginning of display to cursor (inclusive). - _clear_lines 0 $_cur_row - _clear_in_line 0 [expr $_cur_col + 1] $_cur_row - } elseif {$arg == 2} { - # Entire display. - _clear_lines 0 $_rows - } +# Erase in Display. +# +# https://vt100.net/docs/vt510-rm/ED.html +proc Term::_csi_J {args} { + set arg [_default [lindex $args 0] 0] + + _log_cur "Erase in Display ($arg)" { + variable _cur_col + variable _cur_row + variable _rows + variable _cols + + if {$arg == 0} { + # Cursor (inclusive) to end of display. + _clear_in_line $_cur_col $_cols $_cur_row + _clear_lines [expr {$_cur_row + 1}] $_rows + } elseif {$arg == 1} { + # Beginning of display to cursor (inclusive). + _clear_lines 0 $_cur_row + _clear_in_line 0 [expr $_cur_col + 1] $_cur_row + } elseif {$arg == 2} { + # Entire display. + _clear_lines 0 $_rows } } +} - # Erase in Line. - # - # https://vt100.net/docs/vt510-rm/EL.html - proc _csi_K {args} { - set arg [_default [lindex $args 0] 0] +# Erase in Line. +# +# https://vt100.net/docs/vt510-rm/EL.html +proc Term::_csi_K {args} { + set arg [_default [lindex $args 0] 0] - _log_cur "Erase in Line ($arg)" { - variable _cur_col - variable _cur_row - variable _cols + _log_cur "Erase in Line ($arg)" { + variable _cur_col + variable _cur_row + variable _cols - if {$arg == 0} { - # Cursor (inclusive) to end of line. - _clear_in_line $_cur_col $_cols $_cur_row - } elseif {$arg == 1} { - # Beginning of line to cursor (inclusive). - _clear_in_line 0 [expr $_cur_col + 1] $_cur_row - } elseif {$arg == 2} { - # Entire line. - _clear_in_line 0 $_cols $_cur_row - } + if {$arg == 0} { + # Cursor (inclusive) to end of line. + _clear_in_line $_cur_col $_cols $_cur_row + } elseif {$arg == 1} { + # Beginning of line to cursor (inclusive). + _clear_in_line 0 [expr $_cur_col + 1] $_cur_row + } elseif {$arg == 2} { + # Entire line. + _clear_in_line 0 $_cols $_cur_row } } +} - # Insert Line - # - # https://vt100.net/docs/vt510-rm/IL.html - proc _csi_L {args} { - set arg [_default [lindex $args 0] 1] +# Insert Line +# +# https://vt100.net/docs/vt510-rm/IL.html +proc Term::_csi_L {args} { + set arg [_default [lindex $args 0] 1] - _log_cur "Insert Line ($arg)" { - variable _cur_col - variable _cur_row - variable _rows - variable _cols - variable _chars + _log_cur "Insert Line ($arg)" { + variable _cur_col + variable _cur_row + variable _rows + variable _cols + variable _chars - set y [expr $_rows - 2] - set next_y [expr $y + $arg] - while {$y >= $_cur_row} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$next_y) $_chars($x,$y) - } - incr y -1 - incr next_y -1 + set y [expr $_rows - 2] + set next_y [expr $y + $arg] + while {$y >= $_cur_row} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$next_y) $_chars($x,$y) } - - _clear_lines $_cur_row [expr $_cur_row + $arg] + incr y -1 + incr next_y -1 } + + _clear_lines $_cur_row [expr $_cur_row + $arg] } +} - # Delete line. - # - # https://vt100.net/docs/vt510-rm/DL.html - proc _csi_M {args} { - set count [_default [lindex $args 0] 1] +# Delete line. +# +# https://vt100.net/docs/vt510-rm/DL.html +proc Term::_csi_M {args} { + set count [_default [lindex $args 0] 1] - _log_cur "Delete line ($count)" { - variable _cur_row - variable _rows - variable _cols - variable _chars + _log_cur "Delete line ($count)" { + variable _cur_row + variable _rows + variable _cols + variable _chars - set y $_cur_row - set next_y [expr {$y + $count}] - while {$next_y < $_rows} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$y) $_chars($x,$next_y) - } - incr y - incr next_y + set y $_cur_row + set next_y [expr {$y + $count}] + while {$next_y < $_rows} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$y) $_chars($x,$next_y) } - _clear_lines $y $_rows + incr y + incr next_y } + _clear_lines $y $_rows } +} - # Delete Character. - # - # https://vt100.net/docs/vt510-rm/DCH.html - proc _csi_P {args} { - set count [_default [lindex $args 0] 1] - - _log_cur "Delete character ($count)" { - variable _cur_row - variable _cur_col - variable _chars - variable _cols +# Delete Character. +# +# https://vt100.net/docs/vt510-rm/DCH.html +proc Term::_csi_P {args} { + set count [_default [lindex $args 0] 1] - # Move all characters right of the cursor N positions left. - set out_col [expr $_cur_col] - set in_col [expr $_cur_col + $count] + _log_cur "Delete character ($count)" { + variable _cur_row + variable _cur_col + variable _chars + variable _cols - while {$in_col < $_cols} { - set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) - incr in_col - incr out_col - } + # Move all characters right of the cursor N positions left. + set out_col [expr $_cur_col] + set in_col [expr $_cur_col + $count] - # Clear the rest of the line. - _clear_in_line $out_col $_cols $_cur_row + while {$in_col < $_cols} { + set _chars($out_col,$_cur_row) $_chars($in_col,$_cur_row) + incr in_col + incr out_col } + + # Clear the rest of the line. + _clear_in_line $out_col $_cols $_cur_row } +} - # Pan Down - # - # https://vt100.net/docs/vt510-rm/SU.html - proc _csi_S {args} { - set count [_default [lindex $args 0] 1] +# Pan Down +# +# https://vt100.net/docs/vt510-rm/SU.html +proc Term::_csi_S {args} { + set count [_default [lindex $args 0] 1] - _log_cur "Pan Down ($count)" { - variable _cur_col - variable _cur_row - variable _cols - variable _rows - variable _chars + _log_cur "Pan Down ($count)" { + variable _cur_col + variable _cur_row + variable _cols + variable _rows + variable _chars - # The following code is written without consideration for - # the scroll margins. At this time this comment was - # written the tuiterm library doesn't support the scroll - # margins. If/when that changes, then the following will - # need to be updated. + # The following code is written without consideration for + # the scroll margins. At this time this comment was + # written the tuiterm library doesn't support the scroll + # margins. If/when that changes, then the following will + # need to be updated. - set dy 0 - set y $count + set dy 0 + set y $count - while {$y < $_rows} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$dy) $_chars($x,$y) - } - incr y 1 - incr dy 1 + while {$y < $_rows} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$dy) $_chars($x,$y) } - - _clear_lines $dy $_rows + incr y 1 + incr dy 1 } + + _clear_lines $dy $_rows } +} - # Pan Up - # - # https://vt100.net/docs/vt510-rm/SD.html - proc _csi_T {args} { - set count [_default [lindex $args 0] 1] +# Pan Up +# +# https://vt100.net/docs/vt510-rm/SD.html +proc Term::_csi_T {args} { + set count [_default [lindex $args 0] 1] - _log_cur "Pan Up ($count)" { - variable _cur_col - variable _cur_row - variable _cols - variable _rows - variable _chars + _log_cur "Pan Up ($count)" { + variable _cur_col + variable _cur_row + variable _cols + variable _rows + variable _chars - # The following code is written without consideration for - # the scroll margins. At this time this comment was - # written the tuiterm library doesn't support the scroll - # margins. If/when that changes, then the following will - # need to be updated. + # The following code is written without consideration for + # the scroll margins. At this time this comment was + # written the tuiterm library doesn't support the scroll + # margins. If/when that changes, then the following will + # need to be updated. - set y [expr $_rows - $count] - set dy $_rows + set y [expr $_rows - $count] + set dy $_rows - while {$dy >= $count} { - for {set x 0} {$x < $_cols} {incr x} { - set _chars($x,$dy) $_chars($x,$y) - } - incr y -1 - incr dy -1 + while {$dy >= $count} { + for {set x 0} {$x < $_cols} {incr x} { + set _chars($x,$dy) $_chars($x,$y) } - - _clear_lines 0 $count + incr y -1 + incr dy -1 } + + _clear_lines 0 $count } +} - # Erase chars. - # - # https://vt100.net/docs/vt510-rm/ECH.html - proc _csi_X {args} { - set n [_default [lindex $args 0] 1] +# Erase chars. +# +# https://vt100.net/docs/vt510-rm/ECH.html +proc Term::_csi_X {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Erase chars ($n)" { - # Erase characters but don't move cursor. - variable _cur_col - variable _cur_row - variable _attrs - variable _chars + _log_cur "Erase chars ($n)" { + # Erase characters but don't move cursor. + variable _cur_col + variable _cur_row + variable _attrs + variable _chars - set lattr [array get _attrs] - set x $_cur_col - for {set i 0} {$i < $n} {incr i} { - set _chars($x,$_cur_row) [list " " $lattr] - incr x - } + set lattr [array get _attrs] + set x $_cur_col + for {set i 0} {$i < $n} {incr i} { + set _chars($x,$_cur_row) [list " " $lattr] + incr x } } +} - # Cursor Backward Tabulation. - # - # https://vt100.net/docs/vt510-rm/CBT.html - proc _csi_Z {args} { - set n [_default [lindex $args 0] 1] +# Cursor Backward Tabulation. +# +# https://vt100.net/docs/vt510-rm/CBT.html +proc Term::_csi_Z {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Cursor Backward Tabulation ($n)" { - variable _cur_col + _log_cur "Cursor Backward Tabulation ($n)" { + variable _cur_col - set _cur_col [expr {max (int (($_cur_col - 1) / 8) * 8 - ($n - 1) * 8, 0)}] - } + set _cur_col [expr {max (int (($_cur_col - 1) / 8) * 8 - ($n - 1) * 8, 0)}] } +} - # Repeat. - # - # https://www.xfree86.org/current/ctlseqs.html (See `(REP)`) - proc _csi_b {args} { - set n [_default [lindex $args 0] 1] +# Repeat. +# +# https://www.xfree86.org/current/ctlseqs.html (See `(REP)`) +proc Term::_csi_b {args} { + set n [_default [lindex $args 0] 1] - _log_cur "Repeat ($n)" { - variable _last_char + _log_cur "Repeat ($n)" { + variable _last_char - _insert [string repeat $_last_char $n] - } + _insert [string repeat $_last_char $n] } +} - # Vertical Line Position Absolute. - # - # https://vt100.net/docs/vt510-rm/VPA.html - proc _csi_d {args} { - set row [_default [lindex $args 0] 1] +# Vertical Line Position Absolute. +# +# https://vt100.net/docs/vt510-rm/VPA.html +proc Term::_csi_d {args} { + set row [_default [lindex $args 0] 1] - _log_cur "Vertical Line Position Absolute ($row)" { - variable _cur_row - variable _rows + _log_cur "Vertical Line Position Absolute ($row)" { + variable _cur_row + variable _rows - set _cur_row [expr min ($row - 1, $_rows - 1)] - } + set _cur_row [expr min ($row - 1, $_rows - 1)] } +} - # Reset the attributes in attributes array UPVAR_NAME to the default values. - proc _reset_attrs { upvar_name } { - upvar $upvar_name var - array set var { - intensity normal - fg default - bg default - underline 0 - reverse 0 - invisible 0 - blinking 0 +# Set Mode (SM, CSI h) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_h { args } { + foreach item $args { + switch -exact -- $item { + 4 { + # Insert Mode (IRM) + _log "ignored: insert mode" + } + default { + error unsupported + } } } +} - # Translate the color numbers as used in proc _csi_m to a name. - proc _color_attr { n } { - switch -exact -- $n { - 0 { - return black +# Reset Mode (RM, CSI l) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_l { args } { + foreach item $args { + switch -exact -- $item { + 4 { + # Replace Mode (IRM) + _log "ignored: replace mode" + } + default { + error unsupported } + } + } +} + +# Set Scrolling Region (DECSTBM, CSI Ps ; Ps r) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_r { top bottom } { + _log "ignored: set scrolling region" +} + +# Window manipulation (XTWINOPS, CSI Ps ; Ps ; Ps t) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +proc Term::_csi_t { arg1 arg2 arg3 } { + if { $arg1 == 22 && $arg2 == 0 && $arg3 == 0 } { + _log "ignored: Save xterm icon and window title on stack" + return + } + + if { $arg1 == 23 && $arg2 == 0 && $arg3 == 0 } { + _log "ignored: Restore xterm icon and window title from stack" + return + } + + error unsupported +} + +# DECSET (CSI ? h) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +proc Term::_csi_0x3f_h { args } { + foreach item $args { + switch -exact -- $item { 1 { - return red + _log "ignored: Application Cursor Keys" } - 2 { - return green + 7 { + _log "ignored: autowrap mode" } - 3 { - return yellow + 1000 { + _log "ignored: Send Mouse X & Y on button press and release" } - 4 { - return blue + 1006 { + _log "ignored: Enable SGR Mouse Mode" } - 5 { - return magenta + 1049 { + _log "switch to alternate screen" + _set_alternate 1 } - 6 { - return cyan + default { + error unsupported + } + } + } +} + +# DECRST (CSI ? l) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking +proc Term::_csi_0x3f_l { args } { + foreach item $args { + switch -exact -- $item { + 1 { + _log "ignored: Normal Cursor Keys" } 7 { - return white + _log "ignored: no autowrap mode" + } + 1000 { + _log "ignored: Don't send Mouse X & Y on button press and release" + } + 1006 { + _log "ignored: Disable SGR Mouse Mode" + } + 1049 { + _log "switch from alternate screen" + _set_alternate 0 + } + default { + error "unsupported" } - default { error "unsupported color number: $n" } } } +} - # Select Graphic Rendition. - # - # https://vt100.net/docs/vt510-rm/SGR.html - proc _csi_m {args} { - if { [llength $args] == 0 } { - # Apply default. - set args [list 0] +# Reset the attributes in attributes array UPVAR_NAME to the default values. +proc Term::_reset_attrs { upvar_name } { + upvar $upvar_name var + array set var { + intensity normal + fg default + bg default + underline 0 + reverse 0 + invisible 0 + blinking 0 + } +} + +# Translate the color numbers as used in proc _csi_m to a name. +proc Term::_color_attr { n } { + switch -exact -- $n { + 0 { + return black + } + 1 { + return red + } + 2 { + return green } + 3 { + return yellow + } + 4 { + return blue + } + 5 { + return magenta + } + 6 { + return cyan + } + 7 { + return white + } + default { error "unsupported color number: $n" } + } +} - _log_cur "Select Graphic Rendition ([join $args {, }])" { - variable _attrs +# Select Graphic Rendition. +# +# https://vt100.net/docs/vt510-rm/SGR.html +proc Term::_csi_m {args} { + if { [llength $args] == 0 } { + # Apply default. + set args [list 0] + } - foreach item $args { - switch -exact -- $item { - "" - 0 { - _reset_attrs _attrs - } - 1 { - set _attrs(intensity) bold - } - 2 { - set _attrs(intensity) dim - } - 4 { - set _attrs(underline) 1 - } - 5 { - set _attrs(blinking) 1 - } - 7 { - set _attrs(reverse) 1 - } - 8 { - set _attrs(invisible) 1 - } - 22 { - set _attrs(intensity) normal - } - 24 { - set _attrs(underline) 0 - } - 25 { - set _attrs(blinking) 0 - } - 27 { - set _attrs(reverse) 0 - } - 28 { - set _attrs(invisible) 0 - } - 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 { - set _attrs(fg) [_color_attr [expr $item - 30]] - } - 39 { - set _attrs(fg) default - } - 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 { - set _attrs(bg) [_color_attr [expr $item - 40]] - } - 49 { - set _attrs(bg) default - } + _log_cur "Select Graphic Rendition ([join $args {, }])" { + variable _attrs + + foreach item $args { + switch -exact -- $item { + "" - 0 { + _reset_attrs _attrs + } + 1 { + set _attrs(intensity) bold + } + 2 { + set _attrs(intensity) dim + } + 4 { + set _attrs(underline) 1 + } + 5 { + set _attrs(blinking) 1 + } + 7 { + set _attrs(reverse) 1 + } + 8 { + set _attrs(invisible) 1 + } + 22 { + set _attrs(intensity) normal + } + 24 { + set _attrs(underline) 0 + } + 25 { + set _attrs(blinking) 0 + } + 27 { + set _attrs(reverse) 0 + } + 28 { + set _attrs(invisible) 0 + } + 30 - 31 - 32 - 33 - 34 - 35 - 36 - 37 { + set _attrs(fg) [_color_attr [expr $item - 30]] + } + 39 { + set _attrs(fg) default + } + 40 - 41 - 42 - 43 - 44 - 45 - 46 - 47 { + set _attrs(bg) [_color_attr [expr $item - 40]] + } + 49 { + set _attrs(bg) default } } } } +} + +# Request Terminal Parameters (DECREQTPARM) +# +# https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +# https://vt100.net/docs/vt100-ug/chapter3.html +proc Term::_csi_x {} { + # Ignore. +} + +# Insert string at the cursor location. +proc Term::_insert {str} { + _log_cur "Inserted string '$str'" { + _log "Inserting string '$str'" - # Insert string at the cursor location. - proc _insert {str} { - _log_cur "Inserted string '$str'" { - _log "Inserting string '$str'" - - variable _cur_col - variable _cur_row - variable _rows - variable _cols - variable _attrs - variable _chars - set lattr [array get _attrs] - foreach char [split $str {}] { - _log_cur " Inserted char '$char'" { - set _chars($_cur_col,$_cur_row) [list $char $lattr] - incr _cur_col - if {$_cur_col >= $_cols} { - set _cur_col 0 - incr _cur_row - if {$_cur_row >= $_rows} { - error "FIXME scroll" - } + variable _cur_col + variable _cur_row + variable _rows + variable _cols + variable _attrs + variable _chars + set lattr [array get _attrs] + foreach char [split $str {}] { + _log_cur " Inserted char '$char'" { + set _chars($_cur_col,$_cur_row) [list $char $lattr] + incr _cur_col + if {$_cur_col >= $_cols} { + set _cur_col 0 + incr _cur_row + if {$_cur_row >= $_rows} { + error "FIXME scroll" } } } } + + variable _last_char + set _last_char [string index $str end] } +} - # Move the cursor to the (0-based) COL and ROW positions. - proc _move_cursor { col row } { - variable _cols - variable _rows - variable _cur_col - variable _cur_row +# Move the cursor to the (0-based) COL and ROW positions. +proc Term::_move_cursor { col row } { + variable _cols + variable _rows + variable _cur_col + variable _cur_row - if { $col < 0 || $col >= $_cols } { - error "_move_cursor: invalid col value: $col" - } + if { $col < 0 || $col >= $_cols } { + error "_move_cursor: invalid col value: $col" + } - if { $row < 0 || $row >= $_rows } { - error "_move_cursor: invalid row value: $row" - } + if { $row < 0 || $row >= $_rows } { + error "_move_cursor: invalid row value: $row" + } - set _cur_col $col - set _cur_row $row - } + set _cur_col $col + set _cur_row $row +} - # Initialize. - proc _setup {rows cols} { - global stty_init - set stty_init "rows $rows columns $cols" +# Enable or disable alternate screen. +proc Term::_set_alternate { enable } { + variable _alternate + if { $enable == $_alternate } { + return + } + set _alternate $enable - variable _rows - variable _cols - variable _cur_col - variable _cur_row - variable _attrs - variable _resize_count + variable _attrs + variable _chars + variable _cur_col + variable _cur_row - set _rows $rows - set _cols $cols - set _cur_col 0 - set _cur_row 0 - set _resize_count 0 - _reset_attrs _attrs - - _clear_lines 0 $_rows - } - - # Accept some output from gdb and update the screen. - # Return 1 if successful, or 0 if a timeout occurred. - proc accept_gdb_output { } { - global expect_out - gdb_expect { - -re "^\[\x07\x08\x0a\x0d\]" { - scan $expect_out(0,string) %c val - set hexval [format "%02x" $val] - _log "wait_for: _ctl_0x${hexval}" - _ctl_0x${hexval} - } - -re "^\x1b(\[0-9a-zA-Z\])" { - _log "wait_for: unsupported escape" - error "unsupported escape" - } - -re "^\x1b\\\[(\[0-9;\]*)(\[a-zA-Z@`\])" { - set cmd $expect_out(2,string) - set params [split $expect_out(1,string) ";"] - _log "wait_for: _csi_$cmd <<<$expect_out(1,string)>>>" - eval _csi_$cmd $params - } - -re "^\[^\x07\x08\x0a\x0d\x1b\]+" { - _insert $expect_out(0,string) - variable _last_char - set _last_char [string index $expect_out(0,string) end] - } + variable _save_attrs + variable _save_chars + variable _save_cur_col + variable _save_cur_row - timeout { - # Assume a timeout means we somehow missed the - # expected result, and carry on. - warning "timeout in accept_gdb_output" - dump_screen - return 0 - } - } + variable _alternate_setup - return 1 + if { $_alternate_setup } { + set tmp $_save_chars + } + set _save_chars [array get _chars] + if { $_alternate_setup } { + array set _chars $tmp } - # Print arg using "verbose -log" if DEBUG_TUI_MATCHING == 1. - proc debug_tui_matching { arg } { - set debug 0 - if { [info exists ::DEBUG_TUI_MATCHING] } { - set debug $::DEBUG_TUI_MATCHING - } + if { $_alternate_setup } { + set tmp $_save_attrs + } + set _save_attrs [array get _attrs] + if { $_alternate_setup } { + array set _attrs $tmp + } - if { ! $debug } { - return - } + if { $_alternate_setup } { + set tmp $_save_cur_col + } + set _save_cur_col $_cur_col + if { $_alternate_setup } { + set _cur_col $tmp + } - verbose -log "$arg" + if { $_alternate_setup } { + set tmp $_save_cur_row + } + set _save_cur_row $_cur_row + if { $_alternate_setup } { + set _cur_row $tmp } - # Accept some output from gdb and update the screen. WAIT_FOR is - # a regexp matching the line to wait for. Return 0 on timeout, 1 - # on success. - proc wait_for {wait_for} { - global gdb_prompt - variable _cur_col - variable _cur_row + if { ! $_alternate_setup } { + variable _rows + variable _cols + _setup $_rows $_cols + set _alternate_setup 1 + } +} - set fn "wait_for" +# Initialize. +proc Term::_setup {rows cols} { + global stty_init + set stty_init "rows $rows columns $cols" - set prompt_wait_for "(^|\\|)$gdb_prompt \$" - if { $wait_for == "" } { - set wait_for $prompt_wait_for - } + variable _rows + variable _cols + variable _cur_col + variable _cur_row + variable _attrs + variable _resize_count - debug_tui_matching "$fn: regexp: '$wait_for'" + set _rows $rows + set _cols $cols + set _cur_col 0 + set _cur_row 0 + set _resize_count 0 + _reset_attrs _attrs - while 1 { - if { [accept_gdb_output] == 0 } { - return 0 - } + _clear_lines 0 $_rows +} - # If the cursor appears just after the prompt, return. It - # isn't reliable to check this only after an insertion, - # because curses may make "unusual" redrawing decisions. - if {$wait_for == "$prompt_wait_for"} { - set prev [get_line $_cur_row $_cur_col] - } else { - set prev [get_line $_cur_row] - } - if {[regexp -- $wait_for $prev]} { - debug_tui_matching "$fn: match: '$prev'" - if {$wait_for == "$prompt_wait_for"} { - break - } - set wait_for $prompt_wait_for - debug_tui_matching "$fn: regexp prompt: '$wait_for'" - } else { - debug_tui_matching "$fn: mismatch: '$prev'" - } +# Accept some output from gdb and update the screen. +# Return 1 if successful, or 0 if a timeout occurred. +proc Term::accept_gdb_output { {warn 1} } { + global expect_out + + set ctls "\x07\x08\x0a\x0d" + set esc "\x1b" + set re_ctls "\[$ctls\]" + set re_others "\[^$esc$ctls\]" + set have_esc 0 + gdb_expect { + -re ^$re_ctls { + scan $expect_out(0,string) %c val + set hexval [format "%02x" $val] + _log "wait_for: _ctl_0x${hexval}" + _ctl_0x${hexval} + } + -re "^$esc" { + _log "wait_for: ESC" + set have_esc 1 + } + -re "^$re_others+" { + _insert $expect_out(0,string) + } + + timeout { + # Assume a timeout means we somehow missed the + # expected result, and carry on. + warning "timeout in accept_gdb_output" + dump_screen + return 0 } + } + if { !$have_esc } { return 1 } - # Accept some output from gdb and update the screen. Wait for the screen - # region X/Y/WIDTH/HEIGTH to matches REGEXP. Return 0 on timeout, 1 on - # success. - proc wait_for_region_contents {x y width height regexp} { - while 1 { - if { [accept_gdb_output] == 0 } { - return 0 - } + set re_csi [string_to_regexp "\["] + set have_csi 0 + gdb_expect { + -re "^(\[0-9a-zA-Z\])" { + _log "wait_for: unsupported escape" + error "unsupported escape" + } + -re "^(\[\\(\])(\[a-zA-Z\])" { + scan $expect_out(1,string) %c val + set hexval [format "%02x" $val] + set cmd $expect_out(2,string) + eval _esc_0x${hexval}_$cmd + } + -re "^(\[=>\])" { + scan $expect_out(1,string) %c val + set hexval [format "%02x" $val] + _esc_0x$hexval + } + -re "^$re_csi" { + _log "wait_for: CSI" + set have_csi 1 + } - if { [check_region_contents_p $x $y $width $height $regexp] } { - break + timeout { + # Assume a timeout means we somehow missed the + # expected result, and carry on. + if { $warn } { + warning "timeout in accept_gdb_output following ESC" + dump_screen } + _insert "^\[" + return 0 } + } + if { !$have_csi } { return 1 } - # Setup the terminal with dimensions ROWSxCOLS, TERM=ansi, and execute - # BODY. - proc with_tuiterm {rows cols body} { - global env stty_init - variable _TERM - save_vars {env(TERM) env(NO_COLOR) stty_init} { - if { [ishost *-*-*bsd*] } { - setenv TERM ansiw - } else { - setenv TERM ansi + set re_csi_prefix {[?]} + set re_csi_args {[0-9;]} + set re_csi_cmd {[a-zA-Z@`]} + gdb_expect { + -re "^($re_csi_cmd)" { + set cmd $expect_out(1,string) + _log "wait_for: _csi_$cmd" + eval _csi_$cmd + } + -re "^($re_csi_args*)($re_csi_cmd)" { + set params [split $expect_out(1,string) ";"] + set cmd $expect_out(2,string) + _log "wait_for: _csi_$cmd <<<$params>>>" + eval _csi_$cmd $params + } + -re "^($re_csi_prefix?)($re_csi_args*)($re_csi_cmd)" { + set prefix $expect_out(1,string) + set params [split $expect_out(2,string) ";"] + set cmd $expect_out(3,string) + scan $prefix %c val + set hexval [format "%02x" $val] + _log "wait_for: _csi_0x${hexval}_$cmd <<<$expect_out(1,string)>>>" + eval _csi_0x${hexval}_$cmd $params + } + + timeout { + # Assume a timeout means we somehow missed the + # expected result, and carry on. + if { $warn } { + warning "timeout in accept_gdb_output following CSI" + dump_screen } - # Save active TERM variable. - set Term::_TERM $env(TERM) + _insert "^\[\[" + return 0 + } + } - setenv NO_COLOR "" - _setup $rows $cols + return 1 +} - uplevel $body - } +# Print arg using "verbose -log" if DEBUG_TUI_MATCHING == 1. +proc Term::debug_tui_matching { arg } { + set debug 0 + if { [info exists ::DEBUG_TUI_MATCHING] } { + set debug $::DEBUG_TUI_MATCHING } - # Like ::clean_restart, but ensures that gdb starts in an - # environment where the TUI can work. ROWS and COLS are the size - # of the terminal. EXECUTABLE, if given, is passed to - # clean_restart. - proc clean_restart {rows cols {executable {}}} { - with_tuiterm $rows $cols { - save_vars { ::GDBFLAGS } { - # Make GDB not print the directory names. Use this setting to - # remove the differences in test runs due to varying directory - # names. - append ::GDBFLAGS " -ex \"set filename-display basename\"" + if { ! $debug } { + return + } - if {$executable == ""} { - ::clean_restart - } else { - ::clean_restart $executable - } - } + verbose -log "$arg" +} - ::gdb_test_no_output "set pagination off" - } +# Accept some output from gdb and update the screen. WAIT_FOR is +# a regexp matching the line to wait for. Return 0 on timeout, 1 +# on success. +proc Term::wait_for {wait_for} { + global gdb_prompt + variable _cur_col + variable _cur_row + + set fn "wait_for" + + set prompt_wait_for "(^|\\|)$gdb_prompt \$" + if { $wait_for == "" } { + set wait_for $prompt_wait_for } - # Generate prompt on TUIterm. - proc gen_prompt {} { - # Generate a prompt. - send_gdb "echo\n" + debug_tui_matching "$fn: regexp: '$wait_for'" + + while 1 { + if { [accept_gdb_output] == 0 } { + return 0 + } + + # If the cursor appears just after the prompt, return. It + # isn't reliable to check this only after an insertion, + # because curses may make "unusual" redrawing decisions. + if {$wait_for == "$prompt_wait_for"} { + set prev [get_line $_cur_row $_cur_col] + } else { + set prev [get_line $_cur_row] + } + + if { ![regexp -- $wait_for $prev] } { + debug_tui_matching "$fn: mismatch: '$prev'" + continue + } - # Drain the output before the prompt. - gdb_expect { - -re "echo\r\n" { + if {$wait_for == "$prompt_wait_for"} { + # We've detected that the cursor is just after the prompt. + # Now check that there's nothing else on the line. + set prev [get_line $_cur_row] + if { ![regexp -- "(^|\\|)$gdb_prompt +($|\\||\\+)" $prev] } { + debug_tui_matching "$fn: mismatch: '$prev'" + continue } } - # Interpret prompt using TUIterm. - wait_for "" - } + debug_tui_matching "$fn: match: '$prev'" - # Setup ready for starting the tui, but don't actually start it. - # Returns 1 on success, 0 if TUI tests should be skipped. - proc prepare_for_tui {} { - if { [is_remote host] } { - # In clean_restart, we're using "setenv TERM ansi", which has - # effect on build. If we have [is_remote host] == 0, so - # build == host, then it also has effect on host. But for - # [is_remote host] == 1, it has no effect on host. - return 0 + if {$wait_for == "$prompt_wait_for"} { + # Matched the prompt, we're done. + break } - if {![allow_tui_tests]} { + # Now try to match the prompt. + set wait_for $prompt_wait_for + debug_tui_matching "$fn: regexp prompt: '$wait_for'" + } + + return 1 +} + +# Accept some output from gdb and update the screen. Wait for the screen +# region X/Y/WIDTH/HEIGTH to matches REGEXP. Return 0 on timeout, 1 on +# success. +proc Term::wait_for_region_contents {x y width height regexp} { + while 1 { + if { [accept_gdb_output] == 0 } { return 0 } - gdb_test_no_output "set tui border-kind ascii" - gdb_test_no_output "maint set tui-resize-message on" - return 1 + if { [check_region_contents_p $x $y $width $height $regexp] } { + break + } } - # Start the TUI. Returns 1 on success, 0 if TUI tests should be - # skipped. - proc enter_tui {} { - if {![prepare_for_tui]} { + return 1 +} + +# Accept some output from gdb and update the screen. Wait for the current +# screen line to match REGEXP and cursor position POS, unless POS is empty. +# Return 0 on timeout, 1 on success. +proc Term::wait_for_line { regexp {pos ""} } { + variable _cur_row + variable _cur_col + variable _cols + + while 1 { + if { [accept_gdb_output] == 0 } { return 0 } - command_no_prompt_prefix "tui enable" - return 1 - } + if { ![check_region_contents_p 0 $_cur_row $_cols 1 $regexp] } { + continue + } - # Send the command CMD to gdb, then wait for a gdb prompt to be - # seen in the TUI. CMD should not end with a newline -- that will - # be supplied by this function. - proc command {cmd} { - global gdb_prompt - send_gdb "$cmd\n" - set str [string_to_regexp $cmd] - set str "(^|\\|)$gdb_prompt $str" - wait_for $str - } - - # As proc command, but don't wait for an initial prompt. This is used for - # initial terminal commands, where there's no prompt yet. - proc command_no_prompt_prefix {cmd} { - gen_prompt - command $cmd - } - - # Apply the attribute list in ATTRS to attributes array UPVAR_NAME. - # Return a string annotating the changed attributes. - proc apply_attrs { upvar_name attrs } { - set res "" - upvar $upvar_name var - foreach { attr val } $attrs { - if { $var($attr) != $val } { - append res "<$attr:$val>" - set var($attr) $val - } + if { $pos == "" || $_cur_col == $pos } { + break } + } + + return 1 +} + +# In BODY, when using Term::with_tuiterm, use TERM instead of the default. - return $res +proc Term::with_term { term body } { + save_vars { Term::_TERM } { + set Term::_TERM $term + uplevel $body } +} - # Return the text of screen line N. Lines are 0-based. Start at column - # X. If C is non-empty, stop before column C. Columns are also - # zero-based. If ATTRS, annotate with attributes. - proc get_string {n x c {attrs 0}} { - variable _rows - # This can happen during resizing, if the cursor seems to - # temporarily be off-screen. - if {$n >= $_rows} { - return "" +# Setup the terminal with dimensions ROWSxCOLS, TERM=ansi, and execute +# BODY. +proc Term::with_tuiterm {rows cols body} { + global env stty_init + variable _TERM + save_vars {env(TERM) env(NO_COLOR) stty_init} { + if { $Term::_TERM != "" } { + setenv TERM $Term::_TERM + } elseif { [ishost *-*-*bsd*] } { + setenv TERM ansiw + } else { + setenv TERM ansi } + # Save active TERM variable. + set Term::_TERM $env(TERM) - set result "" - variable _cols - variable _chars - set c [_default $c $_cols] - if { $attrs } { - _reset_attrs line_attrs - } - while {$x < $c} { - if { $attrs } { - set char_attrs [lindex $_chars($x,$n) 1] - append result [apply_attrs line_attrs $char_attrs] + setenv NO_COLOR "" + _setup $rows $cols + + uplevel $body + } +} + +# Like ::clean_restart, but ensures that gdb starts in an +# environment where the TUI can work. ROWS and COLS are the size +# of the terminal. EXECUTABLE, if given, is passed to +# clean_restart. +proc Term::clean_restart {rows cols {executable {}}} { + with_tuiterm $rows $cols { + save_vars { ::GDBFLAGS } { + # Make GDB not print the directory names. Use this setting to + # remove the differences in test runs due to varying directory + # names. + append ::GDBFLAGS " -ex \"set filename-display basename\"" + + if {$executable == ""} { + ::clean_restart + } else { + ::clean_restart $executable } - append result [lindex $_chars($x,$n) 0] - incr x } - if { $attrs } { - _reset_attrs zero_attrs - set char_attrs [array get zero_attrs] - append result [apply_attrs line_attrs $char_attrs] - } - return $result - } - # Return the text of screen line N. Lines are 0-based. Start at column - # X. If C is non-empty, stop before column C. Columns are also - # zero-based. Annotate with attributes. - proc get_string_with_attrs { n x c } { - return [get_string $n $x $c 1] + ::gdb_test_no_output "set pagination off" } +} - # Return the text of screen line N. Lines are 0-based. If C is - # non-empty, stop before column C. Columns are also zero-based. If - # ATTRS, annotate with attributes. - proc get_line_1 {n c attrs} { - return [get_string $n 0 $c $attrs] +# Generate prompt on TUIterm. +proc Term::gen_prompt {} { + # Generate a prompt. + send_gdb "echo\n" + + # Drain the output before the prompt. + gdb_expect { + -re "echo\r\n" { + } } - # Return the text of screen line N, without attributes. Lines are - # 0-based. If C is given, stop before column C. Columns are also - # zero-based. - proc get_line {n {c ""} } { - return [get_line_1 $n $c 0] + # Interpret prompt using TUIterm. + wait_for "" +} + +# Setup ready for starting the tui, but don't actually start it. +# Returns 1 on success, 0 if TUI tests should be skipped. +proc Term::prepare_for_tui {} { + if { [is_remote host] } { + # In clean_restart, we're using "setenv TERM ansi", which has + # effect on build. If we have [is_remote host] == 0, so + # build == host, then it also has effect on host. But for + # [is_remote host] == 1, it has no effect on host. + return 0 } - # As get_line, but annotate with attributes. - proc get_line_with_attrs {n {c ""}} { - return [get_line_1 $n $c 1] + if {![allow_tui_tests]} { + return 0 } - # Get just the character at (X, Y). - proc get_char {x y} { - variable _chars - return [lindex $_chars($x,$y) 0] + gdb_test_no_output "set tui border-kind ascii" + gdb_test_no_output "maint set tui-resize-message on" + # When matching GDB output using Term::wait_for, the number of + # matching attempts in wait_for can be influenced by CLI styling. + # Disable it by default to avoid this. + gdb_test_no_output "set style enabled off" + return 1 +} + +# Start the TUI. Returns 1 on success, 0 if TUI tests should be +# skipped. +proc Term::enter_tui {} { + if {![prepare_for_tui]} { + return 0 } - # Get the entire screen as a string. - proc get_all_lines {} { - variable _rows - variable _cols - variable _chars + command_no_prompt_prefix "tui enable" + return 1 +} - set result "" - for {set y 0} {$y < $_rows} {incr y} { - for {set x 0} {$x < $_cols} {incr x} { - append result [lindex $_chars($x,$y) 0] - } - append result "\n" +# Send the command CMD to gdb, then wait for a gdb prompt to be +# seen in the TUI. CMD should not end with a newline -- that will +# be supplied by this function. +proc Term::command {cmd} { + global gdb_prompt + send_gdb "$cmd\n" + set str [string_to_regexp $cmd] + set str "(^|\\|)$gdb_prompt $str" + wait_for $str +} + +# As proc command, but don't wait for an initial prompt. This is used for +# initial terminal commands, where there's no prompt yet. +proc Term::command_no_prompt_prefix {cmd} { + gen_prompt + command $cmd +} + +# Apply the attribute list in ATTRS to attributes array UPVAR_NAME. +# Return a string annotating the changed attributes. +proc Term::apply_attrs { upvar_name attrs } { + set res "" + upvar $upvar_name var + foreach { attr val } $attrs { + if { $var($attr) != $val } { + append res "<$attr:$val>" + set var($attr) $val } + } - return $result + return $res +} + +# Return the text of screen line N. Lines are 0-based. Start at column +# X. If C is non-empty, stop before column C. Columns are also +# zero-based. If ATTRS, annotate with attributes. +proc Term::get_string {n x c {attrs 0}} { + variable _rows + # This can happen during resizing, if the cursor seems to + # temporarily be off-screen. + if {$n >= $_rows} { + return "" } - # Get the text just before the cursor. - proc get_current_line {} { - variable _cur_col - variable _cur_row - return [get_line $_cur_row $_cur_col] + set result "" + variable _cols + variable _chars + set c [_default $c $_cols] + if { $attrs } { + _reset_attrs line_attrs + } + while {$x < $c} { + if { $attrs } { + set char_attrs [lindex $_chars($x,$n) 1] + append result [apply_attrs line_attrs $char_attrs] + } + append result [lindex $_chars($x,$n) 0] + incr x } + if { $attrs } { + _reset_attrs zero_attrs + set char_attrs [array get zero_attrs] + append result [apply_attrs line_attrs $char_attrs] + } + return $result +} - # Helper function for check_box. Returns empty string if the box - # is found, description of why not otherwise. - proc _check_box {x y width height} { - set x2 [expr {$x + $width - 1}] - set y2 [expr {$y + $height - 1}] +# Return the text of screen line N. Lines are 0-based. Start at column +# X. If C is non-empty, stop before column C. Columns are also +# zero-based. Annotate with attributes. +proc Term::get_string_with_attrs { n x c } { + return [get_string $n $x $c 1] +} - verbose -log "_check_box x=$x, y=$y, x2=$x2, y2=$y2, width=$width, height=$height" +# Return the text of screen line N. Lines are 0-based. If C is +# non-empty, stop before column C. Columns are also zero-based. If +# ATTRS, annotate with attributes. +proc Term::get_line_1 {n c attrs} { + return [get_string $n 0 $c $attrs] +} - set c [get_char $x $y] - if {$c != "+"} { - return "ul corner is $c, not +" - } +# Return the text of screen line N, without attributes. Lines are +# 0-based. If C is given, stop before column C. Columns are also +# zero-based. +proc Term::get_line {n {c ""} } { + return [get_line_1 $n $c 0] +} - set c [get_char $x $y2] - if {$c != "+"} { - return "ll corner is $c, not +" - } +# As get_line, but annotate with attributes. +proc Term::get_line_with_attrs {n {c ""}} { + return [get_line_1 $n $c 1] +} - set c [get_char $x2 $y] - if {$c != "+"} { - return "ur corner is $c, not +" - } +# Get just the character at (X, Y). +proc Term::get_char {x y} { + variable _chars + return [lindex $_chars($x,$y) 0] +} - set c [get_char $x2 $y2] - if {$c != "+"} { - return "lr corner is $c, not +" - } +# Get the entire screen as a string. +proc Term::get_all_lines {} { + variable _rows + variable _cols + variable _chars - # Note we do not check the full horizonal borders of the box. - # The top will contain a title, and the bottom may as well, if - # it is overlapped by some other border. However, at most a - # title should appear as '+-VERY LONG TITLE-+', so we can - # check for the '+-' on the left, and '-+' on the right. - set c [get_char [expr {$x + 1}] $y] - if {$c != "-"} { - return "ul title padding is $c, not -" + set result "" + for {set y 0} {$y < $_rows} {incr y} { + for {set x 0} {$x < $_cols} {incr x} { + append result [lindex $_chars($x,$y) 0] } + append result "\n" + } - set c [get_char [expr {$x2 - 1}] $y] - if {$c != "-"} { - return "ul title padding is $c, not -" - } + return $result +} - # Now check the vertical borders. - for {set i [expr {$y + 1}]} {$i < $y2 - 1} {incr i} { - set c [get_char $x $i] - if {$c != "|"} { - return "left side $i is $c, not |" - } +# Get the text just before the cursor. +proc Term::get_current_line {} { + variable _cur_col + variable _cur_row + return [get_line $_cur_row $_cur_col] +} - set c [get_char $x2 $i] - if {$c != "|"} { - return "right side $i is $c, not |" - } - } +# Helper function for check_box. Returns empty string if the box +# is found, description of why not otherwise. +proc Term::_check_box {x y width height} { + set x2 [expr {$x + $width - 1}] + set y2 [expr {$y + $height - 1}] - return "" + verbose -log "_check_box x=$x, y=$y, x2=$x2, y2=$y2, width=$width, height=$height" + + set c [get_char $x $y] + if {$c != "+"} { + return "ul corner is $c, not +" } - # Check for a box at the given coordinates. - proc check_box {test_name x y width height} { - dump_box $x $y $width $height - set why [_check_box $x $y $width $height] - if {$why == ""} { - pass $test_name - } else { - fail "$test_name ($why)" - } + set c [get_char $x $y2] + if {$c != "+"} { + return "ll corner is $c, not +" } - # Wait until a box appears at the given coordinates. - proc wait_for_box {test_name x y width height} { - while 1 { - if { [accept_gdb_output] == 0 } { - return 0 - } + set c [get_char $x2 $y] + if {$c != "+"} { + return "ur corner is $c, not +" + } - set why [_check_box $x $y $width $height] - if {$why == ""} { - pass $test_name - break - } - } + set c [get_char $x2 $y2] + if {$c != "+"} { + return "lr corner is $c, not +" } - # Check whether the text contents of the terminal match the - # regular expression. Note that text styling is not considered. - proc check_contents {test_name regexp} { - dump_screen - set contents [get_all_lines] - gdb_assert {[regexp -- $regexp $contents]} $test_name + # Note we do not check the full horizonal borders of the box. + # The top will contain a title, and the bottom may as well, if + # it is overlapped by some other border. However, at most a + # title should appear as '+-VERY LONG TITLE-+', so we can + # check for the '+-' on the left, and '-+' on the right. + set c [get_char [expr {$x + 1}] $y] + if {$c != "-"} { + return "ul title padding is $c, not -" } - # As check_contents, but check that the text contents of the terminal does - # not match the regular expression. - proc check_contents_not {test_name regexp} { - dump_screen - set contents [get_all_lines] - gdb_assert {![regexp -- $regexp $contents]} $test_name + set c [get_char [expr {$x2 - 1}] $y] + if {$c != "-"} { + return "ul title padding is $c, not -" } - # Get the region of the screen described by X, Y, WIDTH, and - # HEIGHT, and separate the lines using SEP. If ATTRS is true then - # include attribute information in the output. - proc get_region { x y width height sep { attrs false } } { - variable _chars + # Now check the vertical borders. + for {set i [expr {$y + 1}]} {$i < $y2 - 1} {incr i} { + set c [get_char $x $i] + if {$c != "|"} { + return "left side $i is $c, not |" + } - if { $attrs } { - _reset_attrs region_attrs + set c [get_char $x2 $i] + if {$c != "|"} { + return "right side $i is $c, not |" } + } - # Grab the contents of the box, join each line together - # using $sep. - set result "" - for {set yy $y} {$yy < [expr {$y + $height}]} {incr yy} { - if {$yy > $y} { - # Add the end of line sequence only if this isn't the - # first line. - append result $sep - } - for {set xx $x} {$xx < [expr {$x + $width}]} {incr xx} { - if { $attrs } { - set char_attrs [lindex $_chars($xx,$yy) 1] - append result [apply_attrs region_attrs $char_attrs] - } + return "" +} - append result [get_char $xx $yy] - } +# Check for a box at the given coordinates. +proc Term::check_box {test_name x y width height} { + dump_box $x $y $width $height + set why [_check_box $x $y $width $height] + if {$why == ""} { + pass $test_name + } else { + fail "$test_name ($why)" + } +} + +# Wait until a box appears at the given coordinates. +proc Term::wait_for_box {test_name x y width height} { + while 1 { + if { [accept_gdb_output] == 0 } { + return 0 } - if { $attrs } { - _reset_attrs zero_attrs - set char_attrs [array get zero_attrs] - append result [apply_attrs region_attrs $char_attrs] + + set why [_check_box $x $y $width $height] + if {$why == ""} { + pass $test_name + break } - return $result } +} - # Check that the region of the screen described by X, Y, WIDTH, - # and HEIGHT match REGEXP. This is like check_contents except - # only part of the screen is checked. This can be used to check - # the contents within a box (though check_box_contents is a better - # choice for boxes with a border). Return 1 if check succeeded. - proc check_region_contents_p { x y width height regexp } { - variable _chars - dump_box $x $y $width $height +# Check whether the text contents of the terminal match the +# regular expression. Note that text styling is not considered. +proc Term::check_contents {test_name regexp} { + dump_screen + set contents [get_all_lines] + gdb_assert {[regexp -- $regexp $contents]} $test_name +} - # Now grab the contents of the box, join each line together - # with a '\r\n' sequence and match against REGEXP. - set result [get_region $x $y $width $height "\r\n"] - return [regexp -- $regexp $result] - } +# As check_contents, but check that the text contents of the terminal does +# not match the regular expression. +proc Term::check_contents_not {test_name regexp} { + dump_screen + set contents [get_all_lines] + gdb_assert {![regexp -- $regexp $contents]} $test_name +} - # Check that the region of the screen described by X, Y, WIDTH, - # and HEIGHT match REGEXP. As check_region_contents_p, but produce - # a pass/fail message. - proc check_region_contents { test_name x y width height regexp } { - set ok [check_region_contents_p $x $y $width $height $regexp] - gdb_assert {$ok} $test_name - } +# Get the region of the screen described by X, Y, WIDTH, and +# HEIGHT, and separate the lines using SEP. If ATTRS is true then +# include attribute information in the output. +proc Term::get_region { x y width height sep { attrs false } } { + variable _chars - # Check the contents of a box on the screen. This is a little - # like check_contents, but doesn't check the whole screen - # contents, only the contents of a single box. This procedure - # includes (effectively) a call to check_box to ensure there is a - # box where expected, if there is then the contents of the box are - # matched against REGEXP. - proc check_box_contents {test_name x y width height regexp} { - variable _chars + if { $attrs } { + _reset_attrs region_attrs + } - dump_box $x $y $width $height - set why [_check_box $x $y $width $height] - if {$why != ""} { - fail "$test_name (box check: $why)" - return + # Grab the contents of the box, join each line together + # using $sep. + set result "" + for {set yy $y} {$yy < [expr {$y + $height}]} {incr yy} { + if {$yy > $y} { + # Add the end of line sequence only if this isn't the + # first line. + append result $sep } + for {set xx $x} {$xx < [expr {$x + $width}]} {incr xx} { + if { $attrs } { + set char_attrs [lindex $_chars($xx,$yy) 1] + append result [apply_attrs region_attrs $char_attrs] + } - check_region_contents $test_name [expr {$x + 1}] [expr {$y + 1}] \ - [expr {$width - 2}] [expr {$height - 2}] $regexp + append result [get_char $xx $yy] + } + } + if { $attrs } { + _reset_attrs zero_attrs + set char_attrs [array get zero_attrs] + append result [apply_attrs region_attrs $char_attrs] } + return $result +} - # A debugging function to dump the current screen, with line - # numbers. If ATTRS, annotate with attributes. - proc dump_screen { {attrs 0} } { - variable _rows - variable _cols - variable _cur_row - variable _cur_col +# Check that the region of the screen described by X, Y, WIDTH, +# and HEIGHT match REGEXP. This is like check_contents except +# only part of the screen is checked. This can be used to check +# the contents within a box (though check_box_contents is a better +# choice for boxes with a border). Return 1 if check succeeded. +proc Term::check_region_contents_p { x y width height regexp } { + variable _chars + dump_box $x $y $width $height - verbose -log "Screen Dump (size $_cols columns x $_rows rows, cursor at column $_cur_col, row $_cur_row):" + # Now grab the contents of the box, join each line together + # with a '\r\n' sequence and match against REGEXP. + set result [get_region $x $y $width $height "\r\n"] + return [regexp -- $regexp $result] +} - for {set y 0} {$y < $_rows} {incr y} { - set fmt [format %5d $y] - verbose -log "$fmt [get_line_1 $y {} $attrs]" - } +# Check that the region of the screen described by X, Y, WIDTH, +# and HEIGHT match REGEXP. As check_region_contents_p, but produce +# a pass/fail message. +proc Term::check_region_contents { test_name x y width height regexp } { + set ok [check_region_contents_p $x $y $width $height $regexp] + gdb_assert {$ok} $test_name +} + +# Check the contents of a box on the screen. This is a little +# like check_contents, but doesn't check the whole screen +# contents, only the contents of a single box. This procedure +# includes (effectively) a call to check_box to ensure there is a +# box where expected, if there is then the contents of the box are +# matched against REGEXP. +proc Term::check_box_contents {test_name x y width height regexp} { + variable _chars + + dump_box $x $y $width $height + set why [_check_box $x $y $width $height] + if {$why != ""} { + fail "$test_name (box check: $why)" + return } - # As dump_screen, but with attributes annotation. - proc dump_screen_with_attrs {} { - return [dump_screen 1] + check_region_contents $test_name [expr {$x + 1}] [expr {$y + 1}] \ + [expr {$width - 2}] [expr {$height - 2}] $regexp +} + +# A debugging function to dump the current screen, with line +# numbers. If ATTRS, annotate with attributes. +proc Term::dump_screen { {attrs 0} } { + variable _rows + variable _cols + variable _cur_row + variable _cur_col + + verbose -log "Screen Dump (size $_cols columns x $_rows rows, cursor at column $_cur_col, row $_cur_row):" + + for {set y 0} {$y < $_rows} {incr y} { + set fmt [format %5d $y] + verbose -log "$fmt [get_line_1 $y {} $attrs]" } +} - # A debugging function to dump a box from the current screen, with line - # numbers. - proc dump_box { x y width height } { - verbose -log "Box Dump ($width x $height) @ ($x, $y):" - set region [get_region $x $y $width $height "\n"] - set lines [split $region "\n"] - set nr $y - foreach line $lines { - set fmt [format %5d $nr] - verbose -log "$fmt $line" - incr nr - } +# As dump_screen, but with attributes annotation. +proc Term::dump_screen_with_attrs {} { + return [dump_screen 1] +} + +# A debugging function to dump a box from the current screen, with line +# numbers. +proc Term::dump_box { x y width height } { + verbose -log "Box Dump ($width x $height) @ ($x, $y):" + set region [get_region $x $y $width $height "\n"] + set lines [split $region "\n"] + set nr $y + foreach line $lines { + set fmt [format %5d $nr] + verbose -log "$fmt $line" + incr nr } +} - # Resize the terminal. - proc _do_resize {rows cols} { - variable _chars - variable _rows - variable _cols +# Resize the terminal. +proc Term::_do_resize {rows cols} { + variable _chars + variable _rows + variable _cols - set old_rows [expr {min ($_rows, $rows)}] - set old_cols [expr {min ($_cols, $cols)}] + set old_rows [expr {min ($_rows, $rows)}] + set old_cols [expr {min ($_cols, $cols)}] - # Copy locally. - array set local_chars [array get _chars] - unset _chars + # Copy locally. + array set local_chars [array get _chars] + unset _chars - set _rows $rows - set _cols $cols - _clear_lines 0 $_rows + set _rows $rows + set _cols $cols + _clear_lines 0 $_rows - for {set x 0} {$x < $old_cols} {incr x} { - for {set y 0} {$y < $old_rows} {incr y} { - set _chars($x,$y) $local_chars($x,$y) - } + for {set x 0} {$x < $old_cols} {incr x} { + for {set y 0} {$y < $old_rows} {incr y} { + set _chars($x,$y) $local_chars($x,$y) } } +} - proc resize {rows cols {wait_for_msg 1}} { - variable _rows - variable _cols - variable _resize_count +proc Term::resize {rows cols {wait_for_msg 1}} { + variable _rows + variable _cols + variable _resize_count - # expect handles each argument to stty separately. This means - # that gdb will see SIGWINCH twice. Rather than rely on this - # behavior (which, after all, could be changed), we make it - # explicit here. This also simplifies waiting for the redraw. - _do_resize $rows $_cols - stty rows $_rows < $::gdb_tty_name - if { $wait_for_msg } { - wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" - } - incr _resize_count - _do_resize $_rows $cols - stty columns $_cols < $::gdb_tty_name - if { $wait_for_msg } { - wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" - } - incr _resize_count - } + # expect handles each argument to stty separately. This means + # that gdb will see SIGWINCH twice. Rather than rely on this + # behavior (which, after all, could be changed), we make it + # explicit here. This also simplifies waiting for the redraw. + _do_resize $rows $_cols + stty rows $_rows < $::gdb_tty_name + if { $wait_for_msg } { + wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" + } + incr _resize_count + _do_resize $_rows $cols + stty columns $_cols < $::gdb_tty_name + if { $wait_for_msg } { + wait_for "@@ resize done $_resize_count, size = ${_cols}x${rows}" + } + incr _resize_count } |