1#!/bin/sh
2# Check Arm CoreSight trace data recording and synthesized samples
3
4# Uses the 'perf record' to record trace data with Arm CoreSight sinks;
5# then verify if there have any branch samples and instruction samples
6# are generated by CoreSight with 'perf script' and 'perf report'
7# commands.
8
9# SPDX-License-Identifier: GPL-2.0
10# Leo Yan <leo.yan@linaro.org>, 2020
11
12glb_err=0
13
14skip_if_no_cs_etm_event() {
15	perf list | grep -q 'cs_etm//' && return 0
16
17	# cs_etm event doesn't exist
18	return 2
19}
20
21skip_if_no_cs_etm_event || exit 2
22
23perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX)
24file=$(mktemp /tmp/temporary_file.XXXXX)
25
26cleanup_files()
27{
28	rm -f ${perfdata}
29	rm -f ${file}
30	rm -f "${perfdata}.old"
31	trap - exit term int
32	kill -2 $$
33	exit $glb_err
34}
35
36trap cleanup_files exit term int
37
38record_touch_file() {
39	echo "Recording trace (only user mode) with path: CPU$2 => $1"
40	rm -f $file
41	perf record -o ${perfdata} -e cs_etm/@$1/u --per-thread \
42		-- taskset -c $2 touch $file > /dev/null 2>&1
43}
44
45perf_script_branch_samples() {
46	echo "Looking at perf.data file for dumping branch samples:"
47
48	# Below is an example of the branch samples dumping:
49	#   touch  6512          1         branches:u:      ffffb220824c strcmp+0xc (/lib/aarch64-linux-gnu/ld-2.27.so)
50	#   touch  6512          1         branches:u:      ffffb22082e0 strcmp+0xa0 (/lib/aarch64-linux-gnu/ld-2.27.so)
51	#   touch  6512          1         branches:u:      ffffb2208320 strcmp+0xe0 (/lib/aarch64-linux-gnu/ld-2.27.so)
52	perf script -F,-time -i ${perfdata} 2>&1 | \
53		egrep " +$1 +[0-9]+ .* +branches:(.*:)? +" > /dev/null 2>&1
54}
55
56perf_report_branch_samples() {
57	echo "Looking at perf.data file for reporting branch samples:"
58
59	# Below is an example of the branch samples reporting:
60	#   73.04%    73.04%  touch    libc-2.27.so      [.] _dl_addr
61	#    7.71%     7.71%  touch    libc-2.27.so      [.] getenv
62	#    2.59%     2.59%  touch    ld-2.27.so        [.] strcmp
63	perf report --stdio -i ${perfdata} 2>&1 | \
64		egrep " +[0-9]+\.[0-9]+% +[0-9]+\.[0-9]+% +$1 " > /dev/null 2>&1
65}
66
67perf_report_instruction_samples() {
68	echo "Looking at perf.data file for instruction samples:"
69
70	# Below is an example of the instruction samples reporting:
71	#   68.12%  touch    libc-2.27.so   [.] _dl_addr
72	#    5.80%  touch    libc-2.27.so   [.] getenv
73	#    4.35%  touch    ld-2.27.so     [.] _dl_fixup
74	perf report --itrace=i1000i --stdio -i ${perfdata} 2>&1 | \
75		egrep " +[0-9]+\.[0-9]+% +$1" > /dev/null 2>&1
76}
77
78arm_cs_report() {
79	if [ $2 != 0 ]; then
80		echo "$1: FAIL"
81		glb_err=$2
82	else
83		echo "$1: PASS"
84	fi
85}
86
87is_device_sink() {
88	# If the node of "enable_sink" is existed under the device path, this
89	# means the device is a sink device.  Need to exclude 'tpiu' since it
90	# cannot support perf PMU.
91	echo "$1" | egrep -q -v "tpiu"
92
93	if [ $? -eq 0 -a -e "$1/enable_sink" ]; then
94
95		pmu_dev="/sys/bus/event_source/devices/cs_etm/sinks/$2"
96
97		# Warn if the device is not supported by PMU
98		if ! [ -f $pmu_dev ]; then
99			echo "PMU doesn't support $pmu_dev"
100		fi
101
102		return 0
103	fi
104
105	# Otherwise, it's not a sink device
106	return 1
107}
108
109arm_cs_iterate_devices() {
110	for dev in $1/connections/out\:*; do
111
112		# Skip testing if it's not a directory
113		! [ -d $dev ] && continue;
114
115		# Read out its symbol link file name
116		path=`readlink -f $dev`
117
118		# Extract device name from path, e.g.
119		#   path = '/sys/devices/platform/20010000.etf/tmc_etf0'
120		#     `> device_name = 'tmc_etf0'
121		device_name=$(basename $path)
122
123		if is_device_sink $path $device_name; then
124
125			record_touch_file $device_name $2 &&
126			perf_script_branch_samples touch &&
127			perf_report_branch_samples touch &&
128			perf_report_instruction_samples touch
129
130			err=$?
131			arm_cs_report "CoreSight path testing (CPU$2 -> $device_name)" $err
132		fi
133
134		arm_cs_iterate_devices $dev $2
135	done
136}
137
138arm_cs_etm_traverse_path_test() {
139	# Iterate for every ETM device
140	for dev in /sys/bus/coresight/devices/etm*; do
141
142		# Find the ETM device belonging to which CPU
143		cpu=`cat $dev/cpu`
144
145		# Use depth-first search (DFS) to iterate outputs
146		arm_cs_iterate_devices $dev $cpu
147	done
148}
149
150arm_cs_etm_system_wide_test() {
151	echo "Recording trace with system wide mode"
152	perf record -o ${perfdata} -e cs_etm// -a -- ls > /dev/null 2>&1
153
154	perf_script_branch_samples perf &&
155	perf_report_branch_samples perf &&
156	perf_report_instruction_samples perf
157
158	err=$?
159	arm_cs_report "CoreSight system wide testing" $err
160}
161
162arm_cs_etm_snapshot_test() {
163	echo "Recording trace with snapshot mode"
164	perf record -o ${perfdata} -e cs_etm// -S \
165		-- dd if=/dev/zero of=/dev/null > /dev/null 2>&1 &
166	PERFPID=$!
167
168	# Wait for perf program
169	sleep 1
170
171	# Send signal to snapshot trace data
172	kill -USR2 $PERFPID
173
174	# Stop perf program
175	kill $PERFPID
176	wait $PERFPID
177
178	perf_script_branch_samples dd &&
179	perf_report_branch_samples dd &&
180	perf_report_instruction_samples dd
181
182	err=$?
183	arm_cs_report "CoreSight snapshot testing" $err
184}
185
186arm_cs_etm_traverse_path_test
187arm_cs_etm_system_wide_test
188arm_cs_etm_snapshot_test
189exit $glb_err
190