--- /dev/null
+/* 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/>. */
+
+int
+main (int argc, char **argv)
+{
+ return 0;
+}
--- /dev/null
+# 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/>. */
+#
+# Starts a communication with gdbsever setting the remotelog file.
+# Modifies the remotelog with update_log proc, injects an error message
+# instead of the expected replay to the vMustReplyEmpty packet in order
+# to test GDB reacts to the error response properly. After the remotelog
+# modification, this test restarts GDB and starts communication with gdbreply
+# instead of the gdbserver using the remotelog.
+
+load_lib gdbserver-support.exp
+load_lib gdbreplay-support.exp
+
+require allow_gdbserver_tests
+require has_gdbreplay
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
+ return -1
+}
+
+# Connect to gdbserver and run to a breakpoint in main. Record the
+# remotelogfile into the global REMOTELOG.
+proc_with_prefix record_initial_logfile {} {
+ global binfile
+ global remotelog
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ gdb_test_no_output "set sysroot" \
+ "setting sysroot before starting gdbserver"
+
+ # Start gdbserver like:
+ # gdbserver :PORT ....
+ set res [gdbserver_start "" $binfile]
+ set gdbserver_protocol [lindex $res 0]
+ set gdbserver_gdbport [lindex $res 1]
+
+ # The replay log is placed in 'replay.log'.
+ set remotelog [standard_output_file replay.log]
+
+ gdb_test_no_output "set remotelogfile $remotelog" \
+ "setup remotelogfile"
+
+ # Connect to gdbserver.
+ if {![gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0} {
+ unsupported "$testfile (couldn't start gdbserver)"
+ return
+ }
+
+ # If we're connecting as 'remote' then we can't use 'runto'.
+ gdb_breakpoint main
+ gdb_continue_to_breakpoint "continuing to main"
+}
+
+# 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
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ gdb_test_no_output "set sysroot" "setting sysroot"
+
+ set res [gdbreplay_start $remotelog]
+ set gdbserver_protocol [lindex $res 0]
+ set gdbserver_gdbport [lindex $res 1]
+
+ # Connect to gdbreplay.
+ if {![gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0} {
+ unsupported "$testfile (couldn't start gdbreplay)"
+ return
+ }
+ gdb_breakpoint main
+ gdb_continue_to_breakpoint "continue to main"
+}
+
+# Create a modified copy of global REMOTELOG, returning an error for the
+# vMustReplyEmpty packet. Then use gdbreplay to play back the modified
+# 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
+ set output_file [standard_output_file ${testfile}_out.log]
+
+ # Modify the log file by changing the *response* to
+ # the vMustReplayEmty packet to an error.
+ update_log $remotelog $output_file "vMustReplyEmpty" $newline
+
+ clean_restart $binfile
+ # Make sure we're disconnected, in case we're testing with an
+ # extended-remote board, therefore already connected.
+ gdb_test "disconnect" ".*"
+
+ gdb_test_no_output "set sysroot"
+
+ set res [gdbreplay_start $output_file]
+ set gdbserver_protocol [lindex $res 0]
+ set gdbserver_gdbport [lindex $res 1]
+
+ # Connect to gdbreplay.
+ gdb_assert {[gdb_target_cmd_ext $gdbserver_protocol $gdbserver_gdbport \
+ ".*Remote replied unexpectedly to 'vMustReplyEmpty': E.errtext"] == 0} \
+ "remote replied with an error"
+}
+
+record_initial_logfile
+replay_without_error
+replay_with_mustreplyempty_error
--- /dev/null
+# 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/>.
+
+# We're going to reuse some helper function from the gdbserver library.
+load_lib gdbserver-support.exp
+
+if {![info exists GDBREPLAY]} {
+ set GDBREPLAY [findfile $base_dir/../../gdbserver/gdbreplay]
+}
+
+global GDBREPLAY
+verbose "using GDBREPLAY = $GDBREPLAY" 2
+
+# Check is non empty and we're running on the host.
+proc has_gdbreplay {} {
+ global GDBREPLAY
+ if {$GDBREPLAY == ""} {
+ return false
+ }
+
+ # We currently rely on running gdbreplay on the same machine as
+ # GDB.
+ if {[is_remote target]} {
+ return false
+ }
+
+ return true
+}
+
+# Write the command line used to invocate gdbserver to the cmd file.
+
+proc gdbreplay_write_cmd_file { cmdline } {
+ set logfile [standard_output_file_with_gdb_instance gdbreplay.cmd]
+ set cmd_file [open $logfile w]
+ puts $cmd_file $cmdline
+ catch "close $cmd_file"
+}
+
+# Start gdbreplay using REMOTELOG as the log file. Return a list of
+# two elements, the protocol and the hostname:port string. This
+# result list has the same format as gdbserver_start.
+
+proc gdbreplay_start { remotelog } {
+ # Port id -- either specified in baseboard file, or managed here.
+ set portnum [get_portnum]
+
+ # Extract the protocol
+ if [target_info exists gdb_protocol] {
+ set protocol [target_info gdb_protocol]
+ } else {
+ set protocol "remote"
+ }
+
+ # Loop till we find a free port.
+ while 1 {
+ # Fire off the debug agent.
+ set gdbreplay_command "$::GDBREPLAY $remotelog localhost:$portnum"
+
+ gdbreplay_write_cmd_file $gdbreplay_command
+
+ global gdbreplay_spawn_id
+ set gdbreplay_spawn_id [remote_spawn target $gdbreplay_command]
+
+ # Wait for the server to open its TCP socket, so that GDB can connect.
+ expect {
+ -i $gdbreplay_spawn_id
+ -timeout 120
+ -notransfer
+ -re "Replay logfile using" { }
+ -re "Can't (bind address|listen on socket): Address already in use\\.\r\n" {
+ verbose -log "Port $portnum is already in use."
+ set other_portnum [get_portnum]
+ if { $other_portnum != $portnum } {
+ # Bump the port number to avoid the conflict.
+ wait -i $expect_out(spawn_id)
+ set portnum $other_portnum
+ continue
+ }
+ }
+ -re ".*: cannot resolve name: .*\r\n" {
+ error "gdbserver cannot resolve name."
+ }
+ -re "Can't open socket: Address family not supported by protocol." {
+ return {}
+ }
+ timeout {
+ error "Timeout waiting for gdbreplay response."
+ }
+ }
+ break
+ }
+
+ return [list $protocol "localhost:$portnum"]
+}
+
+# MATCH_REGEXP matches lines from GDB to gdbserver. Once a match is
+# found then NEWLINE is used to build a replacement line to send from
+# gdbserver to GDB.
+#
+# Examples of MATCH_REGEXP: "vMustReplyEmpty"
+#
+# Example usage:
+#
+# update_log $logname "${logname}.updated" "vMustReplyEmpty" "E.failed"
+
+proc update_log { filename_in filename_out match_regexp newline } {
+ set fh_in [open $filename_in r]
+ set fh_out [open $filename_out w]
+
+ while { [gets $fh_in line] >= 0 } {
+ # Print the line to the file.
+ puts $fh_out $line
+ if { [regexp $match_regexp $line] } {
+ # print out NEWLINE.
+ puts $fh_out "r +\$${newline}"
+
+ # Don't truncate the file, otherwise gdbreplay will
+ # close the connection early and this might impact
+ # what GDB does. We want GDB to get a chance to
+ # process the error.
+ puts $fh_out "c q"
+ puts $fh_out "w \$qTStatus#49"
+ puts $fh_out "End of log"
+
+ break
+ }
+ }
+
+ close $fh_out
+ close $fh_in
+}
if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
perror_with_name ("Can't bind address");
+ fprintf (stderr, "Replay logfile using %s\n", name);
+ fflush (stderr);
if (p->ai_socktype == SOCK_DGRAM)
remote_desc_in = tmp_desc;
else
fcntl (remote_desc_in, F_SETFL, FASYNC);
#endif
remote_desc_out = remote_desc_in;
-
- fprintf (stderr, "Replay logfile using %s\n", name);
- fflush (stderr);
}
static int