| # Copyright 1992-2023 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 |
| } |
| |
| # Execute BODY, if COND wrapped in proc WRAP. |
| # Instead of writing the verbose and repetitive: |
| # if { $cond } { |
| # wrap $body |
| # } else { |
| # $body |
| # } |
| # we can use instead: |
| # cond_wrap $cond wrap $body |
| |
| proc cond_wrap { cond wrap body } { |
| if { $cond } { |
| $wrap { |
| uplevel 1 $body |
| } |
| } else { |
| uplevel 1 $body |
| } |
| } |
| |
| # Add VAR_ID=VAL to ENV_VAR, unless ENV_VAR already contains a VAR_ID setting. |
| |
| proc set_sanitizer_default { env_var var_id val } { |
| global env |
| |
| if { ![info exists env($env_var) ] |
| || $env($env_var) == "" } { |
| # Set var_id (env_var non-existing / empty case). |
| append env($env_var) $var_id=$val |
| return |
| } |
| |
| if { [regexp $var_id= $env($env_var)] } { |
| # Don't set var_id. It's already set by the user, leave as is. |
| # Note that we could probably get the same result by unconditionally |
| # prepending it, but this way is less likely to cause confusion. |
| return |
| } |
| |
| # Set var_id (env_var not empty case). |
| append env($env_var) : $var_id=$val |
| } |
| |
| set_sanitizer_default TSAN_OPTIONS suppressions \ |
| $srcdir/../tsan-suppressions.txt |
| |
| # If GDB is built with ASAN (and because there are leaks), it will output a |
| # leak report when exiting as well as exit with a non-zero (failure) status. |
| # This can affect tests that are sensitive to what GDB prints on stderr or its |
| # exit status. Add `detect_leaks=0` to the ASAN_OPTIONS environment variable |
| # (which will affect any spawned sub-process) to avoid this. |
| set_sanitizer_default ASAN_OPTIONS detect_leaks 0 |
| |
| # 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 |
| |
| # The path to the GDB binary to test. |
| global GDB |
| |
| # The data directory to use for testing. If this is the empty string, |
| # then we let GDB use its own configured data directory. |
| global GDB_DATA_DIRECTORY |
| |
| # 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] |
| } |
| } else { |
| # If the user specifies GDB on the command line, and doesn't |
| # specify GDB_DATA_DIRECTORY, then assume we're testing an |
| # installed GDB, and let it use its own configured data directory. |
| if ![info exists GDB_DATA_DIRECTORY] { |
| set GDB_DATA_DIRECTORY "" |
| } |
| } |
| verbose "using GDB = $GDB" 2 |
| |
| # The data directory the testing GDB will use. By default, assume |
| # we're testing a non-installed GDB in the build directory. Users may |
| # also explictly override the -data-directory from the command line. |
| if ![info exists GDB_DATA_DIRECTORY] { |
| set GDB_DATA_DIRECTORY "[pwd]/../data-directory" |
| } |
| verbose "using GDB_DATA_DIRECTORY = $GDB_DATA_DIRECTORY" 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 |
| |
| # Append the -data-directory option to pass to GDB to CMDLINE and |
| # return the resulting string. If GDB_DATA_DIRECTORY is empty, |
| # nothing is appended. |
| proc append_gdb_data_directory_option {cmdline} { |
| global GDB_DATA_DIRECTORY |
| |
| if { $GDB_DATA_DIRECTORY != "" } { |
| return "$cmdline -data-directory $GDB_DATA_DIRECTORY" |
| } else { |
| return $cmdline |
| } |
| } |
| |
| # INTERNAL_GDBFLAGS contains flags that the testsuite requires. |
| # `-nw' disables any of the windowed interfaces. |
| # `-nx' disables ~/.gdbinit, so that it doesn't interfere with the tests. |
| # `-iex "set {height,width} 0"' disables pagination. |
| # `-data-directory' points to the data directory, usually in the build |
| # directory. |
| global INTERNAL_GDBFLAGS |
| if ![info exists INTERNAL_GDBFLAGS] { |
| set INTERNAL_GDBFLAGS \ |
| [join [list \ |
| "-nw" \ |
| "-nx" \ |
| {-iex "set height 0"} \ |
| {-iex "set width 0"}]] |
| |
| set INTERNAL_GDBFLAGS [append_gdb_data_directory_option $INTERNAL_GDBFLAGS] |
| } |
| |
| # 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]} { |
| 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" |
| |
| # A regular expression that matches a breakpoint hit with a breakpoint |
| # having several code locations. |
| set bkptno_num_re "$decimal\\.$decimal" |
| |
| # A regular expression that matches a breakpoint hit |
| # with one or several code locations. |
| set bkptno_numopt_re "($decimal\\.$decimal|$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 |
| # |
| # Returns the same as gdb_test_multiple. |
| |
| proc gdb_unload { {msg "file"} } { |
| global GDB |
| global gdb_prompt |
| return [gdb_test_multiple "file" $msg { |
| -re "A program is being debugged already.\r\nAre you sure you want to change the file. .y or n. $" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| |
| -re "No executable file now\\.\r\n" { |
| exp_continue |
| } |
| |
| -re "Discard symbol table from `.*'. .y or n. $" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| |
| -re -wrap "No symbol file now\\." { |
| pass $gdb_test_name |
| } |
| }] |
| } |
| |
| # 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 |
| } |
| -re "$gdb_prompt $" { } |
| } |
| 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 using LINESPEC. |
| # |
| # 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 { linespec 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 "gdb_breakpoint: set breakpoint at $linespec" |
| # The first two regexps are what we get with -g, the third is without -g. |
| gdb_test_multiple "$break_command $linespec" $test_name { |
| -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 $gdb_name_name |
| } |
| return 0 |
| } |
| } |
| -re "Make breakpoint pending.*y or \\\[n\\\]. $" { |
| send_gdb "$pending_response\n" |
| exp_continue |
| } |
| -re "$gdb_prompt $" { |
| if { $print_fail } { |
| fail $test_name |
| } |
| 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. |
| # |
| # 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 { linespec args } { |
| global gdb_prompt |
| global bkptno_numopt_re |
| global decimal |
| |
| delete_breakpoints |
| |
| 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 "runto: run to $linespec" |
| |
| # 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 $linespec may have multiple |
| # elements, and we don't want Tcl to move the remaining elements after |
| # the first to $args. That is why $linespec is wrapped in {}. |
| if ![eval gdb_breakpoint {$linespec} $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|Temporary break).* at .*:$decimal.*$gdb_prompt $" { |
| if { $print_pass } { |
| pass $test_name |
| } |
| return 1 |
| } |
| -re "(?:Breakpoint|Temporary breakpoint) $bkptno_numopt_re, \[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 "Recursive internal problem\\." { |
| perror "Could not resync from internal error (recursive internal problem)" |
| return 0 |
| } |
| -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 |
| } |
| |
| # Fill in the default prompt if PROMPT_REGEXP is empty. |
| # |
| # If WITH_ANCHOR is true and the default prompt is used, append a `$` at the end |
| # of the regexp, to anchor the match at the end of the buffer. |
| proc fill_in_default_prompt {prompt_regexp with_anchor} { |
| if { "$prompt_regexp" == "" } { |
| set prompt "$::gdb_prompt " |
| |
| if { $with_anchor } { |
| append prompt "$" |
| } |
| |
| return $prompt |
| } |
| return $prompt_regexp |
| } |
| |
| # 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" |
| } |
| |
| set prompt_regexp [fill_in_default_prompt $prompt_regexp true] |
| |
| if { $message == "" } { |
| set message $command |
| } |
| |
| if [string match "*\[\r\n\]" $command] { |
| error "Invalid trailing newline in \"$command\" command" |
| } |
| |
| if [string match "*\[\003\004\]" $command] { |
| error "Invalid trailing control code in \"$command\" command" |
| } |
| |
| 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} { |
| 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"] != "" } { |
| verbose -log "Couldn't send $command to GDB." |
| unresolved $message |
| return -1 |
| } |
| # 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"] != "" } { |
| verbose -log "Couldn't send $command to GDB." |
| unresolved $message |
| return -1 |
| } |
| } |
| } |
| |
| 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 |
| } |
| -re "Corrupted shared library list.*$prompt_regexp" { |
| fail "$message (shared library list corrupted)" |
| set result -1 |
| } |
| -re "Invalid cast\.\r\nwarning: Probes-based dynamic linker interface failed.*$prompt_regexp" { |
| fail "$message (probes interface failure)" |
| 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]} { |
| 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]} { |
| 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]} { |
| 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]} { |
| 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]} { |
| 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]} { |
| 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 [-prompt PROMPT_REGEXP] [-lbl] |
| # 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 should ask in response to COMMAND, like |
| # "are you sure?" If this is specified, the test fails if GDB |
| # doesn't print the question. |
| # RESPONSE is the response to send when QUESTION appears. |
| # |
| # -prompt PROMPT_REGEXP specifies a regexp matching the expected prompt |
| # after the command output. If empty, defaults to "$gdb_prompt $". |
| # -no-prompt-anchor specifies that if the default prompt regexp is used, it |
| # should not be anchored at the end of the buffer. This means that the |
| # pattern can match even if there is stuff output after the prompt. Does not |
| # have any effect if -prompt is specified. |
| # -lbl specifies that line-by-line matching will be used. |
| # -nopass specifies that a PASS should not be issued. |
| # |
| # 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 |
| |
| parse_args { |
| {prompt ""} |
| {no-prompt-anchor} |
| {lbl} |
| {nopass} |
| } |
| |
| lassign $args command pattern message question response |
| |
| # Can't have a question without a response. |
| if { $question != "" && $response == "" || [llength $args] > 5 } { |
| error "Unexpected arguments: $args" |
| } |
| |
| if { $message == "" } { |
| set message $command |
| } |
| |
| set prompt [fill_in_default_prompt $prompt [expr !${no-prompt-anchor}]] |
| |
| set saw_question 0 |
| |
| set user_code {} |
| lappend user_code { |
| -re "\[\r\n\]*(?:$pattern)\[\r\n\]+$prompt" { |
| if { $question != "" & !$saw_question} { |
| fail $message |
| } elseif {!$nopass} { |
| pass $message |
| } |
| } |
| } |
| |
| if { $question != "" } { |
| lappend user_code { |
| -re "$question$" { |
| set saw_question 1 |
| send_gdb "$response\n" |
| exp_continue |
| } |
| } |
| } |
| |
| set user_code [join $user_code] |
| |
| set opts {} |
| lappend opts "-prompt" "$prompt" |
| if {$lbl} { |
| lappend opts "-lbl" |
| } |
| |
| return [gdb_test_multiple $command $message {*}$opts $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 [-prompt PROMPT_REGEXP] [-nopass] COMMAND [MESSAGE] |
| # Send a command to GDB and verify that this command generated no output. |
| # |
| # See gdb_test for a description of the -prompt, -no-prompt-anchor, -nopass, |
| # COMMAND, and MESSAGE parameters. |
| |
| proc gdb_test_no_output { args } { |
| global gdb_prompt |
| |
| parse_args { |
| {prompt ""} |
| {no-prompt-anchor} |
| {nopass} |
| } |
| |
| lassign $args command message |
| |
| set prompt [fill_in_default_prompt $prompt [expr !${no-prompt-anchor}]] |
| |
| set command_regex [string_to_regexp $command] |
| gdb_test_multiple $command $message -prompt $prompt { |
| -re "^$command_regex\r\n$prompt" { |
| if {!$nopass} { |
| pass $gdb_test_name |
| } |
| } |
| } |
| } |
| |
| # 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} { |
| set expectthis [lindex $args 1] |
| } else { |
| set expectthis "should never match this bogus string" |
| } |
| set sendthis [lindex $args 0] |
| if {$verbose > 2} { |
| 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} { |
| 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} { |
| perror "$GDB does not exist." |
| exit 1 |
| } |
| } |
| |
| # Put GDBFLAGS last so that tests can put "--args ..." in it. |
| set res [remote_spawn host "$GDB $INTERNAL_GDBFLAGS [host_info gdb_opts] $GDBFLAGS"] |
| 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 "\[\r\n\]\033\\\[.2004h$gdb_prompt $" { |
| # This special case detects what happens when GDB is |
| # started with bracketed paste mode enabled. This mode is |
| # usually forced off (see setting of INPUTRC in |
| # default_gdb_init), but for at least one test we turn |
| # bracketed paste mode back on, and then start GDB. In |
| # that case, this case is hit. |
| 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 want to try to test ada. |
| |
| proc allow_ada_tests {} { |
| return 1 |
| } |
| |
| # 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. |
| |
| gdb_caching_proc skip_python_tests { |
| set output [remote_exec host $::GDB --configuration] |
| return [expr {[string first "--with-python" $output] == -1}] |
| } |
| |
| # 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 dlmopen tests, 0 if we should not. |
| |
| gdb_caching_proc skip_dlmopen_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| # We need shared library support. |
| if { [skip_shlib_tests] } { |
| return 1 |
| } |
| |
| set me "skip_dlmopen_tests" |
| set lib { |
| int foo (void) { |
| return 42; |
| } |
| } |
| set src { |
| #define _GNU_SOURCE |
| #include <dlfcn.h> |
| #include <link.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| int main (void) { |
| struct r_debug *r_debug; |
| ElfW(Dyn) *dyn; |
| void *handle; |
| |
| /* The version is kept at 1 until we create a new namespace. */ |
| handle = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); |
| if (!handle) { |
| printf ("dlmopen failed: %s.\n", dlerror ()); |
| return 1; |
| } |
| |
| r_debug = 0; |
| /* Taken from /usr/include/link.h. */ |
| for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) |
| if (dyn->d_tag == DT_DEBUG) |
| r_debug = (struct r_debug *) dyn->d_un.d_ptr; |
| |
| if (!r_debug) { |
| printf ("r_debug not found.\n"); |
| return 1; |
| } |
| if (r_debug->r_version < 2) { |
| printf ("dlmopen debug not supported.\n"); |
| return 1; |
| } |
| printf ("dlmopen debug supported.\n"); |
| return 0; |
| } |
| } |
| |
| set libsrc [standard_temp_file "libfoo.c"] |
| set libout [standard_temp_file "libfoo.so"] |
| gdb_produce_source $libsrc $lib |
| |
| if { [gdb_compile_shlib $libsrc $libout {debug}] != "" } { |
| verbose -log "failed to build library" |
| return 1 |
| } |
| if { ![gdb_simple_compile $me $src executable \ |
| [list shlib_load debug \ |
| additional_flags=-DDSO_NAME=\"$libout\"]] } { |
| verbose -log "failed to build executable" |
| return 1 |
| } |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load $obj |
| |
| if { [gdb_run_cmd] != 0 } { |
| verbose -log "failed to start skip test" |
| return 1 |
| } |
| gdb_expect { |
| -re "$inferior_exited_re normally.*${gdb_prompt} $" { |
| set skip_dlmopen_tests 0 |
| } |
| -re "$inferior_exited_re with code.*${gdb_prompt} $" { |
| set skip_dlmopen_tests 1 |
| } |
| default { |
| warning "\n$me: default case taken" |
| set skip_dlmopen_tests 1 |
| } |
| } |
| gdb_exit |
| |
| verbose "$me: returning $skip_dlmopen_tests" 2 |
| return $skip_dlmopen_tests |
| } |
| |
| # 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] |
| } |
| |
| # Return an id corresponding to the test prefix stored in $pf_prefix, which |
| # is more suitable for use in a file name. |
| # F.i., for a pf_prefix: |
| # gdb.dwarf2/dw2-lines.exp: \ |
| # cv=5: cdw=64: lv=5: ldw=64: string_form=line_strp: |
| # return an id: |
| # cv-5-cdw-32-lv-5-ldw-64-string_form-line_strp |
| |
| proc prefix_id {} { |
| global pf_prefix |
| set id $pf_prefix |
| |
| # Strip ".exp: " prefix. |
| set id [regsub {.*\.exp: } $id {}] |
| |
| # Strip colon suffix. |
| set id [regsub {:$} $id {}] |
| |
| # Strip spaces. |
| set id [regsub -all { } $id {}] |
| |
| # Replace colons, equal signs. |
| set id [regsub -all \[:=\] $id -] |
| |
| return $id |
| } |
| |
| # 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 |
| } |
| } |
| |
| # Use GDB's 'cd' command to switch to DIR. Return true if the switch |
| # was successful, otherwise, call perror and return false. |
| |
| proc gdb_cd { dir } { |
| set new_dir "" |
| gdb_test_multiple "cd $dir" "" { |
| -re "^cd \[^\r\n\]+\r\n" { |
| exp_continue |
| } |
| |
| -re "^Working directory (\[^\r\n\]+)\\.\r\n" { |
| set new_dir $expect_out(1,string) |
| exp_continue |
| } |
| |
| -re "^$::gdb_prompt $" { |
| if { $new_dir == "" || $new_dir != $dir } { |
| perror "failed to switch to $dir" |
| return false |
| } |
| } |
| } |
| |
| return true |
| } |
| |
| # Use GDB's 'pwd' command to figure out the current working directory. |
| # Return the directory as a string. If we can't figure out the |
| # current working directory, then call perror, and return the empty |
| # string. |
| |
| proc gdb_pwd { } { |
| set dir "" |
| gdb_test_multiple "pwd" "" { |
| -re "^pwd\r\n" { |
| exp_continue |
| } |
| |
| -re "^Working directory (\[^\r\n\]+)\\.\r\n" { |
| set dir $expect_out(1,string) |
| exp_continue |
| } |
| |
| -re "^$::gdb_prompt $" { |
| } |
| } |
| |
| if { $dir == "" } { |
| perror "failed to read GDB's current working directory" |
| } |
| |
| return $dir |
| } |
| |
| # Similar to the with_cwd proc, this proc runs BODY with the current |
| # working directory changed to CWD. |
| # |
| # Unlike with_cwd, the directory change here is done within GDB |
| # itself, so GDB must be running before this proc is called. |
| |
| proc with_gdb_cwd { dir body } { |
| set saved_dir [gdb_pwd] |
| if { $saved_dir == "" } { |
| return |
| } |
| |
| verbose -log "Switching to directory $dir (saved CWD: $saved_dir)." |
| if ![gdb_cd $dir] { |
| return |
| } |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| verbose -log "Switching back to $saved_dir." |
| if ![gdb_cd $saved_dir] { |
| return |
| } |
| |
| # Check that GDB is still alive. If GDB crashed in the above code |
| # then any corefile will have been left in DIR, not the root |
| # testsuite directory. As a result the corefile will not be |
| # brought to the users attention. Instead, if GDB crashed, then |
| # this check should cause a FAIL, which should be enough to alert |
| # the user. |
| set saw_result false |
| gdb_test_multiple "p 123" "" { |
| -re "p 123\r\n" { |
| exp_continue |
| } |
| |
| -re "^\\\$$::decimal = 123\r\n" { |
| set saw_result true |
| exp_continue |
| } |
| |
| -re "^$::gdb_prompt $" { |
| if { !$saw_result } { |
| fail "check gdb is alive in with_gdb_cwd" |
| } |
| } |
| } |
| |
| 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 -nopass "set target-charset $target_charset" |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| gdb_test_no_output -nopass "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*"] || [istarget "loongarch*-*-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 [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 [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 |
| } |
| |
| # 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 pt" "check btrace pt 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 "support was disabled at compile time.*\r\n$gdb_prompt $" { |
| set skip_btrace_tests 1 |
| } |
| -re "^record btrace pt\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 Aarch64 SVE hardware. |
| # Return 1 if so, 0 if it does not. Note this causes a restart of GDB. |
| |
| gdb_caching_proc allow_aarch64_sve_tests { |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "skip_aarch64_sve_tests" |
| |
| if { ![is_aarch64_target]} { |
| return 0 |
| } |
| |
| set compile_flags "{additional_flags=-march=armv8-a+sve}" |
| |
| # Compile a test program containing SVE instructions. |
| set src { |
| int main() { |
| asm volatile ("ptrue p0.b"); |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile $me $src executable $compile_flags]} { |
| return 0 |
| } |
| |
| # Compilation succeeded so now run it via gdb. |
| clean_restart $obj |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Illegal instruction.*${gdb_prompt} $" { |
| verbose -log "\n$me sve hardware not detected" |
| set allow_sve_tests 0 |
| } |
| -re ".*$inferior_exited_re normally.*${gdb_prompt} $" { |
| verbose -log "\n$me: sve hardware detected" |
| set allow_sve_tests 1 |
| } |
| default { |
| warning "\n$me: default case taken" |
| set allow_sve_tests 0 |
| } |
| } |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $allow_sve_tests" 2 |
| return $allow_sve_tests |
| } |
| |
| |
| # A helper that compiles a test case to see if __int128 is supported. |
| proc gdb_int128_helper {lang} { |
| return [gdb_can_simple_compile "i128-for-$lang" { |
| __int128 x; |
| int main() { return 0; } |
| } executable $lang] |
| } |
| |
| # Return true if the C compiler understands the __int128 type. |
| gdb_caching_proc has_int128_c { |
| return [gdb_int128_helper c] |
| } |
| |
| # Return true if the C++ compiler understands the __int128 type. |
| gdb_caching_proc has_int128_cxx { |
| return [gdb_int128_helper c++] |
| } |
| |
| # Return true if the IFUNC feature is unsupported. |
| gdb_caching_proc skip_ifunc_tests { |
| if [gdb_can_simple_compile ifunc { |
| extern void f_ (); |
| typedef void F (void); |
| F* g (void) { return &f_; } |
| void f () __attribute__ ((ifunc ("g"))); |
| } object] { |
| return 0 |
| } else { |
| return 1 |
| } |
| } |
| |
| # Return whether we should skip tests for showing inlined functions in |
| # backtraces. Requires get_compiler_info and get_debug_format. |
| |
| proc skip_inline_frame_tests {} { |
| # GDB only recognizes inlining information in DWARF. |
| if { ! [test_debug_format "DWARF \[0-9\]"] } { |
| return 1 |
| } |
| |
| # GCC before 4.1 does not emit DW_AT_call_file / DW_AT_call_line. |
| if { ([test_compiler_info "gcc-2-*"] |
| || [test_compiler_info "gcc-3-*"] |
| || [test_compiler_info "gcc-4-0-*"]) } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Return whether we should skip tests for showing variables from |
| # inlined functions. Requires get_compiler_info and get_debug_format. |
| |
| proc skip_inline_var_tests {} { |
| # GDB only recognizes inlining information in DWARF. |
| if { ! [test_debug_format "DWARF \[0-9\]"] } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Return a 1 if we should skip tests that require hardware breakpoints |
| |
| proc skip_hw_breakpoint_tests {} { |
| # Skip tests if requested by the board (note that no_hardware_watchpoints |
| # disables both watchpoints and breakpoints) |
| if { [target_info exists gdb,no_hardware_watchpoints]} { |
| return 1 |
| } |
| |
| # These targets support hardware breakpoints natively |
| if { [istarget "i?86-*-*"] |
| || [istarget "x86_64-*-*"] |
| || [istarget "ia64-*-*"] |
| || [istarget "arm*-*-*"] |
| || [istarget "aarch64*-*-*"] |
| || [istarget "s390*-*-*"] } { |
| return 0 |
| } |
| |
| return 1 |
| } |
| |
| # Return a 1 if we should skip tests that require hardware watchpoints |
| |
| proc skip_hw_watchpoint_tests {} { |
| # Skip tests if requested by the board |
| if { [target_info exists gdb,no_hardware_watchpoints]} { |
| return 1 |
| } |
| |
| # These targets support hardware watchpoints natively |
| # Note, not all Power 9 processors support hardware watchpoints due to a HW |
| # bug. Use has_hw_wp_support to check do a runtime check for hardware |
| # watchpoint support on Powerpc. |
| if { [istarget "i?86-*-*"] |
| || [istarget "x86_64-*-*"] |
| || [istarget "ia64-*-*"] |
| || [istarget "arm*-*-*"] |
| || [istarget "aarch64*-*-*"] |
| || ([istarget "powerpc*-*-linux*"] && [has_hw_wp_support]) |
| || [istarget "s390*-*-*"] } { |
| return 0 |
| } |
| |
| return 1 |
| } |
| |
| # Return a 1 if we should skip tests that require *multiple* hardware |
| # watchpoints to be active at the same time |
| |
| proc skip_hw_watchpoint_multi_tests {} { |
| if { [skip_hw_watchpoint_tests] } { |
| return 1 |
| } |
| |
| # These targets support just a single hardware watchpoint |
| if { [istarget "arm*-*-*"] |
| || [istarget "powerpc*-*-linux*"] } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Return a 1 if we should skip tests that require read/access watchpoints |
| |
| proc skip_hw_watchpoint_access_tests {} { |
| if { [skip_hw_watchpoint_tests] } { |
| return 1 |
| } |
| |
| # These targets support just write watchpoints |
| if { [istarget "s390*-*-*"] } { |
| return 1 |
| } |
| |
| return 0 |
| } |
| |
| # Return 1 if we should skip tests that require the runtime unwinder |
| # hook. This must be invoked while gdb is running, after shared |
| # libraries have been loaded. This is needed because otherwise a |
| # shared libgcc won't be visible. |
| |
| proc skip_unwinder_tests {} { |
| global gdb_prompt |
| |
| set ok 0 |
| gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" { |
| -re "= .*no debug info.*_Unwind_DebugHook.*\r\n$gdb_prompt $" { |
| } |
| -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" { |
| set ok 1 |
| } |
| -re "No symbol .* in current context.\r\n$gdb_prompt $" { |
| } |
| } |
| if {!$ok} { |
| gdb_test_multiple "info probe" "check for stap probe in unwinder" { |
| -re ".*libgcc.*unwind.*\r\n$gdb_prompt $" { |
| set ok 1 |
| } |
| -re "\r\n$gdb_prompt $" { |
| } |
| } |
| } |
| return $ok |
| } |
| |
| # Return 1 if we should skip tests that require the libstdc++ stap |
| # probes. This must be invoked while gdb is running, after shared |
| # libraries have been loaded. PROMPT_REGEXP is the expected prompt. |
| |
| proc skip_libstdcxx_probe_tests_prompt { prompt_regexp } { |
| set supported 0 |
| gdb_test_multiple "info probe" "check for stap probe in libstdc++" \ |
| -prompt "$prompt_regexp" { |
| -re ".*libstdcxx.*catch.*\r\n$prompt_regexp" { |
| set supported 1 |
| } |
| -re "\r\n$prompt_regexp" { |
| } |
| } |
| set skip [expr !$supported] |
| return $skip |
| } |
| |
| # As skip_libstdcxx_probe_tests_prompt, with gdb_prompt. |
| |
| proc skip_libstdcxx_probe_tests {} { |
| global gdb_prompt |
| return [skip_libstdcxx_probe_tests_prompt "$gdb_prompt $"] |
| } |
| |
| # Helper for gdb_is_target_* procs. TARGET_NAME is the name of the target |
| # we're looking for (used to build the test name). TARGET_STACK_REGEXP |
| # is a regexp that will match the output of "maint print target-stack" if |
| # the target in question is currently pushed. PROMPT_REGEXP is a regexp |
| # matching the expected prompt after the command output. |
| # |
| # NOTE: GDB must be running BEFORE this procedure is called! |
| |
| proc gdb_is_target_1 { target_name target_stack_regexp prompt_regexp } { |
| global gdb_spawn_id |
| |
| # Throw a Tcl error if gdb isn't already started. |
| if {![info exists gdb_spawn_id]} { |
| error "gdb_is_target_1 called with no running gdb instance" |
| } |
| |
| set test "probe for target ${target_name}" |
| gdb_test_multiple "maint print target-stack" $test \ |
| -prompt "$prompt_regexp" { |
| -re "${target_stack_regexp}${prompt_regexp}" { |
| pass $test |
| return 1 |
| } |
| -re "$prompt_regexp" { |
| pass $test |
| } |
| } |
| return 0 |
| } |
| |
| # Helper for gdb_is_target_remote where the expected prompt is variable. |
| # |
| # NOTE: GDB must be running BEFORE this procedure is called! |
| |
| proc gdb_is_target_remote_prompt { prompt_regexp } { |
| return [gdb_is_target_1 "remote" ".*emote target using gdb-specific protocol.*" $prompt_regexp] |
| } |
| |
| # Check whether we're testing with the remote or extended-remote |
| # targets. |
| # |
| # NOTE: GDB must be running BEFORE this procedure is called! |
| |
| proc gdb_is_target_remote { } { |
| global gdb_prompt |
| |
| return [gdb_is_target_remote_prompt "$gdb_prompt $"] |
| } |
| |
| # Check whether we're testing with the native target. |
| # |
| # NOTE: GDB must be running BEFORE this procedure is called! |
| |
| proc gdb_is_target_native { } { |
| global gdb_prompt |
| |
| return [gdb_is_target_1 "native" ".*native \\(Native process\\).*" "$gdb_prompt $"] |
| } |
| |
| # Return the effective value of use_gdb_stub. |
| # |
| # If the use_gdb_stub global has been set (it is set when the gdb process is |
| # spawned), return that. Otherwise, return the value of the use_gdb_stub |
| # property from the board file. |
| # |
| # This is the preferred way of checking use_gdb_stub, since it allows to check |
| # the value before the gdb has been spawned and it will return the correct value |
| # even when it was overriden by the test. |
| # |
| # Note that stub targets are not able to spawn new inferiors. Use this |
| # check for skipping respective tests. |
| |
| proc use_gdb_stub {} { |
| global use_gdb_stub |
| |
| if [info exists use_gdb_stub] { |
| return $use_gdb_stub |
| } |
| |
| return [target_info exists use_gdb_stub] |
| } |
| |
| # Return 1 if the current remote target is an instance of our GDBserver, 0 |
| # otherwise. Return -1 if there was an error and we can't tell. |
| |
| gdb_caching_proc target_is_gdbserver { |
| global gdb_prompt |
| |
| set is_gdbserver -1 |
| set test "probing for GDBserver" |
| |
| gdb_test_multiple "monitor help" $test { |
| -re "The following monitor commands are supported.*Quit GDBserver.*$gdb_prompt $" { |
| set is_gdbserver 1 |
| } |
| -re "$gdb_prompt $" { |
| set is_gdbserver 0 |
| } |
| } |
| |
| if { $is_gdbserver == -1 } { |
| verbose -log "Unable to tell whether we are using GDBserver or not." |
| } |
| |
| return $is_gdbserver |
| } |
| |
| # N.B. compiler_info is intended to be local to this file. |
| # Call test_compiler_info with no arguments to fetch its value. |
| # Yes, this is counterintuitive when there's get_compiler_info, |
| # but that's the current API. |
| if [info exists compiler_info] { |
| unset compiler_info |
| } |
| |
| # Figure out what compiler I am using. |
| # The result is cached so only the first invocation runs the compiler. |
| # |
| # ARG can be empty or "C++". If empty, "C" is assumed. |
| # |
| # There are several ways to do this, with various problems. |
| # |
| # [ gdb_compile -E $ifile -o $binfile.ci ] |
| # source $binfile.ci |
| # |
| # Single Unix Spec v3 says that "-E -o ..." together are not |
| # specified. And in fact, the native compiler on hp-ux 11 (among |
| # others) does not work with "-E -o ...". Most targets used to do |
| # this, and it mostly worked, because it works with gcc. |
| # |
| # [ catch "exec $compiler -E $ifile > $binfile.ci" exec_output ] |
| # source $binfile.ci |
| # |
| # This avoids the problem with -E and -o together. This almost works |
| # if the build machine is the same as the host machine, which is |
| # usually true of the targets which are not gcc. But this code does |
| # not figure which compiler to call, and it always ends up using the C |
| # compiler. Not good for setting hp_aCC_compiler. Target |
| # hppa*-*-hpux* used to do this. |
| # |
| # [ gdb_compile -E $ifile > $binfile.ci ] |
| # source $binfile.ci |
| # |
| # dejagnu target_compile says that it supports output redirection, |
| # but the code is completely different from the normal path and I |
| # don't want to sweep the mines from that path. So I didn't even try |
| # this. |
| # |
| # set cppout [ gdb_compile $ifile "" preprocess $args quiet ] |
| # eval $cppout |
| # |
| # I actually do this for all targets now. gdb_compile runs the right |
| # compiler, and TCL captures the output, and I eval the output. |
| # |
| # Unfortunately, expect logs the output of the command as it goes by, |
| # and dejagnu helpfully prints a second copy of it right afterwards. |
| # So I turn off expect logging for a moment. |
| # |
| # [ gdb_compile $ifile $ciexe_file executable $args ] |
| # [ remote_exec $ciexe_file ] |
| # [ source $ci_file.out ] |
| # |
| # I could give up on -E and just do this. |
| # I didn't get desperate enough to try this. |
| # |
| # -- chastain 2004-01-06 |
| |
| proc get_compiler_info {{language "c"}} { |
| |
| # For compiler.c, compiler.cc and compiler.F90. |
| global srcdir |
| |
| # I am going to play with the log to keep noise out. |
| global outdir |
| global tool |
| |
| # These come from compiler.c, compiler.cc or compiler.F90. |
| gdb_persistent_global compiler_info_cache |
| |
| if [info exists compiler_info_cache($language)] { |
| # Already computed. |
| return 0 |
| } |
| |
| # Choose which file to preprocess. |
| if { $language == "c++" } { |
| set ifile "${srcdir}/lib/compiler.cc" |
| } elseif { $language == "f90" } { |
| set ifile "${srcdir}/lib/compiler.F90" |
| } elseif { $language == "c" } { |
| set ifile "${srcdir}/lib/compiler.c" |
| } else { |
| perror "Unable to fetch compiler version for language: $language" |
| return -1 |
| } |
| |
| # Run $ifile through the right preprocessor. |
| # Toggle gdb.log to keep the compiler output out of the log. |
| set saved_log [log_file -info] |
| log_file |
| if [is_remote host] { |
| # We have to use -E and -o together, despite the comments |
| # above, because of how DejaGnu handles remote host testing. |
| set ppout "$outdir/compiler.i" |
| gdb_compile "${ifile}" "$ppout" preprocess [list "$language" quiet getting_compiler_info] |
| set file [open $ppout r] |
| set cppout [read $file] |
| close $file |
| } else { |
| # Copy $ifile to temp dir, to work around PR gcc/60447. This will leave the |
| # superfluous .s file in the temp dir instead of in the source dir. |
| set tofile [file tail $ifile] |
| set tofile [standard_temp_file $tofile] |
| file copy -force $ifile $tofile |
| set ifile $tofile |
| set cppout [ gdb_compile "${ifile}" "" preprocess [list "$language" quiet getting_compiler_info] ] |
| } |
| eval log_file $saved_log |
| |
| # Eval the output. |
| set unknown 0 |
| foreach cppline [ split "$cppout" "\n" ] { |
| if { [ regexp "^#" "$cppline" ] } { |
| # line marker |
| } elseif { [ regexp "^\[\n\r\t \]*$" "$cppline" ] } { |
| # blank line |
| } elseif { [ regexp "^\[\n\r\t \]*set\[\n\r\t \]" "$cppline" ] } { |
| # eval this line |
| verbose "get_compiler_info: $cppline" 2 |
| eval "$cppline" |
| } elseif { [ regexp "flang.*warning.*'-fdiagnostics-color=never'" "$cppline"] } { |
| # Both flang preprocessors (llvm flang and classic flang) print a |
| # warning for the unused -fdiagnostics-color=never, so we skip this |
| # output line here. |
| } else { |
| # unknown line |
| verbose -log "get_compiler_info: $cppline" |
| set unknown 1 |
| } |
| } |
| |
| # Set to unknown if for some reason compiler_info didn't get defined. |
| if ![info exists compiler_info] { |
| verbose -log "get_compiler_info: compiler_info not provided" |
| set compiler_info "unknown" |
| } |
| # Also set to unknown compiler if any diagnostics happened. |
| if { $unknown } { |
| verbose -log "get_compiler_info: got unexpected diagnostics" |
| set compiler_info "unknown" |
| } |
| |
| set compiler_info_cache($language) $compiler_info |
| |
| # Log what happened. |
| verbose -log "get_compiler_info: $compiler_info" |
| |
| return 0 |
| } |
| |
| # Return the compiler_info string if no arg is provided. |
| # Otherwise the argument is a glob-style expression to match against |
| # compiler_info. |
| |
| proc test_compiler_info { {compiler ""} {language "c"} } { |
| gdb_persistent_global compiler_info_cache |
| |
| if [get_compiler_info $language] { |
| # An error will already have been printed in this case. Just |
| # return a suitable result depending on how the user called |
| # this function. |
| if [string match "" $compiler] { |
| return "" |
| } else { |
| return false |
| } |
| } |
| |
| # If no arg, return the compiler_info string. |
| if [string match "" $compiler] { |
| return $compiler_info_cache($language) |
| } |
| |
| return [string match $compiler $compiler_info_cache($language)] |
| } |
| |
| # Return true if the C compiler is GCC, otherwise, return false. |
| |
| proc is_c_compiler_gcc {} { |
| set compiler_info [test_compiler_info] |
| set gcc_compiled false |
| regexp "^gcc-(\[0-9\]+)-" "$compiler_info" matchall gcc_compiled |
| return $gcc_compiled |
| } |
| |
| # Return the gcc major version, or -1. |
| # For gcc 4.8.5, the major version is 4.8. |
| # For gcc 7.5.0, the major version 7. |
| # The COMPILER and LANGUAGE arguments are as for test_compiler_info. |
| |
| proc gcc_major_version { {compiler "gcc-*"} {language "c"} } { |
| global decimal |
| if { ![test_compiler_info $compiler $language] } { |
| return -1 |
| } |
| # Strip "gcc-*" to "gcc". |
| regsub -- {-.*} $compiler "" compiler |
| set res [regexp $compiler-($decimal)-($decimal)- \ |
| [test_compiler_info "" $language] \ |
| dummy_var major minor] |
| if { $res != 1 } { |
| return -1 |
| } |
| if { $major >= 5} { |
| return $major |
| } |
| return $major.$minor |
| } |
| |
| proc current_target_name { } { |
| global target_info |
| if [info exists target_info(target,name)] { |
| set answer $target_info(target,name) |
| } else { |
| set answer "" |
| } |
| return $answer |
| } |
| |
| set gdb_wrapper_initialized 0 |
| set gdb_wrapper_target "" |
| set gdb_wrapper_file "" |
| set gdb_wrapper_flags "" |
| |
| proc gdb_wrapper_init { args } { |
| global gdb_wrapper_initialized |
| global gdb_wrapper_file |
| global gdb_wrapper_flags |
| global gdb_wrapper_target |
| |
| if { $gdb_wrapper_initialized == 1 } { return; } |
| |
| if {[target_info exists needs_status_wrapper] && \ |
| [target_info needs_status_wrapper] != "0"} { |
| set result [build_wrapper "testglue.o"] |
| if { $result != "" } { |
| set gdb_wrapper_file [lindex $result 0] |
| if ![is_remote host] { |
| set gdb_wrapper_file [file join [pwd] $gdb_wrapper_file] |
| } |
| set gdb_wrapper_flags [lindex $result 1] |
| } else { |
| warning "Status wrapper failed to build." |
| } |
| } else { |
| set gdb_wrapper_file "" |
| set gdb_wrapper_flags "" |
| } |
| verbose "set gdb_wrapper_file = $gdb_wrapper_file" |
| set gdb_wrapper_initialized 1 |
| set gdb_wrapper_target [current_target_name] |
| } |
| |
| # Determine options that we always want to pass to the compiler. |
| gdb_caching_proc universal_compile_options { |
| set me "universal_compile_options" |
| set options {} |
| |
| set src [standard_temp_file ccopts[pid].c] |
| set obj [standard_temp_file ccopts[pid].o] |
| |
| gdb_produce_source $src { |
| int foo(void) { return 0; } |
| } |
| |
| # Try an option for disabling colored diagnostics. Some compilers |
| # yield colored diagnostics by default (when run from a tty) unless |
| # such an option is specified. |
| set opt "additional_flags=-fdiagnostics-color=never" |
| set lines [target_compile $src $obj object [list "quiet" $opt]] |
| if {[string match "" $lines]} { |
| # Seems to have worked; use the option. |
| lappend options $opt |
| } |
| file delete $src |
| file delete $obj |
| |
| verbose "$me: returning $options" 2 |
| return $options |
| } |
| |
| # Compile the code in $code to a file based on $name, using the flags |
| # $compile_flag as well as debug, nowarning and quiet. |
| # Return 1 if code can be compiled |
| # Leave the file name of the resulting object in the upvar object. |
| |
| proc gdb_simple_compile {name code {type object} {compile_flags {}} {object obj}} { |
| upvar $object obj |
| |
| switch -regexp -- $type { |
| "executable" { |
| set postfix "x" |
| } |
| "object" { |
| set postfix "o" |
| } |
| "preprocess" { |
| set postfix "i" |
| } |
| "assembly" { |
| set postfix "s" |
| } |
| } |
| set ext "c" |
| foreach flag $compile_flags { |
| if { "$flag" == "go" } { |
| set ext "go" |
| break |
| } |
| } |
| set src [standard_temp_file $name-[pid].$ext] |
| set obj [standard_temp_file $name-[pid].$postfix] |
| set compile_flags [concat $compile_flags {debug nowarnings quiet}] |
| |
| gdb_produce_source $src $code |
| |
| verbose "$name: compiling testfile $src" 2 |
| set lines [gdb_compile $src $obj $type $compile_flags] |
| |
| file delete $src |
| |
| if {![string match "" $lines]} { |
| verbose "$name: compilation failed, returning 0" 2 |
| return 0 |
| } |
| return 1 |
| } |
| |
| # Compile the code in $code to a file based on $name, using the flags |
| # $compile_flag as well as debug, nowarning and quiet. |
| # Return 1 if code can be compiled |
| # Delete all created files and objects. |
| |
| proc gdb_can_simple_compile {name code {type object} {compile_flags ""}} { |
| set ret [gdb_simple_compile $name $code $type $compile_flags temp_obj] |
| file delete $temp_obj |
| return $ret |
| } |
| |
| # Some targets need to always link a special object in. Save its path here. |
| global gdb_saved_set_unbuffered_mode_obj |
| set gdb_saved_set_unbuffered_mode_obj "" |
| |
| # Compile source files specified by SOURCE into a binary of type TYPE at path |
| # DEST. gdb_compile is implemented using DejaGnu's target_compile, so the type |
| # parameter and most options are passed directly to it. |
| # |
| # The type can be one of the following: |
| # |
| # - object: Compile into an object file. |
| # - executable: Compile and link into an executable. |
| # - preprocess: Preprocess the source files. |
| # - assembly: Generate assembly listing. |
| # |
| # The following options are understood and processed by gdb_compile: |
| # |
| # - shlib=so_path: Add SO_PATH to the sources, and enable some target-specific |
| # quirks to be able to use shared libraries. |
| # - shlib_load: Link with appropriate libraries to allow the test to |
| # dynamically load libraries at runtime. For example, on Linux, this adds |
| # -ldl so that the test can use dlopen. |
| # - nowarnings: Inhibit all compiler warnings. |
| # - pie: Force creation of PIE executables. |
| # - nopie: Prevent creation of PIE executables. |
| # - macros: Add the required compiler flag to include macro information in |
| # debug information |
| # - text_segment=addr: Tell the linker to place the text segment at ADDR. |
| # - build-id: Ensure the final binary includes a build-id. |
| # |
| # And here are some of the not too obscure options understood by DejaGnu that |
| # influence the compilation: |
| # |
| # - additional_flags=flag: Add FLAG to the compiler flags. |
| # - libs=library: Add LIBRARY to the libraries passed to the linker. The |
| # argument can be a file, in which case it's added to the sources, or a |
| # linker flag. |
| # - ldflags=flag: Add FLAG to the linker flags. |
| # - incdir=path: Add PATH to the searched include directories. |
| # - libdir=path: Add PATH to the linker searched directories. |
| # - ada, c++, f90, go, rust: Compile the file as Ada, C++, |
| # Fortran 90, Go or Rust. |
| # - debug: Build with debug information. |
| # - optimize: Build with optimization. |
| |
| proc gdb_compile {source dest type options} { |
| global GDB_TESTCASE_OPTIONS |
| global gdb_wrapper_file |
| global gdb_wrapper_flags |
| global srcdir |
| global objdir |
| global gdb_saved_set_unbuffered_mode_obj |
| |
| set outdir [file dirname $dest] |
| |
| # If this is set, calling test_compiler_info will cause recursion. |
| if { [lsearch -exact $options getting_compiler_info] == -1 } { |
| set getting_compiler_info false |
| } else { |
| set getting_compiler_info true |
| } |
| |
| # Add platform-specific options if a shared library was specified using |
| # "shlib=librarypath" in OPTIONS. |
| set new_options {} |
| if {[lsearch -exact $options rust] != -1} { |
| # -fdiagnostics-color is not a rustcc option. |
| } else { |
| set new_options [universal_compile_options] |
| } |
| |
| # C/C++ specific settings. |
| if {!$getting_compiler_info |
| && [lsearch -exact $options rust] == -1 |
| && [lsearch -exact $options ada] == -1 |
| && [lsearch -exact $options f90] == -1 |
| && [lsearch -exact $options go] == -1} { |
| |
| # Some C/C++ testcases unconditionally pass -Wno-foo as additional |
| # options to disable some warning. That is OK with GCC, because |
| # by design, GCC accepts any -Wno-foo option, even if it doesn't |
| # support -Wfoo. Clang however warns about unknown -Wno-foo by |
| # default, unless you pass -Wno-unknown-warning-option as well. |
| # We do that here, so that individual testcases don't have to |
| # worry about it. |
| if {[test_compiler_info "clang-*"] || [test_compiler_info "icx-*"]} { |
| lappend new_options "additional_flags=-Wno-unknown-warning-option" |
| } elseif {[test_compiler_info "icc-*"]} { |
| # This is the equivalent for the icc compiler. |
| lappend new_options "additional_flags=-diag-disable=10148" |
| } |
| |
| # icpx/icx give the following warning if '-g' is used without '-O'. |
| # |
| # icpx: remark: Note that use of '-g' without any |
| # optimization-level option will turn off most compiler |
| # optimizations similar to use of '-O0' |
| # |
| # The warning makes dejagnu think that compilation has failed. |
| # |
| # Furthermore, if no -O flag is passed, icx and icc optimize |
| # the code by default. This breaks assumptions in many GDB |
| # tests that the code is unoptimized by default. |
| # |
| # To fix both problems, pass the -O0 flag explicitly, if no |
| # optimization option is given. |
| if {[test_compiler_info "icx-*"] || [test_compiler_info "icc-*"]} { |
| if {[lsearch $options optimize=*] == -1 |
| && [lsearch $options additional_flags=-O*] == -1} { |
| lappend new_options "optimize=-O0" |
| } |
| } |
| |
| # Starting with 2021.7.0 (recognized as icc-20-21-7 by GDB) icc and |
| # icpc are marked as deprecated and both compilers emit the remark |
| # #10441. To let GDB still compile successfully, we disable these |
| # warnings here. |
| if {([lsearch -exact $options c++] != -1 |
| && [test_compiler_info {icc-20-21-[7-9]} c++]) |
| || [test_compiler_info {icc-20-21-[7-9]}]} { |
| lappend new_options "additional_flags=-diag-disable=10441" |
| } |
| } |
| |
| # If the 'build-id' option is used, then ensure that we generate a |
| # build-id. GCC does this by default, but Clang does not, so |
| # enable it now. |
| if {[lsearch -exact $options build-id] > 0 |
| && [test_compiler_info "clang-*"]} { |
| lappend new_options "additional_flags=-Wl,--build-id" |
| } |
| |
| # Treating .c input files as C++ is deprecated in Clang, so |
| # explicitly force C++ language. |
| if { !$getting_compiler_info |
| && [lsearch -exact $options c++] != -1 |
| && [string match *.c $source] != 0 } { |
| |
| # gdb_compile cannot handle this combination of options, the |
| # result is a command like "clang -x c++ foo.c bar.so -o baz" |
| # which tells Clang to treat bar.so as C++. The solution is |
| # to call gdb_compile twice--once to compile, once to link-- |
| # either directly, or via build_executable_from_specs. |
| if { [lsearch $options shlib=*] != -1 } { |
| error "incompatible gdb_compile options" |
| } |
| |
| if {[test_compiler_info "clang-*"]} { |
| lappend new_options early_flags=-x\ c++ |
| } |
| } |
| |
| # Place (and look for) Fortran `.mod` files in the output |
| # directory for this specific test. For Intel compilers the -J |
| # option is not supported so instead use the -module flag. |
| # Additionally, Intel compilers need the -debug-parameters flag set to |
| # emit debug info for all parameters in modules. |
| # |
| # ifx gives the following warning if '-g' is used without '-O'. |
| # |
| # ifx: remark #10440: Note that use of a debug option |
| # without any optimization-level option will turnoff most |
| # compiler optimizations similar to use of '-O0' |
| # |
| # The warning makes dejagnu think that compilation has failed. |
| # |
| # Furthermore, if no -O flag is passed, Intel compilers optimize |
| # the code by default. This breaks assumptions in many GDB |
| # tests that the code is unoptimized by default. |
| # |
| # To fix both problems, pass the -O0 flag explicitly, if no |
| # optimization option is given. |
| if { !$getting_compiler_info && [lsearch -exact $options f90] != -1 } { |
| # Fortran compile. |
| set mod_path [standard_output_file ""] |
| if { [test_compiler_info {gfortran-*} f90] } { |
| lappend new_options "additional_flags=-J${mod_path}" |
| } elseif { [test_compiler_info {ifort-*} f90] |
| || [test_compiler_info {ifx-*} f90] } { |
| lappend new_options "additional_flags=-module ${mod_path}" |
| lappend new_options "additional_flags=-debug-parameters all" |
| |
| if {[lsearch $options optimize=*] == -1 |
| && [lsearch $options additional_flags=-O*] == -1} { |
| lappend new_options "optimize=-O0" |
| } |
| } |
| } |
| |
| set shlib_found 0 |
| set shlib_load 0 |
| foreach opt $options { |
| if {[regexp {^shlib=(.*)} $opt dummy_var shlib_name] |
| && $type == "executable"} { |
| if [test_compiler_info "xlc-*"] { |
| # IBM xlc compiler doesn't accept shared library named other |
| # than .so: use "-Wl," to bypass this |
| lappend source "-Wl,$shlib_name" |
| } elseif { ([istarget "*-*-mingw*"] |
| || [istarget *-*-cygwin*] |
| || [istarget *-*-pe*])} { |
| lappend source "${shlib_name}.a" |
| } else { |
| lappend source $shlib_name |
| } |
| if { $shlib_found == 0 } { |
| set shlib_found 1 |
| if { ([istarget "*-*-mingw*"] |
| || [istarget *-*-cygwin*]) } { |
| lappend new_options "ldflags=-Wl,--enable-auto-import" |
| } |
| if { [test_compiler_info "gcc-*"] || [test_compiler_info "clang-*"] } { |
| # Undo debian's change in the default. |
| # Put it at the front to not override any user-provided |
| # value, and to make sure it appears in front of all the |
| # shlibs! |
| lappend new_options "early_flags=-Wl,--no-as-needed" |
| } |
| } |
| } elseif { $opt == "shlib_load" && $type == "executable" } { |
| set shlib_load 1 |
| } elseif { $opt == "getting_compiler_info" } { |
| # Ignore this setting here as it has been handled earlier in this |
| # procedure. Do not append it to new_options as this will cause |
| # recursion. |
| } elseif {[regexp "^text_segment=(.*)" $opt dummy_var addr]} { |
| if { [linker_supports_Ttext_segment_flag] } { |
| # For GNU ld. |
| lappend new_options "ldflags=-Wl,-Ttext-segment=$addr" |
| } elseif { [linker_supports_image_base_flag] } { |
| # For LLVM's lld. |
| lappend new_options "ldflags=-Wl,--image-base=$addr" |
| } elseif { [linker_supports_Ttext_flag] } { |
| # For old GNU gold versions. |
| lappend new_options "ldflags=-Wl,-Ttext=$addr" |
| } else { |
| error "Don't know how to handle text_segment option." |
| } |
| } else { |
| lappend new_options $opt |
| } |
| } |
| |
| # Ensure stack protector is disabled for GCC, as this causes problems with |
| # DWARF line numbering. |
| # See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88432 |
| # This option defaults to on for Debian/Ubuntu. |
| if { !$getting_compiler_info |
| && [test_compiler_info {gcc-*-*}] |
| && !([test_compiler_info {gcc-[0-3]-*}] |
| || [test_compiler_info {gcc-4-0-*}]) |
| && [lsearch -exact $options rust] == -1} { |
| # Put it at the front to not override any user-provided value. |
| lappend new_options "early_flags=-fno-stack-protector" |
| } |
| |
| # Because we link with libraries using their basename, we may need |
| # (depending on the platform) to set a special rpath value, to allow |
| # the executable to find the libraries it depends on. |
| if { $shlib_load || $shlib_found } { |
| if { ([istarget "*-*-mingw*"] |
| || [istarget *-*-cygwin*] |
| || [istarget *-*-pe*]) } { |
| # Do not need anything. |
| } elseif { [istarget *-*-freebsd*] || [istarget *-*-openbsd*] } { |
| lappend new_options "ldflags=-Wl,-rpath,${outdir}" |
| } else { |
| if { $shlib_load } { |
| lappend new_options "libs=-ldl" |
| } |
| lappend new_options "ldflags=-Wl,-rpath,\\\$ORIGIN" |
| } |
| } |
| set options $new_options |
| |
| if [info exists GDB_TESTCASE_OPTIONS] { |
| lappend options "additional_flags=$GDB_TESTCASE_OPTIONS" |
| } |
| verbose "options are $options" |
| verbose "source is $source $dest $type $options" |
| |
| gdb_wrapper_init |
| |
| if {[target_info exists needs_status_wrapper] && \ |
| [target_info needs_status_wrapper] != "0" && \ |
| $gdb_wrapper_file != "" } { |
| lappend options "libs=${gdb_wrapper_file}" |
| lappend options "ldflags=${gdb_wrapper_flags}" |
| } |
| |
| # Replace the "nowarnings" option with the appropriate additional_flags |
| # to disable compiler warnings. |
| set nowarnings [lsearch -exact $options nowarnings] |
| if {$nowarnings != -1} { |
| if [target_info exists gdb,nowarnings_flag] { |
| set flag "additional_flags=[target_info gdb,nowarnings_flag]" |
| } else { |
| set flag "additional_flags=-w" |
| } |
| set options [lreplace $options $nowarnings $nowarnings $flag] |
| } |
| |
| # Replace the "pie" option with the appropriate compiler and linker flags |
| # to enable PIE executables. |
| set pie [lsearch -exact $options pie] |
| if {$pie != -1} { |
| if [target_info exists gdb,pie_flag] { |
| set flag "additional_flags=[target_info gdb,pie_flag]" |
| } else { |
| # For safety, use fPIE rather than fpie. On AArch64, m68k, PowerPC |
| # and SPARC, fpie can cause compile errors due to the GOT exceeding |
| # a maximum size. On other architectures the two flags are |
| # identical (see the GCC manual). Note Debian9 and Ubuntu16.10 |
| # onwards default GCC to using fPIE. If you do require fpie, then |
| # it can be set using the pie_flag. |
| set flag "additional_flags=-fPIE" |
| } |
| set options [lreplace $options $pie $pie $flag] |
| |
| if [target_info exists gdb,pie_ldflag] { |
| set flag "ldflags=[target_info gdb,pie_ldflag]" |
| } else { |
| set flag "ldflags=-pie" |
| } |
| lappend options "$flag" |
| } |
| |
| # Replace the "nopie" option with the appropriate compiler and linker |
| # flags to disable PIE executables. |
| set nopie [lsearch -exact $options nopie] |
| if {$nopie != -1} { |
| if [target_info exists gdb,nopie_flag] { |
| set flag "additional_flags=[target_info gdb,nopie_flag]" |
| } else { |
| set flag "additional_flags=-fno-pie" |
| } |
| set options [lreplace $options $nopie $nopie $flag] |
| |
| if [target_info exists gdb,nopie_ldflag] { |
| set flag "ldflags=[target_info gdb,nopie_ldflag]" |
| } else { |
| set flag "ldflags=-no-pie" |
| } |
| lappend options "$flag" |
| } |
| |
| set macros [lsearch -exact $options macros] |
| if {$macros != -1} { |
| if { [test_compiler_info "clang-*"] } { |
| set flag "additional_flags=-fdebug-macro" |
| } else { |
| set flag "additional_flags=-g3" |
| } |
| |
| set options [lreplace $options $macros $macros $flag] |
| } |
| |
| if { $type == "executable" } { |
| if { ([istarget "*-*-mingw*"] |
| || [istarget "*-*-*djgpp"] |
| || [istarget "*-*-cygwin*"])} { |
| # Force output to unbuffered mode, by linking in an object file |
| # with a global contructor that calls setvbuf. |
| # |
| # Compile the special object separately for two reasons: |
| # 1) Insulate it from $options. |
| # 2) Avoid compiling it for every gdb_compile invocation, |
| # which is time consuming, especially if we're remote |
| # host testing. |
| # |
| if { $gdb_saved_set_unbuffered_mode_obj == "" } { |
| verbose "compiling gdb_saved_set_unbuffered_obj" |
| set unbuf_src ${srcdir}/lib/set_unbuffered_mode.c |
| set unbuf_obj ${objdir}/set_unbuffered_mode.o |
| |
| set result [gdb_compile "${unbuf_src}" "${unbuf_obj}" object {nowarnings}] |
| if { $result != "" } { |
| return $result |
| } |
| if {[is_remote host]} { |
| set gdb_saved_set_unbuffered_mode_obj set_unbuffered_mode_saved.o |
| } else { |
| set gdb_saved_set_unbuffered_mode_obj ${objdir}/set_unbuffered_mode_saved.o |
| } |
| # Link a copy of the output object, because the |
| # original may be automatically deleted. |
| remote_download host $unbuf_obj $gdb_saved_set_unbuffered_mode_obj |
| } else { |
| verbose "gdb_saved_set_unbuffered_obj already compiled" |
| } |
| |
| # Rely on the internal knowledge that the global ctors are ran in |
| # reverse link order. In that case, we can use ldflags to |
| # avoid copying the object file to the host multiple |
| # times. |
| # This object can only be added if standard libraries are |
| # used. Thus, we need to disable it if -nostdlib option is used |
| if {[lsearch -regexp $options "-nostdlib"] < 0 } { |
| lappend options "ldflags=$gdb_saved_set_unbuffered_mode_obj" |
| } |
| } |
| } |
| |
| cond_wrap [expr $pie != -1 || $nopie != -1] \ |
| with_PIE_multilib_flags_filtered { |
| set result [target_compile $source $dest $type $options] |
| } |
| |
| # Prune uninteresting compiler (and linker) output. |
| regsub "Creating library file: \[^\r\n\]*\[\r\n\]+" $result "" result |
| |
| # Starting with 2021.7.0 icc and icpc are marked as deprecated and both |
| # compilers emit a remark #10441. To let GDB still compile successfully, |
| # we disable these warnings. When $getting_compiler_info is true however, |
| # we do not yet know the compiler (nor its version) and instead prune these |
| # lines from the compiler output to let the get_compiler_info pass. |
| if {$getting_compiler_info} { |
| regsub \ |
| "(icc|icpc): remark #10441: The Intel\\(R\\) C\\+\\+ Compiler Classic \\(ICC\\) is deprecated\[^\r\n\]*" \ |
| "$result" "" result |
| } |
| |
| regsub "\[\r\n\]*$" "$result" "" result |
| regsub "^\[\r\n\]*" "$result" "" result |
| |
| if { $type == "executable" && $result == "" \ |
| && ($nopie != -1 || $pie != -1) } { |
| set is_pie [exec_is_pie "$dest"] |
| if { $nopie != -1 && $is_pie == 1 } { |
| set result "nopie failed to prevent PIE executable" |
| } elseif { $pie != -1 && $is_pie == 0 } { |
| set result "pie failed to generate PIE executable" |
| } |
| } |
| |
| if {[lsearch $options quiet] < 0} { |
| if { $result != "" } { |
| clone_output "gdb compile failed, $result" |
| } |
| } |
| return $result |
| } |
| |
| |
| # This is just like gdb_compile, above, except that it tries compiling |
| # against several different thread libraries, to see which one this |
| # system has. |
| proc gdb_compile_pthreads {source dest type options} { |
| if {$type != "executable"} { |
| return [gdb_compile $source $dest $type $options] |
| } |
| set built_binfile 0 |
| set why_msg "unrecognized error" |
| foreach lib {-lpthreads -lpthread -lthread ""} { |
| # This kind of wipes out whatever libs the caller may have |
| # set. Or maybe theirs will override ours. How infelicitous. |
| set options_with_lib [concat $options [list libs=$lib quiet]] |
| set ccout [gdb_compile $source $dest $type $options_with_lib] |
| switch -regexp -- $ccout { |
| ".*no posix threads support.*" { |
| set why_msg "missing threads include file" |
| break |
| } |
| ".*cannot open -lpthread.*" { |
| set why_msg "missing runtime threads library" |
| } |
| ".*Can't find library for -lpthread.*" { |
| set why_msg "missing runtime threads library" |
| } |
| {^$} { |
| pass "successfully compiled posix threads test case" |
| set built_binfile 1 |
| break |
| } |
| } |
| } |
| if {!$built_binfile} { |
| unsupported "couldn't compile [file tail $source]: ${why_msg}" |
| return -1 |
| } |
| } |
| |
| # Build a shared library from SOURCES. |
| |
| proc gdb_compile_shlib_1 {sources dest options} { |
| set obj_options $options |
| |
| set ada 0 |
| if { [lsearch -exact $options "ada"] >= 0 } { |
| set ada 1 |
| } |
| |
| if { [lsearch -exact $options "c++"] >= 0 } { |
| set info_options "c++" |
| } elseif { [lsearch -exact $options "f90"] >= 0 } { |
| set info_options "f90" |
| } else { |
| set info_options "c" |
| } |
| |
| switch -glob [test_compiler_info "" ${info_options}] { |
| "xlc-*" { |
| lappend obj_options "additional_flags=-qpic" |
| } |
| "clang-*" { |
| if { [istarget "*-*-cygwin*"] |
| || [istarget "*-*-mingw*"] } { |
| lappend obj_options "additional_flags=-fPIC" |
| } else { |
| lappend obj_options "additional_flags=-fpic" |
| } |
| } |
| "gcc-*" { |
| if { [istarget "powerpc*-*-aix*"] |
| || [istarget "rs6000*-*-aix*"] |
| || [istarget "*-*-cygwin*"] |
| || [istarget "*-*-mingw*"] |
| || [istarget "*-*-pe*"] } { |
| lappend obj_options "additional_flags=-fPIC" |
| } else { |
| lappend obj_options "additional_flags=-fpic" |
| } |
| } |
| "icc-*" { |
| lappend obj_options "additional_flags=-fpic" |
| } |
| default { |
| # don't know what the compiler is... |
| lappend obj_options "additional_flags=-fPIC" |
| } |
| } |
| |
| set outdir [file dirname $dest] |
| set objects "" |
| foreach source $sources { |
| if {[file extension $source] == ".o"} { |
| # Already a .o file. |
| lappend objects $source |
| continue |
| } |
| |
| set sourcebase [file tail $source] |
| |
| if { $ada } { |
| # Gnatmake doesn't like object name foo.adb.o, use foo.o. |
| set sourcebase [file rootname $sourcebase] |
| } |
| set object ${outdir}/${sourcebase}.o |
| |
| if { $ada } { |
| # Use gdb_compile_ada_1 instead of gdb_compile_ada to avoid the |
| # PASS message. |
| if {[gdb_compile_ada_1 $source $object object \ |
| $obj_options] != ""} { |
| return -1 |
| } |
| } else { |
| if {[gdb_compile $source $object object \ |
| $obj_options] != ""} { |
| return -1 |
| } |
| } |
| |
| lappend objects $object |
| } |
| |
| set link_options $options |
| if { $ada } { |
| # If we try to use gnatmake for the link, it will interpret the |
| # object file as an .adb file. Remove ada from the options to |
| # avoid it. |
| set idx [lsearch $link_options "ada"] |
| set link_options [lreplace $link_options $idx $idx] |
| } |
| if [test_compiler_info "xlc-*"] { |
| lappend link_options "additional_flags=-qmkshrobj" |
| } else { |
| lappend link_options "additional_flags=-shared" |
| |
| if { ([istarget "*-*-mingw*"] |
| || [istarget *-*-cygwin*] |
| || [istarget *-*-pe*]) } { |
| if { [is_remote host] } { |
| set name [file tail ${dest}] |
| } else { |
| set name ${dest} |
| } |
| lappend link_options "ldflags=-Wl,--out-implib,${name}.a" |
| } else { |
| # Set the soname of the library. This causes the linker on ELF |
| # systems to create the DT_NEEDED entry in the executable referring |
| # to the soname of the library, and not its absolute path. This |
| # (using the absolute path) would be problem when testing on a |
| # remote target. |
| # |
| # In conjunction with setting the soname, we add the special |
| # rpath=$ORIGIN value when building the executable, so that it's |
| # able to find the library in its own directory. |
| set destbase [file tail $dest] |
| lappend link_options "ldflags=-Wl,-soname,$destbase" |
| } |
| } |
| if {[gdb_compile "${objects}" "${dest}" executable $link_options] != ""} { |
| return -1 |
| } |
| if { [is_remote host] |
| && ([istarget "*-*-mingw*"] |
| || [istarget *-*-cygwin*] |
| || [istarget *-*-pe*]) } { |
| set dest_tail_name [file tail ${dest}] |
| remote_upload host $dest_tail_name.a ${dest}.a |
| remote_file host delete $dest_tail_name.a |
| } |
| |
| return "" |
| } |
| |
| # Ignore FLAGS in target board multilib_flags while executing BODY. |
| |
| proc with_multilib_flags_filtered { flags body } { |
| global board |
| |
| # Ignore flags in multilib_flags. |
| set board [target_info name] |
| set multilib_flags_orig [board_info $board multilib_flags] |
| set multilib_flags "" |
| foreach op $multilib_flags_orig { |
| if { [lsearch -exact $flags $op] == -1 } { |
| append multilib_flags " $op" |
| } |
| } |
| |
| save_target_board_info { multilib_flags } { |
| unset_board_info multilib_flags |
| set_board_info multilib_flags "$multilib_flags" |
| set result [uplevel 1 $body] |
| } |
| |
| return $result |
| } |
| |
| # Ignore PIE-related flags in target board multilib_flags while executing BODY. |
| |
| proc with_PIE_multilib_flags_filtered { body } { |
| set pie_flags [list "-pie" "-no-pie" "-fPIE" "-fno-PIE"] |
| return [uplevel 1 [list with_multilib_flags_filtered $pie_flags $body]] |
| } |
| |
| # Build a shared library from SOURCES. Ignore target boards PIE-related |
| # multilib_flags. |
| |
| proc gdb_compile_shlib {sources dest options} { |
| with_PIE_multilib_flags_filtered { |
| set result [gdb_compile_shlib_1 $sources $dest $options] |
| } |
| |
| return $result |
| } |
| |
| # This is just like gdb_compile_shlib, above, except that it tries compiling |
| # against several different thread libraries, to see which one this |
| # system has. |
| proc gdb_compile_shlib_pthreads {sources dest options} { |
| set built_binfile 0 |
| set why_msg "unrecognized error" |
| foreach lib {-lpthreads -lpthread -lthread ""} { |
| # This kind of wipes out whatever libs the caller may have |
| # set. Or maybe theirs will override ours. How infelicitous. |
| set options_with_lib [concat $options [list libs=$lib quiet]] |
| set ccout [gdb_compile_shlib $sources $dest $options_with_lib] |
| switch -regexp -- $ccout { |
| ".*no posix threads support.*" { |
| set why_msg "missing threads include file" |
| break |
| } |
| ".*cannot open -lpthread.*" { |
| set why_msg "missing runtime threads library" |
| } |
| ".*Can't find library for -lpthread.*" { |
| set why_msg "missing runtime threads library" |
| } |
| {^$} { |
| pass "successfully compiled posix threads shlib test case" |
| set built_binfile 1 |
| break |
| } |
| } |
| } |
| if {!$built_binfile} { |
| unsupported "couldn't compile $sources: ${why_msg}" |
| return -1 |
| } |
| } |
| |
| # This is just like gdb_compile_pthreads, above, except that we always add the |
| # objc library for compiling Objective-C programs |
| proc gdb_compile_objc {source dest type options} { |
| set built_binfile 0 |
| set why_msg "unrecognized error" |
| foreach lib {-lobjc -lpthreads -lpthread -lthread solaris} { |
| # This kind of wipes out whatever libs the caller may have |
| # set. Or maybe theirs will override ours. How infelicitous. |
| if { $lib == "solaris" } { |
| set lib "-lpthread -lposix4" |
| } |
| if { $lib != "-lobjc" } { |
| set lib "-lobjc $lib" |
| } |
| set options_with_lib [concat $options [list libs=$lib quiet]] |
| set ccout [gdb_compile $source $dest $type $options_with_lib] |
| switch -regexp -- $ccout { |
| ".*no posix threads support.*" { |
| set why_msg "missing threads include file" |
| break |
| } |
| ".*cannot open -lpthread.*" { |
| set why_msg "missing runtime threads library" |
| } |
| ".*Can't find library for -lpthread.*" { |
| set why_msg "missing runtime threads library" |
| } |
| {^$} { |
| pass "successfully compiled objc with posix threads test case" |
| set built_binfile 1 |
| break |
| } |
| } |
| } |
| if {!$built_binfile} { |
| unsupported "couldn't compile [file tail $source]: ${why_msg}" |
| return -1 |
| } |
| } |
| |
| # Build an OpenMP program from SOURCE. See prefatory comment for |
| # gdb_compile, above, for discussion of the parameters to this proc. |
| |
| proc gdb_compile_openmp {source dest type options} { |
| lappend options "additional_flags=-fopenmp" |
| return [gdb_compile $source $dest $type $options] |
| } |
| |
| # Send a command to GDB. |
| # For options for TYPE see gdb_stdin_log_write |
| |
| proc send_gdb { string {type standard}} { |
| gdb_stdin_log_write $string $type |
| return [remote_send host "$string"] |
| } |
| |
| # Send STRING to the inferior's terminal. |
| |
| proc send_inferior { string } { |
| global inferior_spawn_id |
| |
| if {[catch "send -i $inferior_spawn_id -- \$string" errorInfo]} { |
| return "$errorInfo" |
| } else { |
| return "" |
| } |
| } |
| |
| # |
| # |
| |
| proc gdb_expect { args } { |
| if { [llength $args] == 2 && [lindex $args 0] != "-re" } { |
| set atimeout [lindex $args 0] |
| set expcode [list [lindex $args 1]] |
| } else { |
| set expcode $args |
| } |
| |
| # A timeout argument takes precedence, otherwise of all the timeouts |
| # select the largest. |
| if [info exists atimeout] { |
| set tmt $atimeout |
| } else { |
| set tmt [get_largest_timeout] |
| } |
| |
| set code [catch \ |
| {uplevel remote_expect host $tmt $expcode} string] |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| |
| return -code error -errorinfo $errorInfo -errorcode $errorCode $string |
| } else { |
| return -code $code $string |
| } |
| } |
| |
| # gdb_expect_list TEST SENTINEL LIST -- expect a sequence of outputs |
| # |
| # Check for long sequence of output by parts. |
| # TEST: is the test message to be printed with the test success/fail. |
| # SENTINEL: Is the terminal pattern indicating that output has finished. |
| # LIST: is the sequence of outputs to match. |
| # If the sentinel is recognized early, it is considered an error. |
| # |
| # Returns: |
| # 1 if the test failed, |
| # 0 if the test passes, |
| # -1 if there was an internal error. |
| |
| proc gdb_expect_list {test sentinel list} { |
| global gdb_prompt |
| set index 0 |
| set ok 1 |
| |
| while { ${index} < [llength ${list}] } { |
| set pattern [lindex ${list} ${index}] |
| set index [expr ${index} + 1] |
| verbose -log "gdb_expect_list pattern: /$pattern/" 2 |
| if { ${index} == [llength ${list}] } { |
| if { ${ok} } { |
| gdb_expect { |
| -re "${pattern}${sentinel}" { |
| # pass "${test}, pattern ${index} + sentinel" |
| } |
| -re "${sentinel}" { |
| fail "${test} (pattern ${index} + sentinel)" |
| set ok 0 |
| } |
| -re ".*A problem internal to GDB has been detected" { |
| fail "${test} (GDB internal error)" |
| set ok 0 |
| gdb_internal_error_resync |
| } |
| timeout { |
| fail "${test} (pattern ${index} + sentinel) (timeout)" |
| set ok 0 |
| } |
| } |
| } else { |
| # unresolved "${test}, pattern ${index} + sentinel" |
| } |
| } else { |
| if { ${ok} } { |
| gdb_expect { |
| -re "${pattern}" { |
| # pass "${test}, pattern ${index}" |
| } |
| -re "${sentinel}" { |
| fail "${test} (pattern ${index})" |
| set ok 0 |
| } |
| -re ".*A problem internal to GDB has been detected" { |
| fail "${test} (GDB internal error)" |
| set ok 0 |
| gdb_internal_error_resync |
| } |
| timeout { |
| fail "${test} (pattern ${index}) (timeout)" |
| set ok 0 |
| } |
| } |
| } else { |
| # unresolved "${test}, pattern ${index}" |
| } |
| } |
| } |
| if { ${ok} } { |
| pass "${test}" |
| return 0 |
| } else { |
| return 1 |
| } |
| } |
| |
| # Spawn the gdb process. |
| # |
| # This doesn't expect any output or do any other initialization, |
| # leaving those to the caller. |
| # |
| # Overridable function -- you can override this function in your |
| # baseboard file. |
| |
| proc gdb_spawn { } { |
| default_gdb_spawn |
| } |
| |
| # Spawn GDB with CMDLINE_FLAGS appended to the GDBFLAGS global. |
| |
| proc gdb_spawn_with_cmdline_opts { cmdline_flags } { |
| global GDBFLAGS |
| |
| set saved_gdbflags $GDBFLAGS |
| |
| if {$GDBFLAGS != ""} { |
| append GDBFLAGS " " |
| } |
| append GDBFLAGS $cmdline_flags |
| |
| set res [gdb_spawn] |
| |
| set GDBFLAGS $saved_gdbflags |
| |
| return $res |
| } |
| |
| # Start gdb running, wait for prompt, and disable the pagers. |
| |
| # Overridable function -- you can override this function in your |
| # baseboard file. |
| |
| proc gdb_start { } { |
| default_gdb_start |
| } |
| |
| proc gdb_exit { } { |
| catch default_gdb_exit |
| } |
| |
| # Return true if we can spawn a program on the target and attach to |
| # it. |
| |
| proc can_spawn_for_attach { } { |
| # We use exp_pid to get the inferior's pid, assuming that gives |
| # back the pid of the program. On remote boards, that would give |
| # us instead the PID of e.g., the ssh client, etc. |
| if {[is_remote target]} { |
| verbose -log "can't spawn for attach (target is remote)" |
| return 0 |
| } |
| |
| # The "attach" command doesn't make sense when the target is |
| # stub-like, where GDB finds the program already started on |
| # initial connection. |
| if {[target_info exists use_gdb_stub]} { |
| verbose -log "can't spawn for attach (target is stub)" |
| return 0 |
| } |
| |
| # Assume yes. |
| return 1 |
| } |
| |
| # Centralize the failure checking of "attach" command. |
| # Return 0 if attach failed, otherwise return 1. |
| |
| proc gdb_attach { testpid args } { |
| parse_args { |
| {pattern ""} |
| } |
| |
| if { [llength $args] != 0 } { |
| error "Unexpected arguments: $args" |
| } |
| |
| gdb_test_multiple "attach $testpid" "attach" { |
| -re -wrap "Attaching to.*ptrace: Operation not permitted\\." { |
| unsupported "$gdb_test_name (Operation not permitted)" |
| return 0 |
| } |
| -re -wrap "$pattern" { |
| pass $gdb_test_name |
| return 1 |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Start gdb with "--pid $TESTPID" on the command line and wait for the prompt. |
| # Return 1 if GDB managed to start and attach to the process, 0 otherwise. |
| |
| proc_with_prefix gdb_spawn_attach_cmdline { testpid } { |
| if ![can_spawn_for_attach] { |
| # The caller should have checked can_spawn_for_attach itself |
| # before getting here. |
| error "can't spawn for attach with this target/board" |
| } |
| |
| set test "start gdb with --pid" |
| set res [gdb_spawn_with_cmdline_opts "-quiet --pid=$testpid"] |
| if { $res != 0 } { |
| fail $test |
| return 0 |
| } |
| |
| gdb_test_multiple "" "$test" { |
| -re -wrap "ptrace: Operation not permitted\\." { |
| unsupported "$gdb_test_name (operation not permitted)" |
| return 0 |
| } |
| -re -wrap "ptrace: No such process\\." { |
| fail "$gdb_test_name (no such process)" |
| return 0 |
| } |
| -re -wrap "Attaching to process $testpid\r\n.*" { |
| pass $gdb_test_name |
| } |
| } |
| |
| # Check that we actually attached to a process, in case the |
| # error message is not caught by the patterns above. |
| gdb_test_multiple "info thread" "" { |
| -re -wrap "No threads\\." { |
| fail "$gdb_test_name (no thread)" |
| } |
| -re -wrap "Id.*" { |
| pass $gdb_test_name |
| return 1 |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Kill a progress previously started with spawn_wait_for_attach, and |
| # reap its wait status. PROC_SPAWN_ID is the spawn id associated with |
| # the process. |
| |
| proc kill_wait_spawned_process { proc_spawn_id } { |
| set pid [exp_pid -i $proc_spawn_id] |
| |
| verbose -log "killing ${pid}" |
| remote_exec build "kill -9 ${pid}" |
| |
| verbose -log "closing ${proc_spawn_id}" |
| catch "close -i $proc_spawn_id" |
| verbose -log "waiting for ${proc_spawn_id}" |
| |
| # If somehow GDB ends up still attached to the process here, a |
| # blocking wait hangs until gdb is killed (or until gdb / the |
| # ptracer reaps the exit status too, but that won't happen because |
| # something went wrong.) Passing -nowait makes expect tell Tcl to |
| # wait for the PID in the background. That's fine because we |
| # don't care about the exit status. */ |
| wait -nowait -i $proc_spawn_id |
| } |
| |
| # Returns the process id corresponding to the given spawn id. |
| |
| proc spawn_id_get_pid { spawn_id } { |
| set testpid [exp_pid -i $spawn_id] |
| |
| if { [istarget "*-*-cygwin*"] } { |
| # testpid is the Cygwin PID, GDB uses the Windows PID, which |
| # might be different due to the way fork/exec works. |
| set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ] |
| } |
| |
| return $testpid |
| } |
| |
| # Start a set of programs running and then wait for a bit, to be sure |
| # that they can be attached to. Return a list of processes spawn IDs, |
| # one element for each process spawned. It's a test error to call |
| # this when [can_spawn_for_attach] is false. |
| |
| proc spawn_wait_for_attach { executable_list } { |
| set spawn_id_list {} |
| |
| if ![can_spawn_for_attach] { |
| # The caller should have checked can_spawn_for_attach itself |
| # before getting here. |
| error "can't spawn for attach with this target/board" |
| } |
| |
| foreach {executable} $executable_list { |
| # Note we use Expect's spawn, not Tcl's exec, because with |
| # spawn we control when to wait for/reap the process. That |
| # allows killing the process by PID without being subject to |
| # pid-reuse races. |
| lappend spawn_id_list [remote_spawn target $executable] |
| } |
| |
| sleep 2 |
| |
| return $spawn_id_list |
| } |
| |
| # |
| # gdb_load_cmd -- load a file into the debugger. |
| # ARGS - additional args to load command. |
| # return a -1 if anything goes wrong. |
| # |
| proc gdb_load_cmd { args } { |
| global gdb_prompt |
| |
| if [target_info exists gdb_load_timeout] { |
| set loadtimeout [target_info gdb_load_timeout] |
| } else { |
| set loadtimeout 1600 |
| } |
| send_gdb "load $args\n" |
| verbose "Timeout is now $loadtimeout seconds" 2 |
| gdb_expect $loadtimeout { |
| -re "Loading section\[^\r\]*\r\n" { |
| exp_continue |
| } |
| -re "Start address\[\r\]*\r\n" { |
| exp_continue |
| } |
| -re "Transfer rate\[\r\]*\r\n" { |
| exp_continue |
| } |
| -re "Memory access error\[^\r\]*\r\n" { |
| perror "Failed to load program" |
| return -1 |
| } |
| -re "$gdb_prompt $" { |
| return 0 |
| } |
| -re "(.*)\r\n$gdb_prompt " { |
| perror "Unexpected reponse from 'load' -- $expect_out(1,string)" |
| return -1 |
| } |
| timeout { |
| perror "Timed out trying to load $args." |
| return -1 |
| } |
| } |
| return -1 |
| } |
| |
| # Invoke "gcore". CORE is the name of the core file to write. TEST |
| # is the name of the test case. This will return 1 if the core file |
| # was created, 0 otherwise. If this fails to make a core file because |
| # this configuration of gdb does not support making core files, it |
| # will call "unsupported", not "fail". However, if this fails to make |
| # a core file for some other reason, then it will call "fail". |
| |
| proc gdb_gcore_cmd {core test} { |
| global gdb_prompt |
| |
| set result 0 |
| |
| set re_unsupported \ |
| "(?:Can't create a corefile|Target does not support core file generation\\.)" |
| |
| with_timeout_factor 3 { |
| gdb_test_multiple "gcore $core" $test { |
| -re -wrap "Saved corefile .*" { |
| pass $test |
| set result 1 |
| } |
| -re -wrap $re_unsupported { |
| unsupported $test |
| } |
| } |
| } |
| |
| return $result |
| } |
| |
| # Load core file CORE. TEST is the name of the test case. |
| # This will record a pass/fail for loading the core file. |
| # Returns: |
| # 1 - core file is successfully loaded |
| # 0 - core file loaded but has a non fatal error |
| # -1 - core file failed to load |
| |
| proc gdb_core_cmd { core test } { |
| global gdb_prompt |
| |
| gdb_test_multiple "core $core" "$test" { |
| -re "\\\[Thread debugging using \[^ \r\n\]* enabled\\\]\r\n" { |
| exp_continue |
| } |
| -re " is not a core dump:.*\r\n$gdb_prompt $" { |
| fail "$test (bad file format)" |
| return -1 |
| } |
| -re -wrap "[string_to_regexp $core]: No such file or directory.*" { |
| fail "$test (file not found)" |
| return -1 |
| } |
| -re "Couldn't find .* registers in core file.*\r\n$gdb_prompt $" { |
| fail "$test (incomplete note section)" |
| return 0 |
| } |
| -re "Core was generated by .*\r\n$gdb_prompt $" { |
| pass "$test" |
| return 1 |
| } |
| -re ".*$gdb_prompt $" { |
| fail "$test" |
| return -1 |
| } |
| timeout { |
| fail "$test (timeout)" |
| return -1 |
| } |
| } |
| fail "unsupported output from 'core' command" |
| return -1 |
| } |
| |
| # Return the filename to download to the target and load on the target |
| # for this shared library. Normally just LIBNAME, unless shared libraries |
| # for this target have separate link and load images. |
| |
| proc shlib_target_file { libname } { |
| return $libname |
| } |
| |
| # Return the filename GDB will load symbols from when debugging this |
| # shared library. Normally just LIBNAME, unless shared libraries for |
| # this target have separate link and load images. |
| |
| proc shlib_symbol_file { libname } { |
| return $libname |
| } |
| |
| # Return the filename to download to the target and load for this |
| # executable. Normally just BINFILE unless it is renamed to something |
| # else for this target. |
| |
| proc exec_target_file { binfile } { |
| return $binfile |
| } |
| |
| # Return the filename GDB will load symbols from when debugging this |
| # executable. Normally just BINFILE unless executables for this target |
| # have separate files for symbols. |
| |
| proc exec_symbol_file { binfile } { |
| return $binfile |
| } |
| |
| # Rename the executable file. Normally this is just BINFILE1 being renamed |
| # to BINFILE2, but some targets require multiple binary files. |
| proc gdb_rename_execfile { binfile1 binfile2 } { |
| file rename -force [exec_target_file ${binfile1}] \ |
| [exec_target_file ${binfile2}] |
| if { [exec_target_file ${binfile1}] != [exec_symbol_file ${binfile1}] } { |
| file rename -force [exec_symbol_file ${binfile1}] \ |
| [exec_symbol_file ${binfile2}] |
| } |
| } |
| |
| # "Touch" the executable file to update the date. Normally this is just |
| # BINFILE, but some targets require multiple files. |
| proc gdb_touch_execfile { binfile } { |
| set time [clock seconds] |
| file mtime [exec_target_file ${binfile}] $time |
| if { [exec_target_file ${binfile}] != [exec_symbol_file ${binfile}] } { |
| file mtime [exec_symbol_file ${binfile}] $time |
| } |
| } |
| |
| # Like remote_download but provides a gdb-specific behavior. |
| # |
| # If the destination board is remote, the local file FROMFILE is transferred as |
| # usual with remote_download to TOFILE on the remote board. The destination |
| # filename is added to the CLEANFILES global, so it can be cleaned up at the |
| # end of the test. |
| # |
| # If the destination board is local, the destination path TOFILE is passed |
| # through standard_output_file, and FROMFILE is copied there. |
| # |
| # In both cases, if TOFILE is omitted, it defaults to the [file tail] of |
| # FROMFILE. |
| |
| proc gdb_remote_download {dest fromfile {tofile {}}} { |
| # If TOFILE is not given, default to the same filename as FROMFILE. |
| if {[string length $tofile] == 0} { |
| set tofile [file tail $fromfile] |
| } |
| |
| if {[is_remote $dest]} { |
| # When the DEST is remote, we simply send the file to DEST. |
| global cleanfiles_target cleanfiles_host |
| |
| set destname [remote_download $dest $fromfile $tofile] |
| if { $dest == "target" } { |
| lappend cleanfiles_target $destname |
| } elseif { $dest == "host" } { |
| lappend cleanfiles_host $destname |
| } |
| |
| return $destname |
| } else { |
| # When the DEST is local, we copy the file to the test directory (where |
| # the executable is). |
| # |
| # Note that we pass TOFILE through standard_output_file, regardless of |
| # whether it is absolute or relative, because we don't want the tests |
| # to be able to write outside their standard output directory. |
| |
| set tofile [standard_output_file $tofile] |
| |
| file copy -force $fromfile $tofile |
| |
| return $tofile |
| } |
| } |
| |
| # Copy shlib FILE to the target. |
| |
| proc gdb_download_shlib { file } { |
| return [gdb_remote_download target [shlib_target_file $file]] |
| } |
| |
| # Set solib-search-path to allow gdb to locate shlib FILE. |
| |
| proc gdb_locate_shlib { file } { |
| global gdb_spawn_id |
| |
| if ![info exists gdb_spawn_id] { |
| perror "gdb_load_shlib: GDB is not running" |
| } |
| |
| # If the target is remote, we need to tell gdb where to find the |
| # libraries. |
| if { ![is_remote target] } { |
| return |
| } |
| |
| # We could set this even when not testing remotely, but a user |
| # generally won't set it unless necessary. In order to make the tests |
| # more like the real-life scenarios, we don't set it for local testing. |
| gdb_test "set solib-search-path [file dirname $file]" "" \ |
| "set solib-search-path for [file tail $file]" |
| } |
| |
| # Copy shlib FILE to the target and set solib-search-path to allow gdb to |
| # locate it. |
| |
| proc gdb_load_shlib { file } { |
| set dest [gdb_download_shlib $file] |
| gdb_locate_shlib $file |
| return $dest |
| } |
| |
| # |
| # gdb_load -- load a file into the debugger. Specifying no file |
| # defaults to the executable currently being debugged. |
| # The return value is 0 for success, -1 for failure. |
| # Many files in config/*.exp override this procedure. |
| # |
| proc gdb_load { arg } { |
| if { $arg != "" } { |
| return [gdb_file_cmd $arg] |
| } |
| return 0 |
| } |
| |
| # |
| # with_set -- Execute BODY and set VAR temporary to VAL for the |
| # duration. |
| # |
| proc with_set { var val body } { |
| set save "" |
| set show_re \ |
| "is (\[^\r\n\]+)\\." |
| gdb_test_multiple "show $var" "" { |
| -re -wrap $show_re { |
| set save $expect_out(1,string) |
| } |
| } |
| |
| # Handle 'set to "auto" (currently "i386")'. |
| set save [regsub {^set to} $save ""] |
| set save [regsub {\([^\r\n]+\)$} $save ""] |
| set save [string trim $save] |
| set save [regsub -all {^"|"$} $save ""] |
| |
| if { $save == "" } { |
| perror "Did not manage to set $var" |
| } else { |
| # Set var. |
| set cmd "set $var $val" |
| gdb_test_multiple $cmd "" { |
| -re -wrap "^$cmd" { |
| } |
| -re -wrap " is set to \"?$val\"?\\." { |
| } |
| } |
| } |
| |
| set code [catch {uplevel 1 $body} result] |
| |
| # Restore saved setting. |
| if { $save != "" } { |
| set cmd "set $var $save" |
| gdb_test_multiple $cmd "" { |
| -re -wrap "^$cmd" { |
| } |
| -re -wrap "is set to \"?$save\"?( \\(\[^)\]*\\))?\\." { |
| } |
| } |
| } |
| |
| if {$code == 1} { |
| global errorInfo errorCode |
| return -code $code -errorinfo $errorInfo -errorcode $errorCode $result |
| } else { |
| return -code $code $result |
| } |
| } |
| |
| # |
| # with_complaints -- Execute BODY and set complaints temporary to N for the |
| # duration. |
| # |
| proc with_complaints { n body } { |
| return [uplevel [list with_set complaints $n $body]] |
| } |
| |
| # |
| # gdb_load_no_complaints -- As gdb_load, but in addition verifies that |
| # loading caused no symbol reading complaints. |
| # |
| proc gdb_load_no_complaints { arg } { |
| global gdb_prompt gdb_file_cmd_msg decimal |
| |
| # Temporarily set complaint to a small non-zero number. |
| with_complaints 5 { |
| gdb_load $arg |
| } |
| |
| # Verify that there were no complaints. |
| set re \ |
| [multi_line \ |
| "^(Reading symbols from \[^\r\n\]*" \ |
| ")+(Expanding full symbols from \[^\r\n\]*" \ |
| ")?$gdb_prompt $"] |
| gdb_assert {[regexp $re $gdb_file_cmd_msg]} "No complaints" |
| } |
| |
| # gdb_reload -- load a file into the target. Called before "running", |
| # either the first time or after already starting the program once, |
| # for remote targets. Most files that override gdb_load should now |
| # override this instead. |
| # |
| # INFERIOR_ARGS contains the arguments to pass to the inferiors, as a |
| # single string to get interpreted by a shell. If the target board |
| # overriding gdb_reload is a "stub", then it should arrange things such |
| # these arguments make their way to the inferior process. |
| |
| proc gdb_reload { {inferior_args {}} } { |
| # For the benefit of existing configurations, default to gdb_load. |
| # Specifying no file defaults to the executable currently being |
| # debugged. |
| return [gdb_load ""] |
| } |
| |
| proc gdb_continue { function } { |
| global decimal |
| |
| return [gdb_test "continue" ".*Breakpoint $decimal, $function .*" "continue to $function"] |
| } |
| |
| # Default implementation of gdb_init. |
| proc default_gdb_init { test_file_name } { |
| global gdb_wrapper_initialized |
| global gdb_wrapper_target |
| global gdb_test_file_name |
| global cleanfiles_target |
| global cleanfiles_host |
| global pf_prefix |
| |
| # Reset the timeout value to the default. This way, any testcase |
| # that changes the timeout value without resetting it cannot affect |
| # the timeout used in subsequent testcases. |
| global gdb_test_timeout |
| global timeout |
| set timeout $gdb_test_timeout |
| |
| if { [regexp ".*gdb\.reverse\/.*" $test_file_name] |
| && [target_info exists gdb_reverse_timeout] } { |
| set timeout [target_info gdb_reverse_timeout] |
| } |
| |
| # If GDB_INOTIFY is given, check for writes to '.'. This is a |
| # debugging tool to help confirm that the test suite is |
| # parallel-safe. You need "inotifywait" from the |
| # inotify-tools package to use this. |
| global GDB_INOTIFY inotify_pid |
| if {[info exists GDB_INOTIFY] && ![info exists inotify_pid]} { |
| global outdir tool inotify_log_file |
| |
| set exclusions {outputs temp gdb[.](log|sum) cache} |
| set exclusion_re ([join $exclusions |]) |
| |
| set inotify_log_file [standard_temp_file inotify.out] |
| set inotify_pid [exec inotifywait -r -m -e move,create,delete . \ |
| --exclude $exclusion_re \ |
| |& tee -a $outdir/$tool.log $inotify_log_file &] |
| |
| # Wait for the watches; hopefully this is long enough. |
| sleep 2 |
| |
| # Clear the log so that we don't emit a warning the first time |
| # we check it. |
| set fd [open $inotify_log_file w] |
| close $fd |
| } |
| |
| # Block writes to all banned variables, and invocation of all |
| # banned procedures... |
| global banned_variables |
| global banned_procedures |
| global banned_traced |
| if (!$banned_traced) { |
| foreach banned_var $banned_variables { |
| global "$banned_var" |
| trace add variable "$banned_var" write error |
| } |
| foreach banned_proc $banned_procedures { |
| global "$banned_proc" |
| trace add execution "$banned_proc" enter error |
| } |
| set banned_traced 1 |
| } |
| |
| # We set LC_ALL, LC_CTYPE, and LANG to C so that we get the same |
| # messages as expected. |
| setenv LC_ALL C |
| setenv LC_CTYPE C |
| setenv LANG C |
| |
| # Don't let a .inputrc file or an existing setting of INPUTRC mess |
| # up the test results. Certain tests (style tests and TUI tests) |
| # want to set the terminal to a non-"dumb" value, and for those we |
| # want to disable bracketed paste mode. Versions of Readline |
| # before 8.0 will not understand this and will issue a warning. |
| # We tried using a $if to guard it, but Readline 8.1 had a bug in |
| # its version-comparison code that prevented this for working. |
| setenv INPUTRC [cached_file inputrc "set enable-bracketed-paste off"] |
| |
| # This disables style output, which would interfere with many |
| # tests. |
| setenv TERM "dumb" |
| |
| # If DEBUGINFOD_URLS is set, gdb will try to download sources and |
| # debug info for f.i. system libraries. Prevent this. |
| unset -nocomplain ::env(DEBUGINFOD_URLS) |
| |
| # Ensure that GDBHISTFILE and GDBHISTSIZE are removed from the |
| # environment, we don't want these modifications to the history |
| # settings. |
| unset -nocomplain ::env(GDBHISTFILE) |
| unset -nocomplain ::env(GDBHISTSIZE) |
| |
| # Ensure that XDG_CONFIG_HOME is not set. Some tests setup a fake |
| # home directory in order to test loading settings from gdbinit. |
| # If XDG_CONFIG_HOME is set then GDB will load a gdbinit from |
| # there (if one is present) rather than the home directory setup |
| # in the test. |
| unset -nocomplain ::env(XDG_CONFIG_HOME) |
| |
| # Initialize GDB's pty with a fixed size, to make sure we avoid pagination |
| # during startup. See "man expect" for details about stty_init. |
| global stty_init |
| set stty_init "rows 25 cols 80" |
| |
| # Some tests (for example gdb.base/maint.exp) shell out from gdb to use |
| # grep. Clear GREP_OPTIONS to make the behavior predictable, |
| # especially having color output turned on can cause tests to fail. |
| setenv GREP_OPTIONS "" |
| |
| # Clear $gdbserver_reconnect_p. |
| global gdbserver_reconnect_p |
| set gdbserver_reconnect_p 1 |
| unset gdbserver_reconnect_p |
| |
| # Clear $last_loaded_file |
| global last_loaded_file |
| unset -nocomplain last_loaded_file |
| |
| # Reset GDB number of instances |
| global gdb_instances |
| set gdb_instances 0 |
| |
| set cleanfiles_target {} |
| set cleanfiles_host {} |
| |
| set gdb_test_file_name [file rootname [file tail $test_file_name]] |
| |
| # Make sure that the wrapper is rebuilt |
| # with the appropriate multilib option. |
| if { $gdb_wrapper_target != [current_target_name] } { |
| set gdb_wrapper_initialized 0 |
| } |
| |
| # Unlike most tests, we have a small number of tests that generate |
| # a very large amount of output. We therefore increase the expect |
| # buffer size to be able to contain the entire test output. This |
| # is especially needed by gdb.base/info-macros.exp. |
| match_max -d 65536 |
| # Also set this value for the currently running GDB. |
| match_max [match_max -d] |
| |
| # We want to add the name of the TCL testcase to the PASS/FAIL messages. |
| set pf_prefix "[file tail [file dirname $test_file_name]]/[file tail $test_file_name]:" |
| |
| global gdb_prompt |
| if [target_info exists gdb_prompt] { |
| set gdb_prompt [target_info gdb_prompt] |
| } else { |
| set gdb_prompt "\\(gdb\\)" |
| } |
| global use_gdb_stub |
| if [info exists use_gdb_stub] { |
| unset use_gdb_stub |
| } |
| |
| gdb_setup_known_globals |
| |
| if { [info procs ::gdb_tcl_unknown] != "" } { |
| # Dejagnu overrides proc unknown. The dejagnu version may trigger in a |
| # test-case but abort the entire test run. To fix this, we install a |
| # local version here, which reverts dejagnu's override, and restore |
| # dejagnu's version in gdb_finish. |
| rename ::unknown ::dejagnu_unknown |
| proc unknown { args } { |
| # Use tcl's unknown. |
| set cmd [lindex $args 0] |
| unresolved "testcase aborted due to invalid command name: $cmd" |
| return [uplevel 1 ::gdb_tcl_unknown $args] |
| } |
| } |
| } |
| |
| # Return a path using GDB_PARALLEL. |
| # ARGS is a list of path elements to append to "$objdir/$GDB_PARALLEL". |
| # GDB_PARALLEL must be defined, the caller must check. |
| # |
| # The default value for GDB_PARALLEL is, canonically, ".". |
| # The catch is that tests don't expect an additional "./" in file paths so |
| # omit any directory for the default case. |
| # GDB_PARALLEL is written as "yes" for the default case in Makefile.in to mark |
| # its special handling. |
| |
| proc make_gdb_parallel_path { args } { |
| global GDB_PARALLEL objdir |
| set joiner [list "file" "join" $objdir] |
| if { [info exists GDB_PARALLEL] && $GDB_PARALLEL != "yes" } { |
| lappend joiner $GDB_PARALLEL |
| } |
| set joiner [concat $joiner $args] |
| return [eval $joiner] |
| } |
| |
| # Turn BASENAME into a full file name in the standard output |
| # directory. It is ok if BASENAME is the empty string; in this case |
| # the directory is returned. |
| |
| proc standard_output_file {basename} { |
| global objdir subdir gdb_test_file_name |
| |
| set dir [make_gdb_parallel_path outputs $subdir $gdb_test_file_name] |
| file mkdir $dir |
| # If running on MinGW, replace /c/foo with c:/foo |
| if { [ishost *-*-mingw*] } { |
| set dir [exec sh -c "cd ${dir} && pwd -W"] |
| } |
| return [file join $dir $basename] |
| } |
| |
| # Turn BASENAME into a full file name in the standard output directory. If |
| # GDB has been launched more than once then append the count, starting with |
| # a ".1" postfix. |
| |
| proc standard_output_file_with_gdb_instance {basename} { |
| global gdb_instances |
| set count $gdb_instances |
| |
| if {$count == 0} { |
| return [standard_output_file $basename] |
| } |
| return [standard_output_file ${basename}.${count}] |
| } |
| |
| # Return the name of a file in our standard temporary directory. |
| |
| proc standard_temp_file {basename} { |
| # Since a particular runtest invocation is only executing a single test |
| # file at any given time, we can use the runtest pid to build the |
| # path of the temp directory. |
| set dir [make_gdb_parallel_path temp [pid]] |
| file mkdir $dir |
| return [file join $dir $basename] |
| } |
| |
| # Rename file A to file B, if B does not already exists. Otherwise, leave B |
| # as is and delete A. Return 1 if rename happened. |
| |
| proc tentative_rename { a b } { |
| global errorInfo errorCode |
| set code [catch {file rename -- $a $b} result] |
| if { $code == 1 && [lindex $errorCode 0] == "POSIX" \ |
| && [lindex $errorCode 1] == "EEXIST" } { |
| file delete $a |
| return 0 |
| } |
| if {$code == 1} { |
| return -code error -errorinfo $errorInfo -errorcode $errorCode $result |
| } elseif {$code > 1} { |
| return -code $code $result |
| } |
| return 1 |
| } |
| |
| # Create a file with name FILENAME and contents TXT in the cache directory. |
| # If EXECUTABLE, mark the new file for execution. |
| |
| proc cached_file { filename txt {executable 0}} { |
| set filename [make_gdb_parallel_path cache $filename] |
| |
| if { [file exists $filename] } { |
| return $filename |
| } |
| |
| set dir [file dirname $filename] |
| file mkdir $dir |
| |
| set tmp_filename $filename.[pid] |
| set fd [open $tmp_filename w] |
| puts $fd $txt |
| close $fd |
| |
| if { $executable } { |
| exec chmod +x $tmp_filename |
| } |
| tentative_rename $tmp_filename $filename |
| |
| return $filename |
| } |
| |
| # Return a wrapper around gdb that prevents generating a core file. |
| |
| proc gdb_no_core { } { |
| set script \ |
| [list \ |
| "ulimit -c 0" \ |
| [join [list exec $::GDB {"$@"}]]] |
| set script [join $script "\n"] |
| return [cached_file gdb-no-core.sh $script 1] |
| } |
| |
| # Set 'testfile', 'srcfile', and 'binfile'. |
| # |
| # ARGS is a list of source file specifications. |
| # Without any arguments, the .exp file's base name is used to |
| # compute the source file name. The ".c" extension is added in this case. |
| # If ARGS is not empty, each entry is a source file specification. |
| # If the specification starts with a "." or "-", it is treated as a suffix |
| # to append to the .exp file's base name. |
| # If the specification is the empty string, it is treated as if it |
| # were ".c". |
| # Otherwise it is a file name. |
| # The first file in the list is used to set the 'srcfile' global. |
| # Each subsequent name is used to set 'srcfile2', 'srcfile3', etc. |
| # |
| # Most tests should call this without arguments. |
| # |
| # If a completely different binary file name is needed, then it |
| # should be handled in the .exp file with a suitable comment. |
| |
| proc standard_testfile {args} { |
| global gdb_test_file_name |
| global subdir |
| global gdb_test_file_last_vars |
| |
| # Outputs. |
| global testfile binfile |
| |
| set testfile $gdb_test_file_name |
| set binfile [standard_output_file ${testfile}] |
| |
| if {[llength $args] == 0} { |
| set args .c |
| } |
| |
| # Unset our previous output variables. |
| # This can help catch hidden bugs. |
| if {[info exists gdb_test_file_last_vars]} { |
| foreach varname $gdb_test_file_last_vars { |
| global $varname |
| catch {unset $varname} |
| } |
| } |
| # 'executable' is often set by tests. |
| set gdb_test_file_last_vars {executable} |
| |
| set suffix "" |
| foreach arg $args { |
| set varname srcfile$suffix |
| global $varname |
| |
| # Handle an extension. |
| if {$arg == ""} { |
| set arg $testfile.c |
| } else { |
| set first [string range $arg 0 0] |
| if { $first == "." || $first == "-" } { |
| set arg $testfile$arg |
| } |
| } |
| |
| set $varname $arg |
| lappend gdb_test_file_last_vars $varname |
| |
| if {$suffix == ""} { |
| set suffix 2 |
| } else { |
| incr suffix |
| } |
| } |
| } |
| |
| # The default timeout used when testing GDB commands. We want to use |
| # the same timeout as the default dejagnu timeout, unless the user has |
| # already provided a specific value (probably through a site.exp file). |
| global gdb_test_timeout |
| if ![info exists gdb_test_timeout] { |
| set gdb_test_timeout $timeout |
| } |
| |
| # A list of global variables that GDB testcases should not use. |
| # We try to prevent their use by monitoring write accesses and raising |
| # an error when that happens. |
| set banned_variables { bug_id prms_id } |
| |
| # A list of procedures that GDB testcases should not use. |
| # We try to prevent their use by monitoring invocations and raising |
| # an error when that happens. |
| set banned_procedures { strace } |
| |
| # gdb_init is called by runtest at start, but also by several |
| # tests directly; gdb_finish is only called from within runtest after |
| # each test source execution. |
| # Placing several traces by repetitive calls to gdb_init leads |
| # to problems, as only one trace is removed in gdb_finish. |
| # To overcome this possible problem, we add a variable that records |
| # if the banned variables and procedures are already traced. |
| set banned_traced 0 |
| |
| # Global array that holds the name of all global variables at the time |
| # a test script is started. After the test script has completed any |
| # global not in this list is deleted. |
| array set gdb_known_globals {} |
| |
| # Setup the GDB_KNOWN_GLOBALS array with the names of all current |
| # global variables. |
| proc gdb_setup_known_globals {} { |
| global gdb_known_globals |
| |
| array set gdb_known_globals {} |
| foreach varname [info globals] { |
| set gdb_known_globals($varname) 1 |
| } |
| } |
| |
| # Cleanup the global namespace. Any global not in the |
| # GDB_KNOWN_GLOBALS array is unset, this ensures we don't "leak" |
| # globals from one test script to another. |
| proc gdb_cleanup_globals {} { |
| global gdb_known_globals gdb_persistent_globals |
| |
| foreach varname [info globals] { |
| if {![info exists gdb_known_globals($varname)]} { |
| if { [info exists gdb_persistent_globals($varname)] } { |
| continue |
| } |
| uplevel #0 unset $varname |
| } |
| } |
| } |
| |
| # Create gdb_tcl_unknown, a copy tcl's ::unknown, provided it's present as a |
| # proc. |
| set temp [interp create] |
| if { [interp eval $temp "info procs ::unknown"] != "" } { |
| set old_args [interp eval $temp "info args ::unknown"] |
| set old_body [interp eval $temp "info body ::unknown"] |
| eval proc gdb_tcl_unknown {$old_args} {$old_body} |
| } |
| interp delete $temp |
| unset temp |
| |
| # GDB implementation of ${tool}_init. Called right before executing the |
| # test-case. |
| # Overridable function -- you can override this function in your |
| # baseboard file. |
| proc gdb_init { args } { |
| # A baseboard file overriding this proc and calling the default version |
| # should behave the same as this proc. So, don't add code here, but to |
| # the default version instead. |
| return [default_gdb_init {*}$args] |
| } |
| |
| # GDB implementation of ${tool}_finish. Called right after executing the |
| # test-case. |
| proc gdb_finish { } { |
| global gdbserver_reconnect_p |
| global gdb_prompt |
| global cleanfiles_target |
| global cleanfiles_host |
| global known_globals |
| |
| if { [info procs ::gdb_tcl_unknown] != "" } { |
| # Restore dejagnu's version of proc unknown. |
| rename ::unknown "" |
| rename ::dejagnu_unknown ::unknown |
| } |
| |
| # Exit first, so that the files are no longer in use. |
| gdb_exit |
| |
| if { [llength $cleanfiles_target] > 0 } { |
| eval remote_file target delete $cleanfiles_target |
| set cleanfiles_target {} |
| } |
| if { [llength $cleanfiles_host] > 0 } { |
| eval remote_file host delete $cleanfiles_host |
| set cleanfiles_host {} |
| } |
| |
| # Unblock write access to the banned variables. Dejagnu typically |
| # resets some of them between testcases. |
| global banned_variables |
| global banned_procedures |
| global banned_traced |
| if ($banned_traced) { |
| foreach banned_var $banned_variables { |
| global "$banned_var" |
| trace remove variable "$banned_var" write error |
| } |
| foreach banned_proc $banned_procedures { |
| global "$banned_proc" |
| trace remove execution "$banned_proc" enter error |
| } |
| set banned_traced 0 |
| } |
| |
| global gdb_finish_hooks |
| foreach gdb_finish_hook $gdb_finish_hooks { |
| $gdb_finish_hook |
| } |
| set gdb_finish_hooks [list] |
| |
| gdb_cleanup_globals |
| } |
| |
| global debug_format |
| set debug_format "unknown" |
| |
| # Run the gdb command "info source" and extract the debugging format |
| # information from the output and save it in debug_format. |
| |
| proc get_debug_format { } { |
| global gdb_prompt |
| global expect_out |
| global debug_format |
| |
| set debug_format "unknown" |
| send_gdb "info source\n" |
| gdb_expect 10 { |
| -re "Compiled with (.*) debugging format.\r\n.*$gdb_prompt $" { |
| set debug_format $expect_out(1,string) |
| verbose "debug format is $debug_format" |
| return 1 |
| } |
| -re "No current source file.\r\n$gdb_prompt $" { |
| perror "get_debug_format used when no current source file" |
| return 0 |
| } |
| -re "$gdb_prompt $" { |
| warning "couldn't check debug format (no valid response)." |
| return 1 |
| } |
| timeout { |
| warning "couldn't check debug format (timeout)." |
| return 1 |
| } |
| } |
| } |
| |
| # Return true if FORMAT matches the debug format the current test was |
| # compiled with. FORMAT is a shell-style globbing pattern; it can use |
| # `*', `[...]', and so on. |
| # |
| # This function depends on variables set by `get_debug_format', above. |
| |
| proc test_debug_format {format} { |
| global debug_format |
| |
| return [expr [string match $format $debug_format] != 0] |
| } |
| |
| # Like setup_xfail, but takes the name of a debug format (DWARF 1, |
| # COFF, stabs, etc). If that format matches the format that the |
| # current test was compiled with, then the next test is expected to |
| # fail for any target. Returns 1 if the next test or set of tests is |
| # expected to fail, 0 otherwise (or if it is unknown). Must have |
| # previously called get_debug_format. |
| proc setup_xfail_format { format } { |
| set ret [test_debug_format $format] |
| |
| if {$ret} { |
| setup_xfail "*-*-*" |
| } |
| return $ret |
| } |
| |
| # gdb_get_line_number TEXT [FILE] |
| # |
| # Search the source file FILE, and return the line number of the |
| # first line containing TEXT. If no match is found, an error is thrown. |
| # |
| # TEXT is a string literal, not a regular expression. |
| # |
| # The default value of FILE is "$srcdir/$subdir/$srcfile". If FILE is |
| # specified, and does not start with "/", then it is assumed to be in |
| # "$srcdir/$subdir". This is awkward, and can be fixed in the future, |
| # by changing the callers and the interface at the same time. |
| # In particular: gdb.base/break.exp, gdb.base/condbreak.exp, |
| # gdb.base/ena-dis-br.exp. |
| # |
| # Use this function to keep your test scripts independent of the |
| # exact line numbering of the source file. Don't write: |
| # |
| # send_gdb "break 20" |
| # |
| # This means that if anyone ever edits your test's source file, |
| # your test could break. Instead, put a comment like this on the |
| # source file line you want to break at: |
| # |
| # /* breakpoint spot: frotz.exp: test name */ |
| # |
| # and then write, in your test script (which we assume is named |
| # frotz.exp): |
| # |
| # send_gdb "break [gdb_get_line_number "frotz.exp: test name"]\n" |
| # |
| # (Yes, Tcl knows how to handle the nested quotes and brackets. |
| # Try this: |
| # $ tclsh |
| # % puts "foo [lindex "bar baz" 1]" |
| # foo baz |
| # % |
| # Tcl is quite clever, for a little stringy language.) |
| # |
| # === |
| # |
| # The previous implementation of this procedure used the gdb search command. |
| # This version is different: |
| # |
| # . It works with MI, and it also works when gdb is not running. |
| # |
| # . It operates on the build machine, not the host machine. |
| # |
| # . For now, this implementation fakes a current directory of |
| # $srcdir/$subdir to be compatible with the old implementation. |
| # This will go away eventually and some callers will need to |
| # be changed. |
| # |
| # . The TEXT argument is literal text and matches literally, |
| # not a regular expression as it was before. |
| # |
| # . State changes in gdb, such as changing the current file |
| # and setting $_, no longer happen. |
| # |
| # After a bit of time we can forget about the differences from the |
| # old implementation. |
| # |
| # --chastain 2004-08-05 |
| |
| proc gdb_get_line_number { text { file "" } } { |
| global srcdir |
| global subdir |
| global srcfile |
| |
| if {"$file" == ""} { |
| set file "$srcfile" |
| } |
| if {![regexp "^/" "$file"]} { |
| set file "$srcdir/$subdir/$file" |
| } |
| |
| if {[catch { set fd [open "$file"] } message]} { |
| error "$message" |
| } |
| |
| set found -1 |
| for { set line 1 } { 1 } { incr line } { |
| if {[catch { set nchar [gets "$fd" body] } message]} { |
| error "$message" |
| } |
| if {$nchar < 0} { |
| break |
| } |
| if {[string first "$text" "$body"] >= 0} { |
| set found $line |
| break |
| } |
| } |
| |
| if {[catch { close "$fd" } message]} { |
| error "$message" |
| } |
| |
| if {$found == -1} { |
| error "undefined tag \"$text\"" |
| } |
| |
| return $found |
| } |
| |
| # Continue the program until it ends. |
| # |
| # MSSG is the error message that gets printed. If not given, a |
| # default is used. |
| # COMMAND is the command to invoke. If not given, "continue" is |
| # used. |
| # ALLOW_EXTRA is a flag indicating whether the test should expect |
| # extra output between the "Continuing." line and the program |
| # exiting. By default it is zero; if nonzero, any extra output |
| # is accepted. |
| |
| proc gdb_continue_to_end {{mssg ""} {command continue} {allow_extra 0}} { |
| global inferior_exited_re use_gdb_stub |
| |
| if {$mssg == ""} { |
| set text "continue until exit" |
| } else { |
| set text "continue until exit at $mssg" |
| } |
| if {$allow_extra} { |
| set extra ".*" |
| } else { |
| set extra "" |
| } |
| |
| # By default, we don't rely on exit() behavior of remote stubs -- |
| # it's common for exit() to be implemented as a simple infinite |
| # loop, or a forced crash/reset. For native targets, by default, we |
| # assume process exit is reported as such. If a non-reliable target |
| # is used, we set a breakpoint at exit, and continue to that. |
| if { [target_info exists exit_is_reliable] } { |
| set exit_is_reliable [target_info exit_is_reliable] |
| } else { |
| set exit_is_reliable [expr ! $use_gdb_stub] |
| } |
| |
| if { ! $exit_is_reliable } { |
| if {![gdb_breakpoint "exit"]} { |
| return 0 |
| } |
| gdb_test $command "Continuing..*Breakpoint .*exit.*" \ |
| $text |
| } else { |
| # Continue until we exit. Should not stop again. |
| # Don't bother to check the output of the program, that may be |
| # extremely tough for some remote systems. |
| gdb_test $command \ |
| "Continuing.\[\r\n0-9\]+${extra}(... EXIT code 0\[\r\n\]+|$inferior_exited_re normally).*"\ |
| $text |
| } |
| } |
| |
| proc rerun_to_main {} { |
| global gdb_prompt use_gdb_stub |
| |
| if $use_gdb_stub { |
| gdb_run_cmd |
| gdb_expect { |
| -re ".*Breakpoint .*main .*$gdb_prompt $"\ |
| {pass "rerun to main" ; return 0} |
| -re "$gdb_prompt $"\ |
| {fail "rerun to main" ; return 0} |
| timeout {fail "(timeout) rerun to main" ; return 0} |
| } |
| } else { |
| send_gdb "run\n" |
| gdb_expect { |
| -re "The program .* has been started already.*y or n. $" { |
| send_gdb "y\n" answer |
| exp_continue |
| } |
| -re "Starting program.*$gdb_prompt $"\ |
| {pass "rerun to main" ; return 0} |
| -re "$gdb_prompt $"\ |
| {fail "rerun to main" ; return 0} |
| timeout {fail "(timeout) rerun to main" ; return 0} |
| } |
| } |
| } |
| |
| # Return true if EXECUTABLE contains a .gdb_index or .debug_names index section. |
| |
| proc exec_has_index_section { executable } { |
| set readelf_program [gdb_find_readelf] |
| set res [catch {exec $readelf_program -S $executable \ |
| | grep -E "\.gdb_index|\.debug_names" }] |
| if { $res == 0 } { |
| return 1 |
| } |
| return 0 |
| } |
| |
| # Return list with major and minor version of readelf, or an empty list. |
| gdb_caching_proc readelf_version { |
| set readelf_program [gdb_find_readelf] |
| set res [catch {exec $readelf_program --version} output] |
| if { $res != 0 } { |
| return [list] |
| } |
| set lines [split $output \n] |
| set line [lindex $lines 0] |
| set res [regexp {[ \t]+([0-9]+)[.]([0-9]+)[^ \t]*$} \ |
| $line dummy major minor] |
| if { $res != 1 } { |
| return [list] |
| } |
| return [list $major $minor] |
| } |
| |
| # Return 1 if readelf prints the PIE flag, 0 if is doesn't, and -1 if unknown. |
| proc readelf_prints_pie { } { |
| set version [readelf_version] |
| if { [llength $version] == 0 } { |
| return -1 |
| } |
| set major [lindex $version 0] |
| set minor [lindex $version 1] |
| # It would be better to construct a PIE executable and test if the PIE |
| # flag is printed by readelf, but we cannot reliably construct a PIE |
| # executable if the multilib_flags dictate otherwise |
| # (--target_board=unix/-no-pie/-fno-PIE). |
| return [version_at_least $major $minor 2 26] |
| } |
| |
| # Return 1 if EXECUTABLE is a Position Independent Executable, 0 if it is not, |
| # and -1 if unknown. |
| |
| proc exec_is_pie { executable } { |
| set res [readelf_prints_pie] |
| if { $res != 1 } { |
| return -1 |
| } |
| set readelf_program [gdb_find_readelf] |
| # We're not testing readelf -d | grep "FLAGS_1.*Flags:.*PIE" |
| # because the PIE flag is not set by all versions of gold, see PR |
| # binutils/26039. |
| set res [catch {exec $readelf_program -h $executable} output] |
| if { $res != 0 } { |
| return -1 |
| } |
| set res [regexp -line {^[ \t]*Type:[ \t]*DYN \((Position-Independent Executable|Shared object) file\)$} \ |
| $output] |
| if { $res == 1 } { |
| return 1 |
| } |
| return 0 |
| } |
| |
| # Return true if a test should be skipped due to lack of floating |
| # point support or GDB can't fetch the contents from floating point |
| # registers. |
| |
| gdb_caching_proc gdb_skip_float_test { |
| if [target_info exists gdb,skip_float_tests] { |
| return 1 |
| } |
| |
| # There is an ARM kernel ptrace bug that hardware VFP registers |
| # are not updated after GDB ptrace set VFP registers. The bug |
| # was introduced by kernel commit 8130b9d7b9d858aa04ce67805e8951e3cb6e9b2f |
| # in 2012 and is fixed in e2dfb4b880146bfd4b6aa8e138c0205407cebbaf |
| # in May 2016. In other words, kernels older than 4.6.3, 4.4.14, |
| # 4.1.27, 3.18.36, and 3.14.73 have this bug. |
| # This kernel bug is detected by check how does GDB change the |
| # program result by changing one VFP register. |
| if { [istarget "arm*-*-linux*"] } { |
| |
| set compile_flags {debug nowarnings } |
| |
| # Set up, compile, and execute a test program having VFP |
| # operations. |
| set src [standard_temp_file arm_vfp[pid].c] |
| set exe [standard_temp_file arm_vfp[pid].x] |
| |
| gdb_produce_source $src { |
| int main() { |
| double d = 4.0; |
| int ret; |
| |
| asm ("vldr d0, [%0]" : : "r" (&d)); |
| asm ("vldr d1, [%0]" : : "r" (&d)); |
| asm (".global break_here\n" |
| "break_here:"); |
| asm ("vcmp.f64 d0, d1\n" |
| "vmrs APSR_nzcv, fpscr\n" |
| "bne L_value_different\n" |
| "movs %0, #0\n" |
| "b L_end\n" |
| "L_value_different:\n" |
| "movs %0, #1\n" |
| "L_end:\n" : "=r" (ret) :); |
| |
| /* Return $d0 != $d1. */ |
| return ret; |
| } |
| } |
| |
| verbose "compiling testfile $src" 2 |
| set lines [gdb_compile $src $exe executable $compile_flags] |
| file delete $src |
| |
| if {![string match "" $lines]} { |
| verbose "testfile compilation failed, returning 1" 2 |
| return 0 |
| } |
| |
| # No error message, compilation succeeded so now run it via gdb. |
| # Run the test up to 5 times to detect whether ptrace can |
| # correctly update VFP registers or not. |
| set skip_vfp_test 0 |
| for {set i 0} {$i < 5} {incr i} { |
| global gdb_prompt srcdir subdir |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$exe" |
| |
| runto_main |
| gdb_test "break *break_here" |
| gdb_continue_to_breakpoint "break_here" |
| |
| # Modify $d0 to a different value, so the exit code should |
| # be 1. |
| gdb_test "set \$d0 = 5.0" |
| |
| set test "continue to exit" |
| gdb_test_multiple "continue" "$test" { |
| -re "exited with code 01.*$gdb_prompt $" { |
| } |
| -re "exited normally.*$gdb_prompt $" { |
| # However, the exit code is 0. That means something |
| # wrong in setting VFP registers. |
| set skip_vfp_test 1 |
| break |
| } |
| } |
| } |
| |
| gdb_exit |
| remote_file build delete $exe |
| |
| return $skip_vfp_test |
| } |
| return 0 |
| } |
| |
| # Print a message and return true if a test should be skipped |
| # due to lack of stdio support. |
| |
| proc gdb_skip_stdio_test { msg } { |
| if [target_info exists gdb,noinferiorio] { |
| verbose "Skipping test '$msg': no inferior i/o." |
| return 1 |
| } |
| return 0 |
| } |
| |
| proc gdb_skip_bogus_test { msg } { |
| return 0 |
| } |
| |
| # Return true if XML support is enabled in the host GDB. |
| # NOTE: This must be called while gdb is *not* running. |
| |
| gdb_caching_proc allow_xml_test { |
| global gdb_spawn_id |
| global gdb_prompt |
| global srcdir |
| |
| if { [info exists gdb_spawn_id] } { |
| error "GDB must not be running in allow_xml_tests." |
| } |
| |
| set xml_file [gdb_remote_download host "${srcdir}/gdb.xml/trivial.xml"] |
| |
| gdb_start |
| set xml_missing 0 |
| gdb_test_multiple "set tdesc filename $xml_file" "" { |
| -re ".*XML support was disabled at compile time.*$gdb_prompt $" { |
| set xml_missing 1 |
| } |
| -re ".*$gdb_prompt $" { } |
| } |
| gdb_exit |
| return [expr {!$xml_missing}] |
| } |
| |
| # Return true if argv[0] is available. |
| |
| gdb_caching_proc gdb_has_argv0 { |
| set result 0 |
| |
| # Compile and execute a test program to check whether argv[0] is available. |
| gdb_simple_compile has_argv0 { |
| int main (int argc, char **argv) { |
| return 0; |
| } |
| } executable |
| |
| |
| # Helper proc. |
| proc gdb_has_argv0_1 { exe } { |
| global srcdir subdir |
| global gdb_prompt hex |
| |
| gdb_exit |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$exe" |
| |
| # Set breakpoint on main. |
| gdb_test_multiple "break -q main" "break -q main" { |
| -re "Breakpoint.*${gdb_prompt} $" { |
| } |
| -re "${gdb_prompt} $" { |
| return 0 |
| } |
| } |
| |
| # Run to main. |
| gdb_run_cmd |
| gdb_test_multiple "" "run to main" { |
| -re "Breakpoint.*${gdb_prompt} $" { |
| } |
| -re "${gdb_prompt} $" { |
| return 0 |
| } |
| } |
| |
| set old_elements "200" |
| set test "show print elements" |
| gdb_test_multiple $test $test { |
| -re "Limit on string chars or array elements to print is (\[^\r\n\]+)\\.\r\n$gdb_prompt $" { |
| set old_elements $expect_out(1,string) |
| } |
| } |
| set old_repeats "200" |
| set test "show print repeats" |
| gdb_test_multiple $test $test { |
| -re "Threshold for repeated print elements is (\[^\r\n\]+)\\.\r\n$gdb_prompt $" { |
| set old_repeats $expect_out(1,string) |
| } |
| } |
| gdb_test_no_output "set print elements unlimited" "" |
| gdb_test_no_output "set print repeats unlimited" "" |
| |
| set retval 0 |
| # Check whether argc is 1. |
| gdb_test_multiple "p argc" "p argc" { |
| -re " = 1\r\n${gdb_prompt} $" { |
| |
| gdb_test_multiple "p argv\[0\]" "p argv\[0\]" { |
| -re " = $hex \".*[file tail $exe]\"\r\n${gdb_prompt} $" { |
| set retval 1 |
| } |
| -re "${gdb_prompt} $" { |
| } |
| } |
| } |
| -re "${gdb_prompt} $" { |
| } |
| } |
| |
| gdb_test_no_output "set print elements $old_elements" "" |
| gdb_test_no_output "set print repeats $old_repeats" "" |
| |
| return $retval |
| } |
| |
| set result [gdb_has_argv0_1 $obj] |
| |
| gdb_exit |
| file delete $obj |
| |
| if { !$result |
| && ([istarget *-*-linux*] |
| || [istarget *-*-freebsd*] || [istarget *-*-kfreebsd*] |
| || [istarget *-*-netbsd*] || [istarget *-*-knetbsd*] |
| || [istarget *-*-openbsd*] |
| || [istarget *-*-darwin*] |
| || [istarget *-*-solaris*] |
| || [istarget *-*-aix*] |
| || [istarget *-*-gnu*] |
| || [istarget *-*-cygwin*] || [istarget *-*-mingw32*] |
| || [istarget *-*-*djgpp*] || [istarget *-*-go32*] |
| || [istarget *-wince-pe] || [istarget *-*-mingw32ce*] |
| || [istarget *-*-osf*] |
| || [istarget *-*-dicos*] |
| || [istarget *-*-nto*] |
| || [istarget *-*-*vms*] |
| || [istarget *-*-lynx*178]) } { |
| fail "argv\[0\] should be available on this target" |
| } |
| |
| return $result |
| } |
| |
| # Note: the procedure gdb_gnu_strip_debug will produce an executable called |
| # ${binfile}.dbglnk, which is just like the executable ($binfile) but without |
| # the debuginfo. Instead $binfile has a .gnu_debuglink section which contains |
| # the name of a debuginfo only file. This file will be stored in the same |
| # subdirectory. |
| |
| # Functions for separate debug info testing |
| |
| # starting with an executable: |
| # foo --> original executable |
| |
| # at the end of the process we have: |
| # foo.stripped --> foo w/o debug info |
| # foo.debug --> foo's debug info |
| # foo --> like foo, but with a new .gnu_debuglink section pointing to foo.debug. |
| |
| # Fetch the build id from the file. |
| # Returns "" if there is none. |
| |
| proc get_build_id { filename } { |
| if { ([istarget "*-*-mingw*"] |
| || [istarget *-*-cygwin*]) } { |
| set objdump_program [gdb_find_objdump] |
| set result [catch {set data [exec $objdump_program -p $filename | grep signature | cut "-d " -f4]} output] |
| verbose "result is $result" |
| verbose "output is $output" |
| if {$result == 1} { |
| return "" |
| } |
| return $data |
| } else { |
| set tmp [standard_output_file "${filename}-tmp"] |
| set objcopy_program [gdb_find_objcopy] |
| set result [catch "exec $objcopy_program -j .note.gnu.build-id -O binary $filename $tmp" output] |
| verbose "result is $result" |
| verbose "output is $output" |
| if {$result == 1} { |
| return "" |
| } |
| set fi [open $tmp] |
| fconfigure $fi -translation binary |
| # Skip the NOTE header. |
| read $fi 16 |
| set data [read $fi] |
| close $fi |
| file delete $tmp |
| if {![string compare $data ""]} { |
| return "" |
| } |
| # Convert it to hex. |
| binary scan $data H* data |
| return $data |
| } |
| } |
| |
| # Return the build-id hex string (usually 160 bits as 40 hex characters) |
| # converted to the form: .build-id/ab/cdef1234...89.debug |
| # Return "" if no build-id found. |
| proc build_id_debug_filename_get { filename } { |
| set data [get_build_id $filename] |
| if { $data == "" } { |
| return "" |
| } |
| regsub {^..} $data {\0/} data |
| return ".build-id/${data}.debug" |
| } |
| |
| # Create stripped files for DEST, replacing it. If ARGS is passed, it is a |
| # list of optional flags. The only currently supported flag is no-main, |
| # which removes the symbol entry for main from the separate debug file. |
| # |
| # Function returns zero on success. Function will return non-zero failure code |
| # on some targets not supporting separate debug info (such as i386-msdos). |
| |
| proc gdb_gnu_strip_debug { dest args } { |
| |
| # Use the first separate debug info file location searched by GDB so the |
| # run cannot be broken by some stale file searched with higher precedence. |
| set debug_file "${dest}.debug" |
| |
| set strip_to_file_program [transform strip] |
| set objcopy_program [gdb_find_objcopy] |
| |
| set debug_link [file tail $debug_file] |
| set stripped_file "${dest}.stripped" |
| |
| # Get rid of the debug info, and store result in stripped_file |
| # something like gdb/testsuite/gdb.base/blah.stripped. |
| set result [catch "exec $strip_to_file_program --strip-debug ${dest} -o ${stripped_file}" output] |
| verbose "result is $result" |
| verbose "output is $output" |
| if {$result == 1} { |
| return 1 |
| } |
| |
| # Workaround PR binutils/10802: |
| # Preserve the 'x' bit also for PIEs (Position Independent Executables). |
| set perm [file attributes ${dest} -permissions] |
| file attributes ${stripped_file} -permissions $perm |
| |
| # Get rid of everything but the debug info, and store result in debug_file |
| # This will be in the .debug subdirectory, see above. |
| set result [catch "exec $strip_to_file_program --only-keep-debug ${dest} -o ${debug_file}" output] |
| verbose "result is $result" |
| verbose "output is $output" |
| if {$result == 1} { |
| return 1 |
| } |
| |
| # If no-main is passed, strip the symbol for main from the separate |
| # file. This is to simulate the behavior of elfutils's eu-strip, which |
| # leaves the symtab in the original file only. There's no way to get |
| # objcopy or strip to remove the symbol table without also removing the |
| # debugging sections, so this is as close as we can get. |
| if { [llength $args] == 1 && [lindex $args 0] == "no-main" } { |
| set result [catch "exec $objcopy_program -N main ${debug_file} ${debug_file}-tmp" output] |
| verbose "result is $result" |
| verbose "output is $output" |
| if {$result == 1} { |
| return 1 |
| } |
| file delete "${debug_file}" |
| file rename "${debug_file}-tmp" "${debug_file}" |
| } |
| |
| # Link the two previous output files together, adding the .gnu_debuglink |
| # section to the stripped_file, containing a pointer to the debug_file, |
| # save the new file in dest. |
| # This will be the regular executable filename, in the usual location. |
| set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${dest}" output] |
| verbose "result is $result" |
| verbose "output is $output" |
| if {$result == 1} { |
| return 1 |
| } |
| |
| # Workaround PR binutils/10802: |
| # Preserve the 'x' bit also for PIEs (Position Independent Executables). |
| set perm [file attributes ${stripped_file} -permissions] |
| file attributes ${dest} -permissions $perm |
| |
| return 0 |
| } |
| |
| # Test the output of GDB_COMMAND matches the pattern obtained |
| # by concatenating all elements of EXPECTED_LINES. This makes |
| # it possible to split otherwise very long string into pieces. |
| # If third argument TESTNAME is not empty, it's used as the name of the |
| # test to be printed on pass/fail. |
| proc help_test_raw { gdb_command expected_lines {testname {}} } { |
| set expected_output [join $expected_lines ""] |
| if {$testname != {}} { |
| gdb_test "${gdb_command}" "${expected_output}" $testname |
| return |
| } |
| |
| gdb_test "${gdb_command}" "${expected_output}" |
| } |
| |
| # A regexp that matches the end of help CLASS|PREFIX_COMMAND |
| set help_list_trailer { |
| "Type \"apropos word\" to search for commands related to \"word\"\.[\r\n]+" |
| "Type \"apropos -v word\" for full documentation of commands related to \"word\"\.[\r\n]+" |
| "Command name abbreviations are allowed if unambiguous\." |
| } |
| |
| # Test the output of "help COMMAND_CLASS". EXPECTED_INITIAL_LINES |
| # are regular expressions that should match the beginning of output, |
| # before the list of commands in that class. |
| # LIST_OF_COMMANDS are regular expressions that should match the |
| # list of commands in that class. If empty, the command list will be |
| # matched automatically. The presence of standard epilogue will be tested |
| # automatically. |
| # If last argument TESTNAME is not empty, it's used as the name of the |
| # test to be printed on pass/fail. |
| # Notice that the '[' and ']' characters don't need to be escaped for strings |
| # wrapped in {} braces. |
| proc test_class_help { command_class expected_initial_lines {list_of_commands {}} {testname {}} } { |
| global help_list_trailer |
| if {[llength $list_of_commands]>0} { |
| set l_list_of_commands {"List of commands:[\r\n]+[\r\n]+"} |
| set l_list_of_commands [concat $l_list_of_commands $list_of_commands] |
| set l_list_of_commands [concat $l_list_of_commands {"[\r\n]+[\r\n]+"}] |
| } else { |
| set l_list_of_commands {"List of commands\:.*[\r\n]+"} |
| } |
| set l_stock_body { |
| "Type \"help\" followed by command name for full documentation\.[\r\n]+" |
| } |
| set l_entire_body [concat $expected_initial_lines $l_list_of_commands \ |
| $l_stock_body $help_list_trailer] |
| |
| help_test_raw "help ${command_class}" $l_entire_body $testname |
| } |
| |
| # Like test_class_help but specialised to test "help user-defined". |
| proc test_user_defined_class_help { {list_of_commands {}} {testname {}} } { |
| test_class_help "user-defined" { |
| "User-defined commands\.[\r\n]+" |
| "The commands in this class are those defined by the user\.[\r\n]+" |
| "Use the \"define\" command to define a command\.[\r\n]+" |
| } $list_of_commands $testname |
| } |
| |
| |
| # COMMAND_LIST should have either one element -- command to test, or |
| # two elements -- abbreviated command to test, and full command the first |
| # element is abbreviation of. |
| # The command must be a prefix command. EXPECTED_INITIAL_LINES |
| # are regular expressions that should match the beginning of output, |
| # before the list of subcommands. The presence of |
| # subcommand list and standard epilogue will be tested automatically. |
| proc test_prefix_command_help { command_list expected_initial_lines args } { |
| global help_list_trailer |
| set command [lindex $command_list 0] |
| if {[llength $command_list]>1} { |
| set full_command [lindex $command_list 1] |
| } else { |
| set full_command $command |
| } |
| # Use 'list' and not just {} because we want variables to |
| # be expanded in this list. |
| set l_stock_body [list\ |
| "List of $full_command subcommands\:.*\[\r\n\]+"\ |
| "Type \"help $full_command\" followed by $full_command subcommand name for full documentation\.\[\r\n\]+"] |
| set l_entire_body [concat $expected_initial_lines $l_stock_body $help_list_trailer] |
| if {[llength $args]>0} { |
| help_test_raw "help ${command}" $l_entire_body [lindex $args 0] |
| } else { |
| help_test_raw "help ${command}" $l_entire_body |
| } |
| } |
| |
| # Build executable named EXECUTABLE from specifications that allow |
| # different options to be passed to different sub-compilations. |
| # TESTNAME is the name of the test; this is passed to 'untested' if |
| # something fails. |
| # OPTIONS is passed to the final link, using gdb_compile. If OPTIONS |
| # contains the option "pthreads", then gdb_compile_pthreads is used. |
| # ARGS is a flat list of source specifications, of the form: |
| # { SOURCE1 OPTIONS1 [ SOURCE2 OPTIONS2 ]... } |
| # Each SOURCE is compiled to an object file using its OPTIONS, |
| # using gdb_compile. |
| # Returns 0 on success, -1 on failure. |
| proc build_executable_from_specs {testname executable options args} { |
| global subdir |
| global srcdir |
| |
| set binfile [standard_output_file $executable] |
| |
| set func gdb_compile |
| set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp)$}] |
| if {$func_index != -1} { |
| set func "${func}_[lindex $options $func_index]" |
| } |
| |
| # gdb_compile_shlib and gdb_compile_shlib_pthreads do not use the 3rd |
| # parameter. They also requires $sources while gdb_compile and |
| # gdb_compile_pthreads require $objects. Moreover they ignore any options. |
| if [string match gdb_compile_shlib* $func] { |
| set sources_path {} |
| foreach {s local_options} $args { |
| if {[regexp "^/" "$s"]} { |
| lappend sources_path "$s" |
| } else { |
| lappend sources_path "$srcdir/$subdir/$s" |
| } |
| } |
| set ret [$func $sources_path "${binfile}" $options] |
| } elseif {[lsearch -exact $options rust] != -1} { |
| set sources_path {} |
| foreach {s local_options} $args { |
| if {[regexp "^/" "$s"]} { |
| lappend sources_path "$s" |
| } else { |
| lappend sources_path "$srcdir/$subdir/$s" |
| } |
| } |
| set ret [gdb_compile_rust $sources_path "${binfile}" $options] |
| } else { |
| set objects {} |
| set i 0 |
| foreach {s local_options} $args { |
| if {![regexp "^/" "$s"]} { |
| set s "$srcdir/$subdir/$s" |
| } |
| if { [$func "${s}" "${binfile}${i}.o" object $local_options] != "" } { |
| untested $testname |
| return -1 |
| } |
| lappend objects "${binfile}${i}.o" |
| incr i |
| } |
| set ret [$func $objects "${binfile}" executable $options] |
| } |
| if { $ret != "" } { |
| untested $testname |
| return -1 |
| } |
| |
| return 0 |
| } |
| |
| # Build executable named EXECUTABLE, from SOURCES. If SOURCES are not |
| # provided, uses $EXECUTABLE.c. The TESTNAME paramer is the name of test |
| # to pass to untested, if something is wrong. OPTIONS are passed |
| # to gdb_compile directly. |
| proc build_executable { testname executable {sources ""} {options {debug}} } { |
| if {[llength $sources]==0} { |
| set sources ${executable}.c |
| } |
| |
| set arglist [list $testname $executable $options] |
| foreach source $sources { |
| lappend arglist $source $options |
| } |
| |
| return [eval build_executable_from_specs $arglist] |
| } |
| |
| # Starts fresh GDB binary and loads an optional executable into GDB. |
| # Usage: clean_restart [executable] |
| # EXECUTABLE is the basename of the binary. |
| # Return -1 if starting gdb or loading the executable failed. |
| |
| proc clean_restart { args } { |
| global srcdir |
| global subdir |
| global errcnt |
| global warncnt |
| |
| if { [llength $args] > 1 } { |
| error "bad number of args: [llength $args]" |
| } |
| |
| gdb_exit |
| |
| # This is a clean restart, so reset error and warning count. |
| set errcnt 0 |
| set warncnt 0 |
| |
| # We'd like to do: |
| # if { [gdb_start] == -1 } { |
| # return -1 |
| # } |
| # but gdb_start is a ${tool}_start proc, which doesn't have a defined |
| # return value. So instead, we test for errcnt. |
| gdb_start |
| if { $errcnt > 0 } { |
| return -1 |
| } |
| |
| gdb_reinitialize_dir $srcdir/$subdir |
| |
| if { [llength $args] >= 1 } { |
| set executable [lindex $args 0] |
| set binfile [standard_output_file ${executable}] |
| return [gdb_load ${binfile}] |
| } |
| |
| return 0 |
| } |
| |
| # Prepares for testing by calling build_executable_full, then |
| # clean_restart. |
| # TESTNAME is the name of the test. |
| # Each element in ARGS is a list of the form |
| # { EXECUTABLE OPTIONS SOURCE_SPEC... } |
| # These are passed to build_executable_from_specs, which see. |
| # The last EXECUTABLE is passed to clean_restart. |
| # Returns 0 on success, non-zero on failure. |
| proc prepare_for_testing_full {testname args} { |
| foreach spec $args { |
| if {[eval build_executable_from_specs [list $testname] $spec] == -1} { |
| return -1 |
| } |
| set executable [lindex $spec 0] |
| } |
| clean_restart $executable |
| return 0 |
| } |
| |
| # Prepares for testing, by calling build_executable, and then clean_restart. |
| # Please refer to build_executable for parameter description. |
| proc prepare_for_testing { testname executable {sources ""} {options {debug}}} { |
| |
| if {[build_executable $testname $executable $sources $options] == -1} { |
| return -1 |
| } |
| clean_restart $executable |
| |
| return 0 |
| } |
| |
| # Retrieve the value of EXP in the inferior, represented in format |
| # specified in FMT (using "printFMT"). DEFAULT is used as fallback if |
| # print fails. TEST is the test message to use. It can be omitted, |
| # in which case a test message is built from EXP. |
| |
| proc get_valueof { fmt exp default {test ""} } { |
| global gdb_prompt |
| |
| if {$test == "" } { |
| set test "get valueof \"${exp}\"" |
| } |
| |
| set val ${default} |
| gdb_test_multiple "print${fmt} ${exp}" "$test" { |
| -re "\\$\[0-9\]* = (\[^\r\n\]*)\[\r\n\]*$gdb_prompt $" { |
| set val $expect_out(1,string) |
| pass "$test" |
| } |
| timeout { |
| fail "$test (timeout)" |
| } |
| } |
| return ${val} |
| } |
| |
| # Retrieve the value of local var EXP in the inferior. DEFAULT is used as |
| # fallback if print fails. TEST is the test message to use. It can be |
| # omitted, in which case a test message is built from EXP. |
| |
| proc get_local_valueof { exp default {test ""} } { |
| global gdb_prompt |
| |
| if {$test == "" } { |
| set test "get local valueof \"${exp}\"" |
| } |
| |
| set val ${default} |
| gdb_test_multiple "info locals ${exp}" "$test" { |
| -re "$exp = (\[^\r\n\]*)\[\r\n\]*$gdb_prompt $" { |
| set val $expect_out(1,string) |
| pass "$test" |
| } |
| timeout { |
| fail "$test (timeout)" |
| } |
| } |
| return ${val} |
| } |
| |
| # Retrieve the value of EXP in the inferior, as a signed decimal value |
| # (using "print /d"). DEFAULT is used as fallback if print fails. |
| # TEST is the test message to use. It can be omitted, in which case |
| # a test message is built from EXP. |
| |
| proc get_integer_valueof { exp default {test ""} } { |
| global gdb_prompt |
| |
| if {$test == ""} { |
| set test "get integer valueof \"${exp}\"" |
| } |
| |
| set val ${default} |
| gdb_test_multiple "print /d ${exp}" "$test" { |
| -re "\\$\[0-9\]* = (\[-\]*\[0-9\]*).*$gdb_prompt $" { |
| set val $expect_out(1,string) |
| pass "$test" |
| } |
| timeout { |
| fail "$test (timeout)" |
| } |
| } |
| return ${val} |
| } |
| |
| # Retrieve the value of EXP in the inferior, as an hexadecimal value |
| # (using "print /x"). DEFAULT is used as fallback if print fails. |
| # TEST is the test message to use. It can be omitted, in which case |
| # a test message is built from EXP. |
| |
| proc get_hexadecimal_valueof { exp default {test ""} } { |
| global gdb_prompt |
| |
| if {$test == ""} { |
| set test "get hexadecimal valueof \"${exp}\"" |
| } |
| |
| set val ${default} |
| gdb_test_multiple "print /x ${exp}" $test { |
| -re "\\$\[0-9\]* = (0x\[0-9a-zA-Z\]+).*$gdb_prompt $" { |
| set val $expect_out(1,string) |
| pass "$test" |
| } |
| } |
| return ${val} |
| } |
| |
| # Retrieve the size of TYPE in the inferior, as a decimal value. DEFAULT |
| # is used as fallback if print fails. TEST is the test message to use. |
| # It can be omitted, in which case a test message is 'sizeof (TYPE)'. |
| |
| proc get_sizeof { type default {test ""} } { |
| return [get_integer_valueof "sizeof (${type})" $default $test] |
| } |
| |
| proc get_target_charset { } { |
| global gdb_prompt |
| |
| gdb_test_multiple "show target-charset" "" { |
| -re "The target character set is \"auto; currently (\[^\"\]*)\".*$gdb_prompt $" { |
| return $expect_out(1,string) |
| } |
| -re "The target character set is \"(\[^\"\]*)\".*$gdb_prompt $" { |
| return $expect_out(1,string) |
| } |
| } |
| |
| # Pick a reasonable default. |
| warning "Unable to read target-charset." |
| return "UTF-8" |
| } |
| |
| # Get the address of VAR. |
| |
| proc get_var_address { var } { |
| global gdb_prompt hex |
| |
| # Match output like: |
| # $1 = (int *) 0x0 |
| # $5 = (int (*)()) 0 |
| # $6 = (int (*)()) 0x24 <function_bar> |
| |
| gdb_test_multiple "print &${var}" "get address of ${var}" { |
| -re "\\\$\[0-9\]+ = \\(.*\\) (0|$hex)( <${var}>)?\[\r\n\]+${gdb_prompt} $" |
| { |
| pass "get address of ${var}" |
| if { $expect_out(1,string) == "0" } { |
| return "0x0" |
| } else { |
| return $expect_out(1,string) |
| } |
| } |
| } |
| return "" |
| } |
| |
| # Return the frame number for the currently selected frame |
| proc get_current_frame_number {{test_name ""}} { |
| global gdb_prompt |
| |
| if { $test_name == "" } { |
| set test_name "get current frame number" |
| } |
| set frame_num -1 |
| gdb_test_multiple "frame" $test_name { |
| -re "#(\[0-9\]+) .*$gdb_prompt $" { |
| set frame_num $expect_out(1,string) |
| } |
| } |
| return $frame_num |
| } |
| |
| # Get the current value for remotetimeout and return it. |
| proc get_remotetimeout { } { |
| global gdb_prompt |
| global decimal |
| |
| gdb_test_multiple "show remotetimeout" "" { |
| -re "Timeout limit to wait for target to respond is ($decimal).*$gdb_prompt $" { |
| return $expect_out(1,string) |
| } |
| } |
| |
| # Pick the default that gdb uses |
| warning "Unable to read remotetimeout" |
| return 300 |
| } |
| |
| # Set the remotetimeout to the specified timeout. Nothing is returned. |
| proc set_remotetimeout { timeout } { |
| global gdb_prompt |
| |
| gdb_test_multiple "set remotetimeout $timeout" "" { |
| -re "$gdb_prompt $" { |
| verbose "Set remotetimeout to $timeout\n" |
| } |
| } |
| } |
| |
| # Get the target's current endianness and return it. |
| proc get_endianness { } { |
| global gdb_prompt |
| |
| gdb_test_multiple "show endian" "determine endianness" { |
| -re ".* (little|big) endian.*\r\n$gdb_prompt $" { |
| # Pass silently. |
| return $expect_out(1,string) |
| } |
| } |
| return "little" |
| } |
| |
| # Get the target's default endianness and return it. |
| gdb_caching_proc target_endianness { |
| global gdb_prompt |
| |
| set me "target_endianness" |
| |
| set src { int main() { return 0; } } |
| if {![gdb_simple_compile $me $src executable]} { |
| return 0 |
| } |
| |
| clean_restart $obj |
| if ![runto_main] { |
| return 0 |
| } |
| set res [get_endianness] |
| |
| gdb_exit |
| remote_file build delete $obj |
| |
| return $res |
| } |
| |
| # ROOT and FULL are file names. Returns the relative path from ROOT |
| # to FULL. Note that FULL must be in a subdirectory of ROOT. |
| # For example, given ROOT = /usr/bin and FULL = /usr/bin/ls, this |
| # will return "ls". |
| |
| proc relative_filename {root full} { |
| set root_split [file split $root] |
| set full_split [file split $full] |
| |
| set len [llength $root_split] |
| |
| if {[eval file join $root_split] |
| != [eval file join [lrange $full_split 0 [expr {$len - 1}]]]} { |
| error "$full not a subdir of $root" |
| } |
| |
| return [eval file join [lrange $full_split $len end]] |
| } |
| |
| # If GDB_PARALLEL exists, then set up the parallel-mode directories. |
| if {[info exists GDB_PARALLEL]} { |
| if {[is_remote host]} { |
| unset GDB_PARALLEL |
| } else { |
| file mkdir \ |
| [make_gdb_parallel_path outputs] \ |
| [make_gdb_parallel_path temp] \ |
| [make_gdb_parallel_path cache] |
| } |
| } |
| |
| # Set the inferior's cwd to the output directory, in order to have it |
| # dump core there. This must be called before the inferior is |
| # started. |
| |
| proc set_inferior_cwd_to_output_dir {} { |
| # Note this sets the inferior's cwd ("set cwd"), not GDB's ("cd"). |
| # If GDB crashes, we want its core dump in gdb/testsuite/, not in |
| # the testcase's dir, so we can detect the unexpected core at the |
| # end of the test run. |
| if {![is_remote host]} { |
| set output_dir [standard_output_file ""] |
| gdb_test_no_output "set cwd $output_dir" \ |
| "set inferior cwd to test directory" |
| } |
| } |
| |
| # Get the inferior's PID. |
| |
| proc get_inferior_pid {} { |
| set pid -1 |
| gdb_test_multiple "inferior" "get inferior pid" { |
| -re "process (\[0-9\]*).*$::gdb_prompt $" { |
| set pid $expect_out(1,string) |
| pass $gdb_test_name |
| } |
| } |
| return $pid |
| } |
| |
| # Find the kernel-produced core file dumped for the current testfile |
| # program. PID was the inferior's pid, saved before the inferior |
| # exited with a signal, or -1 if not known. If not on a remote host, |
| # this assumes the core was generated in the output directory. |
| # Returns the name of the core dump, or empty string if not found. |
| |
| proc find_core_file {pid} { |
| # For non-remote hosts, since cores are assumed to be in the |
| # output dir, which we control, we use a laxer "core.*" glob. For |
| # remote hosts, as we don't know whether the dir is being reused |
| # for parallel runs, we use stricter names with no globs. It is |
| # not clear whether this is really important, but it preserves |
| # status quo ante. |
| set files {} |
| if {![is_remote host]} { |
| lappend files core.* |
| } elseif {$pid != -1} { |
| lappend files core.$pid |
| } |
| lappend files ${::testfile}.core |
| lappend files core |
| |
| foreach file $files { |
| if {![is_remote host]} { |
| set names [glob -nocomplain [standard_output_file $file]] |
| if {[llength $names] == 1} { |
| return [lindex $names 0] |
| } |
| } else { |
| if {[remote_file host exists $file]} { |
| return $file |
| } |
| } |
| } |
| return "" |
| } |
| |
| # Check for production of a core file and remove it. PID is the |
| # inferior's pid or -1 if not known. TEST is the test's message. |
| |
| proc remove_core {pid {test ""}} { |
| if {$test == ""} { |
| set test "cleanup core file" |
| } |
| |
| set file [find_core_file $pid] |
| if {$file != ""} { |
| remote_file host delete $file |
| pass "$test (removed)" |
| } else { |
| pass "$test (not found)" |
| } |
| } |
| |
| proc core_find {binfile {deletefiles {}} {arg ""}} { |
| global objdir subdir |
| |
| set destcore "$binfile.core" |
| file delete $destcore |
| |
| # Create a core file named "$destcore" rather than just "core", to |
| # avoid problems with sys admin types that like to regularly prune all |
| # files named "core" from the system. |
| # |
| # Arbitrarily try setting the core size limit to "unlimited" since |
| # this does not hurt on systems where the command does not work and |
| # allows us to generate a core on systems where it does. |
| # |
| # Some systems append "core" to the name of the program; others append |
| # the name of the program to "core"; still others (like Linux, as of |
| # May 2003) create cores named "core.PID". In the latter case, we |
| # could have many core files lying around, and it may be difficult to |
| # tell which one is ours, so let's run the program in a subdirectory. |
| set found 0 |
| set coredir [standard_output_file coredir.[getpid]] |
| file mkdir $coredir |
| catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\"" |
| # remote_exec host "${binfile}" |
| foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" { |
| if [remote_file build exists $i] { |
| remote_exec build "mv $i $destcore" |
| set found 1 |
| } |
| } |
| # Check for "core.PID", "core.EXEC.PID.HOST.TIME", etc. It's fine |
| # to use a glob here as we're looking inside a directory we |
| # created. Also, this procedure only works on non-remote hosts. |
| if { $found == 0 } { |
| set names [glob -nocomplain -directory $coredir core.*] |
| if {[llength $names] == 1} { |
| set corefile [file join $coredir [lindex $names 0]] |
| remote_exec build "mv $corefile $destcore" |
| set found 1 |
| } |
| } |
| if { $found == 0 } { |
| # The braindamaged HPUX shell quits after the ulimit -c above |
| # without executing ${binfile}. So we try again without the |
| # ulimit here if we didn't find a core file above. |
| # Oh, I should mention that any "braindamaged" non-Unix system has |
| # the same problem. I like the cd bit too, it's really neat'n stuff. |
| catch "system \"(cd ${objdir}/${subdir}; ${binfile}; true) >/dev/null 2>&1\"" |
| foreach i "${objdir}/${subdir}/core ${objdir}/${subdir}/core.coremaker.c ${binfile}.core" { |
| if [remote_file build exists $i] { |
| remote_exec build "mv $i $destcore" |
| set found 1 |
| } |
| } |
| } |
| |
| # Try to clean up after ourselves. |
| foreach deletefile $deletefiles { |
| remote_file build delete [file join $coredir $deletefile] |
| } |
| remote_exec build "rmdir $coredir" |
| |
| if { $found == 0 } { |
| warning "can't generate a core file - core tests suppressed - check ulimit -c" |
| return "" |
| } |
| return $destcore |
| } |
| |
| # gdb_target_symbol_prefix compiles a test program and then examines |
| # the output from objdump to determine the prefix (such as underscore) |
| # for linker symbol prefixes. |
| |
| gdb_caching_proc gdb_target_symbol_prefix { |
| # Compile a simple test program... |
| set src { int main() { return 0; } } |
| if {![gdb_simple_compile target_symbol_prefix $src executable]} { |
| return 0 |
| } |
| |
| set prefix "" |
| |
| set objdump_program [gdb_find_objdump] |
| set result [catch "exec $objdump_program --syms $obj" output] |
| |
| if { $result == 0 \ |
| && ![regexp -lineanchor \ |
| { ([^ a-zA-Z0-9]*)main$} $output dummy prefix] } { |
| verbose "gdb_target_symbol_prefix: Could not find main in objdump output; returning null prefix" 2 |
| } |
| |
| file delete $obj |
| |
| return $prefix |
| } |
| |
| # Return 1 if target supports scheduler locking, otherwise return 0. |
| |
| gdb_caching_proc target_supports_scheduler_locking { |
| global gdb_prompt |
| |
| set me "gdb_target_supports_scheduler_locking" |
| |
| set src { int main() { return 0; } } |
| if {![gdb_simple_compile $me $src executable]} { |
| return 0 |
| } |
| |
| clean_restart $obj |
| if ![runto_main] { |
| return 0 |
| } |
| |
| set supports_schedule_locking -1 |
| set current_schedule_locking_mode "" |
| |
| set test "reading current scheduler-locking mode" |
| gdb_test_multiple "show scheduler-locking" $test { |
| -re "Mode for locking scheduler during execution is \"(\[\^\"\]*)\".*$gdb_prompt" { |
| set current_schedule_locking_mode $expect_out(1,string) |
| } |
| -re "$gdb_prompt $" { |
| set supports_schedule_locking 0 |
| } |
| timeout { |
| set supports_schedule_locking 0 |
| } |
| } |
| |
| if { $supports_schedule_locking == -1 } { |
| set test "checking for scheduler-locking support" |
| gdb_test_multiple "set scheduler-locking $current_schedule_locking_mode" $test { |
| -re "Target '\[^'\]+' cannot support this command\..*$gdb_prompt $" { |
| set supports_schedule_locking 0 |
| } |
| -re "$gdb_prompt $" { |
| set supports_schedule_locking 1 |
| } |
| timeout { |
| set supports_schedule_locking 0 |
| } |
| } |
| } |
| |
| if { $supports_schedule_locking == -1 } { |
| set supports_schedule_locking 0 |
| } |
| |
| gdb_exit |
| remote_file build delete $obj |
| verbose "$me: returning $supports_schedule_locking" 2 |
| return $supports_schedule_locking |
| } |
| |
| # Return 1 if compiler supports use of nested functions. Otherwise, |
| # return 0. |
| |
| gdb_caching_proc support_nested_function_tests { |
| # Compile a test program containing a nested function |
| return [gdb_can_simple_compile nested_func { |
| int main () { |
| int foo () { |
| return 0; |
| } |
| return foo (); |
| } |
| } executable] |
| } |
| |
| # gdb_target_symbol returns the provided symbol with the correct prefix |
| # prepended. (See gdb_target_symbol_prefix, above.) |
| |
| proc gdb_target_symbol { symbol } { |
| set prefix [gdb_target_symbol_prefix] |
| return "${prefix}${symbol}" |
| } |
| |
| # gdb_target_symbol_prefix_flags_asm returns a string that can be |
| # added to gdb_compile options to define the C-preprocessor macro |
| # SYMBOL_PREFIX with a value that can be prepended to symbols |
| # for targets which require a prefix, such as underscore. |
| # |
| # This version (_asm) defines the prefix without double quotes |
| # surrounding the prefix. It is used to define the macro |
| # SYMBOL_PREFIX for assembly language files. Another version, below, |
| # is used for symbols in inline assembler in C/C++ files. |
| # |
| # The lack of quotes in this version (_asm) makes it possible to |
| # define supporting macros in the .S file. (The version which |
| # uses quotes for the prefix won't work for such files since it's |
| # impossible to define a quote-stripping macro in C.) |
| # |
| # It's possible to use this version (_asm) for C/C++ source files too, |
| # but a string is usually required in such files; providing a version |
| # (no _asm) which encloses the prefix with double quotes makes it |
| # somewhat easier to define the supporting macros in the test case. |
| |
| proc gdb_target_symbol_prefix_flags_asm {} { |
| set prefix [gdb_target_symbol_prefix] |
| if {$prefix ne ""} { |
| return "additional_flags=-DSYMBOL_PREFIX=$prefix" |
| } else { |
| return ""; |
| } |
| } |
| |
| # gdb_target_symbol_prefix_flags returns the same string as |
| # gdb_target_symbol_prefix_flags_asm, above, but with the prefix |
| # enclosed in double quotes if there is a prefix. |
| # |
| # See the comment for gdb_target_symbol_prefix_flags_asm for an |
| # extended discussion. |
| |
| proc gdb_target_symbol_prefix_flags {} { |
| set prefix [gdb_target_symbol_prefix] |
| if {$prefix ne ""} { |
| return "additional_flags=-DSYMBOL_PREFIX=\"$prefix\"" |
| } else { |
| return ""; |
| } |
| } |
| |
| # A wrapper for 'remote_exec host' that passes or fails a test. |
| # Returns 0 if all went well, nonzero on failure. |
| # TEST is the name of the test, other arguments are as for remote_exec. |
| |
| proc run_on_host { test program args } { |
| verbose -log "run_on_host: $program $args" |
| # remote_exec doesn't work properly if the output is set but the |
| # input is the empty string -- so replace an empty input with |
| # /dev/null. |
| if {[llength $args] > 1 && [lindex $args 1] == ""} { |
| set args [lreplace $args 1 1 "/dev/null"] |
| } |
| set result [eval remote_exec host [list $program] $args] |
| verbose "result is $result" |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| if {$status == 0} { |
| pass $test |
| return 0 |
| } else { |
| verbose -log "run_on_host failed: $output" |
| if { $output == "spawn failed" } { |
| unsupported $test |
| } else { |
| fail $test |
| } |
| return -1 |
| } |
| } |
| |
| # Return non-zero if "board_info debug_flags" mentions Fission. |
| # http://gcc.gnu.org/wiki/DebugFission |
| # Fission doesn't support everything yet. |
| # This supports working around bug 15954. |
| |
| proc using_fission { } { |
| set debug_flags [board_info [target_info name] debug_flags] |
| return [regexp -- "-gsplit-dwarf" $debug_flags] |
| } |
| |
| # Search LISTNAME in uplevel LEVEL caller and set variables according to the |
| # list of valid options with prefix PREFIX described by ARGSET. |
| # |
| # The first member of each one- or two-element list in ARGSET defines the |
| # name of a variable that will be added to the caller's scope. |
| # |
| # If only one element is given to describe an option, it the value is |
| # 0 if the option is not present in (the caller's) ARGS or 1 if |
| # it is. |
| # |
| # If two elements are given, the second element is the default value of |
| # the variable. This is then overwritten if the option exists in ARGS. |
| # If EVAL, then subst is called on the value, which allows variables |
| # to be used. |
| # |
| # Any parse_args elements in (the caller's) ARGS will be removed, leaving |
| # any optional components. |
| # |
| # Example: |
| # proc myproc {foo args} { |
| # parse_list args 1 {{bar} {baz "abc"} {qux}} "-" false |
| # # ... |
| # } |
| # myproc ABC -bar -baz DEF peanut butter |
| # will define the following variables in myproc: |
| # foo (=ABC), bar (=1), baz (=DEF), and qux (=0) |
| # args will be the list {peanut butter} |
| |
| proc parse_list { level listname argset prefix eval } { |
| upvar $level $listname args |
| |
| foreach argument $argset { |
| if {[llength $argument] == 1} { |
| # Normalize argument, strip leading/trailing whitespace. |
| # Allows us to treat {foo} and { foo } the same. |
| set argument [string trim $argument] |
| |
| # No default specified, so we assume that we should set |
| # the value to 1 if the arg is present and 0 if it's not. |
| # It is assumed that no value is given with the argument. |
| set pattern "$prefix$argument" |
| set result [lsearch -exact $args $pattern] |
| |
| if {$result != -1} { |
| set value 1 |
| set args [lreplace $args $result $result] |
| } else { |
| set value 0 |
| } |
| uplevel $level [list set $argument $value] |
| } elseif {[llength $argument] == 2} { |
| # There are two items in the argument. The second is a |
| # default value to use if the item is not present. |
| # Otherwise, the variable is set to whatever is provided |
| # after the item in the args. |
| set arg [lindex $argument 0] |
| set pattern "$prefix[lindex $arg 0]" |
| set result [lsearch -exact $args $pattern] |
| |
| if {$result != -1} { |
| set value [lindex $args [expr $result+1]] |
| if { $eval } { |
| set value [uplevel [expr $level + 1] [list subst $value]] |
| } |
| set args [lreplace $args $result [expr $result+1]] |
| } else { |
| set value [lindex $argument 1] |
| if { $eval } { |
| set value [uplevel $level [list subst $value]] |
| } |
| } |
| uplevel $level [list set $arg $value] |
| } else { |
| error "Badly formatted argument \"$argument\" in argument set" |
| } |
| } |
| } |
| |
| # Search the caller's args variable and set variables according to the list of |
| # valid options described by ARGSET. |
| |
| proc parse_args { argset } { |
| parse_list 2 args $argset "-" false |
| |
| # The remaining args should be checked to see that they match the |
| # number of items expected to be passed into the procedure... |
| } |
| |
| # Process the caller's options variable and set variables according |
| # to the list of valid options described by OPTIONSET. |
| |
| proc parse_options { optionset } { |
| parse_list 2 options $optionset "" true |
| |
| # Require no remaining options. |
| upvar 1 options options |
| if { [llength $options] != 0 } { |
| error "Options left unparsed: $options" |
| } |
| } |
| |
| # Capture the output of COMMAND in a string ignoring PREFIX (a regexp); |
| # return that string. |
| |
| proc capture_command_output { command prefix } { |
| global gdb_prompt |
| global expect_out |
| |
| set test "capture_command_output for $command" |
| |
| set output_string "" |
| gdb_test_multiple $command $test { |
| -re "^(\[^\r\n\]+\r\n)" { |
| if { ![string equal $output_string ""] } { |
| set output_string [join [list $output_string $expect_out(1,string)] ""] |
| } else { |
| set output_string $expect_out(1,string) |
| } |
| exp_continue |
| } |
| |
| -re "^$gdb_prompt $" { |
| } |
| } |
| |
| # Strip the command. |
| set command_re [string_to_regexp ${command}] |
| set output_string [regsub ^$command_re\r\n $output_string ""] |
| |
| # Strip the prefix. |
| if { $prefix != "" } { |
| set output_string [regsub ^$prefix $output_string ""] |
| } |
| |
| # Strip a trailing newline. |
| set output_string [regsub "\r\n$" $output_string ""] |
| |
| return $output_string |
| } |
| |
| # A convenience function that joins all the arguments together, with a |
| # regexp that matches exactly one end of line in between each argument. |
| # This function is ideal to write the expected output of a GDB command |
| # that generates more than a couple of lines, as this allows us to write |
| # each line as a separate string, which is easier to read by a human |
| # being. |
| |
| proc multi_line { args } { |
| if { [llength $args] == 1 } { |
| set hint "forgot {*} before list argument?" |
| error "multi_line called with one argument ($hint)" |
| } |
| return [join $args "\r\n"] |
| } |
| |
| # Similar to the above, but while multi_line is meant to be used to |
| # match GDB output, this one is meant to be used to build strings to |
| # send as GDB input. |
| |
| proc multi_line_input { args } { |
| return [join $args "\n"] |
| } |
| |
| # Return how many newlines there are in the given string. |
| |
| proc count_newlines { string } { |
| return [regexp -all "\n" $string] |
| } |
| |
| # Return the version of the DejaGnu framework. |
| # |
| # The return value is a list containing the major, minor and patch version |
| # numbers. If the version does not contain a minor or patch number, they will |
| # be set to 0. For example: |
| # |
| # 1.6 -> {1 6 0} |
| # 1.6.1 -> {1 6 1} |
| # 2 -> {2 0 0} |
| |
| proc dejagnu_version { } { |
| # The frame_version variable is defined by DejaGnu, in runtest.exp. |
| global frame_version |
| |
| verbose -log "DejaGnu version: $frame_version" |
| verbose -log "Expect version: [exp_version]" |
| verbose -log "Tcl version: [info tclversion]" |
| |
| set dg_ver [split $frame_version .] |
| |
| while { [llength $dg_ver] < 3 } { |
| lappend dg_ver 0 |
| } |
| |
| return $dg_ver |
| } |
| |
| # Define user-defined command COMMAND using the COMMAND_LIST as the |
| # command's definition. The terminating "end" is added automatically. |
| |
| proc gdb_define_cmd {command command_list} { |
| global gdb_prompt |
| |
| set input [multi_line_input {*}$command_list "end"] |
| set test "define $command" |
| |
| gdb_test_multiple "define $command" $test { |
| -re "End with" { |
| gdb_test_multiple $input $test { |
| -re "\r\n$gdb_prompt " { |
| } |
| } |
| } |
| } |
| } |
| |
| # Override the 'cd' builtin with a version that ensures that the |
| # log file keeps pointing at the same file. We need this because |
| # unfortunately the path to the log file is recorded using an |
| # relative path name, and, we sometimes need to close/reopen the log |
| # after changing the current directory. See get_compiler_info. |
| |
| rename cd builtin_cd |
| |
| proc cd { dir } { |
| |
| # Get the existing log file flags. |
| set log_file_info [log_file -info] |
| |
| # Split the flags into args and file name. |
| set log_file_flags "" |
| set log_file_file "" |
| foreach arg [ split "$log_file_info" " "] { |
| if [string match "-*" $arg] { |
| lappend log_file_flags $arg |
| } else { |
| lappend log_file_file $arg |
| } |
| } |
| |
| # If there was an existing file, ensure it is an absolute path, and then |
| # reset logging. |
| if { $log_file_file != "" } { |
| set log_file_file [file normalize $log_file_file] |
| log_file |
| log_file $log_file_flags "$log_file_file" |
| } |
| |
| # Call the builtin version of cd. |
| builtin_cd $dir |
| } |
| |
| # Return a list of all languages supported by GDB, suitable for use in |
| # 'set language NAME'. This doesn't include either the 'local' or |
| # 'auto' keywords. |
| proc gdb_supported_languages {} { |
| return [list c objective-c c++ d go fortran modula-2 asm pascal \ |
| opencl rust minimal ada] |
| } |
| |
| # Check if debugging is enabled for gdb. |
| |
| proc gdb_debug_enabled { } { |
| global gdbdebug |
| |
| # If not already read, get the debug setting from environment or board setting. |
| if {![info exists gdbdebug]} { |
| global env |
| if [info exists env(GDB_DEBUG)] { |
| set gdbdebug $env(GDB_DEBUG) |
| } elseif [target_info exists gdb,debug] { |
| set gdbdebug [target_info gdb,debug] |
| } else { |
| return 0 |
| } |
| } |
| |
| # Ensure it not empty. |
| return [expr { $gdbdebug != "" }] |
| } |
| |
| # Turn on debugging if enabled, or reset if already on. |
| |
| proc gdb_debug_init { } { |
| |
| global gdb_prompt |
| |
| if ![gdb_debug_enabled] { |
| return; |
| } |
| |
| # First ensure logging is off. |
| send_gdb "set logging enabled off\n" |
| |
| set debugfile [standard_output_file gdb.debug] |
| send_gdb "set logging file $debugfile\n" |
| |
| send_gdb "set logging debugredirect\n" |
| |
| global gdbdebug |
| foreach entry [split $gdbdebug ,] { |
| send_gdb "set debug $entry 1\n" |
| } |
| |
| # Now that everything is set, enable logging. |
| send_gdb "set logging enabled on\n" |
| gdb_expect 10 { |
| -re "Copying output to $debugfile.*Redirecting debug output to $debugfile.*$gdb_prompt $" {} |
| timeout { warning "Couldn't set logging file" } |
| } |
| } |
| |
| # Check if debugging is enabled for gdbserver. |
| |
| proc gdbserver_debug_enabled { } { |
| # Always disabled for GDB only setups. |
| return 0 |
| } |
| |
| # Open the file for logging gdb input |
| |
| proc gdb_stdin_log_init { } { |
| gdb_persistent_global in_file |
| |
| if {[info exists in_file]} { |
| # Close existing file. |
| catch "close $in_file" |
| } |
| |
| set logfile [standard_output_file_with_gdb_instance gdb.in] |
| set in_file [open $logfile w] |
| } |
| |
| # Write to the file for logging gdb input. |
| # TYPE can be one of the following: |
| # "standard" : Default. Standard message written to the log |
| # "answer" : Answer to a question (eg "Y"). Not written the log. |
| # "optional" : Optional message. Not written to the log. |
| |
| proc gdb_stdin_log_write { message {type standard} } { |
| |
| global in_file |
| if {![info exists in_file]} { |
| return |
| } |
| |
| # Check message types. |
| switch -regexp -- $type { |
| "answer" { |
| return |
| } |
| "optional" { |
| return |
| } |
| } |
| |
| # Write to the log and make sure the output is there, even in case |
| # of crash. |
| puts -nonewline $in_file "$message" |
| flush $in_file |
| } |
| |
| # Write the command line used to invocate gdb to the cmd file. |
| |
| proc gdb_write_cmd_file { cmdline } { |
| set logfile [standard_output_file_with_gdb_instance gdb.cmd] |
| set cmd_file [open $logfile w] |
| puts $cmd_file $cmdline |
| catch "close $cmd_file" |
| } |
| |
| # Compare contents of FILE to string STR. Pass with MSG if equal, otherwise |
| # fail with MSG. |
| |
| proc cmp_file_string { file str msg } { |
| if { ![file exists $file]} { |
| fail "$msg" |
| return |
| } |
| |
| set caught_error [catch { |
| set fp [open "$file" r] |
| set file_contents [read $fp] |
| close $fp |
| } error_message] |
| if {$caught_error} { |
| error "$error_message" |
| fail "$msg" |
| return |
| } |
| |
| if { $file_contents == $str } { |
| pass "$msg" |
| } else { |
| fail "$msg" |
| } |
| } |
| |
| # Compare FILE1 and FILE2 as binary files. Return 0 if the files are |
| # equal, otherwise, return non-zero. |
| |
| proc cmp_binary_files { file1 file2 } { |
| set fd1 [open $file1] |
| fconfigure $fd1 -translation binary |
| set fd2 [open $file2] |
| fconfigure $fd2 -translation binary |
| |
| set blk_size 1024 |
| while {true} { |
| set blk1 [read $fd1 $blk_size] |
| set blk2 [read $fd2 $blk_size] |
| set diff [string compare $blk1 $blk2] |
| if {$diff != 0 || [eof $fd1] || [eof $fd2]} { |
| close $fd1 |
| close $fd2 |
| return $diff |
| } |
| } |
| } |
| |
| # Does the compiler support CTF debug output using '-gctf' compiler |
| # flag? If not then we should skip these tests. We should also |
| # skip them if libctf was explicitly disabled. |
| |
| gdb_caching_proc skip_ctf_tests { |
| global enable_libctf |
| |
| if {$enable_libctf eq "no"} { |
| return 1 |
| } |
| |
| set can_ctf [gdb_can_simple_compile ctfdebug { |
| int main () { |
| return 0; |
| } |
| } executable "additional_flags=-gctf"] |
| |
| return [expr {!$can_ctf}] |
| } |
| |
| # Return 1 if compiler supports -gstatement-frontiers. Otherwise, |
| # return 0. |
| |
| gdb_caching_proc supports_statement_frontiers { |
| return [gdb_can_simple_compile supports_statement_frontiers { |
| int main () { |
| return 0; |
| } |
| } executable "additional_flags=-gstatement-frontiers"] |
| } |
| |
| # Return 1 if compiler supports -mmpx -fcheck-pointer-bounds. Otherwise, |
| # return 0. |
| |
| gdb_caching_proc supports_mpx_check_pointer_bounds { |
| set flags "additional_flags=-mmpx additional_flags=-fcheck-pointer-bounds" |
| return [gdb_can_simple_compile supports_mpx_check_pointer_bounds { |
| int main () { |
| return 0; |
| } |
| } executable $flags] |
| } |
| |
| # Return 1 if compiler supports -fcf-protection=. Otherwise, |
| # return 0. |
| |
| gdb_caching_proc supports_fcf_protection { |
| return [gdb_can_simple_compile supports_fcf_protection { |
| int main () { |
| return 0; |
| } |
| } executable "additional_flags=-fcf-protection=full"] |
| } |
| |
| # Return true if symbols were read in using -readnow. Otherwise, |
| # return false. |
| |
| proc readnow { } { |
| return [expr {[lsearch -exact $::GDBFLAGS -readnow] != -1 |
| || [lsearch -exact $::GDBFLAGS --readnow] != -1}] |
| } |
| |
| # Return index name if symbols were read in using an index. |
| # Otherwise, return "". |
| |
| proc have_index { objfile } { |
| |
| set res "" |
| set cmd "maint print objfiles $objfile" |
| gdb_test_multiple $cmd "" -lbl { |
| -re "\r\n.gdb_index: faked for \"readnow\"" { |
| set res "" |
| exp_continue |
| } |
| -re "\r\n.gdb_index:" { |
| set res "gdb_index" |
| exp_continue |
| } |
| -re "\r\n.debug_names:" { |
| set res "debug_names" |
| exp_continue |
| } |
| -re -wrap "" { |
| # We don't care about any other input. |
| } |
| } |
| |
| return $res |
| } |
| |
| # Return 1 if partial symbols are available. Otherwise, return 0. |
| |
| proc psymtabs_p { } { |
| global gdb_prompt |
| |
| set cmd "maint info psymtab" |
| gdb_test_multiple $cmd "" { |
| -re "$cmd\r\n$gdb_prompt $" { |
| return 0 |
| } |
| -re -wrap "" { |
| return 1 |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Verify that partial symtab expansion for $filename has state $readin. |
| |
| proc verify_psymtab_expanded { filename readin } { |
| global gdb_prompt |
| |
| set cmd "maint info psymtab" |
| set test "$cmd: $filename: $readin" |
| set re [multi_line \ |
| " \{ psymtab \[^\r\n\]*$filename\[^\r\n\]*" \ |
| " readin $readin" \ |
| ".*"] |
| |
| gdb_test_multiple $cmd $test { |
| -re "$cmd\r\n$gdb_prompt $" { |
| unsupported $gdb_test_name |
| } |
| -re -wrap $re { |
| pass $gdb_test_name |
| } |
| } |
| } |
| |
| # Add a .gdb_index section to PROGRAM. |
| # PROGRAM is assumed to be the output of standard_output_file. |
| # Returns the 0 if there is a failure, otherwise 1. |
| # |
| # STYLE controls which style of index to add, if needed. The empty |
| # string (the default) means .gdb_index; "-dwarf-5" means .debug_names. |
| |
| proc add_gdb_index { program {style ""} } { |
| global srcdir GDB env |
| set contrib_dir "$srcdir/../contrib" |
| set env(GDB) [append_gdb_data_directory_option $GDB] |
| set result [catch "exec $contrib_dir/gdb-add-index.sh $style $program" output] |
| if { $result != 0 } { |
| verbose -log "result is $result" |
| verbose -log "output is $output" |
| return 0 |
| } |
| |
| return 1 |
| } |
| |
| # Add a .gdb_index section to PROGRAM, unless it alread has an index |
| # (.gdb_index/.debug_names). Gdb doesn't support building an index from a |
| # program already using one. Return 1 if a .gdb_index was added, return 0 |
| # if it already contained an index, and -1 if an error occurred. |
| # |
| # STYLE controls which style of index to add, if needed. The empty |
| # string (the default) means .gdb_index; "-dwarf-5" means .debug_names. |
| |
| proc ensure_gdb_index { binfile {style ""} } { |
| global decimal |
| |
| set testfile [file tail $binfile] |
| set test "check if index present" |
| set has_index 0 |
| set has_readnow 0 |
| gdb_test_multiple "mt print objfiles ${testfile}" $test -lbl { |
| -re "\r\n\\.gdb_index: version ${decimal}(?=\r\n)" { |
| set has_index 1 |
| gdb_test_lines "" $gdb_test_name ".*" |
| } |
| -re "\r\n\\.debug_names: exists(?=\r\n)" { |
| set has_index 1 |
| gdb_test_lines "" $gdb_test_name ".*" |
| } |
| -re "\r\n(Cooked index in use|Psymtabs)(?=\r\n)" { |
| gdb_test_lines "" $gdb_test_name ".*" |
| } |
| -re ".gdb_index: faked for \"readnow\"" { |
| set has_readnow 1 |
| gdb_test_lines "" $gdb_test_name ".*" |
| } |
| -re -wrap "" { |
| fail $gdb_test_name |
| } |
| } |
| |
| if { $has_index } { |
| return 0 |
| } |
| |
| if { $has_readnow } { |
| return -1 |
| } |
| |
| if { [add_gdb_index $binfile $style] == "1" } { |
| return 1 |
| } |
| |
| return -1 |
| } |
| |
| # Return 1 if executable contains .debug_types section. Otherwise, return 0. |
| |
| proc debug_types { } { |
| global hex |
| |
| set cmd "maint info sections" |
| gdb_test_multiple $cmd "" { |
| -re -wrap "at $hex: .debug_types.*" { |
| return 1 |
| } |
| -re -wrap "" { |
| return 0 |
| } |
| } |
| |
| return 0 |
| } |
| |
| # Return the addresses in the line table for FILE for which is_stmt is true. |
| |
| proc is_stmt_addresses { file } { |
| global decimal |
| global hex |
| |
| set is_stmt [list] |
| |
| gdb_test_multiple "maint info line-table $file" "" { |
| -re "\r\n$decimal\[ \t\]+$decimal\[ \t\]+($hex)\[ \t\]+Y\[^\r\n\]*" { |
| lappend is_stmt $expect_out(1,string) |
| exp_continue |
| } |
| -re -wrap "" { |
| } |
| } |
| |
| return $is_stmt |
| } |
| |
| # Return 1 if hex number VAL is an element of HEXLIST. |
| |
| proc hex_in_list { val hexlist } { |
| # Normalize val by removing 0x prefix, and leading zeros. |
| set val [regsub ^0x $val ""] |
| set val [regsub ^0+ $val "0"] |
| |
| set re 0x0*$val |
| set index [lsearch -regexp $hexlist $re] |
| return [expr $index != -1] |
| } |
| |
| # Override proc NAME to proc OVERRIDE for the duration of the execution of |
| # BODY. |
| |
| proc with_override { name override body } { |
| # Implementation note: It's possible to implement the override using |
| # rename, like this: |
| # rename $name save_$name |
| # rename $override $name |
| # set code [catch {uplevel 1 $body} result] |
| # rename $name $override |
| # rename save_$name $name |
| # but there are two issues here: |
| # - the save_$name might clash with an existing proc |
| # - the override is no longer available under its original name during |
| # the override |
| # So, we use this more elaborate but cleaner mechanism. |
| |
| # Save the old proc, if it exists. |
| if { [info procs $name] != "" } { |
| set old_args [info args $name] |
| set old_body [info body $name] |
| set existed true |
| } else { |
| set existed false |
| } |
| |
| # Install the override. |
| set new_args [info args $override] |
| set new_body [info body $override] |
| eval proc $name {$new_args} {$new_body} |
| |
| # Execute body. |
| set code [catch {uplevel 1 $body} result] |
| |
| # Restore old proc if it existed on entry, else delete it. |
| if { $existed } { |
| eval proc $name {$old_args} {$old_body} |
| } else { |
| rename $name "" |
| } |
| |
| # Return as appropriate. |
| if { $code == 1 } { |
| global errorInfo errorCode |
| return -code error -errorinfo $errorInfo -errorcode $errorCode $result |
| } elseif { $code > 1 } { |
| return -code $code $result |
| } |
| |
| return $result |
| } |
| |
| # Setup tuiterm.exp environment. To be used in test-cases instead of |
| # "load_lib tuiterm.exp". Calls initialization function and schedules |
| # finalization function. |
| proc tuiterm_env { } { |
| load_lib tuiterm.exp |
| } |
| |
| # Dejagnu has a version of note, but usage is not allowed outside of dejagnu. |
| # Define a local version. |
| proc gdb_note { message } { |
| verbose -- "NOTE: $message" 0 |
| } |
| |
| # Return 1 if compiler supports -fuse-ld=gold, otherwise return 0. |
| gdb_caching_proc have_fuse_ld_gold { |
| set me "have_fuse_ld_gold" |
| set flags "additional_flags=-fuse-ld=gold" |
| set src { int main() { return 0; } } |
| return [gdb_simple_compile $me $src executable $flags] |
| } |
| |
| # Return 1 if compiler supports fvar-tracking, otherwise return 0. |
| gdb_caching_proc have_fvar_tracking { |
| set me "have_fvar_tracking" |
| set flags "additional_flags=-fvar-tracking" |
| set src { int main() { return 0; } } |
| return [gdb_simple_compile $me $src executable $flags] |
| } |
| |
| # Return 1 if linker supports -Ttext-segment, otherwise return 0. |
| gdb_caching_proc linker_supports_Ttext_segment_flag { |
| set me "linker_supports_Ttext_segment_flag" |
| set flags ldflags="-Wl,-Ttext-segment=0x7000000" |
| set src { int main() { return 0; } } |
| return [gdb_simple_compile $me $src executable $flags] |
| } |
| |
| # Return 1 if linker supports -Ttext, otherwise return 0. |
| gdb_caching_proc linker_supports_Ttext_flag { |
| set me "linker_supports_Ttext_flag" |
| set flags ldflags="-Wl,-Ttext=0x7000000" |
| set src { int main() { return 0; } } |
| return [gdb_simple_compile $me $src executable $flags] |
| } |
| |
| # Return 1 if linker supports --image-base, otherwise 0. |
| gdb_caching_proc linker_supports_image_base_flag { |
| set me "linker_supports_image_base_flag" |
| set flags ldflags="-Wl,--image-base=0x7000000" |
| set src { int main() { return 0; } } |
| return [gdb_simple_compile $me $src executable $flags] |
| } |
| |
| |
| # Return 1 if compiler supports scalar_storage_order attribute, otherwise |
| # return 0. |
| gdb_caching_proc supports_scalar_storage_order_attribute { |
| set me "supports_scalar_storage_order_attribute" |
| set src { |
| #include <string.h> |
| struct sle { |
| int v; |
| } __attribute__((scalar_storage_order("little-endian"))); |
| struct sbe { |
| int v; |
| } __attribute__((scalar_storage_order("big-endian"))); |
| struct sle sle; |
| struct sbe sbe; |
| int main () { |
| sle.v = sbe.v = 0x11223344; |
| int same = memcmp (&sle, &sbe, sizeof (int)) == 0; |
| int sso = !same; |
| return sso; |
| } |
| } |
| if { ![gdb_simple_compile $me $src executable ""] } { |
| return 0 |
| } |
| |
| set result [remote_exec target $obj] |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| if { $output != "" } { |
| return 0 |
| } |
| |
| return $status |
| } |
| |
| # Return 1 if compiler supports __GNUC__, otherwise return 0. |
| gdb_caching_proc supports_gnuc { |
| set me "supports_gnuc" |
| set src { |
| #ifndef __GNUC__ |
| #error "No gnuc" |
| #endif |
| } |
| return [gdb_simple_compile $me $src object ""] |
| } |
| |
| # Return 1 if target supports mpx, otherwise return 0. |
| gdb_caching_proc have_mpx { |
| global srcdir |
| |
| set me "have_mpx" |
| if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } { |
| verbose "$me: target does not support mpx, returning 0" 2 |
| return 0 |
| } |
| |
| # Compile a test program. |
| set src { |
| #include "nat/x86-cpuid.h" |
| |
| int main() { |
| unsigned int eax, ebx, ecx, edx; |
| |
| if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx)) |
| return 0; |
| |
| if ((ecx & bit_OSXSAVE) == bit_OSXSAVE) |
| { |
| if (__get_cpuid_max (0, (void *)0) < 7) |
| return 0; |
| |
| __cpuid_count (7, 0, eax, ebx, ecx, edx); |
| |
| if ((ebx & bit_MPX) == bit_MPX) |
| return 1; |
| |
| } |
| return 0; |
| } |
| } |
| set compile_flags "incdir=${srcdir}/.." |
| if {![gdb_simple_compile $me $src executable $compile_flags]} { |
| return 0 |
| } |
| |
| set result [remote_exec target $obj] |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| if { $output != "" } { |
| set status 0 |
| } |
| |
| remote_file build delete $obj |
| |
| if { $status == 0 } { |
| verbose "$me: returning $status" 2 |
| return $status |
| } |
| |
| # Compile program with -mmpx -fcheck-pointer-bounds, try to trigger |
| # 'No MPX support', in other words, see if kernel supports mpx. |
| set src { int main (void) { return 0; } } |
| set comp_flags {} |
| append comp_flags " additional_flags=-mmpx" |
| append comp_flags " additional_flags=-fcheck-pointer-bounds" |
| if {![gdb_simple_compile $me-2 $src executable $comp_flags]} { |
| return 0 |
| } |
| |
| set result [remote_exec target $obj] |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| set status [expr ($status == 0) \ |
| && ![regexp "^No MPX support\r?\n" $output]] |
| |
| remote_file build delete $obj |
| |
| verbose "$me: returning $status" 2 |
| return $status |
| } |
| |
| # Return 1 if target supports avx, otherwise return 0. |
| gdb_caching_proc have_avx { |
| global srcdir |
| |
| set me "have_avx" |
| if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } { |
| verbose "$me: target does not support avx, returning 0" 2 |
| return 0 |
| } |
| |
| # Compile a test program. |
| set src { |
| #include "nat/x86-cpuid.h" |
| |
| int main() { |
| unsigned int eax, ebx, ecx, edx; |
| |
| if (!x86_cpuid (1, &eax, &ebx, &ecx, &edx)) |
| return 0; |
| |
| if ((ecx & (bit_AVX | bit_OSXSAVE)) == (bit_AVX | bit_OSXSAVE)) |
| return 1; |
| else |
| return 0; |
| } |
| } |
| set compile_flags "incdir=${srcdir}/.." |
| if {![gdb_simple_compile $me $src executable $compile_flags]} { |
| return 0 |
| } |
| |
| set result [remote_exec target $obj] |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| if { $output != "" } { |
| set status 0 |
| } |
| |
| remote_file build delete $obj |
| |
| verbose "$me: returning $status" 2 |
| return $status |
| } |
| |
| # Called as |
| # - require ARG... |
| # |
| # ARG can either be a name, or of the form !NAME. |
| # |
| # Each name is a proc to evaluate in the caller's context. It returns |
| # a boolean, and a "!" means to invert the result. If this is |
| # nonzero, all is well. If it is zero, an "untested" is emitted and |
| # this proc causes the caller to return. |
| |
| proc require { args } { |
| foreach arg $args { |
| if {[string index $arg 0] == "!"} { |
| set ok 0 |
| set fn [string range $arg 1 end] |
| } else { |
| set ok 1 |
| set fn $arg |
| } |
| if {$ok != !![uplevel 1 $fn]} { |
| unsupported "require failed: $arg" |
| return -code return 0 |
| } |
| } |
| } |
| |
| # Wait up to ::TIMEOUT seconds for file PATH to exist on the target system. |
| # Return 1 if it does exist, 0 otherwise. |
| |
| proc target_file_exists_with_timeout { path } { |
| for {set i 0} {$i < $::timeout} {incr i} { |
| if { [remote_file target exists $path] } { |
| return 1 |
| } |
| |
| sleep 1 |
| } |
| |
| return 0 |
| } |
| |
| gdb_caching_proc has_hw_wp_support { |
| # Power 9, proc rev 2.2 does not support HW watchpoints due to HW bug. |
| # Need to use a runtime test to determine if the Power processor has |
| # support for HW watchpoints. |
| global srcdir subdir gdb_prompt inferior_exited_re |
| |
| set me "has_hw_wp_support" |
| |
| global gdb_spawn_id |
| if { [info exists gdb_spawn_id] } { |
| error "$me called with running gdb instance" |
| } |
| |
| set compile_flags {debug nowarnings quiet} |
| |
| # Compile a test program to test if HW watchpoints are supported |
| set src { |
| int main (void) { |
| volatile int local; |
| local = 1; |
| if (local == 1) |
| return 1; |
| return 0; |
| } |
| } |
| |
| if {![gdb_simple_compile $me $src executable $compile_flags]} { |
| return 0 |
| } |
| |
| gdb_start |
| gdb_reinitialize_dir $srcdir/$subdir |
| gdb_load "$obj" |
| |
| if ![runto_main] { |
| gdb_exit |
| remote_file build delete $obj |
| |
| set has_hw_wp_support 0 |
| return $has_hw_wp_support |
| } |
| |
| # The goal is to determine if HW watchpoints are available in general. |
| # Use "watch" and then check if gdb responds with hardware watch point. |
| set test "watch local" |
| |
| gdb_test_multiple $test "Check for HW watchpoint support" { |
| -re ".*Hardware watchpoint.*" { |
| # HW watchpoint supported by platform |
| verbose -log "\n$me: Hardware watchpoint detected" |
| set has_hw_wp_support 1 |
| } |
| -re ".*$gdb_prompt $" { |
| set has_hw_wp_support 0 |
| verbose -log "\n$me: Default, hardware watchpoint not deteced" |
| } |
| } |
| |
| gdb_exit |
| remote_file build delete $obj |
| |
| verbose "$me: returning $has_hw_wp_support" 2 |
| return $has_hw_wp_support |
| } |
| |
| # Return a list of all the accepted values of the set command |
| # "SET_CMD SET_ARG". |
| # For example get_set_option_choices "set architecture" "i386". |
| |
| proc get_set_option_choices { set_cmd {set_arg ""} } { |
| set values {} |
| |
| if { $set_arg == "" } { |
| # Add trailing space to signal that we need completion of the choices, |
| # not of set_cmd itself. |
| set cmd "complete $set_cmd " |
| } else { |
| set cmd "complete $set_cmd $set_arg" |
| } |
| |
| # Set test name without trailing space. |
| set test [string trim $cmd] |
| |
| with_set max-completions unlimited { |
| gdb_test_multiple $cmd $test { |
| -re "^[string_to_regexp $cmd]\r\n" { |
| exp_continue |
| } |
| |
| -re "^$set_cmd (\[^\r\n\]+)\r\n" { |
| lappend values $expect_out(1,string) |
| exp_continue |
| } |
| |
| -re "^$::gdb_prompt $" { |
| pass $gdb_test_name |
| } |
| } |
| } |
| |
| return $values |
| } |
| |
| # Return the compiler that can generate 32-bit ARM executables. Used |
| # when testing biarch support on Aarch64. If ARM_CC_FOR_TARGET is |
| # set, use that. If not, try a few common compiler names, making sure |
| # that the executable they produce can run. |
| |
| gdb_caching_proc arm_cc_for_target { |
| if {[info exists ::ARM_CC_FOR_TARGET]} { |
| # If the user specified the compiler explicitly, then don't |
| # check whether the resulting binary runs outside GDB. Assume |
| # that it does, and if it turns out it doesn't, then the user |
| # should get loud FAILs, instead of UNSUPPORTED. |
| return $::ARM_CC_FOR_TARGET |
| } |
| |
| # Fallback to a few common compiler names. Also confirm the |
| # produced binary actually runs on the system before declaring |
| # we've found the right compiler. |
| |
| if [istarget "*-linux*-*"] { |
| set compilers { |
| arm-linux-gnueabi-gcc |
| arm-none-linux-gnueabi-gcc |
| arm-linux-gnueabihf-gcc |
| } |
| } else { |
| set compilers {} |
| } |
| |
| foreach compiler $compilers { |
| if {![is_remote host] && [which $compiler] == 0} { |
| # Avoid "default_target_compile: Can't find |
| # $compiler." warning issued from gdb_compile. |
| continue |
| } |
| |
| set src { int main() { return 0; } } |
| if {[gdb_simple_compile aarch64-32bit \ |
| $src \ |
| executable [list compiler=$compiler]]} { |
| |
| set result [remote_exec target $obj] |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| |
| file delete $obj |
| |
| if { $output == "" && $status == 0} { |
| return $compiler |
| } |
| } |
| } |
| |
| return "" |
| } |
| |
| # Step until the pattern REGEXP is found. Step at most |
| # MAX_STEPS times, but stop stepping once REGEXP is found. |
| # |
| # If REGEXP is found then a single pass is emitted, otherwise, after |
| # MAX_STEPS steps, a single fail is emitted. |
| # |
| # TEST_NAME is the name used in the pass/fail calls. |
| |
| proc gdb_step_until { regexp {test_name ""} {max_steps 10} } { |
| if { $test_name == "" } { |
| set test_name "stepping until regexp" |
| } |
| |
| set count 0 |
| gdb_test_multiple "step" "$test_name" { |
| -re "$regexp\r\n$::gdb_prompt $" { |
| pass $test_name |
| } |
| -re ".*$::gdb_prompt $" { |
| if {$count < $max_steps} { |
| incr count |
| send_gdb "step\n" |
| exp_continue |
| } else { |
| fail $test_name |
| } |
| } |
| } |
| } |
| |
| # Check if the compiler emits epilogue information associated |
| # with the closing brace or with the last statement line. |
| # |
| # This proc restarts GDB |
| # |
| # Returns True if it is associated with the closing brace, |
| # False if it is the last statement |
| gdb_caching_proc have_epilogue_line_info { |
| |
| set main { |
| int |
| main () |
| { |
| return 0; |
| } |
| } |
| if {![gdb_simple_compile "simple_program" $main]} { |
| return False |
| } |
| |
| clean_restart $obj |
| |
| gdb_test_multiple "info line 6" "epilogue test" { |
| -re -wrap ".*starts at address.*and ends at.*" { |
| return True |
| } |
| -re -wrap ".*" { |
| return False |
| } |
| } |
| } |
| |
| # Decompress file BZ2, and return it. |
| |
| proc decompress_bz2 { bz2 } { |
| set copy [standard_output_file [file tail $bz2]] |
| set copy [remote_download build $bz2 $copy] |
| if { $copy == "" } { |
| return $copy |
| } |
| |
| set res [remote_exec build "bzip2" "-df $copy"] |
| if { [lindex $res 0] == -1 } { |
| return "" |
| } |
| |
| set copy [regsub {.bz2$} $copy ""] |
| if { ![remote_file build exists $copy] } { |
| return "" |
| } |
| |
| return $copy |
| } |
| |
| # Return 1 if the output of "ldd FILE" contains regexp DEP, 0 if it doesn't, |
| # and -1 if there was a problem running the command. |
| |
| proc has_dependency { file dep } { |
| set ldd [gdb_find_ldd] |
| set command "$ldd $file" |
| set result [remote_exec host $command] |
| set status [lindex $result 0] |
| set output [lindex $result 1] |
| verbose -log "status of $command is $status" |
| verbose -log "output of $command is $output" |
| if { $status != 0 || $output == "" } { |
| return -1 |
| } |
| return [regexp $dep $output] |
| } |
| |
| # Always load compatibility stuff. |
| load_lib future.exp |