xref: /linux/tools/perf/tests/shell/buildid.sh (revision c31f4aa8fed048fa70e742c4bb49bb48dc489ab3)
1#!/bin/bash
2# build id cache operations
3# SPDX-License-Identifier: GPL-2.0
4
5# skip if there's no readelf
6if ! [ -x "$(command -v readelf)" ]; then
7	echo "failed: no readelf, install binutils"
8	exit 2
9fi
10
11# skip if there's no compiler
12if ! [ -x "$(command -v cc)" ]; then
13	echo "failed: no compiler, install gcc"
14	exit 2
15fi
16
17# check what we need to test windows binaries
18add_pe=1
19run_pe=1
20if ! perf version --build-options | grep -q 'libbfd: .* on '; then
21	echo "WARNING: perf not built with libbfd. PE binaries will not be tested."
22	add_pe=0
23	run_pe=0
24fi
25if ! which wine > /dev/null; then
26	echo "WARNING: wine not found. PE binaries will not be run."
27	run_pe=0
28fi
29
30# set up wine
31if [ ${run_pe} -eq 1 ]; then
32	wineprefix=$(mktemp -d /tmp/perf.wineprefix.XXX)
33	export WINEPREFIX=${wineprefix}
34	# clear display variables to prevent wine from popping up dialogs
35	unset DISPLAY
36	unset WAYLAND_DISPLAY
37fi
38
39build_id_dir=
40ex_source=$(mktemp /tmp/perf_buildid_test.ex.XXX.c)
41ex_md5=$(mktemp /tmp/perf_buildid_test.ex.MD5.XXX)
42ex_sha1=$(mktemp /tmp/perf_buildid_test.ex.SHA1.XXX)
43ex_pe=$(dirname $0)/../pe-file.exe
44data=$(mktemp /tmp/perf_buildid_test.data.XXX)
45log_out=$(mktemp /tmp/perf_buildid_test.log.out.XXX)
46log_err=$(mktemp /tmp/perf_buildid_test.log.err.XXX)
47
48cleanup() {
49	rm -f ${ex_source} ${ex_md5} ${ex_sha1} ${data} ${log_out} ${log_err}
50	if [ ${run_pe} -eq 1 ]; then
51		rm -r ${wineprefix}
52	fi
53        if [ -d ${build_id_dir} ]; then
54		rm -rf ${build_id_dir}
55        fi
56	trap - EXIT TERM INT
57}
58
59trap_cleanup() {
60	echo "Unexpected signal in ${FUNCNAME[1]}"
61	cleanup
62	exit 1
63}
64trap trap_cleanup EXIT TERM INT
65
66# Test program based on the noploop workload.
67cat <<EOF > ${ex_source}
68#include <stdlib.h>
69#include <signal.h>
70#include <unistd.h>
71
72static volatile sig_atomic_t done;
73
74static void sighandler(int sig)
75{
76	(void)sig;
77	done = 1;
78}
79
80int main(int argc, const char **argv)
81{
82	int sec = 1;
83
84	if (argc > 1)
85		sec = atoi(argv[1]);
86
87	signal(SIGINT, sighandler);
88	signal(SIGALRM, sighandler);
89	alarm(sec);
90
91	while (!done)
92		continue;
93
94	return 0;
95}
96EOF
97cc -Wl,--build-id=sha1 ${ex_source} -o ${ex_sha1} -x c -
98cc -Wl,--build-id=md5 ${ex_source} -o ${ex_md5} -x c -
99echo "test binaries: ${ex_sha1} ${ex_md5} ${ex_pe}"
100
101get_build_id()
102{
103	case $1 in
104	*.exe)
105		# We don't have a tool that can pull a nicely formatted build-id out of
106		# a PE file, but we can extract the whole section with objcopy and
107		# format it ourselves. The .buildid section is a Debug Directory
108		# containing a CodeView entry:
109		#     https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#debug-directory-image-only
110		#     https://github.com/dotnet/runtime/blob/da94c022576a5c3bbc0e896f006565905eb137f9/docs/design/specs/PE-COFF.md
111		# The build-id starts at byte 33 and must be rearranged into a GUID.
112		id=`objcopy -O binary --only-section=.buildid $1 /dev/stdout | \
113			cut -c 33-48 | hexdump -ve '/1 "%02x"' | \
114			sed 's@^\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(..\)\(.*\)0a$@\4\3\2\1\6\5\8\7\9@'`
115		;;
116	*)
117		id=`readelf -n ${1} 2>/dev/null | grep 'Build ID' | awk '{print $3}'`
118		;;
119	esac
120	echo ${id}
121}
122
123check()
124{
125	file=$1
126	perf_data=$2
127
128	id=$(get_build_id $file)
129	echo "build id: ${id}"
130
131	id_file=${id#??}
132	id_dir=${id%$id_file}
133	link=$build_id_dir/.build-id/$id_dir/$id_file
134	echo "link: ${link}"
135
136	if [ ! -h $link ]; then
137		echo "failed: link ${link} does not exist"
138		exit 1
139	fi
140
141	cached_file=${build_id_dir}/.build-id/$id_dir/`readlink ${link}`/elf
142	echo "file: ${cached_file}"
143
144	# Check for file permission of original file
145	# in case of pe-file.exe file
146	echo $1 | grep ".exe"
147	if [ $? -eq 0 ]; then
148		if [ -x $1 ] && [ ! -x $cached_file ]; then
149			echo "failed: file ${cached_file} executable does not exist"
150			exit 1
151		fi
152
153		if [ ! -x $cached_file ] && [ ! -e $cached_file ]; then
154			echo "failed: file ${cached_file} does not exist"
155			exit 1
156		fi
157	elif [ ! -x $cached_file ]; then
158		echo "failed: file ${cached_file} does not exist"
159		exit 1
160	fi
161
162	diff ${cached_file} ${1}
163	if [ $? -ne 0 ]; then
164		echo "failed: ${cached_file} do not match"
165		exit 1
166	fi
167
168	${perf} buildid-cache -l | grep -q ${id}
169	if [ $? -ne 0 ]; then
170		echo "failed: ${id} is not reported by \"perf buildid-cache -l\""
171		exit 1
172	fi
173
174	if [ -n "${perf_data}" ]; then
175		${perf} buildid-list -i ${perf_data} | grep -q ${id}
176		if [ $? -ne 0 ]; then
177			echo "failed: ${id} is not reported by \"perf buildid-list -i ${perf_data}\""
178			exit 1
179		fi
180	fi
181
182	echo "OK for ${1}"
183}
184
185test_add()
186{
187	build_id_dir=$(mktemp -d /tmp/perf_buildid_test.debug.XXX)
188	perf="perf --buildid-dir ${build_id_dir}"
189
190	${perf} buildid-cache -v -a ${1}
191	if [ $? -ne 0 ]; then
192		echo "failed: add ${1} to build id cache"
193		exit 1
194	fi
195
196	check ${1}
197
198	rm -rf ${build_id_dir}
199}
200
201test_remove()
202{
203	build_id_dir=$(mktemp -d /tmp/perf_buildid_test.debug.XXX)
204	perf="perf --buildid-dir ${build_id_dir}"
205
206	${perf} buildid-cache -v -a ${1}
207	if [ $? -ne 0 ]; then
208		echo "failed: add ${1} to build id cache"
209		exit 1
210	fi
211
212	id=$(get_build_id ${1})
213	if ! ${perf} buildid-cache -l | grep -q ${id}; then
214		echo "failed: ${id} not in cache"
215		exit 1
216	fi
217
218	${perf} buildid-cache -v -r ${1}
219	if [ $? -ne 0 ]; then
220		echo "failed: remove ${id} from build id cache"
221		exit 1
222	fi
223
224	if ${perf} buildid-cache -l | grep -q ${id}; then
225		echo "failed: ${id} still in cache after remove"
226		exit 1
227	fi
228
229	echo "remove: OK"
230	rm -rf ${build_id_dir}
231}
232
233test_purge()
234{
235	build_id_dir=$(mktemp -d /tmp/perf_buildid_test.debug.XXX)
236	perf="perf --buildid-dir ${build_id_dir}"
237
238	id1=$(get_build_id ${ex_sha1})
239	${perf} buildid-cache -v -a ${ex_sha1}
240	if ! ${perf} buildid-cache -l | grep -q ${id1}; then
241		echo "failed: ${id1} not in cache"
242		exit 1
243	fi
244
245	id2=$(get_build_id ${ex_md5})
246	${perf} buildid-cache -v -a ${ex_md5}
247	if ! ${perf} buildid-cache -l | grep -q ${id2}; then
248		echo "failed: ${id2} not in cache"
249		exit 1
250	fi
251
252	# Purge by path
253	${perf} buildid-cache -v -p ${ex_sha1}
254	if [ $? -ne 0 ]; then
255		echo "failed: purge build id cache of ${ex_sha1}"
256		exit 1
257	fi
258
259	${perf} buildid-cache -v -p ${ex_md5}
260	if [ $? -ne 0 ]; then
261		echo "failed: purge build id cache of ${ex_md5}"
262		exit 1
263	fi
264
265	# Verify both are gone
266	if ${perf} buildid-cache -l | grep -q ${id1}; then
267		echo "failed: ${id1} still in cache after purge"
268		exit 1
269	fi
270
271	if ${perf} buildid-cache -l | grep -q ${id2}; then
272		echo "failed: ${id2} still in cache after purge"
273		exit 1
274	fi
275
276	echo "purge: OK"
277	rm -rf ${build_id_dir}
278}
279
280test_record()
281{
282	build_id_dir=$(mktemp -d /tmp/perf_buildid_test.debug.XXX)
283	perf="perf --buildid-dir ${build_id_dir}"
284
285	echo "running: perf record $*"
286	${perf} record --buildid-all -o ${data} "$@" 1>${log_out} 2>${log_err}
287	if [ $? -ne 0 ]; then
288		echo "failed: record $*"
289		echo "see log: ${log_err}"
290		exit 1
291	fi
292
293	args="$*"
294	check ${args##* } ${data}
295
296	rm -f ${log_out} ${log_err}
297	rm -rf ${build_id_dir}
298	rm -rf ${data}
299}
300
301# add binaries manual via perf buildid-cache -a
302test_add ${ex_sha1}
303test_add ${ex_md5}
304if [ ${add_pe} -eq 1 ]; then
305	test_add ${ex_pe}
306fi
307
308# add binaries via perf record post processing
309test_record ${ex_sha1}
310test_record ${ex_md5}
311if [ ${run_pe} -eq 1 ]; then
312	test_record wine ${ex_pe}
313fi
314
315# remove binaries manually via perf buildid-cache -r
316test_remove ${ex_sha1}
317test_remove ${ex_md5}
318if [ ${add_pe} -eq 1 ]; then
319	test_remove ${ex_pe}
320fi
321
322# purge binaries manually via perf buildid-cache -p
323test_purge
324
325cleanup
326exit 0
327