Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gdb_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gdb_check--master-arm |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 |
success
|
Test passed
|
Commit Message
This test starts a communication with gdbsever setting
the remotelog file. Then, it 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.
Add a lib/gdbreplay-support.exp. update_log proc matches lines
from GDB to gdbserver in a remotelogfile. Once a match is found then
NEWLINE is used to build a replacement line to send from gdbserver to
GDB.
---
gdb/testsuite/gdb.replay/connect.c | 22 ++++
gdb/testsuite/gdb.replay/connect.exp | 99 ++++++++++++++++
gdb/testsuite/lib/gdbreplay-support.exp | 144 ++++++++++++++++++++++++
gdbserver/gdbreplay.cc | 2 +-
4 files changed, 266 insertions(+), 1 deletion(-)
create mode 100644 gdb/testsuite/gdb.replay/connect.c
create mode 100644 gdb/testsuite/gdb.replay/connect.exp
create mode 100644 gdb/testsuite/lib/gdbreplay-support.exp
Comments
>>>>> "Alexandra" == Alexandra Hájková <ahajkova@redhat.com> writes:
Alexandra> +proc gdbreplay_start { remotelog } {
I wonder if anything ensures that this process stops.
Like, if gdb crashes, does gdbreplay remain alive?
I think we used to have some issues with this in the test suite, where
gdb dying would cause an inferior or maybe gdbserver to keep running.
Alexandra> + # Port id -- either specified in baseboard file, or managed here.
Alexandra> + set portnum [get_portnum]
Also I wonder if it would be simpler to just run gdbreplay in pipe mode.
Alexandra> --- a/gdbserver/gdbreplay.cc
Alexandra> +++ b/gdbserver/gdbreplay.cc
Alexandra> @@ -205,6 +205,7 @@ remote_open (const char *name)
Alexandra> if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
Alexandra> perror_with_name ("Can't bind address");
Alexandra> + fprintf (stderr, "Replay logfile using %s\n", name);
Alexandra> if (p->ai_socktype == SOCK_DGRAM)
Alexandra> remote_desc_in = tmp_desc;
Alexandra> else
Alexandra> @@ -259,7 +260,6 @@ remote_open (const char *name)
Alexandra> #endif
Alexandra> remote_desc_out = remote_desc_in;
Alexandra> - fprintf (stderr, "Replay logfile using %s\n", name);
Alexandra> fflush (stderr);
Probably you should move that fflush as well.
Though normally stderr is line buffered and maybe this isn't needed at all.
Tom
On Fri, Nov 22, 2024 at 5:30 PM Tom Tromey <tom@tromey.com> wrote:
> >>>>> "Alexandra" == Alexandra Hájková <ahajkova@redhat.com> writes:
>
> Alexandra> +proc gdbreplay_start { remotelog } {
>
> I wonder if anything ensures that this process stops.
> Like, if gdb crashes, does gdbreplay remain alive?
>
> I think we used to have some issues with this in the test suite, where
> gdb dying would cause an inferior or maybe gdbserver to keep running.
>
> Alexandra> + # Port id -- either specified in baseboard file, or
> managed here.
> Alexandra> + set portnum [get_portnum]
>
> Also I wonder if it would be simpler to just run gdbreplay in pipe mode.
>
> Hi Tom,
>
I experimented with GDB dying during first gdbserver, then during gdbreplay.
If GDB is dying and connected to gdbserver, gdbserver normally remaining
alive, waiting for GDB to reconnect:
"Remote side has terminated connection. GDBserver will reopen the connection."
But if I make GDB crash during this connect.exp test, gdbserver is not
running afterwards,
probably being killed by dejagnu.
When I make GDB crash when it's connected to gdbreplay, gdbreplay exits
with:
"Error during read from gdb".
Thank you,
Alexandra
new file mode 100644
@@ -0,0 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2024 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;
+}
new file mode 100644
@@ -0,0 +1,99 @@
+# Copyright 2024 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
+ }
+
+# 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 gdbreplay"
+
+# 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, making sure GDB reads in the binary correctly.
+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"
+
+clean_restart $binfile
+# Make sure we're disconnected, in case we're testing with an
+# extended-remote board, therefore already connected.
+gdb_test "disconnect2" ".*"
+
+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]
+
+gdb_test "target $gdbserver_protocol $gdbserver_gdbport" ".*" \
+ "set target remote to gdbserver"
+gdb_breakpoint main
+gdb_continue_to_breakpoint "continue to main"
+
+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 "disconnect3" ".*"
+
+gdb_test_no_output "set sysroot"
+
+set res [gdbreplay_start $output_file]
+set gdbserver_protocol [lindex $res 0]
+set gdbserver_gdbport [lindex $res 1]
+
+gdb_test "target $gdbserver_protocol $gdbserver_gdbport" ".*Remote replied unexpectedly.*"\
+ "set target remote to gdbreplay"
new file mode 100644
@@ -0,0 +1,144 @@
+# Copyright 2024 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]
+} else {
+ set GDBREPLAY ""
+}
+
+global GDBREPLAY
+verbose "using GDBREPLAY = $GDBREPLAY" 2
+
+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
+}
@@ -205,6 +205,7 @@ remote_open (const char *name)
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);
if (p->ai_socktype == SOCK_DGRAM)
remote_desc_in = tmp_desc;
else
@@ -259,7 +260,6 @@ remote_open (const char *name)
#endif
remote_desc_out = remote_desc_in;
- fprintf (stderr, "Replay logfile using %s\n", name);
fflush (stderr);
}