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 
47 def find_line_in_file(pattern, filename):
48  if not os.path.isfile(filename):
49  logging.warning("Cannot open file {}".format(filename))
50  return None
51  with open(filename) as logfile:
52  lines = re.findall("{}.*$".format(pattern), logfile.read(), re.MULTILINE)
53  if len(lines) == 0:
54  logging.warning("Could not find pattern \"{}\" in file {}".format(pattern, filename))
55  return None
56  return lines[0]
57 
58 
59 def get_num_from_checklog(filename):
60  line = find_line_in_file('Found messages in', filename)
61  if line is None:
62  logging.warning("Cannot extract number of messages from {}".format(filename))
63  return None
64  logging.debug("line: {}".format(line))
65  m = re.search(r'\‍((.+?)\‍):', line)
66  return m.group(1)
67 
68 
69 def get_num_histos(filename):
70  line = find_line_in_file('Total histograms:', filename)
71  if line is None:
72  logging.warning("Cannot extract number of histograms from {}".format(filename))
73  return None
74  logging.debug("line: {}".format(line))
75  return line.split()[-1]
76 
77 
78 def convert_to_megabytes(number, unit):
79  multipliers = {
80  'B': 1.0/(1024**2),
81  'kB': 1.0/1024,
82  'MB': 1,
83  "GB": 1024,
84  'TB': 1024**2
85  }
86  for unit_name, mult in multipliers.items():
87  if unit_name == unit:
88  return float(number)*mult
89  logging.error("Unit conversion failed from {} to MB".format(unit))
90  return None
91 
92 
94  words = line[0].split()
95  mem_end = words[5:7]
96  logging.debug("mem_end = {}".format(mem_end))
97  mem_delta = words[8:10]
98  logging.debug("mem_delta = {}".format(mem_delta))
99  mem_mb = convert_to_megabytes(mem_end[0], mem_end[1])
100  logging.debug("mem_mb = {}".format(mem_mb))
101  delta_mb = convert_to_megabytes(mem_delta[0], mem_delta[1])
102  logging.debug("delta_mb = {}".format(delta_mb))
103  return mem_mb, delta_mb
104 
105 
106 def analyse_perfmon(filename):
107  if not os.path.isfile(filename):
108  logging.warning("Cannot open file {}".format(filename))
109  return None
110  with open(filename) as logfile:
111  first_line = -1
112  last_line = -1
113  all_lines = logfile.readlines()
114  for i, line in enumerate(all_lines):
115  if first_line >= 0 and last_line >= 0:
116  break
117  if "=== [evt - slice] ===" in line:
118  first_line = i
119  elif "=== [fin - slice] ===" in line:
120  last_line = i
121  if first_line < 0 or last_line < 0:
122  logging.warning("Cannot extract memory usage information from {}".format(filename))
123  return None
124  evt_mon_lines = all_lines[first_line:last_line]
125  vmem_line = re.findall("^VMem.*$", '\n'.join(evt_mon_lines), re.MULTILINE)
126  rss_line = re.findall("^RSS.*$", '\n'.join(evt_mon_lines), re.MULTILINE)
127  logging.debug("vmem_line = {}".format(vmem_line))
128  logging.debug("rss_line = {}".format(rss_line))
129  if len(vmem_line) == 0:
130  logging.warning("Cannot extract VMem information from {}".format(filename))
131  if len(rss_line) == 0:
132  logging.warning("Cannot extract RSS information from {}".format(filename))
133  vmem, dvmem = extract_mem_perfmon(vmem_line)
134  rss, drss = extract_mem_perfmon(rss_line)
135  data = LastUpdatedOrderedDict()
136  data['vmem'] = "{0:.3f}".format(vmem)
137  data['delta-vmem'] = "{0:.3f}".format(dvmem)
138  data['rss'] = "{0:.3f}".format(rss)
139  data['delta-rss'] = "{0:.3f}".format(drss)
140  return data
141 
142 
143 def mem_func(x_arr, x_trans, init_slope, exec_slope):
144  retval = []
145  for x in x_arr:
146  if x < x_trans:
147  retval.append(init_slope * x)
148  else:
149  retval.append(exec_slope * x + (init_slope - exec_slope) * x_trans)
150  return retval
151 
152 
153 def find_dmem_prmon(xdata, ydata, label, filename):
154  popt, pcov = curve_fit(mem_func, xdata, ydata, bounds=(0, [0.9*max(xdata), np.inf, np.inf]))
155  logging.debug("Fit result: %s", str(popt))
156  plot_prmon(xdata, ydata, label, filename, popt)
157  x_trans = popt[0]
158  x_last = xdata.iloc[-1]
159  dmem_v = mem_func([x_last, x_trans], popt[0], popt[1], popt[2])
160  return dmem_v[0] - dmem_v[1]
161 
162 
163 def plot_prmon(xdata, ydata, name, filename, params=[]):
164  plt.plot(xdata, ydata, 'b-', label=name)
165  if len(params)>0:
166  plt.plot(xdata, mem_func(xdata, *params), 'r-', label='{:s} fit, exec slope={:.2f} kB/s'.format(name, params[2]))
167  plt.xlabel('wtime [s]')
168  plt.ylabel(name+' [kB]')
169  plt.legend()
170  plt.title('{:s} from {:s}'.format(name, filename))
171  plt.savefig('prmon_{:s}.pdf'.format(name), bbox_inches='tight')
172  plt.clf()
173 
174 
175 def analyse_prmon(filename):
176  try:
177  prmon_data = read_csv(filename, sep='\t')
178  except IOError:
179  logging.warning("Cannot open file {}".format(filename))
180  return None
181  time_v = prmon_data['wtime']
182  pss_v = prmon_data['pss']
183  rss_v = prmon_data['rss']
184  vmem_v = prmon_data['vmem']
185  data = LastUpdatedOrderedDict()
186  data['vmem'] = "{0:.3f}".format(convert_to_megabytes(max(vmem_v), 'kB'))
187  data['rss'] = "{0:.3f}".format(convert_to_megabytes(max(rss_v), 'kB'))
188  data['pss'] = "{0:.3f}".format(convert_to_megabytes(max(pss_v), 'kB'))
189  if len(time_v) < 80:
190  logging.info('Not enough prmon data points, skipping memory slope fitting')
191  # Save plots without fitting and return
192  plot_prmon(time_v, pss_v, 'pss', filename)
193  plot_prmon(time_v, rss_v, 'rss', filename)
194  plot_prmon(time_v, vmem_v, 'vmem', filename)
195  return data
196  d_pss = find_dmem_prmon(time_v, pss_v, 'pss', filename)
197  d_rss = find_dmem_prmon(time_v, rss_v, 'rss', filename)
198  d_vmem = find_dmem_prmon(time_v, vmem_v, 'vmem', filename)
199  data['delta-vmem'] = "{0:.3f}".format(convert_to_megabytes(d_vmem, 'kB'))
200  data['delta-rss'] = "{0:.3f}".format(convert_to_megabytes(d_rss, 'kB'))
201  data['delta-pss'] = "{0:.3f}".format(convert_to_megabytes(d_pss, 'kB'))
202  return data
203 
204 
205 def main():
206  data = LastUpdatedOrderedDict()
207 
208  # Get number of errors
209  checklog_log = first_existing_file(['checklog.log', 'CheckLog.log'])
210  ne = get_num_from_checklog(checklog_log) if checklog_log else None
211  logging.debug("ne: {}".format(ne))
212  if ne is None:
213  logging.warning("Failed to read number of errors from the log")
214  data['num-errors'] = 'n/a'
215  else:
216  data['num-errors'] = ne
217 
218  # Get number of warnings
219  warnings_log = first_existing_file(['warnings.log', 'Warnings.log'])
220  nw = get_num_from_checklog(warnings_log) if warnings_log else None
221  logging.debug("nw: {}".format(nw))
222  if nw is None:
223  logging.warning("Failed to read number of warnings from the log")
224  data['num-warnings'] = 'n/a'
225  else:
226  data['num-warnings'] = nw
227 
228  # Get number of histograms
229  histcount_log = first_existing_file(['histSizes.log', 'HistCount.log'])
230  nh = get_num_histos(histcount_log) if histcount_log else None
231  logging.debug("nh: {}".format(nh))
232  if nh is None:
233  logging.warning("Failed to read number of histograms from the log")
234  data['num-histograms'] = 'n/a'
235  else:
236  data['num-histograms'] = nh
237 
238  # Get memory usage information from prmon
239  if do_prmon:
240  prmon_log = newest_file(r'prmon\..*\.txt')
241  if not prmon_log:
242  prmon_log = first_existing_file(['prmon.full.RDOtoRDOTrigger', 'prmon.full.RAWtoESD', 'prmon.full.ESDtoAOD', 'prmon.full.RAWtoALL'])
243  if not prmon_log:
244  logging.info("No prmon output found, the result will be empty")
245  data['prmon'] = 'n/a'
246  else:
247  logging.info("Analysing prmon output from %s", prmon_log)
248  prmon_data = analyse_prmon(prmon_log)
249  if prmon_data is None:
250  logging.warning("Could not analyse prmon output, the result will be empty")
251  data['prmon'] = 'n/a'
252  else:
253  data['prmon'] = prmon_data
254 
255  # Get memory usage information from PerfMon
256  perfmon_log = newest_file(r'.*perfmon\.summary\.txt')
257  if not perfmon_log:
258  logging.info("No PerfMon output found, the result will be empty")
259  data['memory-usage'] = 'n/a'
260  else:
261  logging.info("Analysing PerfMon output from %s", perfmon_log)
262  perfmon_data = analyse_perfmon(perfmon_log)
263  if perfmon_data is None:
264  logging.warning("Could not analyse PerfMon output, the result will be empty")
265  data['memory-usage'] = 'n/a'
266  else:
267  data['memory-usage'] = perfmon_data
268 
269  # Save data to JSON file
270  with open('extra-results.json', 'w') as outfile:
271  json.dump(data, outfile, indent=4)
272 
273 
274 if "__main__" in __name__:
275  sys.exit(main())
max
#define max(a, b)
Definition: cfImp.cxx:41
trig-test-json.plot_prmon
def plot_prmon(xdata, ydata, name, filename, params=[])
Definition: trig-test-json.py:163
trig-test-json.get_num_histos
def get_num_histos(filename)
Definition: trig-test-json.py:69
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:153
trig-test-json.analyse_prmon
def analyse_prmon(filename)
Definition: trig-test-json.py:175
trig-test-json.mem_func
def mem_func(x_arr, x_trans, init_slope, exec_slope)
Definition: trig-test-json.py:143
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:106
trig-test-json.get_num_from_checklog
def get_num_from_checklog(filename)
Definition: trig-test-json.py:59
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.find_line_in_file
def find_line_in_file(pattern, filename)
Definition: trig-test-json.py:47
trig-test-json.convert_to_megabytes
def convert_to_megabytes(number, unit)
Definition: trig-test-json.py:78
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:205
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:93