Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

# Copyright 2020 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 { [string first "with-debuginfod" \
	 [eval exec $GDB $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}] != "" } {
    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}
		}
	    }
	}
    }
}

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]"
}

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 }
	    "failed to bind to port" { kill_wait_spawned_process $spawn_id }
	    timeout {
		fail "find port timeout"
		return -1
	    }
	}
    }

    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 $binfile
    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 debugaltlink file
    clean_restart
    gdb_test "file ${binfile}_alt.o" \
	".*Reading symbols from ${binfile}_alt.o\.\.\.*" \
	"file [file tail ${binfile}_alt.o]"
}

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
}