Skip to content

Commit 0c343af

Browse files
Claire Jensenacmel
authored andcommitted
perf test: JSON format checking
Add field checking tests for perf stat JSON output. Sanity checks the expected number of fields are present, that the expected keys are present and they have the correct values. Committer notes: Had to fix this: - $(INSTALL) tests/shell/lib/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib' \ + $(INSTALL) tests/shell/lib/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \ Committer testing: [root@quaco ~]# perf test json 90: perf stat JSON output linter : Ok [root@quaco ~]# set -o vi [root@quaco ~]# perf test -v json 90: perf stat JSON output linter : --- start --- test child forked, pid 560794 Checking json output: no args [Success] Checking json output: system wide [Success] Checking json output: system wide Checking json output: system wide no aggregation [Success] Checking json output: interval [Success] Checking json output: event [Success] Checking json output: per core [Success] Checking json output: per thread [Success] Checking json output: per die [Success] Checking json output: per node [Success] Checking json output: per socket [Success] test child finished with 0 ---- end ---- perf stat JSON output linter: Ok [root@quaco ~]# Signed-off-by: Claire Jensen <[email protected]> Acked-by: Namhyung Kim <[email protected]> Tested-by: Arnaldo Carvalho de Melo <[email protected]> Cc: Alexander Shishkin <[email protected]> Cc: Alyssa Ross <[email protected]> Cc: Claire Jensen <[email protected]> Cc: Florian Fischer <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: James Clark <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Kan Liang <[email protected]> Cc: Like Xu <[email protected]> Cc: Mark Rutland <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Sandipan Das <[email protected]> Cc: Stephane Eranian <[email protected]> Cc: Xing Zhengjun <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ian Rogers <[email protected]> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
1 parent df936ca commit 0c343af

File tree

3 files changed

+245
-1
lines changed

3 files changed

+245
-1
lines changed

