blob: 74d026464b92ae0cd1c483bd0be2c948254b1567 [file] [log] [blame]
# 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/>.
# Test debuginfod functionality
standard_testfile main.c
load_lib dwarf.exp
if { [which debuginfod] == 0 } {
untested "cannot find debuginfod"
return -1
}
if { [which curl] == 0 } {
untested "cannot find curl"
return -1
}
# 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 } {
untested "gdb not configured with debuginfod"
return -1
}
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
set sourcetmp [standard_output_file tmp-${srcfile}]
set outputdir [standard_output_file {}]
# Make a copy source file that we can move around
if { [catch {file copy -force ${srcdir}/${subdir}/${srcfile} \
[standard_output_file ${sourcetmp}]}] != 0 } {
error "create temporary file"
return -1
}
if { [gdb_compile "$sourcetmp" "$binfile" executable {debug}] != "" } {
untested "failed to compile"
return -1
}
if { [gdb_compile "$sourcetmp" "${binfile}2" executable {debug}] != "" } {
fail "compile"
return -1
}
# Write some assembly that just has a .gnu_debugaltlink section.
# Copied from testsuite/gdb.dwarf2/dwzbuildid.exp.
proc write_just_debugaltlink {filename dwzname buildid} {
set asm_file [standard_output_file $filename]
Dwarf::assemble $asm_file {
upvar dwzname dwzname
upvar buildid buildid
gnu_debugaltlink $dwzname $buildid
# Only the DWARF reader checks .gnu_debugaltlink, so make sure
# there is a bit of DWARF in here.
cu {} {
compile_unit {{language @DW_LANG_C}} {
}
}
}
}
# Write some DWARF that also sets the buildid.
# Copied from testsuite/gdb.dwarf2/dwzbuildid.exp.
proc write_dwarf_file {filename buildid {value 99}} {
set asm_file [standard_output_file $filename]
Dwarf::assemble $asm_file {
declare_labels int_label int_label2
upvar buildid buildid
upvar value value
build_id $buildid
cu {} {
compile_unit {{language @DW_LANG_C}} {
int_label2: base_type {
{name int}
{byte_size 4 sdata}
{encoding @DW_ATE_signed}
}
constant {
{name the_int}
{type :$int_label2}
{const_value $value data1}
}
}
}
}
}
set corefile [standard_output_file "corefile"]
proc no_url { } {
global binfile outputdir debugdir
setenv DEBUGINFOD_URLS ""
# Test that gdb cannot find source without debuginfod
clean_restart $binfile
gdb_test_no_output "set substitute-path $outputdir /dev/null" \
"set substitute-path"
gdb_test "list" ".*No such file or directory.*"
# Strip symbols into separate file and move it so gdb cannot find it \
without debuginfod
if { [gdb_gnu_strip_debug $binfile ""] != 0 } {
fail "strip debuginfo"
return -1
}
set debugdir [standard_output_file "debug"]
set debuginfo [standard_output_file "fetch_src_and_symbols.debug"]
file mkdir $debugdir
file rename -force $debuginfo $debugdir
# Test that gdb cannot find symbols without debuginfod
clean_restart $binfile
gdb_test "file" ".*No symbol file.*"
set buildid "01234567890abcdef0123456"
write_just_debugaltlink ${binfile}_has_altlink.S ${binfile}_dwz.o \
$buildid
write_dwarf_file ${binfile}_dwz.S $buildid
if {[gdb_compile ${binfile}_has_altlink.S ${binfile}_alt.o object \
nodebug] != ""} {
fail "compile main with altlink"
return -1
}
if {[gdb_compile ${binfile}_dwz.S ${binfile}_dwz.o object \
nodebug] != ""} {
fail "compile altlink"
return -1
}
file rename -force ${binfile}_dwz.o $debugdir
# Test that gdb cannot find dwz without debuginfod.
clean_restart
gdb_test "file ${binfile}_alt.o" \
".*could not find '.gnu_debugaltlink'.*" \
"file [file tail ${binfile}_alt.o]"
# Generate a core file and test that gdb cannot find the executable
clean_restart ${binfile}2
gdb_test "start" "Temporary breakpoint.*"
gdb_test "generate-core-file $::corefile" "Saved corefile $::corefile" \
"file [file tail $::corefile] gen"
file rename -force ${binfile}2 $debugdir
clean_restart
gdb_test "core $::corefile" ".*in ?? ().*" "file [file tail $::corefile]"
}
# Test that GDB prints the debuginfod URLs when loading files. URLS
# is the string set in the DEBUGINFOD_URLS environment variable.
# PATTERN_RE is the URLs pattern we expect to see out of GDB. TEST is
# the test name.
proc test_urls {urls pattern_re test} {
setenv DEBUGINFOD_URLS $urls
clean_restart
if {$pattern_re != ""} {
set urls_re " +${pattern_re}\r\n"
} else {
set urls_re ""
}
# Use "with confirm off" to avoid having to deal with the
# "Enable debuginfod for this session? (y or [n])" question.
gdb_test "with confirm off -- file $::binfile" \
"following URLs:\r\n${urls_re}Debuginfod .*" \
$test
}
proc local_url { } {
global binfile outputdir db debugdir
# Find an unused port
set port 7999
set found 0
while { ! $found } {
incr port
if { $port == 65536 } {
fail "no available ports"
return -1
}
spawn debuginfod -vvvv -d $db -p $port -F $debugdir
expect {
"started http server on IPv4 IPv6 port=$port" { set found 1 }
"started http server on IPv4 port=$port" { set found 1 }
"started http server on IPv6 port=$port" {}
"failed to bind to port" {}
timeout {
fail "find port timeout"
return -1
}
}
if { ! $found } {
kill_wait_spawned_process $spawn_id
}
}
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 http://127.0.0.1:$port/metrics} got
if { [regexp $m $got] } {
break
}
incr timelim -1
}
if { $timelim == 0 } {
fail "server init timeout"
return -1
}
}
# Point the client to the server
setenv DEBUGINFOD_URLS http://127.0.0.1:$port
# gdb should now find the symbol and source files
clean_restart
gdb_test "file $binfile" "" "file [file tail $binfile]" "Enable debuginfod?.*" "y"
gdb_test_no_output "set substitute-path $outputdir /dev/null" \
"set substitute-path"
gdb_test "br main" "Breakpoint 1 at.*file.*"
gdb_test "l" ".*This program is distributed in the hope.*"
# gdb should now find the executable file
clean_restart
gdb_test "core $::corefile" ".*return 0.*" "file [file tail $::corefile]" \
"Enable debuginfod?.*" "y"
# gdb should now find the debugaltlink file
clean_restart
gdb_test "file ${binfile}_alt.o" \
".*Downloading.*separate debug info.*" \
"file [file tail ${binfile}_alt.o]" \
".*Enable debuginfod?.*" "y"
# Configure debuginfod with commands
unsetenv DEBUGINFOD_URLS
clean_restart
gdb_test "file $binfile" ".*No debugging symbols.*" \
"file [file tail $binfile] cmd"
gdb_test_no_output "set debuginfod enabled off"
gdb_test_no_output "set debuginfod urls http://127.0.0.1:$port"
# gdb shouldn't find the debuginfo since debuginfod has been disabled
gdb_test "file $binfile" ".*No debugging symbols.*" \
"file [file tail $binfile] cmd off"
# Enable debuginfod and fetch the debuginfo
gdb_test_no_output "set debuginfod enabled on"
gdb_test "file $binfile" ".*Reading symbols from.*debuginfo.*" \
"file [file tail $binfile] cmd on"
# Test that URLs are printed correctly for the first-use notice.
# Empty URLS disables Debuginfod.
setenv DEBUGINFOD_URLS ""
clean_restart
# Disable confirmation to avoid having to deal with a query. See
# test_urls.
set file_cmd "with confirm off -- file $binfile"
gdb_test_multiple $file_cmd "notice empty URL" {
-re -wrap "This GDB supports auto-downloading.*" {
fail $gdb_test_name
}
-re -wrap "" {
pass $gdb_test_name
}
}
# Whitespace-only URLS disables Debuginfod.
setenv DEBUGINFOD_URLS " "
clean_restart
gdb_test_multiple $file_cmd "notice whitespace URL" {
-re -wrap "This GDB supports auto-downloading.*" {
fail $gdb_test_name
}
-re -wrap "" {
pass $gdb_test_name
}
}
set url "http://127.0.0.1:$port"
test_urls $url \
"<$url>" \
"notice 1 URL"
test_urls " $url " \
"<$url>" \
"notice 1 URL with whitespace"
set url2 "127.0.0.1:$port"
test_urls "$url $url2" \
"<$url>\r\n +<$url2>" \
"notice 2 URLs"
test_urls " $url $url2 " \
"<$url>\r\n +<$url2>" \
"notice 2 URLs with whitespace"
}
set envlist \
[list \
env(DEBUGINFOD_URLS) \
env(DEBUGINFOD_TIMEOUT) \
env(DEBUGINFOD_CACHE_PATH)]
save_vars $envlist {
setenv DEBUGINFOD_TIMEOUT 30
setenv DEBUGINFOD_CACHE_PATH $cache
with_test_prefix no_url no_url
with_test_prefix local_url local_url
}