binutils-gdb/gdb/testsuite/lib/debuginfod-support.exp

197 lines
5.9 KiB
Plaintext
Raw Normal View History

# Copyright 2020-2022 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/>.
# Helper functions to make it easier to write debuginfod tests.
# Return true if the debuginfod tests should be skipped, otherwise, return
# false.
proc skip_debuginfod_tests {} {
if [is_remote host] {
return true
}
if { [which debuginfod] == 0 } {
return true
}
if { [which curl] == 0 } {
untested "cannot find curl"
return true
}
# Skip testing if gdb was not configured with debuginfod.
#
# If GDB is built with ASan, it warns that some signal handlers
# (installed by ASan) exist on startup. That makes TCL's exec throw an
# error. Disable that by passing --quiet.
if { [string first "with-debuginfod" \
[eval exec $::GDB --quiet $::INTERNAL_GDBFLAGS \
--configuration]] == -1 } {
return true
}
return false
}
# Create two directories within the current output directory. One directory
# will be used by GDB as the client cache to hold downloaded debug
# information, and the other directory will be used by the debuginfod server
# as its cache of the parsed debug files that will be served to GDB.
#
# Call this proc with the names to two variables, these variables will be
# set in the parent scope with the paths to the two directories.
#
# This proc allocates the names for the directories, but doesn't create
# them. In fact, if the directories already exist, this proc will delete
# them, this ensures that any existing contents are also deleted.
proc prepare_for_debuginfod { cache_var db_var } {
upvar $cache_var cache
upvar $db_var db
set cache [standard_output_file ".client_cache"]
set db [standard_output_file ".debuginfod.db"]
# Delete any preexisting test files.
file delete -force $cache
file delete -force $db
}
# Run BODY with the three environment variables required to control
# debuginfod set. The timeout is set based on the usual timeouts used by
# GDB within dejagnu (see get_largest_timeout), the debuginfod cache is set
# to CACHE (this is where downloaded debug data is placed), and the
# debuginfod urls environment variable is set to be the empty string.
#
# Within BODY you should start a debuginfod server and set the environment
# variable DEBUGINFOD_URLS as appropriate (see start_debuginfod for details).
#
# The reason that this proc doesn't automatically start debuginfod, is that
# in some test cases we want to initially test with debuginfod not running
# and/or disabled.
proc with_debuginfod_env { cache body } {
set envlist \
[list \
env(DEBUGINFOD_URLS) \
env(DEBUGINFOD_TIMEOUT) \
env(DEBUGINFOD_CACHE_PATH)]
save_vars $envlist {
setenv DEBUGINFOD_TIMEOUT [get_largest_timeout]
setenv DEBUGINFOD_CACHE_PATH $cache
setenv DEBUGINFOD_URLS ""
uplevel 1 $body
}
}
# Start a debuginfod server. DB is the directory to use for the server's
# database cache, while DEBUGDIR is a directory containing all the debug
# information that the server should server.
#
# This proc will try to find an available port to start the server on, will
# start the server, and check that the server has started correctly.
#
# If the server starts correctly, then this proc will return the url that
# should be used to communicate with the server. If the server can't be
# started, then an error will be printed, and an empty string returned.
#
# If the server is successfully started then the global variable
# debuginfod_spawn_id will be set with the spawn_id of the debuginfod
# process.
proc start_debuginfod { db debugdir } {
global debuginfod_spawn_id spawn_id
# Find an unused port.
set port 7999
set found false
while { ! $found } {
incr port
if { $port == 65536 } {
perror "no available ports"
return ""
}
if { [info exists spawn_id] } {
set old_spawn_id $spawn_id
}
spawn debuginfod -vvvv -d $db -p $port -F $debugdir
set debuginfod_spawn_id $spawn_id
if { [info exists old_spawn_id] } {
set spawn_id $old_spawn_id
unset old_spawn_id
}
expect {
-i $debuginfod_spawn_id
"started http server on IPv4 IPv6 port=$port" { set found true }
"started http server on IPv4 port=$port" { set found true }
"started http server on IPv6 port=$port" {}
"failed to bind to port" {}
timeout {
stop_debuginfod
perror "find port timeout"
return ""
}
}
if { ! $found } {
stop_debuginfod
}
}
set url "http://127.0.0.1:$port"
set metrics [list "ready 1" \
"thread_work_total{role=\"traverse\"} 1" \
"thread_work_pending{role=\"scan\"} 0" \
"thread_busy{role=\"scan\"} 0"]
# Check server metrics to confirm init has completed.
foreach m $metrics {
set timelim 20
while { $timelim != 0 } {
sleep 0.5
catch {exec curl -s $url/metrics} got
if { [regexp $m $got] } {
break
}
incr timelim -1
}
if { $timelim == 0 } {
stop_debuginfod
perror "server init timeout"
return ""
}
}
return $url
}
# If the global debuginfod_spawn_id exists, then kill that process and unset
# the debuginfod_spawn_id global. This can be used to shutdown the
# debuginfod server.
proc stop_debuginfod { } {
global debuginfod_spawn_id
if [info exists debuginfod_spawn_id] {
kill_wait_spawned_process $debuginfod_spawn_id
unset debuginfod_spawn_id
}
}