# Copyright 2014-2024 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Utility procedures, shared between test suite domains. # A helper procedure to retrieve commands to send to GDB before a program # is started. proc gdb_init_commands {} { set commands "" if [target_info exists gdb_init_command] { lappend commands [target_info gdb_init_command] } if [target_info exists gdb_init_commands] { set commands [concat $commands [target_info gdb_init_commands]] } return $commands } # Given an input string, adds backslashes as needed to create a # regexp that will match the string. proc string_to_regexp {str} { set result $str regsub -all {[]?*+.|(){}^$\[\\]} $str {\\&} result return $result } # Convenience function that calls string_to_regexp for each arg, and # joins the results using "\r\n". proc multi_line_string_to_regexp { args } { set res [lmap arg $args {string_to_regexp $arg}] return [multi_line {*}$res] } # Given a list of strings, adds backslashes as needed to each string to # create a regexp that will match the string, and join the result. proc string_list_to_regexp { args } { set result "" foreach arg $args { set arg [string_to_regexp $arg] append result $arg } return $result } # Wrap STR in an ANSI terminal escape sequences -- one to set the # style to STYLE, and one to reset the style to the default. The # return value is suitable for use as a regular expression. # STYLE can either be the payload part of an ANSI terminal sequence, # or a shorthand for one of the gdb standard styles: "file", # "function", "variable", "address", etc. proc style {str style} { set fg 39 set bg 49 set intensity 22 set reverse 27 switch -exact -- $style { title { set intensity 1 } command { set intensity 1 } file { set fg 32 } function { set fg 33 } highlight { set fg 31 } variable { set fg 36 } address { set fg 34 } metadata { set intensity 2 } version { set fg 35; set intensity 1 } line-number { set intensity 2 } none { return $str } } return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m" } # gdb_get_bp_addr num # # Purpose: # Get address of a particular breakpoint. # # Parameter: # The parameter "num" indicates the number of the breakpoint to get. # Note that *currently* this parameter must be an integer value. # E.g., -1 means that we're gonna get the first internal breakpoint; # 2 means to get the second user-defined breakpoint. # # Return: # First address for a particular breakpoint. # # TODO: # It would be nice if this procedure could accept floating point value. # E.g., 'gdb_get_bp_addr 1.2' means to get the address of the second # location of breakpoint #1. # proc gdb_get_bp_addr { num } { gdb_test_multiple "maint info break $num" "find address of specified bp $num" { -re -wrap ".*(0x\[0-9a-f\]+).*" { return $expect_out(1,string) } } return "" } # Compare the version numbers in L1 to those in L2 using OP, and # return 1 if the comparison is true. OP can be "<", "<=", ">", ">=", # or "==". It is ok if the lengths of the lists differ. proc version_compare { l1 op l2 } { switch -exact $op { "==" - "<=" - "<" {} ">=" { # a >= b => b <= a set x $l2 set l2 $l1 set l1 $x set op "<=" } ">" { # a > b => b < a set x $l2 set l2 $l1 set l1 $x set op "<" } default { error "unsupported op: $op" } } # Handle ops < and ==. foreach v1 $l1 v2 $l2 { if {$v1 == ""} { # This is: "1.2 OP 1.2.1". if {$op != "=="} { return 1 } return 0 } if {$v2 == ""} { # This is: "1.2.1 OP 1.2". return 0 } if {$v1 == $v2} { continue } return [expr $v1 $op $v2] } if {$op == "<"} { # They are equal. return 0 } return 1 } # Acquire lock file LOCKFILE. Tries forever until the lock file is # successfully created. proc lock_file_acquire {lockfile} { verbose -log "acquiring lock file: $::subdir/${::gdb_test_file_name}.exp" while {true} { if {![catch {open $lockfile {WRONLY CREAT EXCL}} rc]} { set msg "locked by $::subdir/${::gdb_test_file_name}.exp" verbose -log "lock file: $msg" # For debugging, put info in the lockfile about who owns # it. puts $rc $msg flush $rc return [list $rc $lockfile] } after 10 } } # Release a lock file. proc lock_file_release {info} { verbose -log "releasing lock file: $::subdir/${::gdb_test_file_name}.exp" if {![catch {fconfigure [lindex $info 0]}]} { if {![catch { close [lindex $info 0] file delete -force [lindex $info 1] } rc]} { return "" } else { return -code error "Error releasing lockfile: '$rc'" } } else { error "invalid lock" } } # Return directory where we keep lock files. proc lock_dir {} { if { [info exists ::GDB_LOCK_DIR] } { # When using check//. return $::GDB_LOCK_DIR } return [make_gdb_parallel_path cache] } # Run body under lock LOCK_FILE. proc with_lock { lock_file body } { if {[info exists ::GDB_PARALLEL]} { set lock_file [file join [lock_dir] $lock_file] set lock_rc [lock_file_acquire $lock_file] } set code [catch {uplevel 1 $body} result] if {[info exists ::GDB_PARALLEL]} { lock_file_release $lock_rc } if {$code == 1} { global errorInfo errorCode return -code $code -errorinfo $errorInfo -errorcode $errorCode $result } else { return -code $code $result } }