blob: fc4dc520d355a36ecd3633cbd3fc1515b7c16f57 [file] [log] [blame] [edit]
# 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
}