1#!/bin/bash
2# perf stat CSV output linter
3# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
4# Tests various perf stat CSV output commands for the
5# correct number of fields and the CSV separator set to ','.
6
7set -e
8
9skip_test=0
10csv_sep=@
11
12function commachecker()
13{
14	local -i cnt=0
15	local exp=0
16
17	case "$1"
18	in "--no-args")		exp=6
19	;; "--system-wide")	exp=6
20	;; "--event")		exp=6
21	;; "--interval")	exp=7
22	;; "--per-thread")	exp=7
23	;; "--system-wide-no-aggr")	exp=7
24				[ $(uname -m) = "s390x" ] && exp='^[6-7]$'
25	;; "--per-core")	exp=8
26	;; "--per-socket")	exp=8
27	;; "--per-node")	exp=8
28	;; "--per-die")		exp=8
29	esac
30
31	while read line
32	do
33		# Check for lines beginning with Failed
34		x=${line:0:6}
35		[ "$x" = "Failed" ] && continue
36
37		# Count the number of commas
38		x=$(echo $line | tr -d -c $csv_sep)
39		cnt="${#x}"
40		# echo $line $cnt
41		[[ ! "$cnt" =~ $exp ]] && {
42			echo "wrong number of fields. expected $exp in $line" 1>&2
43			exit 1;
44		}
45	done
46	return 0
47}
48
49# Return true if perf_event_paranoid is > $1 and not running as root.
50function ParanoidAndNotRoot()
51{
52	 [ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ]
53}
54
55check_no_args()
56{
57	echo -n "Checking CSV output: no args "
58	perf stat -x$csv_sep true 2>&1 | commachecker --no-args
59	echo "[Success]"
60}
61
62check_system_wide()
63{
64	echo -n "Checking CSV output: system wide "
65	if ParanoidAndNotRoot 0
66	then
67		echo "[Skip] paranoid and not root"
68		return
69	fi
70	perf stat -x$csv_sep -a true 2>&1 | commachecker --system-wide
71	echo "[Success]"
72}
73
74check_system_wide_no_aggr()
75{
76	echo -n "Checking CSV output: system wide "
77	if ParanoidAndNotRoot 0
78	then
79		echo "[Skip] paranoid and not root"
80		return
81	fi
82	echo -n "Checking CSV output: system wide no aggregation "
83	perf stat -x$csv_sep -A -a --no-merge true 2>&1 | commachecker --system-wide-no-aggr
84	echo "[Success]"
85}
86
87check_interval()
88{
89	echo -n "Checking CSV output: interval "
90	perf stat -x$csv_sep -I 1000 true 2>&1 | commachecker --interval
91	echo "[Success]"
92}
93
94
95check_event()
96{
97	echo -n "Checking CSV output: event "
98	perf stat -x$csv_sep -e cpu-clock true 2>&1 | commachecker --event
99	echo "[Success]"
100}
101
102check_per_core()
103{
104	echo -n "Checking CSV output: per core "
105	if ParanoidAndNotRoot 0
106	then
107		echo "[Skip] paranoid and not root"
108		return
109	fi
110	perf stat -x$csv_sep --per-core -a true 2>&1 | commachecker --per-core
111	echo "[Success]"
112}
113
114check_per_thread()
115{
116	echo -n "Checking CSV output: per thread "
117	if ParanoidAndNotRoot 0
118	then
119		echo "[Skip] paranoid and not root"
120		return
121	fi
122	perf stat -x$csv_sep --per-thread -a true 2>&1 | commachecker --per-thread
123	echo "[Success]"
124}
125
126check_per_die()
127{
128	echo -n "Checking CSV output: per die "
129	if ParanoidAndNotRoot 0
130	then
131		echo "[Skip] paranoid and not root"
132		return
133	fi
134	perf stat -x$csv_sep --per-die -a true 2>&1 | commachecker --per-die
135	echo "[Success]"
136}
137
138check_per_node()
139{
140	echo -n "Checking CSV output: per node "
141	if ParanoidAndNotRoot 0
142	then
143		echo "[Skip] paranoid and not root"
144		return
145	fi
146	perf stat -x$csv_sep --per-node -a true 2>&1 | commachecker --per-node
147	echo "[Success]"
148}
149
150check_per_socket()
151{
152	echo -n "Checking CSV output: per socket "
153	if ParanoidAndNotRoot 0
154	then
155		echo "[Skip] paranoid and not root"
156		return
157	fi
158	perf stat -x$csv_sep --per-socket -a true 2>&1 | commachecker --per-socket
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
205exit 0
206