| # Copyright 1992-2021 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 |
| } |
| |
| # List of procs to run in gdb_finish. |
| set gdb_finish_hooks [list] |
| |
| # Variable in which we keep track of globals that are allowed to be live |
| # across test-cases. |
| array set gdb_persistent_globals {} |
| |
| # Mark variable names in ARG as a persistent global, and declare them as |
| # global in the calling context. Can be used to rewrite "global var_a var_b" |
| # into "gdb_persistent_global var_a var_b". |
| proc gdb_persistent_global { args } { |
| global gdb_persistent_globals |
| foreach varname $args { |
| uplevel 1 global $varname |
| set gdb_persistent_globals($varname) 1 |
| } |
| } |
| |
| # Mark variable names in ARG as a persistent global. |
| proc gdb_persistent_global_no_decl { args } { |
| global gdb_persistent_globals |
| foreach varname $args { |
| set gdb_persistent_globals($varname) 1 |
| } |
| } |
| |
| # Override proc load_lib. |
| rename load_lib saved_load_lib |
| # Run the runtest version of load_lib, and mark all variables that were |
| # created by this call as persistent. |
| proc load_lib { file } { |
| array set known_global {} |
| foreach varname [info globals] { |
| set known_globals($varname) 1 |
| } |
| |
| set code [catch "saved_load_lib $file" result] |
| |
| foreach varname [info globals] { |
| if { ![info exists known_globals($varname)] } { |
| gdb_persistent_global_no_decl $varname |
| } |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code error -errorinfo $errorInfo -errorcode $errorCode $result |
| } elseif {$code > 1} { |
| return -code $code $result |
| } |
| |
| return $result |
| } |
| |
| load_lib libgloss.exp |
| load_lib cache.exp |
| load_lib gdb-utils.exp |
| load_lib memory.exp |
| load_lib check-test-names.exp |
| |
| global GDB |
| |
| # The spawn ID used for I/O interaction with the inferior. For native |
| # targets, or remote targets that can do I/O through GDB |
| # (semi-hosting) this will be the same as the host/GDB's spawn ID. |
| # Otherwise, the board may set this to some other spawn ID. E.g., |
| # when debugging with GDBserver, this is set to GDBserver's spawn ID, |
| # so input/output is done on gdbserver's tty. |
| global inferior_spawn_id |
| |
| if [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" 2 |
| |
| # GDBFLAGS is available for the user to set on the command line. |
| # E.g. make check RUNTESTFLAGS=GDBFLAGS=mumble |
| # Testcases may use it to add additional flags, but they must: |
| # - append new flags, not overwrite |
| # - restore the original value when done |
| global GDBFLAGS |
| if ![info exists GDBFLAGS] { |
| set GDBFLAGS "" |
| } |
| verbose "using GDBFLAGS = $GDBFLAGS" 2 |
| |
| # Make the build data directory available to tests. |
| set BUILD_DATA_DIRECTORY "[pwd]/../data-directory" |
| |
| # INTERNAL_GDBFLAGS contains flags that the testsuite requires. |
| global INTERNAL_GDBFLAGS |
| if ![info exists INTERNAL_GDBFLAGS] { |
| set INTERNAL_GDBFLAGS \ |
| [join [list \ |
| "-nw" \ |
| "-nx" \ |
| "-data-directory $BUILD_DATA_DIRECTORY" \ |
| {-iex "set height 0"} \ |
| {-iex "set width 0"}]] |
| } |
| |
| # The variable gdb_prompt is a regexp which matches the gdb prompt. |
| # Set it if it is not already set. This is also set by default_gdb_init |
| # but it's not clear what removing one of them will break. |
| # See with_gdb_prompt for more details on prompt handling. |
| global gdb_prompt |
| if ![info exists gdb_prompt] then { |
| set gdb_prompt "\\(gdb\\)" |
| } |
| |
| # A regexp that matches the pagination prompt. |
| set pagination_prompt \ |
| "--Type <RET> for more, q to quit, c to continue without paging--" |
| |
| # The variable fullname_syntax_POSIX is a regexp which matches a POSIX |
| # absolute path ie. /foo/ |
| set fullname_syntax_POSIX {/[^\n]*/} |
| # The variable fullname_syntax_UNC is a regexp which matches a Windows |
| # UNC path ie. \\D\foo\ |
| set fullname_syntax_UNC {\\\\[^\\]+\\[^\n]+\\} |
| # 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 {\\[^\\][^\n]*\\} |
| # 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]:[^\n]*\\} |
| # 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 EXEEXT |
| global env |
| |
| if ![info exists env(EXEEXT)] { |
| set EXEEXT "" |
| } else { |
| set EXEEXT $env(EXEEXT) |
| } |
| |
| set octal "\[0-7\]+" |
| |
| set inferior_exited_re "(?:\\\[Inferior \[0-9\]+ \\(\[^\n\r\]*\\) exited)" |
| |
| # A regular expression that matches a value history number. |
| # E.g., $1, $2, etc. |
| set valnum_re "\\\$$decimal" |
| |
| ### Only procedures should come after this point. |
| |
| # |
| # gdb_version -- extract and print the version number of GDB |
| # |
| proc default_gdb_version {} { |
| global GDB |
| global INTERNAL_GDBFLAGS GDBFLAGS |
| global gdb_prompt |
| global inotify_pid |
| |
| if {[info exists inotify_pid]} { |
| eval exec kill $inotify_pid |
| } |
| |
| set output [remote_exec host "$GDB $INTERNAL_GDBFLAGS --version"] |
| 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 $INTERNAL_GDBFLAGS $GDBFLAGS\n" |
| } else { |
| clone_output "$GDB on remote host version $version $INTERNAL_GDBFLAGS $GDBFLAGS\n" |
| } |
| } |
| |
| proc gdb_version { } { |
| return [default_gdb_version] |
| } |
| |
| # |
| # gdb_unload -- unload a file if one is loaded |
| # Return 0 on success, -1 on error. |
| # |
| |
| proc gdb_unload {} { |
| 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.*Are you sure you want to change the file.*y or n. $" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| -re "Discard symbol table from .*y or n.*$" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| -re "$gdb_prompt $" {} |
| -re "A problem internal to GDB has been detected" { |
| perror "Couldn't unload file in $GDB (GDB internal error)." |
| gdb_internal_error_resync |
| return -1 |
| } |
| timeout { |
| perror "couldn't unload file in $GDB (timeout)." |
| return -1 |
| } |
| } |
| return 0 |
| } |
| |
| # 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 |
| # |
| set timeout 100 |
| |
| set msg "delete all breakpoints in delete_breakpoints" |
| set deleted 0 |
| gdb_test_multiple "delete breakpoints" "$msg" { |
| -re "Delete all breakpoints.*y or n.*$" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| -re "$gdb_prompt $" { |
| set deleted 1 |
| } |
| } |
| |
| if {$deleted} { |
| # Confirm with "info breakpoints". |
| set deleted 0 |
| set msg "info breakpoints" |
| gdb_test_multiple $msg $msg { |
| -re "No breakpoints or watchpoints..*$gdb_prompt $" { |
| set deleted 1 |
| } |
| -re "$gdb_prompt $" { |
| } |
| } |
| } |
| |
| if {!$deleted} { |
| perror "breakpoints not deleted" |
| } |
| } |
| |
| # Returns true iff the target supports using the "run" command. |
| |
| proc target_can_use_run_cmd {} { |
| if [target_info exists use_gdb_stub] { |
| # In this case, when we connect, the inferior is already |
| # running. |
| return 0 |
| } |
| |
| # Assume yes. |
| return 1 |
| } |
| |
| # Generic run command. |
| # |
| # Return 0 if we could start the program, -1 if we could not. |
| # |
| # The second pattern below matches up to the first newline *only*. |
| # Using ``.*$'' could swallow up output that we attempt to match |
| # elsewhere. |
| # |
| # INFERIOR_ARGS is passed as arguments to the start command, so may contain |
| # inferior arguments. |
| # |
| # N.B. This function does not wait for gdb to return to the prompt, |
| # that is the caller's responsibility. |
| |
| proc gdb_run_cmd { {inferior_args {}} } { |
| global gdb_prompt use_gdb_stub |
| |
| foreach command [gdb_init_commands] { |
| send_gdb "$command\n" |
| gdb_expect 30 { |
| -re "$gdb_prompt $" { } |
| default { |
| perror "gdb_init_command for target failed" |
| return |
| } |
| } |
| } |
| |
| if $use_gdb_stub { |
| if [target_info exists gdb,do_reload_on_run] { |
| if { [gdb_reload $inferior_args] != 0 } { |
| return -1 |
| } |
| send_gdb "continue\n" |
| gdb_expect 60 { |
| -re "Continu\[^\r\n\]*\[\r\n\]" {} |
| default {} |
| } |
| return 0 |
| } |
| |
| 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 -1 |
| } |
| 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 -1 |
| } |
| -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" answer |
| } |
| -re "The program is not being run.*$gdb_prompt $" { |
| if { [gdb_reload $inferior_args] != 0 } { |
| return -1 |
| } |
| send_gdb "jump *$start\n" |
| } |
| timeout { |
| perror "Jump to start() failed (timeout)" |
| return -1 |
| } |
| } |
| } |
| |
| return 0 |
| } |
| |
| if [target_info exists gdb,do_reload_on_run] { |
| if { [gdb_reload $inferior_args] != 0 } { |
| return -1 |
| } |
| } |
| send_gdb "run $inferior_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" answer |
| exp_continue |
| } |
| -notransfer -re "Starting program: \[^\r\n\]*" {} |
| -notransfer -re "$gdb_prompt $" { |
| # There is no more input expected. |
| } |
| -notransfer -re "A problem internal to GDB has been detected" { |
| # Let caller handle this. |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Generic start command. Return 0 if we could start the program, -1 |
| # if we could not. |
| # |
| # INFERIOR_ARGS is passed as arguments to the start command, so may contain |
| # inferior arguments. |
| # |
| # N.B. This function does not wait for gdb to return to the prompt, |
| # that is the caller's responsibility. |
| |
| proc gdb_start_cmd { {inferior_args {}} } { |
| global gdb_prompt use_gdb_stub |
| |
| foreach command [gdb_init_commands] { |
| send_gdb "$command\n" |
| gdb_expect 30 { |
| -re "$gdb_prompt $" { } |
| default { |
| perror "gdb_init_command for target failed" |
| return -1 |
| } |
| } |
| } |
| |
| if $use_gdb_stub { |
| return -1 |
| } |
| |
| send_gdb "start $inferior_args\n" |
| # 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" answer |
| exp_continue |
| } |
| -notransfer -re "Starting program: \[^\r\n\]*" { |
| return 0 |
| } |
| } |
| return -1 |
| } |
| |
| # Generic starti command. Return 0 if we could start the program, -1 |
| # if we could not. |
| # |
| # INFERIOR_ARGS is passed as arguments to the starti command, so may contain |
| # inferior arguments. |
| # |
| # N.B. This function does not wait for gdb to return to the prompt, |
| # that is the caller's responsibility. |
| |
| proc gdb_starti_cmd { {inferior_args {}} } { |
| global gdb_prompt use_gdb_stub |
| |
| foreach command [gdb_init_commands] { |
| send_gdb "$command\n" |
| gdb_expect 30 { |
| -re "$gdb_prompt $" { } |
| default { |
| perror "gdb_init_command for target failed" |
| return -1 |
| } |
| } |
| } |
| |
| if $use_gdb_stub { |
| return -1 |
| } |
| |
| send_gdb "starti $inferior_args\n" |
| gdb_expect 60 { |
| -re "The program .* has been started already.*y or n. $" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| -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, temporary, |
| # message, no-message and qualified. |
| # The result is 1 for success, 0 for failure. |
| # |
| # Note: The handling of message vs no-message is messed up, but it's based |
| # on historical usage. By default this function does not print passes, |
| # only fails. |
| # no-message: turns off printing of fails (and passes, but they're already off) |
| # message: turns on printing of passes (and fails, but they're already on) |
| |
| proc gdb_breakpoint { function args } { |
| global gdb_prompt |
| global decimal |
| |
| set pending_response n |
| if {[lsearch -exact $args allow-pending] != -1} { |
| set pending_response y |
| } |
| |
| set break_command "break" |
| set break_message "Breakpoint" |
| if {[lsearch -exact $args temporary] != -1} { |
| set break_command "tbreak" |
| set break_message "Temporary breakpoint" |
| } |
| |
| if {[lsearch -exact $args qualified] != -1} { |
| append break_command " -qualified" |
| } |
| |
| set print_pass 0 |
| set print_fail 1 |
| set no_message_loc [lsearch -exact $args no-message] |
| set message_loc [lsearch -exact $args message] |
| # The last one to appear in args wins. |
| if { $no_message_loc > $message_loc } { |
| set print_fail 0 |
| } elseif { $message_loc > $no_message_loc } { |
| set print_pass 1 |
| } |
| |
| set test_name "setting breakpoint at $function" |
| |
| 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 "$break_message \[0-9\]* at .*: file .*, line $decimal.\r\n$gdb_prompt $" {} |
| -re "$break_message \[0-9\]*: file .*, line $decimal.\r\n$gdb_prompt $" {} |
| -re "$break_message \[0-9\]* at .*$gdb_prompt $" {} |
| -re "$break_message \[0-9\]* \\(.*\\) pending.*$gdb_prompt $" { |
| if {$pending_response == "n"} { |
| if { $print_fail } { |
| fail $test_name |
| } |
| return 0 |
| } |
| } |
| -re "Make breakpoint pending.*y or \\\[n\\\]. $" { |
| send_gdb "$pending_response\n" |
| exp_continue |
| } |
| -re "A problem internal to GDB has been detected" { |
| if { $print_fail } { |
| fail "$test_name (GDB internal error)" |
| } |
| gdb_internal_error_resync |
| return 0 |
| } |
| -re "$gdb_prompt $" { |
| if { $print_fail } { |
| fail $test_name |
| } |
| return 0 |
| } |
| eof { |
| perror "GDB process no longer exists" |
| global gdb_spawn_id |
| set wait_status [wait -i $gdb_spawn_id] |
| verbose -log "GDB process exited with wait status $wait_status" |
| if { $print_fail } { |
| fail "$test_name (eof)" |
| } |
| return 0 |
| } |
| timeout { |
| if { $print_fail } { |
| fail "$test_name (timeout)" |
| } |
| return 0 |
| } |
| } |
| if { $print_pass } { |
| pass $test_name |
| } |
| 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 are additional arguments, pass them to gdb_breakpoint. |
| # We recognize no-message/message ourselves. |
| # The default is no-message. |
| # no-message is messed up here, like gdb_breakpoint: to preserve |
| # historical usage fails are always printed by default. |
| # no-message: turns off printing of fails (and passes, but they're already off) |
| # message: turns on printing of passes (and fails, but they're already on) |
| |
| proc runto { function args } { |
| global gdb_prompt |
| global decimal |
| |
| delete_breakpoints |
| |
| # Default to "no-message". |
| set args "no-message $args" |
| |
| set print_pass 0 |
| set print_fail 1 |
| set no_message_loc [lsearch -exact $args no-message] |
| set message_loc [lsearch -exact $args message] |
| # The last one to appear in args wins. |
| if { $no_message_loc > $message_loc } { |
| set print_fail 0 |
| } elseif { $message_loc > $no_message_loc } { |
| set print_pass 1 |
| } |
| |
| set test_name "running to $function in runto" |
| |
| # We need to use eval here to pass our varargs args to gdb_breakpoint |
| # which is also a varargs function. |
| # But we also have to be careful because $function may have multiple |
| # elements, and we don't want Tcl to move the remaining elements after |
| # the first to $args. That is why $function is wrapped in {}. |
| if ![eval gdb_breakpoint {$function} $args] { |
| 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 $" { |
| if { $print_pass } { |
| pass $test_name |
| } |
| return 1 |
| } |
| -re "Breakpoint \[0-9\]*, \[0-9xa-f\]* in .*$gdb_prompt $" { |
| if { $print_pass } { |
| pass $test_name |
| } |
| return 1 |
| } |
| -re "The target does not support running in non-stop mode.\r\n$gdb_prompt $" { |
| if { $print_fail } { |
| unsupported "non-stop mode not supported" |
| } |
| return 0 |
| } |
| -re ".*A problem internal to GDB has been detected" { |
| # Always emit a FAIL if we encounter an internal error: internal |
| # errors are never expected. |
| fail "$test_name (GDB internal error)" |
| gdb_internal_error_resync |
| return 0 |
| } |
| -re "$gdb_prompt $" { |
| if { $print_fail } { |
| fail $test_name |
| } |
| return 0 |
| } |
| eof { |
| if { $print_fail } { |
| fail "$test_name (eof)" |
| } |
| return 0 |
| } |
| timeout { |
| if { $print_fail } { |
| fail "$test_name (timeout)" |
| } |
| return 0 |
| } |
| } |
| if { $print_pass } { |
| pass $test_name |
| } |
| return 1 |
| } |
| |
| # Ask gdb to run until we hit a breakpoint at main. |
| # |
| # N.B. This function deletes all existing breakpoints. |
| # If you don't want that, use gdb_start_cmd. |
| |
| proc runto_main { } { |
| return [runto main qualified] |
| } |
| |
| ### 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 {location_pattern .*}} { |
| global gdb_prompt |
| set full_name "continue to breakpoint: $name" |
| |
| set kfail_pattern "Process record does not support instruction 0xfae64 at.*" |
| gdb_test_multiple "continue" $full_name { |
| -re "(?:Breakpoint|Temporary breakpoint) .* (at|in) $location_pattern\r\n$gdb_prompt $" { |
| pass $full_name |
| } |
| -re "\[\r\n\]*(?:$kfail_pattern)\[\r\n\]+$gdb_prompt $" { |
| kfail "gdb/25038" $full_name |
| } |
| } |
| } |
| |
| |
| # 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 {} { |
| global gdb_prompt |
| |
| verbose -log "Resyncing due to internal error." |
| |
| set count 0 |
| while {$count < 10} { |
| gdb_expect { |
| -re "Quit this debugging session\\? \\(y or n\\) $" { |
| send_gdb "n\n" answer |
| incr count |
| } |
| -re "Create a core file of GDB\\? \\(y or n\\) $" { |
| send_gdb "n\n" answer |
| incr count |
| } |
| -re "$gdb_prompt $" { |
| # We're resynchronized. |
| return 1 |
| } |
| timeout { |
| perror "Could not resync from internal error (timeout)" |
| return 0 |
| } |
| eof { |
| perror "Could not resync from internal error (eof)" |
| return 0 |
| } |
| } |
| } |
| perror "Could not resync from internal error (resync count exceeded)" |
| return 0 |
| } |
| |
| |
| # gdb_test_multiple COMMAND MESSAGE [ -prompt PROMPT_REGEXP] [ -lbl ] |
| # EXPECT_ARGUMENTS |
| # Send a command to gdb; test the result. |
| # |
| # COMMAND is the command to execute, send to GDB with send_gdb. If |
| # this is the null string no command is sent. |
| # MESSAGE is a message to be printed with the built-in failure patterns |
| # if one of them matches. If MESSAGE is empty COMMAND will be used. |
| # -prompt PROMPT_REGEXP specifies a regexp matching the expected prompt |
| # after the command output. If empty, defaults to "$gdb_prompt $". |
| # -lbl specifies that line-by-line matching will be used. |
| # EXPECT_ARGUMENTS will be fed to expect in addition to the standard |
| # patterns. Pattern elements will be evaluated in the caller's |
| # context; action elements will be executed in the caller's context. |
| # Unlike patterns for gdb_test, these patterns should generally include |
| # the final newline and prompt. |
| # |
| # Returns: |
| # 1 if the test failed, according to a built-in failure pattern |
| # 0 if only user-supplied patterns matched |
| # -1 if there was an internal error. |
| # |
| # You can use this function thus: |
| # |
| # gdb_test_multiple "print foo" "test foo" { |
| # -re "expected output 1" { |
| # pass "test foo" |
| # } |
| # -re "expected output 2" { |
| # fail "test foo" |
| # } |
| # } |
| # |
| # Within action elements you can also make use of the variable |
| # gdb_test_name. This variable is setup automatically by |
| # gdb_test_multiple, and contains the value of MESSAGE. You can then |
| # write this, which is equivalent to the above: |
| # |
| # gdb_test_multiple "print foo" "test foo" { |
| # -re "expected output 1" { |
| # pass $gdb_test_name |
| # } |
| # -re "expected output 2" { |
| # fail $gdb_test_name |
| # } |
| # } |
| # |
| # Like with "expect", you can also specify the spawn id to match with |
| # -i "$id". Interesting spawn ids are $inferior_spawn_id and |
| # $gdb_spawn_id. The former matches inferior I/O, while the latter |
| # matches GDB I/O. E.g.: |
| # |
| # send_inferior "hello\n" |
| # gdb_test_multiple "continue" "test echo" { |
| # -i "$inferior_spawn_id" -re "^hello\r\nhello\r\n$" { |
| # pass "got echo" |
| # } |
| # -i "$gdb_spawn_id" -re "Breakpoint.*$gdb_prompt $" { |
| # fail "hit breakpoint" |
| # } |
| # } |
| # |
| # The standard patterns, such as "Inferior exited..." and "A problem |
| # ...", all being implicitly appended to that list. These are always |
| # expected from $gdb_spawn_id. IOW, callers do not need to worry |
| # about resetting "-i" back to $gdb_spawn_id explicitly. |
| # |
| # In EXPECT_ARGUMENTS we can use a -wrap pattern flag, that wraps the regexp |
| # pattern as gdb_test wraps its message argument. |
| # This allows us to rewrite: |
| # gdb_test <command> <pattern> <message> |
| # into: |
| # gdb_test_multiple <command> <message> { |
| # -re -wrap <pattern> { |
| # pass $gdb_test_name |
| # } |
| # } |
| # |
| # In EXPECT_ARGUMENTS, a pattern flag -early can be used. It makes sure the |
| # pattern is inserted before any implicit pattern added by gdb_test_multiple. |
| # Using this pattern flag, we can f.i. setup a kfail for an assertion failure |
| # <assert> during gdb_continue_to_breakpoint by the rewrite: |
| # gdb_continue_to_breakpoint <msg> <pattern> |
| # into: |
| # set breakpoint_pattern "(?:Breakpoint|Temporary breakpoint) .* (at|in)" |
| # gdb_test_multiple "continue" "continue to breakpoint: <msg>" { |
| # -early -re "internal-error: <assert>" { |
| # setup_kfail gdb/nnnnn "*-*-*" |
| # exp_continue |
| # } |
| # -re "$breakpoint_pattern <pattern>\r\n$gdb_prompt $" { |
| # pass $gdb_test_name |
| # } |
| # } |
| # |
| proc gdb_test_multiple { command message args } { |
| global verbose use_gdb_stub |
| global gdb_prompt pagination_prompt |
| global GDB |
| global gdb_spawn_id |
| global inferior_exited_re |
| upvar timeout timeout |
| upvar expect_out expect_out |
| global any_spawn_id |
| |
| set line_by_line 0 |
| set prompt_regexp "" |
| for {set i 0} {$i < [llength $args]} {incr i} { |
| set arg [lindex $args $i] |
| if { $arg == "-prompt" } { |
| incr i |
| set prompt_regexp [lindex $args $i] |
| } elseif { $arg == "-lbl" } { |
| set line_by_line 1 |
| } else { |
| set user_code $arg |
| break |
| } |
| } |
| if { [expr $i + 1] < [llength $args] } { |
| error "Too many arguments to gdb_test_multiple" |
| } elseif { ![info exists user_code] } { |
| error "Too few arguments to gdb_test_multiple" |
| } |
| |
| if { "$prompt_regexp" == "" } { |
| set prompt_regexp "$gdb_prompt $" |
| } |
| |
| if { $message == "" } { |
| set message $command |
| } |
| |
| if [string match "*\[\r\n\]" $command] { |
| error "Invalid trailing newline in \"$message\" test" |
| } |
| |
| if [string match "*\[\r\n\]*" $message] { |
| error "Invalid newline in \"$message\" test" |
| } |
| |
| if {$use_gdb_stub |
| && [regexp -nocase {^\s*(r|run|star|start|at|att|atta|attac|attach)\M} \ |
| $command]} { |
| error "gdbserver does not support $command without extended-remote" |
| } |
| |
| # TCL/EXPECT WART ALERT |
| # Expect does something very strange when it receives a single braced |
| # argument. It splits it along word separators and performs substitutions. |
| # This means that { "[ab]" } is evaluated as "[ab]", but { "\[ab\]" } is |
| # evaluated as "\[ab\]". But that's not how TCL normally works; inside a |
| # double-quoted list item, "\[ab\]" is just a long way of representing |
| # "[ab]", because the backslashes will be removed by lindex. |
| |
| # Unfortunately, there appears to be no easy way to duplicate the splitting |
| # that expect will do from within TCL. And many places make use of the |
| # "\[0-9\]" construct, so we need to support that; and some places make use |
| # of the "[func]" construct, so we need to support that too. In order to |
| # get this right we have to substitute quoted list elements differently |
| # from braced list elements. |
| |
| # We do this roughly the same way that Expect does it. We have to use two |
| # lists, because if we leave unquoted newlines in the argument to uplevel |
| # they'll be treated as command separators, and if we escape newlines |
| # we mangle newlines inside of command blocks. This assumes that the |
| # input doesn't contain a pattern which contains actual embedded newlines |
| # at this point! |
| |
| regsub -all {\n} ${user_code} { } subst_code |
| set subst_code [uplevel list $subst_code] |
| |
| set processed_code "" |
| set early_processed_code "" |
| # The variable current_list holds the name of the currently processed |
| # list, either processed_code or early_processed_code. |
| set current_list "processed_code" |
| set patterns "" |
| set expecting_action 0 |
| set expecting_arg 0 |
| set wrap_pattern 0 |
| foreach item $user_code subst_item $subst_code { |
| if { $item == "-n" || $item == "-notransfer" || $item == "-nocase" } { |
| lappend $current_list $item |
| continue |
| } |
| if { $item == "-indices" || $item == "-re" || $item == "-ex" } { |
| lappend $current_list $item |
| continue |
| } |
| if { $item == "-early" } { |
| set current_list "early_processed_code" |
| continue |
| } |
| if { $item == "-timeout" || $item == "-i" } { |
| set expecting_arg 1 |
| lappend $current_list $item |
| continue |
| } |
| if { $item == "-wrap" } { |
| set wrap_pattern 1 |
| continue |
| } |
| if { $expecting_arg } { |
| set expecting_arg 0 |
| lappend $current_list $subst_item |
| continue |
| } |
| if { $expecting_action } { |
| lappend $current_list "uplevel [list $item]" |
| set expecting_action 0 |
| # Cosmetic, no effect on the list. |
| append $current_list "\n" |
| # End the effect of -early, it only applies to one action. |
| set current_list "processed_code" |
| continue |
| } |
| set expecting_action 1 |
| if { $wrap_pattern } { |
| # Wrap subst_item as is done for the gdb_test PATTERN argument. |
| lappend $current_list \ |
| "\[\r\n\]*(?:$subst_item)\[\r\n\]+$gdb_prompt $" |
| set wrap_pattern 0 |
| } else { |
| lappend $current_list $subst_item |
| } |
| if {$patterns != ""} { |
| append patterns "; " |
| } |
| append patterns "\"$subst_item\"" |
| } |
| |
| # Also purely cosmetic. |
| regsub -all {\r} $patterns {\\r} patterns |
| regsub -all {\n} $patterns {\\n} patterns |
| |
| if $verbose>2 then { |
| send_user "Sending \"$command\" to gdb\n" |
| send_user "Looking to match \"$patterns\"\n" |
| send_user "Message is \"$message\"\n" |
| } |
| |
| set result -1 |
| set string "${command}\n" |
| if { $command != "" } { |
| set multi_line_re "\[\r\n\] *>" |
| while { "$string" != "" } { |
| set foo [string first "\n" "$string"] |
| set len [string length "$string"] |
| if { $foo < [expr $len - 1] } { |
| set str [string range "$string" 0 $foo] |
| if { [send_gdb "$str"] != "" } { |
| perror "Couldn't send $command to GDB." |
| } |
| # since we're checking if each line of the multi-line |
| # command are 'accepted' by GDB here, |
| # we need to set -notransfer expect option so that |
| # command output is not lost for pattern matching |
| # - guo |
| gdb_expect 2 { |
| -notransfer -re "$multi_line_re$" { verbose "partial: match" 3 } |
| timeout { verbose "partial: timeout" 3 } |
| } |
| set string [string range "$string" [expr $foo + 1] end] |
| set multi_line_re "$multi_line_re.*\[\r\n\] *>" |
| } else { |
| break |
| } |
| } |
| if { "$string" != "" } { |
| if { [send_gdb "$string"] != "" } { |
| perror "Couldn't send $command to GDB." |
| } |
| } |
| } |
| |
| set code $early_processed_code |
| append code { |
| -re ".*A problem internal to GDB has been detected" { |
| fail "$message (GDB internal error)" |
| gdb_internal_error_resync |
| set result -1 |
| } |
| -re "\\*\\*\\* DOSEXIT code.*" { |
| if { $message != "" } { |
| fail "$message" |
| } |
| set result -1 |
| } |
| } |
| append code $processed_code |
| |
| # Reset the spawn id, in case the processed code used -i. |
| append code { |
| -i "$gdb_spawn_id" |
| } |
| |
| append code { |
| -re "Ending remote debugging.*$prompt_regexp" { |
| if ![isnative] then { |
| warning "Can`t communicate to remote target." |
| } |
| gdb_exit |
| gdb_start |
| set result -1 |
| } |
| -re "Undefined\[a-z\]* command:.*$prompt_regexp" { |
| perror "Undefined command \"$command\"." |
| fail "$message" |
| set result 1 |
| } |
| -re "Ambiguous command.*$prompt_regexp" { |
| perror "\"$command\" is not a unique command name." |
| fail "$message" |
| set result 1 |
| } |
| -re "$inferior_exited_re with code \[0-9\]+.*$prompt_regexp" { |
| if ![string match "" $message] then { |
| set errmsg "$message (the program exited)" |
| } else { |
| set errmsg "$command (the program exited)" |
| } |
| fail "$errmsg" |
| set result -1 |
| } |
| -re "$inferior_exited_re normally.*$prompt_regexp" { |
| if ![string match "" $message] then { |
| set errmsg "$message (the program exited)" |
| } else { |
| set errmsg "$command (the program exited)" |
| } |
| fail "$errmsg" |
| set result -1 |
| } |
| -re "The program is not being run.*$prompt_regexp" { |
| if ![string match "" $message] then { |
| set errmsg "$message (the program is no longer running)" |
| } else { |
| set errmsg "$command (the program is no longer running)" |
| } |
| fail "$errmsg" |
| set result -1 |
| } |
| -re "\r\n$prompt_regexp" { |
| if ![string match "" $message] then { |
| fail "$message" |
| } |
| set result 1 |
| } |
| -re "$pagination_prompt" { |
| send_gdb "\n" |
| perror "Window too small." |
| fail "$message" |
| set result -1 |
| } |
| -re "\\((y or n|y or \\\[n\\\]|\\\[y\\\] or n)\\) " { |
| send_gdb "n\n" answer |
| gdb_expect -re "$prompt_regexp" |
| fail "$message (got interactive prompt)" |
| set result -1 |
| } |
| -re "\\\[0\\\] cancel\r\n\\\[1\\\] all.*\r\n> $" { |
| send_gdb "0\n" |
| gdb_expect -re "$prompt_regexp" |
| fail "$message (got breakpoint menu)" |
| set result -1 |
| } |
| |
| -i $gdb_spawn_id |
| eof { |
| perror "GDB process no longer exists" |
| set wait_status [wait -i $gdb_spawn_id] |
| verbose -log "GDB process exited with wait status $wait_status" |
| if { $message != "" } { |
| fail "$message" |
| } |
| return -1 |
| } |
| } |
| |
| if {$line_by_line} { |
| append code { |
| -re "\r\n\[^\r\n\]*(?=\r\n)" { |
| exp_continue |
| } |
| } |
| } |
| |
| # Now patterns that apply to any spawn id specified. |
| append code { |
| -i $any_spawn_id |
| eof { |
| perror "Process no longer exists" |
| if { $message != "" } { |
| fail "$message" |
| } |
| return -1 |
| } |
| full_buffer { |
| perror "internal buffer is full." |
| fail "$message" |
| set result -1 |
| } |
| timeout { |
| if ![string match "" $message] then { |
| fail "$message (timeout)" |
| } |
| set result 1 |
| } |
| } |
| |
| # remote_expect calls the eof section if there is an error on the |
| # expect call. We already have eof sections above, and we don't |
| # want them to get called in that situation. Since the last eof |
| # section becomes the error section, here we define another eof |
| # section, but with an empty spawn_id list, so that it won't ever |
| # match. |
| append code { |
| -i "" eof { |
| # This comment is here because the eof section must not be |
| # the empty string, otherwise remote_expect won't realize |
| # it exists. |
| } |
| } |
| |
| # Create gdb_test_name in the parent scope. If this variable |
| # already exists, which it might if we have nested calls to |
| # gdb_test_multiple, then preserve the old value, otherwise, |
| # create a new variable in the parent scope. |
| upvar gdb_test_name gdb_test_name |
| if { [info exists gdb_test_name] } { |
| set gdb_test_name_old "$gdb_test_name" |
| } |
| set gdb_test_name "$message" |
| |
| set result 0 |
| set code [catch {gdb_expect $code} string] |
| |
| # Clean up the gdb_test_name variable. If we had a |
| # previous value then restore it, otherwise, delete the variable |
| # from the parent scope. |
| if { [info exists gdb_test_name_old] } { |
| set gdb_test_name "$gdb_test_name_old" |
| } else { |
| unset gdb_test_name |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code error -errorinfo $errorInfo -errorcode $errorCode $string |
| } elseif {$code > 1} { |
| return -code $code $string |
| } |
| return $result |
| } |
| |
| # Usage: gdb_test_multiline NAME INPUT RESULT {INPUT RESULT} ... |
| # Run a test named NAME, consisting of multiple lines of input. |
| # After each input line INPUT, search for result line RESULT. |
| # Succeed if all results are seen; fail otherwise. |
| |
| proc gdb_test_multiline { name args } { |
| global gdb_prompt |
| set inputnr 0 |
| foreach {input result} $args { |
| incr inputnr |
| if {[gdb_test_multiple $input "$name: input $inputnr: $input" { |
| -re "\[\r\n\]*($result)\[\r\n\]+($gdb_prompt | *>)$" { |
| pass $gdb_test_name |
| } |
| }]} { |
| return 1 |
| } |
| } |
| return 0 |
| } |
| |
| |
| # gdb_test COMMAND PATTERN MESSAGE QUESTION RESPONSE |
| # Send a command to gdb; test the result. |
| # |
| # COMMAND is the command to execute, send to GDB with send_gdb. If |
| # this is the null string no command is sent. |
| # PATTERN is the pattern to match for a PASS, and must NOT include |
| # the \r\n sequence immediately before the gdb prompt. This argument |
| # may be omitted to just match the prompt, ignoring whatever output |
| # precedes it. |
| # MESSAGE is an optional message to be printed. If this is |
| # omitted, then the pass/fail messages use the command string as the |
| # message. (If this is the empty string, then sometimes we don't |
| # call pass or fail at all; I don't understand this at all.) |
| # QUESTION is a question GDB may ask in response to COMMAND, like |
| # "are you sure?" |
| # RESPONSE is the response to send if QUESTION appears. |
| # |
| # Returns: |
| # 1 if the test failed, |
| # 0 if the test passes, |
| # -1 if there was an internal error. |
| # |
| proc gdb_test { args } { |
| global gdb_prompt |
| upvar timeout timeout |
| |
| if [llength $args]>2 then { |
| set message [lindex $args 2] |
| } else { |
| set message [lindex $args 0] |
| } |
| set command [lindex $args 0] |
| set pattern [lindex $args 1] |
| |
| set user_code {} |
| lappend user_code { |
| -re "\[\r\n\]*(?:$pattern)\[\r\n\]+$gdb_prompt $" { |
| if ![string match "" $message] then { |
| pass "$message" |
| } |
| } |
| } |
| |
| if { [llength $args] == 5 } { |
| set question_string [lindex $args 3] |
| set response_string [lindex $args 4] |
| lappend user_code { |
| -re "(${question_string})$" { |
| send_gdb "$response_string\n" |
| exp_continue |
| } |
| } |
| } |
| |
| set user_code [join $user_code] |
| return [gdb_test_multiple $command $message $user_code] |
| } |
| |
| # Return 1 if version MAJOR.MINOR is at least AT_LEAST_MAJOR.AT_LEAST_MINOR. |
| proc version_at_least { major minor at_least_major at_least_minor} { |
| if { $major > $at_least_major } { |
| return 1 |
| } elseif { $major == $at_least_major \ |
| && $minor >= $at_least_minor } { |
| return 1 |
| } else { |
| return 0 |
| } |
| } |
| |
| # Return 1 if tcl version used is at least MAJOR.MINOR |
| proc tcl_version_at_least { major minor } { |
| global tcl_version |
| regexp {^([0-9]+)\.([0-9]+)$} $tcl_version \ |
| dummy tcl_version_major tcl_version_minor |
| return [version_at_least $tcl_version_major $tcl_version_minor \ |
| $major $minor] |
| } |
| |
| if { [tcl_version_at_least 8 5] == 0 } { |
| # lrepeat was added in tcl 8.5. Only add if missing. |
| proc lrepeat { n element } { |
| if { [string is integer -strict $n] == 0 } { |
| error "expected integer but got \"$n\"" |
| } |
| if { $n < 0 } { |
| error "bad count \"$n\": must be integer >= 0" |
| } |
| set res [list] |
| for {set i 0} {$i < $n} {incr i} { |
| lappend res $element |
| } |
| return $res |
| } |
| } |
| |
| # gdb_test_no_output COMMAND MESSAGE |
| # Send a command to GDB and verify that this command generated no output. |
| # |
| # See gdb_test_multiple for a description of the COMMAND and MESSAGE |
| # parameters. If MESSAGE is ommitted, then COMMAND will be used as |
| # the message. (If MESSAGE is the empty string, then sometimes we do not |
| # call pass or fail at all; I don't understand this at all.) |
| |
| proc gdb_test_no_output { args } { |
| global gdb_prompt |
| set command [lindex $args 0] |
| if [llength $args]>1 then { |
| set message [lindex $args 1] |
| } else { |
| set message $command |
| } |
| |
| set command_regex [string_to_regexp $command] |
| gdb_test_multiple $command $message { |
| -re "^$command_regex\r\n$gdb_prompt $" { |
| if ![string match "" $message] then { |
| pass "$message" |
| } |
| } |
| } |
| } |
| |
| # Send a command and then wait for a sequence of outputs. |
| # This is useful when the sequence is long and contains ".*", a single |
| # regexp to match the entire output can get a timeout much easier. |
| # |
| # COMMAND is the command to execute, send to GDB with send_gdb. If |
| # this is the null string no command is sent. |
| # TEST_NAME is passed to pass/fail. COMMAND is used if TEST_NAME is "". |
| # EXPECTED_OUTPUT_LIST is a list of regexps of expected output, which are |
| # processed in order, and all must be present in the output. |
| # |
| # The -prompt switch can be used to override the prompt expected at the end of |
| # the output sequence. |
| # |
| # It is unnecessary to specify ".*" at the beginning or end of any regexp, |
| # there is an implicit ".*" between each element of EXPECTED_OUTPUT_LIST. |
| # There is also an implicit ".*" between the last regexp and the gdb prompt. |
| # |
| # Like gdb_test and gdb_test_multiple, the output is expected to end with the |
| # gdb prompt, which must not be specified in EXPECTED_OUTPUT_LIST. |
| # |
| # Returns: |
| # 1 if the test failed, |
| # 0 if the test passes, |
| # -1 if there was an internal error. |
| |
| proc gdb_test_sequence { args } { |
| global gdb_prompt |
| |
| parse_args {{prompt ""}} |
| |
| if { $prompt == "" } { |
| set prompt "$gdb_prompt $" |
| } |
| |
| if { [llength $args] != 3 } { |
| error "Unexpected # of arguments, expecting: COMMAND TEST_NAME EXPECTED_OUTPUT_LIST" |
| } |
| |
| lassign $args command test_name expected_output_list |
| |
| if { $test_name == "" } { |
| set test_name $command |
| } |
| |
| lappend expected_output_list ""; # implicit ".*" before gdb prompt |
| |
| if { $command != "" } { |
| send_gdb "$command\n" |
| } |
| |
| return [gdb_expect_list $test_name $prompt $expected_output_list] |
| } |
| |
| |
| # Match output of COMMAND using RE. Read output line-by-line. |
| # Report pass/fail with MESSAGE. |
| # For a command foo with output: |
| # (gdb) foo^M |
| # <line1>^M |
| # <line2>^M |
| # (gdb) |
| # the portion matched using RE is: |
| # '<line1>^M |
| # <line2>^M |
| # ' |
| # |
| # Optionally, additional -re-not <regexp> arguments can be specified, to |
| # ensure that a regexp is not match by the COMMAND output. |
| # Such an additional argument generates an additional PASS/FAIL of the form: |
| # PASS: test-case.exp: $message: pattern not matched: <regexp> |
| |
| proc gdb_test_lines { command message re args } { |
| set re_not [list] |
| |
| for {set i 0} {$i < [llength $args]} {incr i} { |
| set arg [lindex $args $i] |
| if { $arg == "-re-not" } { |
| incr i |
| if { [llength $args] == $i } { |
| error "Missing argument for -re-not" |
| break |
| } |
| set arg [lindex $args $i] |
| lappend re_not $arg |
| } else { |
| error "Unhandled argument: $arg" |
| } |
| } |
| |
| if { $message == ""} { |
| set message $command |
| } |
| |
| set lines "" |
| gdb_test_multiple $command $message { |
| -re "\r\n(\[^\r\n\]*)(?=\r\n)" { |
| set line $expect_out(1,string) |
| if { $lines eq "" } { |
| append lines "$line" |
| } else { |
| append lines "\r\n$line" |
| } |
| exp_continue |
| } |
| -re -wrap "" { |
| append lines "\r\n" |
| } |
| } |
| |
| gdb_assert { [regexp $re $lines] } $message |
| |
| foreach re $re_not { |
| gdb_assert { ![regexp $re $lines] } "$message: pattern not matched: $re" |
| } |
| } |
| |
| # Test that a command gives an error. For pass or fail, return |
| # a 1 to indicate that more tests can proceed. However a timeout |
| # is a serious error, generates a special fail message, and causes |
| # a 0 to be returned to indicate that more tests are likely to fail |
| # as well. |
| |
| proc test_print_reject { args } { |
| global gdb_prompt |
| global verbose |
| |
| if [llength $args]==2 then { |
| set expectthis [lindex $args 1] |
| } else { |
| set expectthis "should never match this bogus string" |
| } |
| set sendthis [lindex $args 0] |
| if $verbose>2 then { |
| send_user "Sending \"$sendthis\" to gdb\n" |
| send_user "Looking to match \"$expectthis\"\n" |
| } |
| send_gdb "$sendthis\n" |
| #FIXME: Should add timeout as parameter. |
| gdb_expect { |
| -re "A .* in expression.*\\.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "Invalid syntax in expression.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "Junk after end of expression.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "Invalid number.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "Invalid character constant.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "No symbol table is loaded.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "No symbol .* in current context.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "Unmatched single quote.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "A character constant must contain at least one character.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re "$expectthis.*$gdb_prompt $" { |
| pass "reject $sendthis" |
| return 1 |
| } |
| -re ".*$gdb_prompt $" { |
| fail "reject $sendthis" |
| return 1 |
| } |
| default { |
| fail "reject $sendthis (eof or timeout)" |
| return 0 |
| } |
| } |
| } |
| |
| |
| # Same as gdb_test, but the second parameter is not a regexp, |
| # but a string that must match exactly. |
| |
| proc gdb_test_exact { args } { |
| upvar timeout timeout |
| |
| set command [lindex $args 0] |
| |
| # This applies a special meaning to a null string pattern. Without |
| # this, "$pattern\r\n$gdb_prompt $" will match anything, including error |
| # messages from commands that should have no output except a new |
| # prompt. With this, only results of a null string will match a null |
| # string pattern. |
| |
| set pattern [lindex $args 1] |
| if [string match $pattern ""] { |
| set pattern [string_to_regexp [lindex $args 0]] |
| } else { |
| set pattern [string_to_regexp [lindex $args 1]] |
| } |
| |
| # It is most natural to write the pattern argument with only |
| # embedded \n's, especially if you are trying to avoid Tcl quoting |
| # problems. But gdb_expect really wants to see \r\n in patterns. So |
| # transform the pattern here. First transform \r\n back to \n, in |
| # case some users of gdb_test_exact already do the right thing. |
| regsub -all "\r\n" $pattern "\n" pattern |
| regsub -all "\n" $pattern "\r\n" pattern |
| if [llength $args]==3 then { |
| set message [lindex $args 2] |
| return [gdb_test $command $pattern $message] |
| } |
| |
| return [gdb_test $command $pattern] |
| } |
| |
| # Wrapper around gdb_test_multiple that looks for a list of expected |
| # output elements, but which can appear in any order. |
| # CMD is the gdb command. |
| # NAME is the name of the test. |
| # ELM_FIND_REGEXP specifies how to partition the output into elements to |
| # compare. |
| # ELM_EXTRACT_REGEXP specifies the part of ELM_FIND_REGEXP to compare. |
| # RESULT_MATCH_LIST is a list of exact matches for each expected element. |
| # All elements of RESULT_MATCH_LIST must appear for the test to pass. |
| # |
| # A typical use of ELM_FIND_REGEXP/ELM_EXTRACT_REGEXP is to extract one line |
| # of text per element and then strip trailing \r\n's. |
| # Example: |
| # gdb_test_list_exact "foo" "bar" \ |
| # "\[^\r\n\]+\[\r\n\]+" \ |
| # "\[^\r\n\]+" \ |
| # { \ |
| # {expected result 1} \ |
| # {expected result 2} \ |
| # } |
| |
| proc gdb_test_list_exact { cmd name elm_find_regexp elm_extract_regexp result_match_list } { |
| global gdb_prompt |
| |
| set matches [lsort $result_match_list] |
| set seen {} |
| gdb_test_multiple $cmd $name { |
| "$cmd\[\r\n\]" { exp_continue } |
| -re $elm_find_regexp { |
| set str $expect_out(0,string) |
| verbose -log "seen: $str" 3 |
| regexp -- $elm_extract_regexp $str elm_seen |
| verbose -log "extracted: $elm_seen" 3 |
| lappend seen $elm_seen |
| exp_continue |
| } |
| -re "$gdb_prompt $" { |
| set failed "" |
| foreach got [lsort $seen] have $matches { |
| if {![string equal $got $have]} { |
| set failed $have |
| break |
| } |
| } |
| if {[string length $failed] != 0} { |
| fail "$name ($failed not found)" |
| } else { |
| pass $name |
| } |
| } |
| } |
| } |
| |
| # gdb_test_stdio COMMAND INFERIOR_PATTERN GDB_PATTERN MESSAGE |
| # Send a command to gdb; expect inferior and gdb output. |
| # |
| # See gdb_test_multiple for a description of the COMMAND and MESSAGE |
| # parameters. |
| # |
| # INFERIOR_PATTERN is the pattern to match against inferior output. |
| # |
| # GDB_PATTERN is the pattern to match against gdb output, and must NOT |
| # include the \r\n sequence immediately before the gdb prompt, nor the |
| # prompt. The default is empty. |
| # |
| # Both inferior and gdb patterns must match for a PASS. |
| # |
| # If MESSAGE is ommitted, then COMMAND will be used as the message. |
| # |
| # Returns: |
| # 1 if the test failed, |
| # 0 if the test passes, |
| # -1 if there was an internal error. |
| # |
| |
| proc gdb_test_stdio {command inferior_pattern {gdb_pattern ""} {message ""}} { |
| global inferior_spawn_id gdb_spawn_id |
| global gdb_prompt |
| |
| if {$message == ""} { |
| set message $command |
| } |
| |
| set inferior_matched 0 |
| set gdb_matched 0 |
| |
| # Use an indirect spawn id list, and remove the inferior spawn id |
| # from the expected output as soon as it matches, in case |
| # $inferior_pattern happens to be a prefix of the resulting full |
| # gdb pattern below (e.g., "\r\n"). |
| global gdb_test_stdio_spawn_id_list |
| set gdb_test_stdio_spawn_id_list "$inferior_spawn_id" |
| |
| # Note that if $inferior_spawn_id and $gdb_spawn_id are different, |
| # then we may see gdb's output arriving before the inferior's |
| # output. |
| set res [gdb_test_multiple $command $message { |
| -i gdb_test_stdio_spawn_id_list -re "$inferior_pattern" { |
| set inferior_matched 1 |
| if {!$gdb_matched} { |
| set gdb_test_stdio_spawn_id_list "" |
| exp_continue |
| } |
| } |
| -i $gdb_spawn_id -re "$gdb_pattern\r\n$gdb_prompt $" { |
| set gdb_matched 1 |
| if {!$inferior_matched} { |
| exp_continue |
| } |
| } |
| }] |
| if {$res == 0} { |
| pass $message |
| } else { |
| verbose -log "inferior_matched=$inferior_matched, gdb_matched=$gdb_matched" |
| } |
| return $res |
| } |
| |
| # Wrapper around gdb_test_multiple to be used when testing expression |
| # evaluation while 'set debug expression 1' is in effect. |
| # Looks for some patterns that indicates the expression was rejected. |
| # |
| # CMD is the command to execute, which should include an expression |
| # that GDB will need to parse. |
| # |
| # OUTPUT is the expected output pattern. |
| # |
| # TESTNAME is the name to be used for the test, defaults to CMD if not |
| # given. |
| proc gdb_test_debug_expr { cmd output {testname "" }} { |
| global gdb_prompt |
| |
| if { ${testname} == "" } { |
| set testname $cmd |
| } |
| |
| gdb_test_multiple $cmd $testname { |
| -re ".*Invalid expression.*\r\n$gdb_prompt $" { |
| fail $gdb_test_name |
| } |
| -re ".*\[\r\n\]$output\r\n$gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| } |
| } |
| |
| # get_print_expr_at_depths EXP OUTPUTS |
| # |
| # Used for testing 'set print max-depth'. Prints the expression EXP |
| # with 'set print max-depth' set to various depths. OUTPUTS is a list |
| # of `n` different patterns to match at each of the depths from 0 to |
| # (`n` - 1). |
| # |
| # This proc does one final check with the max-depth set to 'unlimited' |
| # which is tested against the last pattern in the OUTPUTS list. The |
| # OUTPUTS list is therefore required to match every depth from 0 to a |
| # depth where the whole of EXP is printed with no ellipsis. |
| # |
| # This proc leaves the 'set print max-depth' set to 'unlimited'. |
| proc gdb_print_expr_at_depths {exp outputs} { |
| for { set depth 0 } { $depth <= [llength $outputs] } { incr depth } { |
| if { $depth == [llength $outputs] } { |
| set expected_result [lindex $outputs [expr [llength $outputs] - 1]] |
| set depth_string "unlimited" |
| } else { |
| set expected_result [lindex $outputs $depth] |
| set depth_string $depth |
| } |
| |
| with_test_prefix "exp='$exp': depth=${depth_string}" { |
| gdb_test_no_output "set print max-depth ${depth_string}" |
| gdb_test "p $exp" "$expected_result" |
| } |
| } |
| } |
| |
| |
| |
| # Issue a PASS and return true if evaluating CONDITION in the caller's |
| # frame returns true, and issue a FAIL and return false otherwise. |
| # MESSAGE is the pass/fail message to be printed. If MESSAGE is |
| # omitted or is empty, then the pass/fail messages use the condition |
| # string as the message. |
| |
| proc gdb_assert { condition {message ""} } { |
| if { $message == ""} { |
| set message $condition |
| } |
| |
| set code [catch {uplevel 1 expr $condition} res] |
| if {$code == 1} { |
| # If code is 1 (TCL_ERROR), it means evaluation failed and res contains |
| # an error message. Print the error message, and set res to 0 since we |
| # want to return a boolean. |
| warning "While evaluating expression in gdb_assert: $res" |
| unresolved $message |
| set res 0 |
| } elseif { !$res } { |
| fail $message |
| } else { |
| pass $message |
| } |
| return $res |
| } |
| |
| proc gdb_reinitialize_dir { subdir } { |
| global gdb_prompt |
| |
| if [is_remote host] { |
| return "" |
| } |
| send_gdb "dir\n" |
| gdb_expect 60 { |
| -re "Reinitialize source path to empty.*y or n. " { |
| send_gdb "y\n" answer |
| gdb_expect 60 { |
| -re "Source directories searched.*$gdb_prompt $" { |
| send_gdb "dir $subdir\n" |
| gdb_expect 60 { |
| -re "Source directories searched.*$gdb_prompt $" { |
| verbose "Dir set to $subdir" |
| } |
| -re "$gdb_prompt $" { |
| perror "Dir \"$subdir\" failed." |
| } |
| } |
| } |
| -re "$gdb_prompt $" { |
| perror "Dir \"$subdir\" failed." |
| } |
| } |
| } |
| -re "$gdb_prompt $" { |
| perror "Dir \"$subdir\" failed." |
| } |
| } |
| } |
| |
| # |
| # gdb_exit -- exit the GDB, killing the target program if necessary |
| # |
| proc default_gdb_exit {} { |
| global GDB |
| global INTERNAL_GDBFLAGS GDBFLAGS |
| global gdb_spawn_id inferior_spawn_id |
| global inotify_log_file |
| |
| if ![info exists gdb_spawn_id] { |
| return |
| } |
| |
| verbose "Quitting $GDB $INTERNAL_GDBFLAGS $GDBFLAGS" |
| |
| if {[info exists inotify_log_file] && [file exists $inotify_log_file]} { |
| set fd [open $inotify_log_file] |
| set data [read -nonewline $fd] |
| close $fd |
| |
| if {[string compare $data ""] != 0} { |
| warning "parallel-unsafe file creations noticed" |
| |
| # Clear the log. |
| set fd [open $inotify_log_file w] |
| close $fd |
| } |
| } |
| |
| if { [is_remote host] && [board_info host exists fileid] } { |
| send_gdb "quit\n" |
| gdb_expect 10 { |
| -re "y or n" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| -re "DOSEXIT code" { } |
| default { } |
| } |
| } |
| |
| if ![is_remote host] { |
| remote_close host |
| } |
| unset gdb_spawn_id |
| unset ::gdb_tty_name |
| unset inferior_spawn_id |
| } |
| |
| # Load a file into the debugger. |
| # The return value is 0 for success, -1 for failure. |
| # |
| # This procedure also set the global variable GDB_FILE_CMD_DEBUG_INFO |
| # to one of these values: |
| # |
| # debug file was loaded successfully and has debug information |
| # nodebug file was loaded successfully and has no debug information |
| # lzma file was loaded, .gnu_debugdata found, but no LZMA support |
| # compiled in |
| # fail file was not loaded |
| # |
| # This procedure also set the global variable GDB_FILE_CMD_MSG to the |
| # output of the file command in case of success. |
| # |
| # I tried returning this information as part of the return value, |
| # but ran into a mess because of the many re-implementations of |
| # gdb_load in config/*.exp. |
| # |
| # TODO: gdb.base/sepdebug.exp and gdb.stabs/weird.exp might be able to use |
| # this if they can get more information set. |
| |
| proc gdb_file_cmd { arg } { |
| global gdb_prompt |
| global GDB |
| global last_loaded_file |
| |
| # GCC for Windows target may create foo.exe given "-o foo". |
| if { ![file exists $arg] && [file exists "$arg.exe"] } { |
| set arg "$arg.exe" |
| } |
| |
| # Save this for the benefit of gdbserver-support.exp. |
| set last_loaded_file $arg |
| |
| # Set whether debug info was found. |
| # Default to "fail". |
| global gdb_file_cmd_debug_info gdb_file_cmd_msg |
| set gdb_file_cmd_debug_info "fail" |
| |
| if [is_remote host] { |
| set arg [remote_download host $arg] |
| if { $arg == "" } { |
| perror "download failed" |
| return -1 |
| } |
| } |
| |
| # The file command used to kill the remote target. For the benefit |
| # of the testsuite, preserve this behavior. Mark as optional so it doesn't |
| # get written to the stdin log. |
| send_gdb "kill\n" optional |
| gdb_expect 120 { |
| -re "Kill the program being debugged. .y or n. $" { |
| send_gdb "y\n" answer |
| verbose "\t\tKilling previous program being debugged" |
| exp_continue |
| } |
| -re "$gdb_prompt $" { |
| # OK. |
| } |
| } |
| |
| send_gdb "file $arg\n" |
| set new_symbol_table 0 |
| set basename [file tail $arg] |
| gdb_expect 120 { |
| -re "(Reading symbols from.*LZMA support was disabled.*$gdb_prompt $)" { |
| verbose "\t\tLoaded $arg into $GDB; .gnu_debugdata found but no LZMA available" |
| set gdb_file_cmd_msg $expect_out(1,string) |
| set gdb_file_cmd_debug_info "lzma" |
| return 0 |
| } |
| -re "(Reading symbols from.*no debugging symbols found.*$gdb_prompt $)" { |
| verbose "\t\tLoaded $arg into $GDB with no debugging symbols" |
| set gdb_file_cmd_msg $expect_out(1,string) |
| set gdb_file_cmd_debug_info "nodebug" |
| return 0 |
| } |
| -re "(Reading symbols from.*$gdb_prompt $)" { |
| verbose "\t\tLoaded $arg into $GDB" |
| set gdb_file_cmd_msg $expect_out(1,string) |
| set gdb_file_cmd_debug_info "debug" |
| return 0 |
| } |
| -re "Load new symbol table from \".*\".*y or n. $" { |
| if { $new_symbol_table > 0 } { |
| perror [join [list "Couldn't load $basename," |
| "interactive prompt loop detected."]] |
| return -1 |
| } |
| send_gdb "y\n" answer |
| incr new_symbol_table |
| set suffix "-- with new symbol table" |
| set arg "$arg $suffix" |
| set basename "$basename $suffix" |
| exp_continue |
| } |
| -re "No such file or directory.*$gdb_prompt $" { |
| perror "($basename) No such file or directory" |
| return -1 |
| } |
| -re "A problem internal to GDB has been detected" { |
| perror "Couldn't load $basename into GDB (GDB internal error)." |
| gdb_internal_error_resync |
| return -1 |
| } |
| -re "$gdb_prompt $" { |
| perror "Couldn't load $basename into GDB." |
| return -1 |
| } |
| timeout { |
| perror "Couldn't load $basename into GDB (timeout)." |
| return -1 |
| } |
| eof { |
| # This is an attempt to detect a core dump, but seems not to |
| # work. Perhaps we need to match .* followed by eof, in which |
| # gdb_expect does not seem to have a way to do that. |
| perror "Couldn't load $basename into GDB (eof)." |
| return -1 |
| } |
| } |
| } |
| |
| # The expect "spawn" function puts the tty name into the spawn_out |
| # array; but dejagnu doesn't export this globally. So, we have to |
| # wrap spawn with our own function and poke in the built-in spawn |
| # so that we can capture this value. |
| # |
| # If available, the TTY name is saved to the LAST_SPAWN_TTY_NAME global. |
| # Otherwise, LAST_SPAWN_TTY_NAME is unset. |
| |
| proc spawn_capture_tty_name { args } { |
| set result [uplevel builtin_spawn $args] |
| upvar spawn_out spawn_out |
| if { [info exists spawn_out(slave,name)] } { |
| set ::last_spawn_tty_name $spawn_out(slave,name) |
| } else { |
| # If a process is spawned as part of a pipe line (e.g. passing |
| # -leaveopen to the spawn proc) then the spawned process is no |
| # assigned a tty and spawn_out(slave,name) will not be set. |
| # In that case we want to ensure that last_spawn_tty_name is |
| # not set. |
| # |
| # If the previous process spawned was also not assigned a tty |
| # (e.g. multiple processed chained in a pipeline) then |
| # last_spawn_tty_name will already be unset, so, if we don't |
| # use -nocomplain here we would otherwise get an error. |
| unset -nocomplain ::last_spawn_tty_name |
| } |
| return $result |
| } |
| |
| rename spawn builtin_spawn |
| rename spawn_capture_tty_name spawn |
| |
| # Default gdb_spawn procedure. |
| |
| proc default_gdb_spawn { } { |
| global use_gdb_stub |
| global GDB |
| global INTERNAL_GDBFLAGS GDBFLAGS |
| global gdb_spawn_id |
| |
| # Set the default value, it may be overriden later by specific testfile. |
| # |
| # Use `set_board_info use_gdb_stub' for the board file to flag the inferior |
| # is already started after connecting and run/attach are not supported. |
| # This is used for the "remote" protocol. After GDB starts you should |
| # check global $use_gdb_stub instead of the board as the testfile may force |
| # a specific different target protocol itself. |
| set use_gdb_stub [target_info exists use_gdb_stub] |
| |
| verbose "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS" |
| gdb_write_cmd_file "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS" |
| |
| if [info exists gdb_spawn_id] { |
| return 0 |
| } |
| |
| if ![is_remote host] { |
| if { [which $GDB] == 0 } then { |
| perror "$GDB does not exist." |
| exit 1 |
| } |
| } |
| set res [remote_spawn host "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts]"] |
| if { $res < 0 || $res == "" } { |
| perror "Spawning $GDB failed." |
| return 1 |
| } |
| |
| set gdb_spawn_id $res |
| set ::gdb_tty_name $::last_spawn_tty_name |
| return 0 |
| } |
| |
| # Default gdb_start procedure. |
| |
| proc default_gdb_start { } { |
| global gdb_prompt |
| global gdb_spawn_id |
| global inferior_spawn_id |
| |
| if [info exists gdb_spawn_id] { |
| return 0 |
| } |
| |
| # Keep track of the number of times GDB has been launched. |
| global gdb_instances |
| incr gdb_instances |
| |
| gdb_stdin_log_init |
| |
| set res [gdb_spawn] |
| if { $res != 0} { |
| return $res |
| } |
| |
| # Default to assuming inferior I/O is done on GDB's terminal. |
| if {![info exists inferior_spawn_id]} { |
| set inferior_spawn_id $gdb_spawn_id |
| } |
| |
| # When running over NFS, particularly if running many simultaneous |
| # tests on different hosts all using the same server, things can |
| # get really slow. Give gdb at least 3 minutes to start up. |
| gdb_expect 360 { |
| -re "\[\r\n\]$gdb_prompt $" { |
| verbose "GDB initialized." |
| } |
| -re "$gdb_prompt $" { |
| perror "GDB never initialized." |
| unset gdb_spawn_id |
| return -1 |
| } |
| timeout { |
| perror "(timeout) GDB never initialized after 10 seconds." |
| remote_close host |
| unset gdb_spawn_id |
| return -1 |
| } |
| eof { |
| perror "(eof) GDB never initialized." |
| unset gdb_spawn_id |
| return -1 |
| } |
| } |
| |
| # force the height to "unlimited", so no pagers get used |
| |
| send_gdb "set height 0\n" |
| gdb_expect 10 { |
| -re "$gdb_prompt $" { |
| verbose "Setting height to 0." 2 |
| } |
| timeout { |
| warning "Couldn't set the height to 0" |
| } |
| } |
| # force the width to "unlimited", so no wraparound occurs |
| send_gdb "set width 0\n" |
| gdb_expect 10 { |
| -re "$gdb_prompt $" { |
| verbose "Setting width to 0." 2 |
| } |
| timeout { |
| warning "Couldn't set the width to 0." |
| } |
| } |
| |
| gdb_debug_init |
| return 0 |
| } |
| |
| # Utility procedure to give user control of the gdb prompt in a script. It is |
| # meant to be used for debugging test cases, and should not be left in the |
| # test cases code. |
| |
| proc gdb_interact { } { |
| global gdb_spawn_id |
| set spawn_id $gdb_spawn_id |
| |
| send_user "+------------------------------------------+\n" |
| send_user "| Script interrupted, you can now interact |\n" |
| send_user "| with by gdb. Type >>> to continue. |\n" |
| send_user "+------------------------------------------+\n" |
| |
| interact { |
| ">>>" return |
| } |
| } |
| |
| # Examine the output of compilation to determine whether compilation |
| # failed or not. If it failed determine whether it is due to missing |
| # compiler or due to compiler error. Report pass, fail or unsupported |
| # as appropriate. |
| |
| proc gdb_compile_test {src output} { |
| set msg "compilation [file tail $src]" |
| |
| if { $output == "" } { |
| pass $msg |
| return |
| } |
| |
| if { [regexp {^[a-zA-Z_0-9]+: Can't find [^ ]+\.$} $output] |
| || [regexp {.*: command not found[\r|\n]*$} $output] |
| || [regexp {.*: [^\r\n]*compiler not installed[^\r\n]*[\r|\n]*$} $output] } { |
| unsupported "$msg (missing compiler)" |
| return |
| } |
| |
| set gcc_re ".*: error: unrecognized command line option " |
| set clang_re ".*: error: unsupported option " |
| if { [regexp "(?:$gcc_re|$clang_re)(\[^ \t;\r\n\]*)" $output dummy option] |
| && $option != "" } { |
| unsupported "$msg (unsupported option $option)" |
| return |
| } |
| |
| # Unclassified compilation failure, be more verbose. |
| verbose -log "compilation failed: $output" 2 |
| fail "$msg" |
| } |
| |
| # Return a 1 for configurations for which we don't even want to try to |
| # test C++. |
| |
| proc skip_cplus_tests {} { |
| if { [istarget "h8300-*-*"] } { |
| return 1 |
| } |
| |
| # The C++ IO streams are too large for HC11/HC12 and are thus not |
| # available. The gdb C++ tests use them and don't compile. |
| if { [istarget "m6811-*-*"] } { |
| return 1 |
| } |
| if { [istarget "m6812-*-*"] } { |
| return 1 |
| } |
| return 0 |
| } |
| |
| # Return a 1 for configurations for which don't have both C++ and the STL. |
| |
| proc skip_stl_tests {} { |
| return [skip_cplus_tests] |
| } |
| |
| # Return a 1 if I don't even want to try to test FORTRAN. |
| |
| proc skip_fortran_tests {} { |
| return 0 |
| } |
| |
| # Return a 1 if I don't even want to try to test ada. |
| |
| proc skip_ada_tests {} { |
| return 0 |
| } |
| |
| # Return a 1 if I don't even want to try to test GO. |
| |
| proc skip_go_tests {} { |
| return 0 |
| } |
| |
| # Return a 1 if I don't even want to try to test D. |
| |
| proc skip_d_tests {} { |
| return 0 |
| } |
| |
| # Return 1 to skip Rust tests, 0 to try them. |
| proc skip_rust_tests {} { |
| if { ![isnative] } { |
| return 1 |
| } |
| |
| # The rust compiler does not support "-m32", skip. |
| global board board_info |
| set board [target_info name] |
| if {[board_info $board exists multilib_flags]} { |
| foreach flag [board_info $board multilib_flags] { |
| if { $flag == "-m32" } { |
| return 1 |
| } |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Return a 1 for configurations that do not support Python scripting. |
| # PROMPT_REGEXP is the expected prompt. |
| |
| proc skip_python_tests_prompt { prompt_regexp } { |
| global gdb_py_is_py3k |
| |
| gdb_test_multiple "python print ('test')" "verify python support" \ |
| -prompt "$prompt_regexp" { |
| -re "not supported.*$prompt_regexp" { |
| unsupported "Python support is disabled." |
| return 1 |
| } |
| -re "$prompt_regexp" {} |
| } |
| |
| gdb_test_multiple "python print (sys.version_info\[0\])" "check if python 3" \ |
| -prompt "$prompt_regexp" { |
| -re "3.*$prompt_regexp" { |
| set gdb_py_is_py3k 1 |
| } |
| -re ".*$prompt_regexp" { |
| set gdb_py_is_py3k 0 |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Return a 1 for configurations that do not support Python scripting. |
| # Note: This also sets various globals that specify which version of Python |
| # is in use. See skip_python_tests_prompt. |
| |
| proc skip_python_tests {} { |
| global gdb_prompt |
| return [skip_python_tests_prompt "$gdb_prompt $"] |
| } |
| |
| # Return a 1 if we should skip shared library tests. |
| |
| proc skip_shlib_tests {} { |
| # Run the shared library tests on native systems. |
| if {[isnative]} { |
| return 0 |
| } |
| |
| # An abbreviated list of remote targets where we should be able to |
| # run shared library tests. |
| if {([istarget *-*-linux*] |
| || [istarget *-*-*bsd*] |
| || [istarget *-*-solaris2*] |
| || [istarget *-*-mingw*] |
| || [istarget *-*-cygwin*] |
| || [istarget *-*-pe*])} { |
| return 0 |
| } |
| |
| return 1 |
| } |
| |
| # Return 1 if we should skip tui related tests. |
| |
| proc skip_tui_tests {} { |
| global gdb_prompt |
| |
| gdb_test_multiple "help layout" "verify tui support" { |
| -re "Undefined command: \"layout\".*$gdb_prompt $" { |
| return 1 |
| } |
| -re "$gdb_prompt $" { |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Test files shall make sure all the test result lines in gdb.sum are |
| # unique in a test run, so that comparing the gdb.sum files of two |
| # test runs gives correct results. Test files that exercise |
| # variations of the same tests more than once, shall prefix the |
| # different test invocations with different identifying strings in |
| # order to make them unique. |
| # |
| # About test prefixes: |
| # |
| # $pf_prefix is the string that dejagnu prints after the result (FAIL, |
| # PASS, etc.), and before the test message/name in gdb.sum. E.g., the |
| # underlined substring in |
| # |
| # PASS: gdb.base/mytest.exp: some test |
| # ^^^^^^^^^^^^^^^^^^^^ |
| # |
| # is $pf_prefix. |
| # |
| # The easiest way to adjust the test prefix is to append a test |
| # variation prefix to the $pf_prefix, using the with_test_prefix |
| # procedure. E.g., |
| # |
| # proc do_tests {} { |
| # gdb_test ... ... "test foo" |
| # gdb_test ... ... "test bar" |
| # |
| # with_test_prefix "subvariation a" { |
| # gdb_test ... ... "test x" |
| # } |
| # |
| # with_test_prefix "subvariation b" { |
| # gdb_test ... ... "test x" |
| # } |
| # } |
| # |
| # with_test_prefix "variation1" { |
| # ...do setup for variation 1... |
| # do_tests |
| # } |
| # |
| # with_test_prefix "variation2" { |
| # ...do setup for variation 2... |
| # do_tests |
| # } |
| # |
| # Results in: |
| # |
| # PASS: gdb.base/mytest.exp: variation1: test foo |
| # PASS: gdb.base/mytest.exp: variation1: test bar |
| # PASS: gdb.base/mytest.exp: variation1: subvariation a: test x |
| # PASS: gdb.base/mytest.exp: variation1: subvariation b: test x |
| # PASS: gdb.base/mytest.exp: variation2: test foo |
| # PASS: gdb.base/mytest.exp: variation2: test bar |
| # PASS: gdb.base/mytest.exp: variation2: subvariation a: test x |
| # PASS: gdb.base/mytest.exp: variation2: subvariation b: test x |
| # |
| # If for some reason more flexibility is necessary, one can also |
| # manipulate the pf_prefix global directly, treating it as a string. |
| # E.g., |
| # |
| # global pf_prefix |
| # set saved_pf_prefix |
| # append pf_prefix "${foo}: bar" |
| # ... actual tests ... |
| # set pf_prefix $saved_pf_prefix |
| # |
| |
| # Run BODY in the context of the caller, with the current test prefix |
| # (pf_prefix) appended with one space, then PREFIX, and then a colon. |
| # Returns the result of BODY. |
| # |
| proc with_test_prefix { prefix body } { |
| global pf_prefix |
| |
| set saved $pf_prefix |
| append pf_prefix " " $prefix ":" |
| set code [catch {uplevel 1 $body} result] |
| set pf_prefix $saved |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Wrapper for foreach that calls with_test_prefix on each iteration, |
| # including the iterator's name and current value in the prefix. |
| |
| proc foreach_with_prefix {var list body} { |
| upvar 1 $var myvar |
| foreach myvar $list { |
| with_test_prefix "$var=$myvar" { |
| set code [catch {uplevel 1 $body} result] |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } elseif {$code == 3} { |
| break |
| } elseif {$code == 2} { |
| return -code $code $result |
| } |
| } |
| } |
| |
| # Like TCL's native proc, but defines a procedure that wraps its body |
| # within 'with_test_prefix "$proc_name" { ... }'. |
| proc proc_with_prefix {name arguments body} { |
| # Define the advertised proc. |
| proc $name $arguments [list with_test_prefix $name $body] |
| } |
| |
| |
| # Run BODY in the context of the caller. After BODY is run, the variables |
| # listed in VARS will be reset to the values they had before BODY was run. |
| # |
| # This is useful for providing a scope in which it is safe to temporarily |
| # modify global variables, e.g. |
| # |
| # global INTERNAL_GDBFLAGS |
| # global env |
| # |
| # set foo GDBHISTSIZE |
| # |
| # save_vars { INTERNAL_GDBFLAGS env($foo) env(HOME) } { |
| # append INTERNAL_GDBFLAGS " -nx" |
| # unset -nocomplain env(GDBHISTSIZE) |
| # gdb_start |
| # gdb_test ... |
| # } |
| # |
| # Here, although INTERNAL_GDBFLAGS, env(GDBHISTSIZE) and env(HOME) may be |
| # modified inside BODY, this proc guarantees that the modifications will be |
| # undone after BODY finishes executing. |
| |
| proc save_vars { vars body } { |
| array set saved_scalars { } |
| array set saved_arrays { } |
| set unset_vars { } |
| |
| foreach var $vars { |
| # First evaluate VAR in the context of the caller in case the variable |
| # name may be a not-yet-interpolated string like env($foo) |
| set var [uplevel 1 list $var] |
| |
| if [uplevel 1 [list info exists $var]] { |
| if [uplevel 1 [list array exists $var]] { |
| set saved_arrays($var) [uplevel 1 [list array get $var]] |
| } else { |
| set saved_scalars($var) [uplevel 1 [list set $var]] |
| } |
| } else { |
| lappend unset_vars $var |
| } |
| } |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| foreach {var value} [array get saved_scalars] { |
| uplevel 1 [list set $var $value] |
| } |
| |
| foreach {var value} [array get saved_arrays] { |
| uplevel 1 [list unset $var] |
| uplevel 1 [list array set $var $value] |
| } |
| |
| foreach var $unset_vars { |
| uplevel 1 [list unset -nocomplain $var] |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # As save_vars, but for variables stored in the board_info for the |
| # target board. |
| # |
| # Usage example: |
| # |
| # save_target_board_info { multilib_flags } { |
| # global board |
| # set board [target_info name] |
| # unset_board_info multilib_flags |
| # set_board_info multilib_flags "$multilib_flags" |
| # ... |
| # } |
| |
| proc save_target_board_info { vars body } { |
| global board board_info |
| set board [target_info name] |
| |
| array set saved_target_board_info { } |
| set unset_target_board_info { } |
| |
| foreach var $vars { |
| if { [info exists board_info($board,$var)] } { |
| set saved_target_board_info($var) [board_info $board $var] |
| } else { |
| lappend unset_target_board_info $var |
| } |
| } |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| foreach {var value} [array get saved_target_board_info] { |
| unset_board_info $var |
| set_board_info $var $value |
| } |
| |
| foreach var $unset_target_board_info { |
| unset_board_info $var |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Run tests in BODY with the current working directory (CWD) set to |
| # DIR. When BODY is finished, restore the original CWD. Return the |
| # result of BODY. |
| # |
| # This procedure doesn't check if DIR is a valid directory, so you |
| # have to make sure of that. |
| |
| proc with_cwd { dir body } { |
| set saved_dir [pwd] |
| verbose -log "Switching to directory $dir (saved CWD: $saved_dir)." |
| cd $dir |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| verbose -log "Switching back to $saved_dir." |
| cd $saved_dir |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Run tests in BODY with GDB prompt and variable $gdb_prompt set to |
| # PROMPT. When BODY is finished, restore GDB prompt and variable |
| # $gdb_prompt. |
| # Returns the result of BODY. |
| # |
| # Notes: |
| # |
| # 1) If you want to use, for example, "(foo)" as the prompt you must pass it |
| # as "(foo)", and not the regexp form "\(foo\)" (expressed as "\\(foo\\)" in |
| # TCL). PROMPT is internally converted to a suitable regexp for matching. |
| # We do the conversion from "(foo)" to "\(foo\)" here for a few reasons: |
| # a) It's more intuitive for callers to pass the plain text form. |
| # b) We need two forms of the prompt: |
| # - a regexp to use in output matching, |
| # - a value to pass to the "set prompt" command. |
| # c) It's easier to convert the plain text form to its regexp form. |
| # |
| # 2) Don't add a trailing space, we do that here. |
| |
| proc with_gdb_prompt { prompt body } { |
| global gdb_prompt |
| |
| # Convert "(foo)" to "\(foo\)". |
| # We don't use string_to_regexp because while it works today it's not |
| # clear it will work tomorrow: the value we need must work as both a |
| # regexp *and* as the argument to the "set prompt" command, at least until |
| # we start recording both forms separately instead of just $gdb_prompt. |
| # The testsuite is pretty-much hardwired to interpret $gdb_prompt as the |
| # regexp form. |
| regsub -all {[]*+.|()^$\[\\]} $prompt {\\&} prompt |
| |
| set saved $gdb_prompt |
| |
| verbose -log "Setting gdb prompt to \"$prompt \"." |
| set gdb_prompt $prompt |
| gdb_test_no_output "set prompt $prompt " "" |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| verbose -log "Restoring gdb prompt to \"$saved \"." |
| set gdb_prompt $saved |
| gdb_test_no_output "set prompt $saved " "" |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Run tests in BODY with target-charset setting to TARGET_CHARSET. When |
| # BODY is finished, restore target-charset. |
| |
| proc with_target_charset { target_charset body } { |
| global gdb_prompt |
| |
| set saved "" |
| gdb_test_multiple "show target-charset" "" { |
| -re "The target character set is \".*; currently (.*)\"\..*$gdb_prompt " { |
| set saved $expect_out(1,string) |
| } |
| -re "The target character set is \"(.*)\".*$gdb_prompt " { |
| set saved $expect_out(1,string) |
| } |
| -re ".*$gdb_prompt " { |
| fail "get target-charset" |
| } |
| } |
| |
| gdb_test_no_output "set target-charset $target_charset" "" |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| gdb_test_no_output "set target-charset $saved" "" |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Switch the default spawn id to SPAWN_ID, so that gdb_test, |
| # mi_gdb_test etc. default to using it. |
| |
| proc switch_gdb_spawn_id {spawn_id} { |
| global gdb_spawn_id |
| global board board_info |
| |
| set gdb_spawn_id $spawn_id |
| set board [host_info name] |
| set board_info($board,fileid) $spawn_id |
| } |
| |
| # Clear the default spawn id. |
| |
| proc clear_gdb_spawn_id {} { |
| global gdb_spawn_id |
| global board board_info |
| |
| unset -nocomplain gdb_spawn_id |
| set board [host_info name] |
| unset -nocomplain board_info($board,fileid) |
| } |
| |
| # Run BODY with SPAWN_ID as current spawn id. |
| |
| proc with_spawn_id { spawn_id body } { |
| global gdb_spawn_id |
| |
| if [info exists gdb_spawn_id] { |
| set saved_spawn_id $gdb_spawn_id |
| } |
| |
| switch_gdb_spawn_id $spawn_id |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| if [info exists saved_spawn_id] { |
| switch_gdb_spawn_id $saved_spawn_id |
| } else { |
| clear_gdb_spawn_id |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Select the largest timeout from all the timeouts: |
| # - the local "timeout" variable of the scope two levels above, |
| # - the global "timeout" variable, |
| # - the board variable "gdb,timeout". |
| |
| proc get_largest_timeout {} { |
| upvar #0 timeout gtimeout |
| upvar 2 timeout timeout |
| |
| set tmt 0 |
| if [info exists timeout] { |
| set tmt $timeout |
| } |
| if { [info exists gtimeout] && $gtimeout > $tmt } { |
| set tmt $gtimeout |
| } |
| if { [target_info exists gdb,timeout] |
| && [target_info gdb,timeout] > $tmt } { |
| set tmt [target_info gdb,timeout] |
| } |
| if { $tmt == 0 } { |
| # Eeeeew. |
| set tmt 60 |
| } |
| |
| return $tmt |
| } |
| |
| # Run tests in BODY with timeout increased by factor of FACTOR. When |
| # BODY is finished, restore timeout. |
| |
| proc with_timeout_factor { factor body } { |
| global timeout |
| |
| set savedtimeout $timeout |
| |
| set timeout [expr [get_largest_timeout] * $factor] |
| set code [catch {uplevel 1 $body} result] |
| |
| set timeout $savedtimeout |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # Run BODY with timeout factor FACTOR if check-read1 is used. |
| |
| proc with_read1_timeout_factor { factor body } { |
| if { [info exists ::env(READ1)] == 1 && $::env(READ1) == 1 } { |
| # Use timeout factor |
| } else { |
| # Reset timeout factor |
| set factor 1 |
| } |
| return [uplevel [list with_timeout_factor $factor $body]] |
| } |
| |
| # Return 1 if _Complex types are supported, otherwise, return 0. |
| |
| gdb_caching_proc support_complex_tests { |
| |
| if { [gdb_skip_float_test] } { |
| # If floating point is not supported, _Complex is not |
| # supported. |
| return 0 |
| } |
| |
| # Compile a test program containing _Complex types. |
| |
| return [gdb_can_simple_compile complex { |
| int main() { |
| _Complex float cf; |
| _Complex double cd; |
| _Complex long double cld; |
| return 0; |
| } |
| } executable] |
| } |
| |
| # Return 1 if compiling go is supported. |
| gdb_caching_proc support_go_compile { |
| |
| return [gdb_can_simple_compile go-hello { |
| package main |
| import "fmt" |
| func main() { |
| fmt.Println("hello world") |
| } |
| } executable go] |
| } |
| |
| # Return 1 if GDB can get a type for siginfo from the target, otherwise |
| # return 0. |
| |
| proc supports_get_siginfo_type {} { |
| if { [istarget "*-*-linux*"] } { |
| return 1 |
| } else { |
| return 0 |
| } |
| } |
| |
| # Return 1 if memory tagging is supported at runtime, otherwise return 0. |
| |
| gdb_caching_proc supports_memtag { |
| global gdb_prompt |
| |
| gdb_test_multiple "memory-tag check" "" { |
| -re "Memory tagging not supported or disabled by the current architecture\..*$gdb_prompt $" { |
| return 0 |
| } |
| -re "Argument required \\(address or pointer\\).*$gdb_prompt $" { |
| return 1 |
| } |
| } |
| return 0 |
| } |
| |
| # Return 1 if the target supports hardware single stepping. |
| |
| proc can_hardware_single_step {} { |
| |
| if { [istarget "arm*-*-*"] || [istarget "mips*-*-*"] |
| || [istarget "tic6x-*-*"] || [istarget "sparc*-*-linux*"] |
| || [istarget "nios2-*-*"] || [istarget "riscv*-*-linux*"] } { |
| return 0 |
| } |
| |
| return 1 |
| } |
| |
| # Return 1 if target hardware or OS supports single stepping to signal |
| # handler, otherwise, return 0. |
| |
| proc can_single_step_to_signal_handler {} { |
| # Targets don't have hardware single step. On these targets, when |
| # a signal is delivered during software single step, gdb is unable |
| # to determine the next instruction addresses, because start of signal |
| # handler is one of them. |
| return [can_hardware_single_step] |
| } |
| |
| # Return 1 if target supports process record, otherwise return 0. |
| |
| proc supports_process_record {} { |
| |
| if [target_info exists gdb,use_precord] { |
| return [target_info gdb,use_precord] |
| } |
| |
| if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"] |
| || [istarget "i\[34567\]86-*-linux*"] |
| || [istarget "aarch64*-*-linux*"] |
| || [istarget "powerpc*-*-linux*"] |
| || [istarget "s390*-*-linux*"] } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Return 1 if target supports reverse debugging, otherwise return 0. |
| |
| proc supports_reverse {} { |
| |
| if [target_info exists gdb,can_reverse] { |
| return [target_info gdb,can_reverse] |
| } |
| |
| if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"] |
| || [istarget "i\[34567\]86-*-linux*"] |
| || [istarget "aarch64*-*-linux*"] |
| || [istarget "powerpc*-*-linux*"] |
| || [istarget "s390*-*-linux*"] } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Return 1 if readline library is used. |
| |
| proc readline_is_used { } { |
| global gdb_prompt |
| |
| gdb_test_multiple "show editing" "" { |
| -re ".*Editing of command lines as they are typed is on\..*$gdb_prompt $" { |
| return 1 |
| } |
| -re ".*$gdb_prompt $" { |
| return 0 |
| } |
| } |
| } |
| |
| # Return 1 if target is ELF. |
| gdb_caching_proc is_elf_target { |
| set me "is_elf_target" |
| |
| set src { int foo () {return 0;} } |
| if {![gdb_simple_compile elf_target $src]} { |
| return 0 |
| } |
| |
| set fp_obj [open $obj "r"] |
| fconfigure $fp_obj -translation binary |
| set data [read $fp_obj] |
| close $fp_obj |
| |
| file delete $obj |
| |
| set ELFMAG "\u007FELF" |
| |
| if {[string compare -length 4 $data $ELFMAG] != 0} { |
| verbose "$me: returning 0" 2 |
| return 0 |
| } |
| |
| verbose "$me: returning 1" 2 |
| return 1 |
| } |
| |
| # Return 1 if the memory at address zero is readable. |
| |
| gdb_caching_proc is_address_zero_readable { |
| global gdb_prompt |
| |
| set ret 0 |
| gdb_test_multiple "x 0" "" { |
| -re "Cannot access memory at address 0x0.*$gdb_prompt $" { |
| set ret 0 |
| } |
| -re ".*$gdb_prompt $" { |
| set ret 1 |
| } |
| } |
| |
| return $ret |
| } |
| |
| # Produce source file NAME and write SOURCES into it. |
| |
| proc gdb_produce_source { name sources } { |
| set index 0 |
| set f [open $name "w"] |
| |
| puts $f $sources |
| close $f |
| } |
| |
| # Return 1 if target is ILP32. |
| # This cannot be decided simply from looking at the target string, |
| # as it might depend on externally passed compiler options like -m64. |
| gdb_caching_proc is_ilp32_target { |
| return [gdb_can_simple_compile is_ilp32_target { |
| int dummy[sizeof (int) == 4 |
| && sizeof (void *) == 4 |
| && sizeof (long) == 4 ? 1 : -1]; |
| }] |
| } |
| |
| # Return 1 if target is LP64. |
| # This cannot be decided simply from looking at the target string, |
| # as it might depend on externally passed compiler options like -m64. |
| gdb_caching_proc is_lp64_target { |
| return [gdb_can_simple_compile is_lp64_target { |
| int dummy[sizeof (int) == 4 |
| && sizeof (void *) == 8 |
| && sizeof (long) == 8 ? 1 : -1]; |
| }] |
| } |
| |
| # Return 1 if target has 64 bit addresses. |
| # This cannot be decided simply from looking at the target string, |
| # as it might depend on externally passed compiler options like -m64. |
| gdb_caching_proc is_64_target { |
| return [gdb_can_simple_compile is_64_target { |
| int function(void) { return 3; } |
| int dummy[sizeof (&function) == 8 ? 1 : -1]; |
| }] |
| } |
| |
| # Return 1 if target has x86_64 registers - either amd64 or x32. |
| # x32 target identifies as x86_64-*-linux*, therefore it cannot be determined |
| # just from the target string. |
| gdb_caching_proc is_amd64_regs_target { |
| if {![istarget "x86_64-*-*"] && ![istarget "i?86-*"]} { |
| return 0 |
| } |
| |
| return [gdb_can_simple_compile is_amd64_regs_target { |
| int main (void) { |
| asm ("incq %rax"); |
| asm ("incq %r15"); |
| |
| return 0; |
| } |
| }] |
| } |
| |
| # Return 1 if this target is an x86 or x86-64 with -m32. |
| proc is_x86_like_target {} { |
| if {![istarget "x86_64-*-*"] && ![istarget i?86-*]} { |
| return 0 |
| } |
| return [expr [is_ilp32_target] && ![is_amd64_regs_target]] |
| } |
| |
| # Return 1 if this target is an arm or aarch32 on aarch64. |
| |
| gdb_caching_proc is_aarch32_target { |
| if { [istarget "arm*-*-*"] } { |
| return 1 |
| } |
| |
| if { ![istarget "aarch64*-*-*"] } { |
| return 0 |
| } |
| |
| set list {} |
| foreach reg \ |
| {r0 r1 r2 r3} { |
| lappend list "\tmov $reg, $reg" |
| } |
| |
| return [gdb_can_simple_compile aarch32 [join $list \n]] |
| } |
| |
| # Return 1 if this target is an aarch64, either lp64 or ilp32. |
| |
| proc is_aarch64_target {} { |
| if { ![istarget "aarch64*-*-*"] } { |
| return 0 |
| } |
| |
| return [expr ![is_aarch32_target]] |
| } |
| |
| # Return 1 if displaced stepping is supported on target, otherwise, return 0. |
| proc support_displaced_stepping {} { |
| |
| if { [istarget "x86_64-*-linux*"] || [istarget "i\[34567\]86-*-linux*"] |
| || [istarget "arm*-*-linux*"] || [istarget "powerpc-*-linux*"] |
| || [istarget "powerpc64-*-linux*"] || [istarget "s390*-*-*"] |
| || [istarget "aarch64*-*-linux*"] } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Run a test on the target to see if it supports vmx hardware. Return 0 if so, |
| # 1 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. |
| |
| gdb_caching_proc skip_altivec_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_altivec_tests" |
| |
| # Some simulators are known to not support VMX instructions. |
| if { [istarget powerpc-*-eabi] || [istarget powerpc*-*-eabispe] } { |
| verbose "$me: target known to not support VMX, returning 1" 2 |
| return 1 |
| } |
| |
| # Make sure we have a compiler that understands altivec. |
| if [get_compiler_info] { |
| warning "Could not get compiler info" |
| return 1 |
| } |
| if [test_compiler_info gcc*] { |
| set compile_flags "additional_flags=-maltivec" |
| } elseif [test_compiler_info xlc*] { |
| set compile_flags "additional_flags=-qaltivec" |
| } else { |
| verbose "Could not compile with altivec support, returning 1" 2 |
| return 1 |
| } |
| |
| # Compile a test program containing VMX instructions. |
| set src { |
| int main() { |
| #ifdef __MACH__ |
| asm volatile ("vor v0,v0,v0"); |
| #else |
| asm volatile ("vor 0,0,0"); |
| #endif |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile $me $src executable $compile_flags]} { |
| return 1 |
| } |
| |
| # Compilation succeeded so now run it via gdb. |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "\n$me altivec hardware not detected" |
| set skip_vmx_tests 1 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "\n$me: altivec hardware detected" |
| set skip_vmx_tests 0 |
| } |
| default { |
| warning "\n$me: default case taken" |
| set skip_vmx_tests 1 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_vmx_tests" 2 |
| return $skip_vmx_tests |
| } |
| |
| # Run a test on the power target to see if it supports ISA 3.1 instructions |
| gdb_caching_proc skip_power_isa_3_1_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_power_isa_3_1_tests" |
| |
| # Compile a test program containing ISA 3.1 instructions. |
| set src { |
| int main() { |
| asm volatile ("pnop"); // marker |
| asm volatile ("nop"); |
| return 0; |
| } |
| } |
| |
| if {![gdb_simple_compile $me $src executable ]} { |
| return 1 |
| } |
| |
| # No error message, compilation succeeded so now run it via gdb. |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "\n$me Power ISA 3.1 hardware not detected" |
| set skip_power_isa_3_1_tests 1 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "\n$me: Power ISA 3.1 hardware detected" |
| set skip_power_isa_3_1_tests 0 |
| } |
| default { |
| warning "\n$me: default case taken" |
| set skip_power_isa_3_1_tests 1 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_power_isa_3_1_tests" 2 |
| return $skip_power_isa_3_1_tests |
| } |
| |
| # Run a test on the target to see if it supports vmx hardware. Return 0 if so, |
| # 1 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. |
| |
| gdb_caching_proc skip_vsx_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_vsx_tests" |
| |
| # Some simulators are known to not support Altivec instructions, so |
| # they won't support VSX instructions as well. |
| if { [istarget powerpc-*-eabi] || [istarget powerpc*-*-eabispe] } { |
| verbose "$me: target known to not support VSX, returning 1" 2 |
| return 1 |
| } |
| |
| # Make sure we have a compiler that understands altivec. |
| if [get_compiler_info] { |
| warning "Could not get compiler info" |
| return 1 |
| } |
| if [test_compiler_info gcc*] { |
| set compile_flags "additional_flags=-mvsx" |
| } elseif [test_compiler_info xlc*] { |
| set compile_flags "additional_flags=-qasm=gcc" |
| } else { |
| verbose "Could not compile with vsx support, returning 1" 2 |
| return 1 |
| } |
| |
| # Compile a test program containing VSX instructions. |
| set src { |
| int main() { |
| double a[2] = { 1.0, 2.0 }; |
| #ifdef __MACH__ |
| asm volatile ("lxvd2x v0,v0,%[addr]" : : [addr] "r" (a)); |
| #else |
| asm volatile ("lxvd2x 0,0,%[addr]" : : [addr] "r" (a)); |
| #endif |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile $me $src executable $compile_flags]} { |
| return 1 |
| } |
| |
| # No error message, compilation succeeded so now run it via gdb. |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "\n$me VSX hardware not detected" |
| set skip_vsx_tests 1 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "\n$me: VSX hardware detected" |
| set skip_vsx_tests 0 |
| } |
| default { |
| warning "\n$me: default case taken" |
| set skip_vsx_tests 1 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_vsx_tests" 2 |
| return $skip_vsx_tests |
| } |
| |
| # Run a test on the target to see if it supports TSX hardware. Return 0 if so, |
| # 1 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. |
| |
| gdb_caching_proc skip_tsx_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_tsx_tests" |
| |
| # Compile a test program. |
| set src { |
| int main() { |
| asm volatile ("xbegin .L0"); |
| asm volatile ("xend"); |
| asm volatile (".L0: nop"); |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile $me $src executable]} { |
| return 1 |
| } |
| |
| # No error message, compilation succeeded so now run it via gdb. |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "$me: TSX hardware not detected." |
| set skip_tsx_tests 1 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "$me: TSX hardware detected." |
| set skip_tsx_tests 0 |
| } |
| default { |
| warning "\n$me: default case taken." |
| set skip_tsx_tests 1 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_tsx_tests" 2 |
| return $skip_tsx_tests |
| } |
| |
| # Run a test on the target to see if it supports avx512bf16. Return 0 if so, |
| # 1 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. |
| |
| gdb_caching_proc skip_avx512bf16_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_avx512bf16_tests" |
| if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } { |
| verbose "$me: target does not support avx512bf16, returning 1" 2 |
| return 1 |
| } |
| |
| # Compile a test program. |
| set src { |
| int main() { |
| asm volatile ("vcvtne2ps2bf16 %xmm0, %xmm1, %xmm0"); |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile $me $src executable]} { |
| return 1 |
| } |
| |
| # No error message, compilation succeeded so now run it via gdb. |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "$me: avx512bf16 hardware not detected." |
| set skip_avx512bf16_tests 1 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "$me: avx512bf16 hardware detected." |
| set skip_avx512bf16_tests 0 |
| } |
| default { |
| warning "\n$me: default case taken." |
| set skip_avx512bf16_tests 1 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_avx512bf16_tests" 2 |
| return $skip_avx512bf16_tests |
| } |
| |
| # Run a test on the target to see if it supports avx512fp16. Return 0 if so, |
| # 1 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. |
| |
| gdb_caching_proc skip_avx512fp16_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_avx512fp16_tests" |
| if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } { |
| verbose "$me: target does not support avx512fp16, returning 1" 2 |
| return 1 |
| } |
| |
| # Compile a test program. |
| set src { |
| int main() { |
| asm volatile ("vcvtps2phx %xmm1, %xmm0"); |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile $me $src executable]} { |
| return 1 |
| } |
| |
| # No error message, compilation succeeded so now run it via gdb. |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "$me: avx512fp16 hardware not detected." |
| set skip_avx512fp16_tests 1 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "$me: avx512fp16 hardware detected." |
| set skip_avx512fp16_tests 0 |
| } |
| default { |
| warning "\n$me: default case taken." |
| set skip_avx512fp16_tests 1 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_avx512fp16_tests" 2 |
| return $skip_avx512fp16_tests |
| } |
| |
| # Run a test on the target to see if it supports btrace hardware. Return 0 if so, |
| # 1 if it does not. Based on 'check_vmx_hw_available' from the GCC testsuite. |
| |
| gdb_caching_proc skip_btrace_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_btrace_tests" |
| if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } { |
| verbose "$me: target does not support btrace, returning 1" 2 |
| return 1 |
| } |
| |
| # Compile a test program. |
| set src { int main() { return 0; } } |
| if {![gdb_simple_compile $me $src executable]} { |
| return 1 |
| } |
| |
| # 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] { |
| return 1 |
| } |
| # In case of an unexpected output, we return 2 as a fail value. |
| set skip_btrace_tests 2 |
| gdb_test_multiple "record btrace" "check btrace support" { |
| -re "You can't do that when your target is.*\r\n$gdb_prompt $" { |
| set skip_btrace_tests 1 |
| } |
| -re "Target does not support branch tracing.*\r\n$gdb_prompt $" { |
| set skip_btrace_tests 1 |
| } |
| -re "Could not enable branch tracing.*\r\n$gdb_prompt $" { |
| set skip_btrace_tests 1 |
| } |
| -re "^record btrace\r\n$gdb_prompt $" { |
| set skip_btrace_tests 0 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $skip_btrace_tests" 2 |
| return $skip_btrace_tests |
| } |
| |
| # Run a test on the target to see if it supports btrace pt hardware. |
| # Return 0 if so, 1 if it does not. Based on 'check_vmx_hw_available' |
| # from the GCC testsuite. |
| |
| gdb_caching_proc skip_btrace_pt_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_btrace_tests" |
| if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } { |
| verbose "$me: target does not support btrace, returning 1" 2 |
| return 1 |
| } |
| |
| |