xref: /linux/tools/perf/tests/shell/lib/perf_json_output_lint.py (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
10c343af2SClaire Jensen#!/usr/bin/python
20c343af2SClaire Jensen# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
30c343af2SClaire Jensen# Basic sanity check of perf JSON output as specified in the man page.
40c343af2SClaire Jensen
50c343af2SClaire Jensenimport argparse
60c343af2SClaire Jensenimport sys
70c343af2SClaire Jensenimport json
80c343af2SClaire Jensen
90c343af2SClaire Jensenap = argparse.ArgumentParser()
100c343af2SClaire Jensenap.add_argument('--no-args', action='store_true')
110c343af2SClaire Jensenap.add_argument('--interval', action='store_true')
120c343af2SClaire Jensenap.add_argument('--system-wide-no-aggr', action='store_true')
130c343af2SClaire Jensenap.add_argument('--system-wide', action='store_true')
140c343af2SClaire Jensenap.add_argument('--event', action='store_true')
150c343af2SClaire Jensenap.add_argument('--per-core', action='store_true')
160c343af2SClaire Jensenap.add_argument('--per-thread', action='store_true')
17bfce728dSK Prateek Nayakap.add_argument('--per-cache', action='store_true')
18*cbc917a1SYicong Yangap.add_argument('--per-cluster', action='store_true')
190c343af2SClaire Jensenap.add_argument('--per-die', action='store_true')
200c343af2SClaire Jensenap.add_argument('--per-node', action='store_true')
210c343af2SClaire Jensenap.add_argument('--per-socket', action='store_true')
22760eafb2SIan Rogersap.add_argument('--file', type=argparse.FileType('r'), default=sys.stdin)
230c343af2SClaire Jensenargs = ap.parse_args()
240c343af2SClaire Jensen
25760eafb2SIan RogersLines = args.file.readlines()
260c343af2SClaire Jensen
270c343af2SClaire Jensendef isfloat(num):
280c343af2SClaire Jensen  try:
290c343af2SClaire Jensen    float(num)
300c343af2SClaire Jensen    return True
310c343af2SClaire Jensen  except ValueError:
320c343af2SClaire Jensen    return False
330c343af2SClaire Jensen
340c343af2SClaire Jensen
350c343af2SClaire Jensendef isint(num):
360c343af2SClaire Jensen  try:
370c343af2SClaire Jensen    int(num)
380c343af2SClaire Jensen    return True
390c343af2SClaire Jensen  except ValueError:
400c343af2SClaire Jensen    return False
410c343af2SClaire Jensen
420c343af2SClaire Jensendef is_counter_value(num):
430c343af2SClaire Jensen  return isfloat(num) or num == '<not counted>' or num == '<not supported>'
440c343af2SClaire Jensen
450c343af2SClaire Jensendef check_json_output(expected_items):
460c343af2SClaire Jensen  checks = {
470c343af2SClaire Jensen      'aggregate-number': lambda x: isfloat(x),
480c343af2SClaire Jensen      'core': lambda x: True,
490c343af2SClaire Jensen      'counter-value': lambda x: is_counter_value(x),
500c343af2SClaire Jensen      'cgroup': lambda x: True,
510c343af2SClaire Jensen      'cpu': lambda x: isint(x),
52bfce728dSK Prateek Nayak      'cache': lambda x: True,
53*cbc917a1SYicong Yang      'cluster': lambda x: True,
540c343af2SClaire Jensen      'die': lambda x: True,
550c343af2SClaire Jensen      'event': lambda x: True,
560c343af2SClaire Jensen      'event-runtime': lambda x: isfloat(x),
570c343af2SClaire Jensen      'interval': lambda x: isfloat(x),
580c343af2SClaire Jensen      'metric-unit': lambda x: True,
590c343af2SClaire Jensen      'metric-value': lambda x: isfloat(x),
60e2595550SKan Liang      'metricgroup': lambda x: True,
610c343af2SClaire Jensen      'node': lambda x: True,
620c343af2SClaire Jensen      'pcnt-running': lambda x: isfloat(x),
630c343af2SClaire Jensen      'socket': lambda x: True,
640c343af2SClaire Jensen      'thread': lambda x: True,
650c343af2SClaire Jensen      'unit': lambda x: True,
660c343af2SClaire Jensen  }
670c343af2SClaire Jensen  input = '[\n' + ','.join(Lines) + '\n]'
680c343af2SClaire Jensen  for item in json.loads(input):
693de34f85SIan Rogers    if expected_items != -1:
703de34f85SIan Rogers      count = len(item)
7118b687d7SKan Liang      if count != expected_items and count >= 1 and count <= 6 and 'metric-value' in item:
723de34f85SIan Rogers        # Events that generate >1 metric may have isolated metric
7318b687d7SKan Liang        # values and possibly other prefixes like interval, core,
7418b687d7SKan Liang        # aggregate-number, or event-runtime/pcnt-running from multiplexing.
753de34f85SIan Rogers        pass
76e2595550SKan Liang      elif count != expected_items and count >= 1 and count <= 5 and 'metricgroup' in item:
77e2595550SKan Liang        pass
783de34f85SIan Rogers      elif count != expected_items:
793de34f85SIan Rogers        raise RuntimeError(f'wrong number of fields. counted {count} expected {expected_items}'
803de34f85SIan Rogers                           f' in \'{item}\'')
810c343af2SClaire Jensen    for key, value in item.items():
820c343af2SClaire Jensen      if key not in checks:
830c343af2SClaire Jensen        raise RuntimeError(f'Unexpected key: key={key} value={value}')
840c343af2SClaire Jensen      if not checks[key](value):
850c343af2SClaire Jensen        raise RuntimeError(f'Check failed for: key={key} value={value}')
860c343af2SClaire Jensen
870c343af2SClaire Jensen
880c343af2SClaire Jensentry:
890c343af2SClaire Jensen  if args.no_args or args.system_wide or args.event:
900c343af2SClaire Jensen    expected_items = 7
913de34f85SIan Rogers  elif args.interval or args.per_thread or args.system_wide_no_aggr:
920c343af2SClaire Jensen    expected_items = 8
93*cbc917a1SYicong Yang  elif args.per_core or args.per_socket or args.per_node or args.per_die or args.per_cluster or args.per_cache:
943de34f85SIan Rogers    expected_items = 9
950c343af2SClaire Jensen  else:
960c343af2SClaire Jensen    # If no option is specified, don't check the number of items.
970c343af2SClaire Jensen    expected_items = -1
980c343af2SClaire Jensen  check_json_output(expected_items)
990c343af2SClaire Jensenexcept:
1000c343af2SClaire Jensen  print('Test failed for input:\n' + '\n'.join(Lines))
1010c343af2SClaire Jensen  raise
102