Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <cjense@google.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alyssa Ross <hi@alyssa.is>
Cc: Claire Jensen <clairej735@gmail.com>
Cc: Florian Fischer <florian.fischer@muhq.space>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Like Xu <likexu@tencent.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sandipan Das <sandipan.das@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Xing Zhengjun <zhengjun.xing@linux.intel.com>
Link: https://lore.kernel.org/r/20220805200105.2020995-3-irogers@google.com
Signed-off-by: Ian Rogers <irogers@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Claire Jensen and committed by
Arnaldo Carvalho de Melo
0c343af2 df936cad

+245 -1
+2 -1
tools/perf/Makefile.perf
··· 1005 1005 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \ 1006 1006 $(INSTALL) tests/shell/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell'; \ 1007 1007 $(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' 1009 1010 1010 1011 install-bin: install-tools install-tests install-traceevent-plugins 1011 1012
+96
tools/perf/tests/shell/lib/perf_json_output_lint.py
··· 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
+147
tools/perf/tests/shell/stat+json_output.sh
··· 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