tools/perf/Makefile.perf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,8 @@ install-tests: all install-gtk
10051005
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \
10061006
$(INSTALL) tests/shell/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \
10071007
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \
1008-
$(INSTALL) tests/shell/lib/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'
1008+
$(INSTALL) tests/shell/lib/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'; \
1009+
$(INSTALL) tests/shell/lib/*.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/lib'
10091010

10101011
install-bin: install-tools install-tests install-traceevent-plugins
10111012

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/python
2+
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
3+
# Basic sanity check of perf JSON output as specified in the man page.
4+
5+
import argparse
6+
import sys
7+
import json
8+
9+
ap = argparse.ArgumentParser()
10+
ap.add_argument('--no-args', action='store_true')
11+
ap.add_argument('--interval', action='store_true')
12+
ap.add_argument('--system-wide-no-aggr', action='store_true')
13+
ap.add_argument('--system-wide', action='store_true')
14+
ap.add_argument('--event', action='store_true')
15+
ap.add_argument('--per-core', action='store_true')
16+
ap.add_argument('--per-thread', action='store_true')
17+
ap.add_argument('--per-die', action='store_true')
18+
ap.add_argument('--per-node', action='store_true')
19+
ap.add_argument('--per-socket', action='store_true')
20+
args = ap.parse_args()
21+
22+
Lines = sys.stdin.readlines()
23+
24+
def isfloat(num):
25+
try:
26+
float(num)
27+
return True
28+
except ValueError:
29+
return False
30+
31+
32+
def isint(num):
33+
try:
34+
int(num)
35+
return True
36+
except ValueError:
37+
return False
38+
39+
def is_counter_value(num):
40+
return isfloat(num) or num == '<not counted>' or num == '<not supported>'
41+
42+
def check_json_output(expected_items):
43+
if expected_items != -1:
44+
for line in Lines:
45+
if 'failed' not in line:
46+
count = 0
47+
count = line.count(',')
48+
if count != expected_items and count >= 1 and count <= 3 and 'metric-value' in line:
49+
# Events that generate >1 metric may have isolated metric
50+
# values and possibly other prefixes like interval, core and
51+
# aggregate-number.
52+
continue
53+
if count != expected_items:
54+
raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}'
55+
f' in \'{line}\'')
56+
checks = {
57+
'aggregate-number': lambda x: isfloat(x),
58+
'core': lambda x: True,
59+
'counter-value': lambda x: is_counter_value(x),
60+
'cgroup': lambda x: True,
61+
'cpu': lambda x: isint(x),
62+
'die': lambda x: True,
63+
'event': lambda x: True,
64+
'event-runtime': lambda x: isfloat(x),
65+
'interval': lambda x: isfloat(x),
66+
'metric-unit': lambda x: True,
67+
'metric-value': lambda x: isfloat(x),
68+
'node': lambda x: True,
69+
'pcnt-running': lambda x: isfloat(x),
70+
'socket': lambda x: True,
71+
'thread': lambda x: True,
72+
'unit': lambda x: True,
73+
}
74+
input = '[\n' + ','.join(Lines) + '\n]'
75+
for item in json.loads(input):
76+
for key, value in item.items():
77+
if key not in checks:
78+
raise RuntimeError(f'Unexpected key: key={key} value={value}')
79+
if not checks[key](value):
80+
raise RuntimeError(f'Check failed for: key={key} value={value}')
81+
82+
83+
try:
84+
if args.no_args or args.system_wide or args.event:
85+
expected_items = 6
86+
elif args.interval or args.per_thread or args.system_wide_no_aggr:
87+
expected_items = 7
88+
elif args.per_core or args.per_socket or args.per_node or args.per_die:
89+
expected_items = 8
90+
else:
91+
# If no option is specified, don't check the number of items.
92+
expected_items = -1
93+
check_json_output(expected_items)
94+
except:
95+
print('Test failed for input:\n' + '\n'.join(Lines))
96+
raise
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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+
7+
set -e
8+
9+
pythonchecker=$(dirname $0)/lib/perf_json_output_lint.py
10+
if [ "x$PYTHON" == "x" ]
11+
then
12+
if which python3 > /dev/null
13+
then
14+
PYTHON=python3
15+
elif which python > /dev/null
16+
then
17+
PYTHON=python
18+
else
19+
echo Skipping test, python not detected please set environment variable PYTHON.
20+
exit 2
21+
fi
22+
fi
23+
24+
# Return true if perf_event_paranoid is > $1 and not running as root.
25+
function ParanoidAndNotRoot()
26+
{
27+
[ $(id -u) != 0 ] && [ $(cat /proc/sys/kernel/perf_event_paranoid) -gt $1 ]
28+
}
29+
30+
check_no_args()
31+
{
32+
echo -n "Checking json output: no args "
33+
perf stat -j true 2>&1 | $PYTHON $pythonchecker --no-args
34+
echo "[Success]"
35+
}
36+
37+
check_system_wide()
38+
{
39+
echo -n "Checking json output: system wide "
40+
if ParanoidAndNotRoot 0
41+
then
42+
echo "[Skip] paranoia and not root"
43+
return
44+
fi
45+
perf stat -j -a true 2>&1 | $PYTHON $pythonchecker --system-wide
46+
echo "[Success]"
47+
}
48+
49+
check_system_wide_no_aggr()
50+
{
51+
echo -n "Checking json output: system wide "
52+
if ParanoidAndNotRoot 0
53+
then
54+
echo "[Skip] paranoia and not root"
55+
return
56+
fi
57+
echo -n "Checking json output: system wide no aggregation "
58+
perf stat -j -A -a --no-merge true 2>&1 | $PYTHON $pythonchecker --system-wide-no-aggr
59+
echo "[Success]"
60+
}
61+
62+
check_interval()
63+
{
64+
echo -n "Checking json output: interval "
65+
perf stat -j -I 1000 true 2>&1 | $PYTHON $pythonchecker --interval
66+
echo "[Success]"
67+
}
68+
69+
70+
check_event()
71+
{
72+
echo -n "Checking json output: event "
73+
perf stat -j -e cpu-clock true 2>&1 | $PYTHON $pythonchecker --event
74+
echo "[Success]"
75+
}
76+
77+
check_per_core()
78+
{
79+
echo -n "Checking json output: per core "
80+
if ParanoidAndNotRoot 0
81+
then
82+
echo "[Skip] paranoia and not root"
83+
return
84+
fi
85+
perf stat -j --per-core -a true 2>&1 | $PYTHON $pythonchecker --per-core
86+
echo "[Success]"
87+
}
88+
89+
check_per_thread()
90+
{
91+
echo -n "Checking json output: per thread "
92+
if ParanoidAndNotRoot 0
93+
then
94+
echo "[Skip] paranoia and not root"
95+
return
96+
fi
97+
perf stat -j --per-thread -a true 2>&1 | $PYTHON $pythonchecker --per-thread
98+
echo "[Success]"
99+
}
100+
101+
check_per_die()
102+
{
103+
echo -n "Checking json output: per die "
104+
if ParanoidAndNotRoot 0
105+
then
106+
echo "[Skip] paranoia and not root"
107+
return
108+
fi
109+
perf stat -j --per-die -a true 2>&1 | $PYTHON $pythonchecker --per-die
110+
echo "[Success]"
111+
}
112+
113+
check_per_node()
114+
{
115+
echo -n "Checking json output: per node "
116+
if ParanoidAndNotRoot 0
117+
then
118+
echo "[Skip] paranoia and not root"
119+
return
120+
fi
121+
perf stat -j --per-node -a true 2>&1 | $PYTHON $pythonchecker --per-node
122+
echo "[Success]"
123+
}
124+
125+
check_per_socket()
126+
{
127+
echo -n "Checking json output: per socket "
128+
if ParanoidAndNotRoot 0
129+
then
130+
echo "[Skip] paranoia and not root"
131+
return
132+
fi
133+
perf stat -j --per-socket -a true 2>&1 | $PYTHON $pythonchecker --per-socket
134+
echo "[Success]"
135+
}
136+
137+
check_no_args
138+
check_system_wide
139+
check_system_wide_no_aggr
140+
check_interval
141+
check_event
142+
check_per_core
143+
check_per_thread
144+
check_per_die
145+
check_per_node
146+
check_per_socket
147+
exit 0

0 commit comments

Comments
 (0)