1#!/bin/bash
2# perf stat JSON output linter
3# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
4# Checks various perf stat JSON output commands for the
5# correct number of fields.
6
7set -e
8
9skip_test=0
10
11pythonchecker=$(dirname $0)/lib/perf_json_output_lint.py
12if [ "x$PYTHON" == "x" ]
13then
14	if which python3 > /dev/null
15	then
16		PYTHON=python3
17	elif which python > /dev/null
18	then
19		PYTHON=python
20	else
21		echo Skipping test, python not detected please set environment variable PYTHON.
22		exit 2
23	fi
24fi
25
26stat_output=$(mktemp /tmp/__perf_test.stat_output.json.XXXXX)
27
28cleanup() {
29  rm -f "${stat_output}"
30
31  trap - EXIT TERM INT
32}
33
34trap_cleanup() {
35  cleanup
36  exit 1
37}
38trap trap_cleanup EXIT TERM INT
39
40# Return true if perf_event_paranoid is > $1 and not running as root.
41function ParanoidAndNotRoot()
42{
43	 [ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ]
44}
45
46check_no_args()
47{
48	echo -n "Checking json output: no args "
49	perf stat -j -o "${stat_output}" true
50	$PYTHON $pythonchecker --no-args --file "${stat_output}"
51	echo "[Success]"
52}
53
54check_system_wide()
55{
56	echo -n "Checking json output: system wide "
57	if ParanoidAndNotRoot 0
58	then
59		echo "[Skip] paranoia and not root"
60		return
61	fi
62	perf stat -j -a -o "${stat_output}" true
63	$PYTHON $pythonchecker --system-wide --file "${stat_output}"
64	echo "[Success]"
65}
66
67check_system_wide_no_aggr()
68{
69	echo -n "Checking json output: system wide no aggregation "
70	if ParanoidAndNotRoot 0
71	then
72		echo "[Skip] paranoia and not root"
73		return
74	fi
75	perf stat -j -A -a --no-merge -o "${stat_output}" true
76	$PYTHON $pythonchecker --system-wide-no-aggr --file "${stat_output}"
77	echo "[Success]"
78}
79
80check_interval()
81{
82	echo -n "Checking json output: interval "
83	perf stat -j -I 1000 -o "${stat_output}" true
84	$PYTHON $pythonchecker --interval --file "${stat_output}"
85	echo "[Success]"
86}
87
88
89check_event()
90{
91	echo -n "Checking json output: event "
92	perf stat -j -e cpu-clock -o "${stat_output}" true
93	$PYTHON $pythonchecker --event --file "${stat_output}"
94	echo "[Success]"
95}
96
97check_per_core()
98{
99	echo -n "Checking json output: per core "
100	if ParanoidAndNotRoot 0
101	then
102		echo "[Skip] paranoia and not root"
103		return
104	fi
105	perf stat -j --per-core -a -o "${stat_output}" true
106	$PYTHON $pythonchecker --per-core --file "${stat_output}"
107	echo "[Success]"
108}
109
110check_per_thread()
111{
112	echo -n "Checking json output: per thread "
113	if ParanoidAndNotRoot 0
114	then
115		echo "[Skip] paranoia and not root"
116		return
117	fi
118	perf stat -j --per-thread -a -o "${stat_output}" true
119	$PYTHON $pythonchecker --per-thread --file "${stat_output}"
120	echo "[Success]"
121}
122
123check_per_die()
124{
125	echo -n "Checking json output: per die "
126	if ParanoidAndNotRoot 0
127	then
128		echo "[Skip] paranoia and not root"
129		return
130	fi
131	perf stat -j --per-die -a -o "${stat_output}" true
132	$PYTHON $pythonchecker --per-die --file "${stat_output}"
133	echo "[Success]"
134}
135
136check_per_node()
137{
138	echo -n "Checking json output: per node "
139	if ParanoidAndNotRoot 0
140	then
141		echo "[Skip] paranoia and not root"
142		return
143	fi
144	perf stat -j --per-node -a -o "${stat_output}" true
145	$PYTHON $pythonchecker --per-node --file "${stat_output}"
146	echo "[Success]"
147}
148
149check_per_socket()
150{
151	echo -n "Checking json output: per socket "
152	if ParanoidAndNotRoot 0
153	then
154		echo "[Skip] paranoia and not root"
155		return
156	fi
157	perf stat -j --per-socket -a -o "${stat_output}" true
158	$PYTHON $pythonchecker --per-socket --file "${stat_output}"
159	echo "[Success]"
160}
161
162# The perf stat options for per-socket, per-core, per-die
163# and -A ( no_aggr mode ) uses the info fetched from this
164# directory: "/sys/devices/system/cpu/cpu*/topology". For
165# example, socket value is fetched from "physical_package_id"
166# file in topology directory.
167# Reference: cpu__get_topology_int in util/cpumap.c
168# If the platform doesn't expose topology information, values
169# will be set to -1. For example, incase of pSeries platform
170# of powerpc, value for  "physical_package_id" is restricted
171# and set to -1. Check here validates the socket-id read from
172# topology file before proceeding further
173
174FILE_LOC="/sys/devices/system/cpu/cpu*/topology/"
175FILE_NAME="physical_package_id"
176
177check_for_topology()
178{
179	if ! ParanoidAndNotRoot 0
180	then
181		socket_file=`ls $FILE_LOC/$FILE_NAME | head -n 1`
182		[ -z $socket_file ] && return 0
183		socket_id=`cat $socket_file`
184		[ $socket_id == -1 ] && skip_test=1
185		return 0
186	fi
187}
188
189check_for_topology
190check_no_args
191check_system_wide
192check_interval
193check_event
194check_per_thread
195check_per_node
196if [ $skip_test -ne 1 ]
197then
198	check_system_wide_no_aggr
199	check_per_core
200	check_per_die
201	check_per_socket
202else
203	echo "[Skip] Skipping tests for system_wide_no_aggr, per_core, per_die and per_socket since socket id exposed via topology is invalid"
204fi
205cleanup
206exit 0
207