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