1#!/bin/bash 2# CoreSight synthesized callchain (exclusive) 3# SPDX-License-Identifier: GPL-2.0 4 5glb_err=1 6 7if ! tmpdir=$(mktemp -d /tmp/perf-cs-callchain-test.XXXXXX); then 8 echo "mktemp failed" 9 exit 1 10fi 11 12cleanup_files() 13{ 14 rm -rf "$tmpdir" 15} 16 17trap cleanup_files EXIT 18trap 'cleanup_files; exit $glb_err' TERM INT 19 20skip_if_system_is_not_ready() 21{ 22 perf list | grep -Pzq 'cs_etm//' || { 23 echo "[Skip] cs_etm event is not available" >&2 24 return 2 25 } 26 27 # Requires root for trace in kernel 28 [ "$(id -u)" = 0 ] || { 29 echo "[Skip] No root permission" >&2 30 return 2 31 } 32 33 return 0 34} 35 36record_trace() 37{ 38 local data=$1 39 local script=$2 40 41 local cf="$tmpdir/ctl" 42 local af="$tmpdir/ack" 43 44 mkfifo "$cf" "$af" 45 46 perf record -o "$data" -e cs_etm// --per-thread -D -1 --control fifo:"$cf","$af" -- \ 47 perf test --record-ctl fifo:"$cf","$af" -w callchain >/dev/null 2>&1 && 48 49 # It is safe to use 'i3i' with a three-instruction interval, since the 50 # workload is compiled with -O0. 51 perf script --itrace=g16i3il64 -i "$data" > "$script" 52} 53 54callchain_regex_1() 55{ 56 printf '%s' \ 57'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\ 58'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 59'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 60'([[:space:]]+[[:xdigit:]]+ .*\n)*' 61} 62 63callchain_regex_2() 64{ 65 printf '%s' \ 66'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\ 67'[[:space:]]+[[:xdigit:]]+ callchain_do_syscall\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 68'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 69'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 70'([[:space:]]+[[:xdigit:]]+ .*\n)*' 71} 72 73callchain_regex_3() 74{ 75 printf '%s' \ 76'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\ 77'[[:space:]]+[[:xdigit:]]+ syscall(@plt)?\+0x[[:xdigit:]]+ \(.*\)\n'\ 78'[[:space:]]+[[:xdigit:]]+ callchain_do_syscall\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 79'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 80'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 81'([[:space:]]+[[:xdigit:]]+ .*\n)*' 82} 83 84callchain_regex_4() 85{ 86 printf '%s' \ 87'perf[[:space:]]+[0-9]+[[:space:]]+\[[0-9]+\][[:space:]]+([0-9.]+:[[:space:]]+)?[0-9]+ instructions:[[:space:]]*\n'\ 88'[[:space:]]+[[:xdigit:]]+ .*\+0x[[:xdigit:]]+ \(\[kernel\.kallsyms\]\)\n'\ 89'[[:space:]]+[[:xdigit:]]+ syscall(@plt)?\+0x[[:xdigit:]]+ \(.*\)\n'\ 90'[[:space:]]+[[:xdigit:]]+ callchain_do_syscall\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 91'[[:space:]]+[[:xdigit:]]+ callchain_foo\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 92'[[:space:]]+[[:xdigit:]]+ callchain\+0x[[:xdigit:]]+ \(.*/perf\)\n'\ 93'([[:space:]]+[[:xdigit:]]+ .*\n)*' 94} 95 96find_after_line() 97{ 98 local regex="$1" 99 local file="$2" 100 local start="$3" 101 local offset 102 local line 103 104 # Search in byte offset 105 offset=$( 106 tail -n +"$start" "$file" | 107 grep -Pzob -m1 "$regex" | 108 tr '\0' '\n' | 109 sed -n 's/^\([0-9][0-9]*\):.*/\1/p;q' 110 ) 111 112 if [ -z "$offset" ]; then 113 echo "Failed to match regex after line $start" >&2 114 echo "Regex:" >&2 115 printf '%s\n' "$regex" >&2 116 echo "Context from line $start:" >&2 117 sed -n "${start},$((start + 100))p" "$file" >&2 118 return 1 119 fi 120 121 # Convert from offset to line 122 line=$( 123 tail -n +"$start" "$file" | 124 head -c "$offset" | 125 wc -l 126 ) 127 128 echo "$((start + line))" 129} 130 131check_callchain_flow() 132{ 133 local file="$1" 134 local l1 l2 l3 l4 l5 l6 l7 135 136 # Callchain push 137 l1=$(find_after_line "$(callchain_regex_1)" "$file" 1) || return 1 138 l2=$(find_after_line "$(callchain_regex_2)" "$file" "$((l1 + 1))") || return 1 139 l3=$(find_after_line "$(callchain_regex_3)" "$file" "$((l2 + 1))") || return 1 140 l4=$(find_after_line "$(callchain_regex_4)" "$file" "$((l3 + 1))") || return 1 141 142 # Callchain pop 143 l5=$(find_after_line "$(callchain_regex_3)" "$file" "$((l4 + 1))") || return 1 144 l6=$(find_after_line "$(callchain_regex_2)" "$file" "$((l5 + 1))") || return 1 145 l7=$(find_after_line "$(callchain_regex_1)" "$file" "$((l6 + 1))") || return 1 146 147 echo "Callchain flow matched:" 148 echo " l1=$l1 l2=$l2 l3=$l3 l4=$l4 l5=$l5 l6=$l6 l7=$l7" 149 150 return 0 151} 152 153run_test() 154{ 155 local data=$tmpdir/perf.data 156 local script=$tmpdir/perf.script 157 158 if ! record_trace "$data" "$script"; then 159 echo "perf record/script failed" 160 return 161 fi 162 163 check_callchain_flow "$script" || return 164 165 glb_err=0 166} 167 168skip_if_system_is_not_ready || exit 2 169 170run_test 171 172exit $glb_err 173