ATLAS Offline Software
trig-test-json.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
4 #
5 
6 # This script parses outputs of trigger nightly test post-processing steps
7 # and creates a JSON file with extra data other than result codes
8 # (which are handled by ART).
9 
10 import json
11 import re
12 import sys
13 import logging
14 import os
15 from collections import OrderedDict
16 from TrigValTools.TrigARTUtils import first_existing_file, newest_file
17 
18 logging.basicConfig(stream=sys.stdout,
19  format='%(levelname)-8s %(message)s',
20  level=logging.INFO)
21 do_prmon = True
22 mod_name = ''
23 try:
24  mod_name = 'numpy'
25  import numpy as np
26  mod_name = 'pandas'
27  from pandas import read_csv
28  mod_name = 'scipy.optimize'
29  from scipy.optimize import curve_fit
30  mod_name = 'matplotlib'
31  import matplotlib
32  matplotlib.use('PDF')
33  import matplotlib.pyplot as plt
34 except Exception as e:
35  do_prmon = False
36  logging.warning('Failed to import module %s, prmon output analysis will be skipped! %s', mod_name, e)
37 
38 class LastUpdatedOrderedDict(OrderedDict):
39  'Store items in the order the keys were last added'
40 
41  def __setitem__(self, key, value):
42  if key in self:
43  del self[key]
44  OrderedDict.__setitem__(self, key, value)
45 
46 def get_num_from_checklog(filename):
47  if not os.path.isfile(filename):
48  logging.warning(f"Cannot open file {filename}")
49  return None
50  with open(filename) as logfile:
51  if re.search("No error/warning messages found in.*$", logfile.read(), re.MULTILINE) is not None:
52  logging.debug("Number of errors/warnings: 0")
53  return 0
54  logfile.seek(0)
55  match = re.search(r'Found (\d+) (\w+) message\‍(s\‍) in.*$', logfile.read(), re.MULTILINE)
56  if match is None:
57  logging.warning(f"Cannot extract number of messages from {filename}")
58  return None
59  logging.debug(f"Number of {match[2]}s: {match[1]}")
60  return int(match[1])
61 
62 def get_num_histos(filename):
63  if not os.path.isfile(filename):
64  logging.warning(f"Cannot open file {filename}")
65  return None
66  with open(filename) as logfile:
67  match = re.search(r"Total histograms:.* (\d+)$", logfile.read(), re.MULTILINE)
68  if match is None:
69  logging.warning("Cannot extract number of histograms from {}".format(filename))
70  return None
71  logging.debug(f"Found {match[1]} histograms")
72  return int(match[1])
73 
74 def convert_to_megabytes(number, unit):
75  multipliers = {
76  'B': 1.0/(1024**2),
77  'kB': 1.0/1024,
78  'MB': 1,
79  "GB": 1024,
80  'TB': 1024**2
81  }
82  for unit_name, mult in multipliers.items():
83  if unit_name == unit:
84  return float(number)*mult
85  logging.error("Unit conversion failed from {} to MB".format(unit))
86  return None
87 
88 
90  words = line[0].split()
91  mem_end = words[5:7]
92  logging.debug("mem_end = {}".format(mem_end))
93  mem_delta = words[8:10]
94  logging.debug("mem_delta = {}".format(mem_delta))
95  mem_mb = convert_to_megabytes(mem_end[0], mem_end[1])
96  logging.debug("mem_mb = {}".format(mem_mb))
97  delta_mb = convert_to_megabytes(mem_delta[0], mem_delta[1])
98  logging.debug("delta_mb = {}".format(delta_mb))
99  return mem_mb, delta_mb
100 
101 
102 def analyse_perfmon(filename):
103  if not os.path.isfile(filename):
104  logging.warning("Cannot open file {}".format(filename))
105  return None
106  with open(filename) as logfile:
107  first_line = -1
108  last_line = -1
109  all_lines = logfile.readlines()
110  for i, line in enumerate(all_lines):
111  if first_line >= 0 and last_line >= 0:
112  break
113  if "=== [evt - slice] ===" in line:
114  first_line = i
115  elif "=== [fin - slice] ===" in line:
116  last_line = i
117  if first_line < 0 or last_line < 0:
118  logging.warning("Cannot extract memory usage information from {}".format(filename))
119  return None
120  evt_mon_lines = all_lines[first_line:last_line]
121  vmem_line = re.findall("^VMem.*$", '\n'.join(evt_mon_lines), re.MULTILINE)
122  rss_line = re.findall("^RSS.*$", '\n'.join(evt_mon_lines), re.MULTILINE)
123  logging.debug("vmem_line = {}".format(vmem_line))
124  logging.debug("rss_line = {}".format(rss_line))
125  if len(vmem_line) == 0:
126  logging.warning("Cannot extract VMem information from {}".format(filename))
127  if len(rss_line) == 0:
128  logging.warning("Cannot extract RSS information from {}".format(filename))
129  vmem, dvmem = extract_mem_perfmon(vmem_line)
130  rss, drss = extract_mem_perfmon(rss_line)
131  data = LastUpdatedOrderedDict()
132  data['vmem'] = "{0:.3f}".format(vmem)
133  data['delta-vmem'] = "{0:.3f}".format(dvmem)
134  data['rss'] = "{0:.3f}".format(rss)
135  data['delta-rss'] = "{0:.3f}".format(drss)
136  return data
137 
138 
139 def mem_func(x_arr, x_trans, init_slope, exec_slope):
140  retval = []
141  for x in x_arr:
142  if x < x_trans:
143  retval.append(init_slope * x)
144  else:
145  retval.append(exec_slope * x + (init_slope - exec_slope) * x_trans)
146  return retval
147 
148 
149 def find_dmem_prmon(xdata, ydata, label, filename):
150  popt, pcov = curve_fit(mem_func, xdata, ydata, bounds=(0, [0.9*max(xdata), np.inf, np.inf]))
151  logging.debug("Fit result: %s", str(popt))
152  plot_prmon(xdata, ydata, label, filename, popt)
153  x_trans = popt[0]
154  x_last = xdata.iloc[-1]
155  dmem_v = mem_func([x_last, x_trans], popt[0], popt[1], popt[2])
156  return dmem_v[0] - dmem_v[1]
157 
158 
159 def plot_prmon(xdata, ydata, name, filename, params=[]):
160  plt.plot(xdata, ydata, 'b-', label=name)
161  if len(params)>0:
162  plt.plot(xdata, mem_func(xdata, *params), 'r-', label='{:s} fit, exec slope={:.2f} kB/s'.format(name, params[2]))
163  plt.xlabel('wtime [s]')
164  plt.ylabel(name+' [kB]')
165  plt.legend()
166  plt.title('{:s} from {:s}'.format(name, filename))
167  plt.savefig('prmon_{:s}.pdf'.format(name), bbox_inches='tight')
168  plt.clf()
169 
170 
171 def analyse_prmon(filename):
172  try:
173  prmon_data = read_csv(filename, sep='\t')
174  except IOError:
175  logging.warning("Cannot open file {}".format(filename))
176  return None
177  time_v = prmon_data['wtime']
178  pss_v = prmon_data['pss']
179  rss_v = prmon_data['rss']
180  vmem_v = prmon_data['vmem']
181  data = LastUpdatedOrderedDict()
182  data['vmem'] = "{0:.3f}".format(convert_to_megabytes(max(vmem_v), 'kB'))
183  data['rss'] = "{0:.3f}".format(convert_to_megabytes(max(rss_v), 'kB'))
184  data['pss'] = "{0:.3f}".format(convert_to_megabytes(max(pss_v), 'kB'))
185  if len(time_v) < 80:
186  logging.info('Not enough prmon data points, skipping memory slope fitting')
187  # Save plots without fitting and return
188  plot_prmon(time_v, pss_v, 'pss', filename)
189  plot_prmon(time_v, rss_v, 'rss', filename)
190  plot_prmon(time_v, vmem_v, 'vmem', filename)
191  return data
192  d_pss = find_dmem_prmon(time_v, pss_v, 'pss', filename)
193  d_rss = find_dmem_prmon(time_v, rss_v, 'rss', filename)
194  d_vmem = find_dmem_prmon(time_v, vmem_v, 'vmem', filename)
195  data['delta-vmem'] = "{0:.3f}".format(convert_to_megabytes(d_vmem, 'kB'))
196  data['delta-rss'] = "{0:.3f}".format(convert_to_megabytes(d_rss, 'kB'))
197  data['delta-pss'] = "{0:.3f}".format(convert_to_megabytes(d_pss, 'kB'))
198  return data
199 
200 
201 def main():
202  data = LastUpdatedOrderedDict()
203 
204  # Get number of errors
205  checklog_log = first_existing_file(['checklog.log', 'CheckLog.log'])
206  ne = get_num_from_checklog(checklog_log) if checklog_log else None
207  logging.debug("ne: {}".format(ne))
208  if ne is None:
209  logging.warning("Failed to read number of errors from the log")
210  data['num-errors'] = 'n/a'
211  else:
212  data['num-errors'] = ne
213 
214  # Get number of warnings
215  warnings_log = first_existing_file(['warnings.log', 'Warnings.log'])
216  nw = get_num_from_checklog(warnings_log) if warnings_log else None
217  logging.debug("nw: {}".format(nw))
218  if nw is None:
219  logging.warning("Failed to read number of warnings from the log")
220  data['num-warnings'] = 'n/a'
221  else:
222  data['num-warnings'] = nw
223 
224  # Get number of histograms
225  histcount_log = first_existing_file(['histSizes.log', 'HistCount.log'])
226  nh = get_num_histos(histcount_log) if histcount_log else None
227  logging.debug("nh: {}".format(nh))
228  if nh is None:
229  logging.warning("Failed to read number of histograms from the log")
230  data['num-histograms'] = 'n/a'
231  else:
232  data['num-histograms'] = nh
233 
234  # Get memory usage information from prmon
235  if do_prmon:
236  prmon_log = newest_file(r'prmon\..*\.txt')
237  if not prmon_log:
238  prmon_log = first_existing_file(['prmon.full.RDOtoRDOTrigger', 'prmon.full.RAWtoESD', 'prmon.full.ESDtoAOD', 'prmon.full.RAWtoALL'])
239  if not prmon_log:
240  logging.info("No prmon output found, the result will be empty")
241  data['prmon'] = 'n/a'
242  else:
243  logging.info("Analysing prmon output from %s", prmon_log)
244  prmon_data = analyse_prmon(prmon_log)
245  if prmon_data is None:
246  logging.warning("Could not analyse prmon output, the result will be empty")
247  data['prmon'] = 'n/a'
248  else:
249  data['prmon'] = prmon_data
250 
251  # Get memory usage information from PerfMon
252  perfmon_log = newest_file(r'.*perfmon\.summary\.txt')
253  if not perfmon_log:
254  logging.info("No PerfMon output found, the result will be empty")
255  data['memory-usage'] = 'n/a'
256  else:
257  logging.info("Analysing PerfMon output from %s", perfmon_log)
258  perfmon_data = analyse_perfmon(perfmon_log)
259  if perfmon_data is None:
260  logging.warning("Could not analyse PerfMon output, the result will be empty")
261  data['memory-usage'] = 'n/a'
262  else:
263  data['memory-usage'] = perfmon_data
264 
265  # Save data to JSON file
266  with open('extra-results.json', 'w') as outfile:
267  json.dump(data, outfile, indent=4)
268 
269 
270 if "__main__" in __name__:
271  sys.exit(main())
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
trig-test-json.plot_prmon
def plot_prmon(xdata, ydata, name, filename, params=[])
Definition: trig-test-json.py:159
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
trig-test-json.get_num_histos
def get_num_histos(filename)
Definition: trig-test-json.py:62
python.TrigARTUtils.newest_file
def newest_file(pattern)
Definition: TrigARTUtils.py:70
trig-test-json.find_dmem_prmon
def find_dmem_prmon(xdata, ydata, label, filename)
Definition: trig-test-json.py:149
trig-test-json.analyse_prmon
def analyse_prmon(filename)
Definition: trig-test-json.py:171
trig-test-json.mem_func
def mem_func(x_arr, x_trans, init_slope, exec_slope)
Definition: trig-test-json.py:139
trig-test-json.LastUpdatedOrderedDict.__setitem__
def __setitem__(self, key, value)
Definition: trig-test-json.py:41
trig-test-json.analyse_perfmon
def analyse_perfmon(filename)
Definition: trig-test-json.py:102
trig-test-json.get_num_from_checklog
def get_num_from_checklog(filename)
Definition: trig-test-json.py:46
python.TrigARTUtils.first_existing_file
def first_existing_file(file_list)
Definition: TrigARTUtils.py:59
trig-test-json.LastUpdatedOrderedDict
Definition: trig-test-json.py:38
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
Trk::open
@ open
Definition: BinningType.h:40
trig-test-json.convert_to_megabytes
def convert_to_megabytes(number, unit)
Definition: trig-test-json.py:74
trig-test-json.format
format
Definition: trig-test-json.py:19
str
Definition: BTagTrackIpAccessor.cxx:11
trig-test-json.main
def main()
Definition: trig-test-json.py:201
readCCLHist.float
float
Definition: readCCLHist.py:83
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
trig-test-json.extract_mem_perfmon
def extract_mem_perfmon(line)
Definition: trig-test-json.py:89