1#!/bin/bash 2# 3# Copyright (C) 2019 Red Hat, Inc. 4# This file is part of elfutils. 5# 6# This file is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 3 of the License, or 9# (at your option) any later version. 10# 11# elfutils is distributed in the hope that it will be useful, but 12# WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <http://www.gnu.org/licenses/>. 18 19. $srcdir/test-subr.sh # includes set -e 20 21DB=${PWD}/.debuginfod_tmp.sqlite 22tempfiles $DB 23export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache 24 25PID1=0 26PID2=0 27 28cleanup() 29{ 30 if [ $PID1 -ne 0 ]; then kill $PID1; wait $PID1; fi 31 if [ $PID2 -ne 0 ]; then kill $PID2; wait $PID2; fi 32 33 rm -rf F R L ${PWD}/.client_cache* 34 exit_cleanup 35} 36 37# clean up trash if we were aborted early 38trap cleanup 0 1 2 3 5 9 15 39 40# find an unused port number 41while true; do 42 PORT1=`expr '(' $RANDOM % 1000 ')' + 9000` 43 ss -atn | fgrep ":$PORT1" || break 44done 45 46# We want to run debuginfod in the background. We also want to start 47# it with the same check/installcheck-sensitive LD_LIBRARY_PATH stuff 48# that the testrun alias sets. But: we if we just use 49# testrun .../debuginfod 50# it runs in a subshell, with different pid, so not helpful. 51# 52# So we gather the LD_LIBRARY_PATH with this cunning trick: 53ldpath=`testrun sh -c 'echo $LD_LIBRARY_PATH'` 54 55mkdir F R L 56# not tempfiles F R L - they are directories which we clean up manually 57ln -s ${abs_builddir}/dwfllines L/foo # any program not used elsewhere in this test 58 59wait_ready() 60{ 61 port=$1; 62 what=$2; 63 value=$3; 64 timeout=20; 65 66 echo "Wait $timeout seconds on $port for metric $what to change to $value" 67 while [ $timeout -gt 0 ]; do 68 mvalue="$(curl -s http://127.0.0.1:$port/metrics \ 69 | grep "$what" | awk '{print $NF}')" 70 if [ -z "$mvalue" ]; then mvalue=0; fi 71 echo "metric $what: $mvalue" 72 if [ "$mvalue" -eq "$value" ]; then 73 break; 74 fi 75 sleep 0.5; 76 ((timeout--)); 77 done; 78 79 if [ $timeout -eq 0 ]; then 80 echo "metric $what never changed to $value on port $port" 81 exit 1; 82 fi 83} 84 85env LD_LIBRARY_PATH=$ldpath DEBUGINFOD_URLS= ${abs_builddir}/../debuginfod/debuginfod -F -R -d $DB -p $PORT1 -t0 -g0 R F L & 86PID1=$! 87# Server must become ready 88wait_ready $PORT1 'ready' 1 89export DEBUGINFOD_URLS=http://127.0.0.1:$PORT1/ # or without trailing / 90 91# Be patient when run on a busy machine things might take a bit. 92export DEBUGINFOD_TIMEOUT=10 93 94# We use -t0 and -g0 here to turn off time-based scanning & grooming. 95# For testing purposes, we just sic SIGUSR1 / SIGUSR2 at the process. 96 97######################################################################## 98 99# Compile a simple program, strip its debuginfo and save the build-id. 100# Also move the debuginfo into another directory so that elfutils 101# cannot find it without debuginfod. 102echo "int main() { return 0; }" > ${PWD}/prog.c 103tempfiles prog.c 104gcc -g -o prog ${PWD}/prog.c 105 ${abs_top_builddir}/src/strip -g -f prog.debug ${PWD}/prog 106BUILDID=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ 107 -a prog | grep 'Build ID' | cut -d ' ' -f 7` 108 109mv prog F 110mv prog.debug F 111kill -USR1 $PID1 112# Wait till both files are in the index. 113wait_ready $PORT1 'thread_work_total{file="F"}' 2 114 115######################################################################## 116 117# Test whether elfutils, via the debuginfod client library dlopen hooks, 118# is able to fetch debuginfo from the local debuginfod. 119testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog 1 120 121######################################################################## 122 123# Test whether debuginfod-find is able to fetch those files. 124rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests 125filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID` 126cmp $filename F/prog.debug 127 128filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID` 129cmp $filename F/prog 130 131filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $BUILDID ${PWD}/prog.c` 132cmp $filename ${PWD}/prog.c 133 134######################################################################## 135 136# Add artifacts to the search paths and test whether debuginfod finds them while already running. 137 138# Build another, non-stripped binary 139echo "int main() { return 0; }" > ${PWD}/prog2.c 140tempfiles prog2.c 141gcc -g -o prog2 ${PWD}/prog2.c 142BUILDID2=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ 143 -a prog2 | grep 'Build ID' | cut -d ' ' -f 7` 144 145mv prog2 F 146kill -USR1 $PID1 147# Now there should be 3 files in the index 148wait_ready $PORT1 'thread_work_total{file="F"}' 3 149 150# Rerun same tests for the prog2 binary 151filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find -v debuginfo $BUILDID2 2>vlog` 152cmp $filename F/prog2 153cat vlog 154grep -q Progress vlog 155tempfiles vlog 156filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID2` 157cmp $filename F/prog2 158filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $BUILDID2 ${PWD}/prog2.c` 159cmp $filename ${PWD}/prog2.c 160 161cp -rp ${abs_srcdir}/debuginfod-rpms R 162kill -USR1 $PID1 163# All rpms need to be in the index 164rpms=$(find R -name \*rpm | wc -l) 165wait_ready $PORT1 'scanned_total{source="rpm"}' $rpms 166 167kill -USR1 $PID1 # two hits of SIGUSR1 may be needed to resolve .debug->dwz->srefs 168# Expect all source files found in the rpms (they are all called hello.c :) 169# We will need to extract all rpms (in their own directory) and could all 170# sources referenced in the .debug files. 171mkdir extracted 172cd extracted 173subdir=0; 174newrpms=$(find ../R -name \*\.rpm) 175for i in $newrpms; do 176 subdir=$[$subdir+1]; 177 mkdir $subdir; 178 cd $subdir; 179 ls -lah ../$i 180 rpm2cpio ../$i | cpio -id; 181 cd ..; 182done 183sourcefiles=$(find -name \*\\.debug \ 184 | env LD_LIBRARY_PATH=$ldpath xargs \ 185 ${abs_top_builddir}/src/readelf --debug-dump=decodedline \ 186 | grep mtime: | wc --lines) 187cd .. 188rm -rf extracted 189 190wait_ready $PORT1 'found_sourcerefs_total{source="rpm"}' $sourcefiles 191 192# Run a bank of queries against the debuginfod-rpms test cases 193 194rpm_test() { 195 __BUILDID=$1 196 __SOURCEPATH=$2 197 __SOURCESHA1=$3 198 199 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $__BUILDID` 200 buildid=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ 201 -a $filename | grep 'Build ID' | cut -d ' ' -f 7` 202 test $__BUILDID = $buildid 203 204 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $__BUILDID` 205 buildid=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ 206 -a $filename | grep 'Build ID' | cut -d ' ' -f 7` 207 test $__BUILDID = $buildid 208 209 filename=`testrun ${abs_top_builddir}/debuginfod/debuginfod-find source $__BUILDID $__SOURCEPATH` 210 hash=`cat $filename | sha1sum | awk '{print $1}'` 211 test $__SOURCESHA1 = $hash 212} 213 214 215# common source file sha1 216SHA=f4a1a8062be998ae93b8f1cd744a398c6de6dbb1 217# fedora30 218rpm_test c36708a78618d597dee15d0dc989f093ca5f9120 /usr/src/debug/hello2-1.0-2.x86_64/hello.c $SHA 219rpm_test 41a236eb667c362a1c4196018cc4581e09722b1b /usr/src/debug/hello2-1.0-2.x86_64/hello.c $SHA 220# rhel7 221rpm_test bc1febfd03ca05e030f0d205f7659db29f8a4b30 /usr/src/debug/hello-1.0/hello.c $SHA 222rpm_test f0aa15b8aba4f3c28cac3c2a73801fefa644a9f2 /usr/src/debug/hello-1.0/hello.c $SHA 223# rhel6 224rpm_test bbbf92ebee5228310e398609c23c2d7d53f6e2f9 /usr/src/debug/hello-1.0/hello.c $SHA 225rpm_test d44d42cbd7d915bc938c81333a21e355a6022fb7 /usr/src/debug/hello-1.0/hello.c $SHA 226 227RPM_BUILDID=d44d42cbd7d915bc938c81333a21e355a6022fb7 # in rhel6/ subdir, for a later test 228 229 230######################################################################## 231 232# Drop some of the artifacts, run a groom cycle; confirm that 233# debuginfod has forgotten them, but remembers others 234 235rm -r R/debuginfod-rpms/rhel6/* 236kill -USR2 $PID1 # groom cycle 237# Expect 3 rpms to be deleted by the groom 238wait_ready $PORT1 'groom{statistic="file d/e"}' 3 239 240rm -rf $DEBUGINFOD_CACHE_PATH # clean it from previous tests 241 242testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $RPM_BUILDID && false || true 243 244testrun ${abs_top_builddir}/debuginfod/debuginfod-find executable $BUILDID2 245 246######################################################################## 247 248# Federation mode 249 250# find another unused port 251while true; do 252 PORT2=`expr '(' $RANDOM % 1000 ')' + 9000` 253 ss -atn | fgrep ":$PORT2" || break 254done 255 256export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache2 257mkdir -p $DEBUGINFOD_CACHE_PATH 258# NB: inherits the DEBUGINFOD_URLS to the first server 259# NB: run in -L symlink-following mode for the L subdir 260env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../debuginfod/debuginfod -F -d ${DB}_2 -p $PORT2 -L L & 261PID2=$! 262tempfiles ${DB}_2 263wait_ready $PORT2 'ready' 1 264 265# have clients contact the new server 266export DEBUGINFOD_URLS=http://127.0.0.1:$PORT2 267rm -rf $DEBUGINFOD_CACHE_PATH 268testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID 269 270# confirm that first server can't resolve symlinked info in L/ but second can 271BUILDID=`env LD_LIBRARY_PATH=$ldpath ${abs_builddir}/../src/readelf \ 272 -a L/foo | grep 'Build ID' | cut -d ' ' -f 7` 273file L/foo 274file -L L/foo 275export DEBUGINFOD_URLS=http://127.0.0.1:$PORT1 276rm -rf $DEBUGINFOD_CACHE_PATH 277testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID && false || true 278export DEBUGINFOD_URLS=http://127.0.0.1:$PORT2 279testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID 280 281 282# test parallel queries in client 283export DEBUGINFOD_CACHE_PATH=${PWD}/.client_cache3 284mkdir -p $DEBUGINFOD_CACHE_PATH 285export DEBUGINFOD_URLS="BAD http://127.0.0.1:$PORT1 127.0.0.1:$PORT1 http://127.0.0.1:$PORT2 DNE" 286 287testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog2 1 288 289######################################################################## 290 291# Fetch some metrics, if curl program is installed 292if type curl 2>/dev/null; then 293 curl http://127.0.0.1:$PORT1/badapi 294 curl http://127.0.0.1:$PORT1/metrics 295 curl http://127.0.0.1:$PORT2/metrics 296 curl http://127.0.0.1:$PORT1/metrics | grep -q 'http_responses_total.*result.*error' 297 curl http://127.0.0.1:$PORT2/metrics | grep -q 'http_responses_total.*result.*upstream' 298fi 299 300######################################################################## 301 302# Run the tests again without the servers running. The target file should 303# be found in the cache. 304 305kill -INT $PID1 $PID2 306wait $PID1 $PID2 307PID1=0 308PID2=0 309tempfiles .debuginfod_* 310 311testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog2 1 312 313######################################################################## 314 315# Trigger a cache clean and run the tests again. The clients should be unable to 316# find the target. 317echo 0 > $DEBUGINFOD_CACHE_PATH/cache_clean_interval_s 318echo 0 > $DEBUGINFOD_CACHE_PATH/max_unused_age_s 319 320testrun ${abs_builddir}/debuginfod_build_id_find -e F/prog 1 321 322testrun ${abs_top_builddir}/debuginfod/debuginfod-find debuginfo $BUILDID2 && false || true 323 324exit 0 325