📄 gdb.exp
字号:
# Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,# 2003, 2004, 2005, 2007, 2008 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 was written by Fred Fish. (fnf@cygnus.com)# Generic gdb subroutines that should work for any target. If these# need to be modified for any target, it can be done with a variable# or by passing arguments.if {$tool == ""} { # Tests would fail, logs on get_compiler_info() would be missing. send_error "`site.exp' not found, run `make site.exp'!\n" exit 2}load_lib libgloss.expglobal GDBif [info exists TOOL_EXECUTABLE] { set GDB $TOOL_EXECUTABLE;}if ![info exists GDB] { if ![is_remote host] { set GDB [findfile $base_dir/../../gdb/gdb "$base_dir/../../gdb/gdb" [transform gdb]] } else { set GDB [transform gdb]; }}verbose "using GDB = $GDB" 2global GDBFLAGSif ![info exists GDBFLAGS] { set GDBFLAGS "-nx"}verbose "using GDBFLAGS = $GDBFLAGS" 2# The variable gdb_prompt is a regexp which matches the gdb prompt.# Set it if it is not already set.global gdb_promptif ![info exists gdb_prompt] then { set gdb_prompt "\[(\]gdb\[)\]"}# The variable fullname_syntax_POSIX is a regexp which matches a POSIX # absolute path ie. /foo/ set fullname_syntax_POSIX "/.*/"# The variable fullname_syntax_UNC is a regexp which matches a Windows # UNC path ie. \\D\foo\ set fullname_syntax_UNC {\\\\[^\\]+\\.+\\}# The variable fullname_syntax_DOS_CASE is a regexp which matches a # particular DOS case that GDB most likely will output# ie. \foo\, but don't match \\.*\ set fullname_syntax_DOS_CASE {\\[^\\].*\\}# The variable fullname_syntax_DOS is a regexp which matches a DOS path# ie. a:\foo\ && a:foo\ set fullname_syntax_DOS {[a-zA-Z]:.*\\}# The variable fullname_syntax is a regexp which matches what GDB considers# an absolute path. It is currently debatable if the Windows style paths # d:foo and \abc should be considered valid as an absolute path.# Also, the purpse of this regexp is not to recognize a well formed # absolute path, but to say with certainty that a path is absolute.set fullname_syntax "($fullname_syntax_POSIX|$fullname_syntax_UNC|$fullname_syntax_DOS_CASE|$fullname_syntax_DOS)"# Needed for some tests under Cygwin.global EXEEXTglobal envif ![info exists env(EXEEXT)] { set EXEEXT ""} else { set EXEEXT $env(EXEEXT)}set octal "\[0-7\]+"### Only procedures should come after this point.## gdb_version -- extract and print the version number of GDB#proc default_gdb_version {} { global GDB global GDBFLAGS global gdb_prompt set fileid [open "gdb_cmd" w]; puts $fileid "q"; close $fileid; set cmdfile [remote_download host "gdb_cmd"]; set output [remote_exec host "$GDB -nw --command $cmdfile"] remote_file build delete "gdb_cmd"; remote_file host delete "$cmdfile"; set tmp [lindex $output 1]; set version "" regexp " \[0-9\]\[^ \t\n\r\]+" "$tmp" version if ![is_remote host] { clone_output "[which $GDB] version $version $GDBFLAGS\n" } else { clone_output "$GDB on remote host version $version $GDBFLAGS\n" }}proc gdb_version { } { return [default_gdb_version];}## gdb_unload -- unload a file if one is loaded#proc gdb_unload {} { global verbose global GDB global gdb_prompt send_gdb "file\n" gdb_expect 60 { -re "No executable file now\[^\r\n\]*\[\r\n\]" { exp_continue } -re "No symbol file now\[^\r\n\]*\[\r\n\]" { exp_continue } -re "A program is being debugged already..*Kill it.*y or n. $"\ { send_gdb "y\n" verbose "\t\tKilling previous program being debugged" exp_continue } -re "Discard symbol table from .*y or n.*$" { send_gdb "y\n" exp_continue } -re "$gdb_prompt $" {} timeout { perror "couldn't unload file in $GDB (timed out)." return -1 } }}# Many of the tests depend on setting breakpoints at various places and# running until that breakpoint is reached. At times, we want to start# with a clean-slate with respect to breakpoints, so this utility proc # lets us do this without duplicating this code everywhere.#proc delete_breakpoints {} { global gdb_prompt # we need a larger timeout value here or this thing just confuses # itself. May need a better implementation if possible. - guo # send_gdb "delete breakpoints\n" gdb_expect 100 { -re "Delete all breakpoints.*y or n.*$" { send_gdb "y\n"; exp_continue } -re "$gdb_prompt $" { # This happens if there were no breakpoints } timeout { perror "Delete all breakpoints in delete_breakpoints (timeout)" ; return } } send_gdb "info breakpoints\n" gdb_expect 100 { -re "No breakpoints or watchpoints..*$gdb_prompt $" {} -re "$gdb_prompt $" { perror "breakpoints not deleted" ; return } -re "Delete all breakpoints.*or n.*$" { send_gdb "y\n"; exp_continue } timeout { perror "info breakpoints (timeout)" ; return } }}## Generic run command.## The second pattern below matches up to the first newline *only*.# Using ``.*$'' could swallow up output that we attempt to match# elsewhere.#proc gdb_run_cmd {args} { global gdb_prompt if [target_info exists gdb_init_command] { send_gdb "[target_info gdb_init_command]\n"; gdb_expect 30 { -re "$gdb_prompt $" { } default { perror "gdb_init_command for target failed"; return; } } } if [target_info exists use_gdb_stub] { if [target_info exists gdb,do_reload_on_run] { if { [gdb_reload] != 0 } { return; } send_gdb "continue\n"; gdb_expect 60 { -re "Continu\[^\r\n\]*\[\r\n\]" {} default {} } return; } if [target_info exists gdb,start_symbol] { set start [target_info gdb,start_symbol]; } else { set start "start"; } send_gdb "jump *$start\n" set start_attempt 1; while { $start_attempt } { # Cap (re)start attempts at three to ensure that this loop # always eventually fails. Don't worry about trying to be # clever and not send a command when it has failed. if [expr $start_attempt > 3] { perror "Jump to start() failed (retry count exceeded)"; return; } set start_attempt [expr $start_attempt + 1]; gdb_expect 30 { -re "Continuing at \[^\r\n\]*\[\r\n\]" { set start_attempt 0; } -re "No symbol \"_start\" in current.*$gdb_prompt $" { perror "Can't find start symbol to run in gdb_run"; return; } -re "No symbol \"start\" in current.*$gdb_prompt $" { send_gdb "jump *_start\n"; } -re "No symbol.*context.*$gdb_prompt $" { set start_attempt 0; } -re "Line.* Jump anyway.*y or n. $" { send_gdb "y\n" } -re "The program is not being run.*$gdb_prompt $" { if { [gdb_reload] != 0 } { return; } send_gdb "jump *$start\n"; } timeout { perror "Jump to start() failed (timeout)"; return } } } if [target_info exists gdb_stub] { gdb_expect 60 { -re "$gdb_prompt $" { send_gdb "continue\n" } } } return } if [target_info exists gdb,do_reload_on_run] { if { [gdb_reload] != 0 } { return; } } send_gdb "run $args\n"# This doesn't work quite right yet.# Use -notransfer here so that test cases (like chng-sym.exp)# may test for additional start-up messages. gdb_expect 60 { -re "The program .* has been started already.*y or n. $" { send_gdb "y\n" exp_continue } -notransfer -re "Starting program: \[^\r\n\]*" {} }}# Generic start command. Return 0 if we could start the program, -1# if we could not.proc gdb_start_cmd {args} { global gdb_prompt if [target_info exists gdb_init_command] { send_gdb "[target_info gdb_init_command]\n"; gdb_expect 30 { -re "$gdb_prompt $" { } default { perror "gdb_init_command for target failed"; return; } } } if [target_info exists use_gdb_stub] { return -1 } send_gdb "start $args\n" gdb_expect 60 { -re "The program .* has been started already.*y or n. $" { send_gdb "y\n" exp_continue } # Use -notransfer here so that test cases (like chng-sym.exp) # may test for additional start-up messages. -notransfer -re "Starting program: \[^\r\n\]*" { return 0 } } return -1}# Set a breakpoint at FUNCTION. If there is an additional argument it is# a list of options; the supported options are allow-pending and temporary.proc gdb_breakpoint { function args } { global gdb_prompt global decimal set pending_response n if {[lsearch -exact [lindex $args 0] allow-pending] != -1} { set pending_response y } set break_command "break" if {[lsearch -exact [lindex $args 0] temporary] != -1} { set break_command "tbreak" } send_gdb "$break_command $function\n" # The first two regexps are what we get with -g, the third is without -g. gdb_expect 30 { -re "Breakpoint \[0-9\]* at .*: file .*, line $decimal.\r\n$gdb_prompt $" {} -re "Breakpoint \[0-9\]*: file .*, line $decimal.\r\n$gdb_prompt $" {} -re "Breakpoint \[0-9\]* at .*$gdb_prompt $" {} -re "Breakpoint \[0-9\]* \\(.*\\) pending.*$gdb_prompt $" { if {$pending_response == "n"} { fail "setting breakpoint at $function" return 0 } } -re "Make breakpoint pending.*y or \\\[n\\\]. $" { send_gdb "$pending_response\n" exp_continue } -re "$gdb_prompt $" { fail "setting breakpoint at $function" ; return 0 } timeout { fail "setting breakpoint at $function (timeout)" ; return 0 } } return 1;} # Set breakpoint at function and run gdb until it breaks there.# Since this is the only breakpoint that will be set, if it stops# at a breakpoint, we will assume it is the one we want. We can't# just compare to "function" because it might be a fully qualified,# single quoted C++ function specifier. If there's an additional argument,# pass it to gdb_breakpoint.proc runto { function args } { global gdb_prompt global decimal delete_breakpoints if ![gdb_breakpoint $function [lindex $args 0]] { return 0; } gdb_run_cmd # the "at foo.c:36" output we get with -g. # the "in func" output we get without -g. gdb_expect 30 { -re "Break.* at .*:$decimal.*$gdb_prompt $" { return 1 } -re "Breakpoint \[0-9\]*, \[0-9xa-f\]* in .*$gdb_prompt $" { return 1 } -re "$gdb_prompt $" { fail "running to $function in runto" return 0 } timeout { fail "running to $function in runto (timeout)" return 0 } } return 1}## runto_main -- ask gdb to run until we hit a breakpoint at main.# The case where the target uses stubs has to be handled# specially--if it uses stubs, assuming we hit# breakpoint() and just step out of the function.#proc runto_main { } { global gdb_prompt global decimal if ![target_info exists gdb_stub] { return [runto main] } delete_breakpoints gdb_step_for_stub; return 1}### Continue, and expect to hit a breakpoint.### Report a pass or fail, depending on whether it seems to have### worked. Use NAME as part of the test name; each call to### continue_to_breakpoint should use a NAME which is unique within### that test file.proc gdb_continue_to_breakpoint {name} { global gdb_prompt set full_name "continue to breakpoint: $name" send_gdb "continue\n" gdb_expect { -re "Breakpoint .* at .*\r\n$gdb_prompt $" { pass $full_name } -re ".*$gdb_prompt $" { fail $full_name } timeout { fail "$full_name (timeout)" } }}# gdb_internal_error_resync:## Answer the questions GDB asks after it reports an internal error# until we get back to a GDB prompt. Decline to quit the debugging# session, and decline to create a core file. Return non-zero if the# resync succeeds.## This procedure just answers whatever questions come up until it sees# a GDB prompt; it doesn't require you to have matched the input up to# any specific point. However, it only answers questions it sees in# the output itself, so if you've matched a question, you had better# answer it yourself before calling this.## You can use this function thus:## gdb_expect {# ...# -re ".*A problem internal to GDB has been detected" {# gdb_internal_error_resync# }# ...# }#proc gdb_internal_error_resync {} {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -