• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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