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