xref: /linux/tools/perf/tests/shell/coresight/callchain.sh (revision 473f6c8f437b049f8ec015d57cd59bb983b1d85c)
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