ATLAS Offline Software
trfReports.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 
10 
11 __version__ = '$Revision: 784023 $'
12 
13 import pickle as pickle
14 import json
15 import os.path
16 import platform
17 import pprint
18 import sys
19 try:
20  import distro
21 except ImportError:
22  pass
23 
24 from xml.etree import ElementTree
25 
26 import logging
27 msg = logging.getLogger(__name__)
28 
29 import PyJobTransforms.trfExceptions as trfExceptions
30 import PyJobTransforms.trfArgClasses as trfArgClasses
31 
32 from PyJobTransforms.trfExitCodes import trfExit
33 from PyJobTransforms.trfUtils import shQuoteStrings, isodate, prettyXML, calcCpuTime, calcWallTime
34 
35 
36 defaultFileReport = {'input': 'name', 'temporary': None, 'output': 'full'}
37 
38 
40  def __init__(self):
41  self._dataDictionary = {}
42  pass
43 
44 
47  def __str__(self):
48  return pprint.pformat(self.python())
49 
50 
52  def python(self, fast = False, fileReport = defaultFileReport):
53  return {}
54 
55 
58  def json(self, fast = False):
59  return json.dumps(self.python, type)
60 
61 
63  def classicEltree(self, fast = False):
64  return ElementTree.Element('POOLFILECATALOG')
65 
66 
68  def classicPython(self, fast = False):
69  return {}
70 
71  def writeJSONReport(self, filename, sort_keys = True, indent = 2, fast = False, fileReport = defaultFileReport):
72  with open(filename, 'w') as report:
73  try:
74  if not self._dataDictionary:
75  self._dataDictionary = self.python(fast=fast, fileReport=fileReport)
76 
77  json.dump(self._dataDictionary, report, sort_keys = sort_keys, indent = indent)
78  except TypeError as e:
79  # TypeError means we had an unserialisable object - re-raise as a trf internal
80  message = 'TypeError raised during JSON report output: {0!s}'.format(e)
81  msg.error(message)
82  raise trfExceptions.TransformReportException(trfExit.nameToCode('TRF_INTERNAL_REPORT_ERROR'), message)
83 
84  def writeTxtReport(self, filename, dumpEnv = True, fast = False, fileReport = defaultFileReport):
85  with open(filename, 'w') as report:
86  if not self._dataDictionary:
87  self._dataDictionary = self.python(fast = fast, fileReport = fileReport)
88 
89  print('# {0} file generated on'.format(self.__class__.__name__), isodate(), file=report)
90  print(pprint.pformat(self._dataDictionary), file=report)
91  if dumpEnv:
92  print('# Environment dump', file=report)
93  eKeys = list(os.environ)
94  eKeys.sort()
95  for k in eKeys:
96  print('%s=%s' % (k, os.environ[k]), file=report)
97  print('# Machine report', file=report)
98  print(pprint.pformat(machineReport().python(fast = fast)), file=report)
99 
100  def writeGPickleReport(self, filename, fast = False):
101  with open(filename, 'wb') as report:
102  pickle.dump(self.classicPython(fast = fast), report)
103 
104  def writeClassicXMLReport(self, filename, fast = False):
105  with open(filename, 'w') as report:
106  print(prettyXML(self.classicEltree(fast = fast), poolFileCatalogFormat = True), file=report)
107 
108  def writePilotPickleReport(self, filename, fast = False, fileReport = defaultFileReport):
109  with open(filename, 'w') as report:
110  if not self._dataDictionary:
111  self._dataDictionary = self.python(fast = fast, fileReport = fileReport)
112 
113  pickle.dump(self._dataDictionary, report)
114 
115 
116 
118 
120  _reportVersion = '2.1.2'
121  _metadataKeyMap = {'AMIConfig': 'AMI', }
122  _maxMsgLen = 256
123  _truncationMsg = " (truncated)"
124 
125 
127  def __init__(self, parentTrf):
128  super(trfJobReport, self).__init__()
129  self._trf = parentTrf
131  self._dbDataTotal = 0
132  self._dbTimeTotal = 0.0
133 
134 
140  def python(self, fast = False, fileReport = defaultFileReport):
141  myDict = {'name': self._trf.name,
142  'reportVersion': self._reportVersion,
143  'cmdLine': ' '.join(shQuoteStrings(sys.argv)),
144  'exitAcronym': trfExit.codeToName(self._trf.exitCode),
145  'exitCode': self._trf.exitCode,
146  'created': isodate(),
147  'resource': {'executor': {}, 'transform': {}},
148  'files': {}
149  }
150  if len(self._trf.exitMsg) > self._maxMsgLen:
151  myDict['exitMsg'] = self._trf.exitMsg[:self._maxMsgLen-len(self._truncationMsg)] + self._truncationMsg
152  myDict['exitMsgExtra'] = self._trf.exitMsg[self._maxMsgLen-len(self._truncationMsg):]
153  else:
154  myDict['exitMsg'] = self._trf.exitMsg
155  myDict['exitMsgExtra'] = ""
156 
157  # Iterate over files
158  for fileType in ('input', 'output', 'temporary'):
159  if fileReport[fileType]:
160  myDict['files'][fileType] = []
161  # Should have a dataDictionary, unless something went wrong very early...
162  for dataType, dataArg in self._trf._dataDictionary.items():
163  if isinstance(dataArg, list): # Always skip lists from the report (auxiliary files)
164  continue
165  if dataArg.auxiliaryFile: # Always skip auxilliary files from the report
166  continue
167  if fileReport[dataArg.io]:
168  entry = {"type": dataType}
169  entry.update(trfFileReport(dataArg).python(fast = fast, type = fileReport[dataArg.io]))
170  # Supress RAW if all subfiles had nentries == 0
171  if 'subFiles' in entry and len(entry['subFiles']) == 0 and isinstance(dataArg, trfArgClasses.argBSFile) :
172  msg.info('No subFiles for entry {0}, suppressing from report.'.format(entry['argName']))
173  else:
174  myDict['files'][dataArg.io].append(entry)
175 
176  # We report on all executors, in execution order
177  myDict['executor'] = []
178  if hasattr(self._trf, '_executorPath'):
179  for executionStep in self._trf._executorPath:
180  exe = self._trf._executorDictionary[executionStep['name']]
181  myDict['executor'].append(trfExecutorReport(exe).python(fast = fast))
182  # Executor resources are gathered here to unify where this information is held
183  # and allow T0/PanDA to just store this JSON fragment on its own
184  myDict['resource']['executor'][exe.name] = exeResourceReport(exe, self)
185  for mergeStep in exe.myMerger:
186  myDict['resource']['executor'][mergeStep.name] = exeResourceReport(mergeStep, self)
187  if self._dbDataTotal > 0 or self._dbTimeTotal > 0:
188  myDict['resource']['dbDataTotal'] = self._dbDataTotal
189  myDict['resource']['dbTimeTotal'] = self.roundoff(self._dbTimeTotal)
190  # Resource consumption
191  reportTime = os.times()
192 
193  # Calculate total cpu time we used -
194  myCpuTime = reportTime[0] + reportTime[1]
195  childCpuTime = reportTime[2] + reportTime[3]
196  wallTime = reportTime[4] - self._trf.transformStart[4]
197  cpuTime = myCpuTime
198  cpuTimeTotal = 0
199  cpuTimePerWorker = myCpuTime
200  maxWorkers = 1
201  msg.debug('Raw cpu resource consumption: transform {0}, children {1}'.format(myCpuTime, childCpuTime))
202  # Reduce childCpuTime by times reported in the executors (broken for MP...?)
203  for exeName, exeReport in myDict['resource']['executor'].items():
204  if 'mpworkers' in exeReport:
205  if exeReport['mpworkers'] > maxWorkers : maxWorkers = exeReport['mpworkers']
206  try:
207  msg.debug('Subtracting {0}s time for executor {1}'.format(exeReport['cpuTime'], exeName))
208  childCpuTime -= exeReport['cpuTime']
209  except TypeError:
210  pass
211  try:
212  cpuTime += exeReport['cpuTime']
213  cpuTimeTotal += exeReport['total']['cpuTime']
214  if 'cpuTimePerWorker' in exeReport:
215  msg.debug('Adding {0}s to cpuTimePerWorker'.format(exeReport['cpuTimePerWorker']))
216  cpuTimePerWorker += exeReport['cpuTimePerWorker']
217  else:
218  msg.debug('Adding nonMP cpuTime {0}s to cpuTimePerWorker'.format(exeReport['cpuTime']))
219  cpuTimePerWorker += exeReport['cpuTime']
220  except TypeError:
221  pass
222 
223  msg.debug('maxWorkers: {0}, cpuTimeTotal: {1}, cpuTimePerWorker: {2}'.format(maxWorkers, cpuTime, cpuTimePerWorker))
224  reportGenerationCpuTime = reportGenerationWallTime = None
225  if self._trf.outFileValidationStop and reportTime:
226  reportGenerationCpuTime = calcCpuTime(self._trf.outFileValidationStop, reportTime)
227  reportGenerationWallTime = calcWallTime(self._trf.outFileValidationStop, reportTime)
228 
229  myDict['resource']['transform'] = {'cpuTime': self.roundoff(myCpuTime),
230  'cpuTimeTotal': self.roundoff(cpuTimeTotal),
231  'externalCpuTime': self.roundoff(childCpuTime),
232  'wallTime': self.roundoff(wallTime),
233  'transformSetup': {'cpuTime': self.roundoff(self._trf.transformSetupCpuTime),
234  'wallTime': self.roundoff(self._trf.transformSetupWallTime)},
235  'inFileValidation': {'cpuTime': self.roundoff(self._trf.inFileValidationCpuTime),
236  'wallTime': self.roundoff(self._trf.inFileValidationWallTime)},
237  'outFileValidation': {'cpuTime': self.roundoff(self._trf.outFileValidationCpuTime),
238  'wallTime': self.roundoff(self._trf.outFileValidationWallTime)},
239  'reportGeneration': {'cpuTime': self.roundoff(reportGenerationCpuTime),
240  'wallTime': self.roundoff(reportGenerationWallTime)}, }
241  if self._trf.processedEvents:
242  myDict['resource']['transform']['processedEvents'] = self._trf.processedEvents
243  myDict['resource']['transform']['trfPredata'] = self._trf.trfPredata
244  # check for devision by zero for fast jobs, unit tests
245  if wallTime > 0:
246  myDict['resource']['transform']['cpuEfficiency'] = round(cpuTime/maxWorkers/wallTime, 4)
247  myDict['resource']['transform']['cpuPWEfficiency'] = round(cpuTimePerWorker/wallTime, 4)
248  myDict['resource']['machine'] = machineReport().python(fast = fast)
249 
250  return myDict
251 
252 
253  def classicEltree(self, fast = False):
254  trfTree = ElementTree.Element('POOLFILECATALOG')
255  # Extract some executor parameters here
256  for exeKey in ('preExec', 'postExec', 'preInclude', 'postInclude'):
257  if exeKey in self._trf.argdict:
258  for substep, pyfrag in self._trf.argdict[exeKey].value.items():
259  if substep == 'all':
260  ElementTree.SubElement(trfTree, 'META', type = 'string', name = exeKey, value = str(pyfrag))
261  else:
262  ElementTree.SubElement(trfTree, 'META', type = 'string', name = exeKey + '_' + substep, value = str(pyfrag))
263  for exeKey in ('autoConfiguration', 'AMIConfig', 'AMITag'):
264  if exeKey in self._trf.argdict:
265  if exeKey in self._metadataKeyMap:
266  classicName = self._metadataKeyMap[exeKey]
267  else:
268  classicName = exeKey
269  ElementTree.SubElement(trfTree, 'META', type = 'string', name = classicName,
270  value = str(self._trf.argdict[exeKey].value))
271 
272  # Now add information about output files
273  for dataArg in self._trf._dataDictionary.values():
274  if isinstance(dataArg, list): # Always skip lists from the report (auxiliary files)
275  continue
276  if dataArg.io == 'output':
277  for fileEltree in trfFileReport(dataArg).classicEltreeList(fast = fast):
278  trfTree.append(fileEltree)
279 
280  return trfTree
281 
282 
285  def classicPython(self, fast = False):
286  # Things we can get directly from the transform
287  trfDict = {'jobInputs' : [], # Always empty?
288  'jobOutputs' : [], # Filled in below...
289  'more' : {'Machine' : 'unknown'},
290  'trfAcronym' : trfExit.codeToName(self._trf.exitCode),
291  'trfCode' : self._trf.exitCode,
292  'trfExitCode' : self._trf.exitCode,
293  }
294 
295  if self._trf.lastExecuted is not None:
296  trfDict.update({'athAcronym' : self._trf.lastExecuted.errMsg,
297  'athCode' : self._trf.lastExecuted.rc})
298 
299 
300  # Emulate the NEEDCHECK behaviour
301  if hasattr(self._trf, '_executorPath'):
302  for executor in self._trf._executorPath:
303  if hasattr(executor, '_logScan') and self._trf.exitCode == 0:
304  if executor._logScan._levelCounter['FATAL'] > 0 or executor._logScan._levelCounter['CRITICAL'] > 0:
305  # This should not happen!
306  msg.warning('Found FATAL/CRITICAL errors and exit code 0 - reseting to TRF_LOGFILE_FAIL')
307  self._trf.exitCode = trfExit.nameToCode('TRF_LOGFILE_FAIL')
308  trfDict['trfAcronym'] = 'TRF_LOGFILE_FAIL'
309  elif executor._logScan._levelCounter['ERROR'] > 0:
310  msg.warning('Found errors in logfile scan - changing exit acronymn to NEEDCHECK.')
311  trfDict['trfAcronym'] = 'NEEDCHECK'
312 
313  # Now add files
314  fileArgs = self._trf.getFiles(io = 'output')
315  for fileArg in fileArgs:
316  # N.B. In the original Tier 0 gpickles there was executor
317  # information added for each file (such as autoConfiguration, preExec).
318  # However, Luc tells me it is ignored, so let's not bother.
319  trfDict['jobOutputs'].extend(trfFileReport(fileArg).classicPython(fast = fast))
320  # AMITag and friends is added per-file, but it's known only to the transform, so set it here:
321  for argdictKey in ('AMITag', 'autoConfiguration',):
322  if argdictKey in self._trf.argdict:
323  trfDict['jobOutputs'][-1]['more']['metadata'][argdictKey] = self._trf.argdict[argdictKey].value
324  # Mangle substep argumemts back to the old format
325  for substepKey in ('preExec', 'postExec', 'preInclude', 'postInclude'):
326  if substepKey in self._trf.argdict:
327  for substep, values in self._trf.argdict[substepKey].value.items():
328  if substep == 'all':
329  trfDict['jobOutputs'][-1]['more']['metadata'][substepKey] = values
330  else:
331  trfDict['jobOutputs'][-1]['more']['metadata'][substepKey + '_' + substep] = values
332 
333  # Now retrieve the input event count
334  nentries = 'UNKNOWN'
335  for fileArg in self._trf.getFiles(io = 'input'):
336  thisArgNentries = fileArg.nentries
337  if isinstance(thisArgNentries, int):
338  if nentries == 'UNKNOWN':
339  nentries = thisArgNentries
340  elif thisArgNentries != nentries:
341  msg.warning('Found a file with different event count than others: {0} != {1} for {2}'.format(thisArgNentries, nentries, fileArg))
342  # Take highest number?
343  if thisArgNentries > nentries:
344  nentries = thisArgNentries
345  trfDict['nevents'] = nentries
346 
347  # Tier 0 expects the report to be in a top level dictionary under the prodsys key
348  return {'prodsys' : trfDict}
349 
350  # Helper method to format values to number of decimals configured for this jobReport.
351  # Safely allows possible and valid None values within jobReport.
352  def roundoff(self, value):
353  return round(value, self._precisionDigits) if (value is not None) else value
354 
355 
356 
358  def __init__(self, executor):
359  self._exe = executor
360 
361 
364  def python(self, fast = False):
365  reportDict = {'name': self._exe.name,
366  'rc' : self._exe.rc,
367  'validation' : self._exe.isValidated,
368  'statusOK' : self._exe.hasExecuted and self._exe.isValidated and (not bool(self._exe.rc)),
369  'errMsg': self._exe.errMsg,
370  'exeConfig' : {}
371  }
372  # Add executor config information
373  for k, v in self._exe.extraMetadata.items():
374  reportDict['exeConfig'][k] = v
375 
376  # Do we have a logscan to add?
377  if hasattr(self._exe, '_logScan'):
378  try:
379  json.dumps(self._exe._logScan.python)
380  reportDict['logfileReport'] = self._exe._logScan.python
381  except UnicodeDecodeError as e:
382  msg.error('Problem with serialising logfile report as JSON - this will be skipped from the report ({0})'.format(e))
383  reportDict['metaData'] = self._exe._logScan._metaData
384 
385  # Asetup information
386  if hasattr(self._exe, '_asetup'):
387  reportDict['asetup'] = self._exe._asetup
388 
389  return reportDict
390 
391 
392 
394  # Class variable with the mapping from internal metadata keys to classic keys
395  # In the case of 'size' it's the same, but it's convenient to list it here to be able to just loop over dictionary
396  _internalToClassicMap = {'conditions_tag' : 'conditionsTag',
397  'beam_type' : 'beamType',
398  'geometry' : 'geometryVersion',
399  'nentries' : 'events',
400  'file_size': 'size',
401  }
402  # Similar for Tier 0 file metadata keys
403  _internalToGpickleMap = {'file_guid' : 'GUID',
404  'checkSum' : 'checkSum', # We don't have this, but Tier 0 want it in the dictionary
405  'nentries' : 'events',
406  'file_size' : 'size',
407  }
408  # Keep a separate map for the data which Tier 0 gets in the 'more : {}' dictionary
409  _internalToGpickleMoreMap = {'beam_type' : 'beamType',
410  'conditions_tag' : 'conditionsTag',
411  'geometry' : 'geometryVersion',
412  }
413 
414 
416  def __init__(self, fileArg):
417  self._fileArg = fileArg
418 
419 
424  def python(self, fast = False, type = 'full'):
425  # First entity contains shared properties - same for all files in this argFile
426  if type == 'name':
427  fileArgProps = {'dataset': self._fileArg.dataset,
428  'nentries': self._fileArg.getnentries(fast),
429  'subFiles' : []}
430  elif type == 'full':
431  fileArgProps = {'dataset' : self._fileArg.dataset,
432  'type' : self._fileArg.type,
433  'subFiles' : [],
434  'argName' : self._fileArg.name,
435  }
436  else:
437  raise trfExceptions.TransformReportException(trfExit.nameToCode('TRF_INTERNAL_REPORT_ERROR'),
438  'Unknown file report type ({0}) in the file report for {1}'.format(type, self._fileArg))
439 
440 
443  uniqueBasenames = set([ os.path.basename(fname) for fname in self._fileArg.value ])
444  uniqueDirectories = set([ os.path.dirname(os.path.relpath(os.path.normpath(fname))) for fname in self._fileArg.value ])
445  if len(uniqueBasenames) != len(self._fileArg.value):
446  msg.info('Detected two files with the same basename in a file argument - report for file {0} will be produced with the path as a key'.format(self._fileArg))
447  basenameReport = False
448  elif len(uniqueDirectories) > 1:
449  msg.warning('Detected output files in different directories - report for file {0} will be produced with the path as a key'.format(self._fileArg))
450  basenameReport = False
451  else:
452  basenameReport = True
453  suppressed = []
454  for fname in self._fileArg.value:
455  subFile = None
456  if basenameReport:
457  subFile = self.singleFilePython(fname, fast = fast, type = type)
458  else:
459  subFile = self.singleFilePython(fname, fast = fast, type = type, basename = False)
460  if subFile is not None:
461  # if nentries == 0 for DRAW, suppress subfile from report
462  if 'nentries' in subFile and subFile['nentries'] == 0 and isinstance(self._fileArg, trfArgClasses.argBSFile):
463  msg.info('Suppressing file {0}, nentries is 0'.format(subFile['name']))
464  suppressed.append(subFile['name'])
465  else:
466  fileArgProps['subFiles'].append(subFile)
467 
468  return fileArgProps
469 
470 
475  def singleFilePython(self, filename, fast = False, type = 'full', basename = True):
476  if filename not in self._fileArg.value:
477  raise trfExceptions.TransformReportException(trfExit.nameToCode('TRF_INTERNAL_REPORT_ERROR'),
478  'Unknown file ({0}) in the file report for {1}'.format(filename, self._fileArg))
479  if basename:
480  entry = {'name': os.path.basename(filename)}
481  else:
482  entry = {'name': os.path.relpath(os.path.normpath(filename))}
483  if type == 'name':
484  # For 'name' we return only the GUID
485  entry.update(self._fileArg.getMetadata(files = filename, populate = not fast, metadataKeys = ['file_guid'])[filename])
486  elif type == 'full':
487  # Suppress io because it's the key at a higher level and _exists because it's internal
488  entry.update(self._fileArg.getMetadata(files = filename, populate = not fast, maskMetadataKeys = ['io', '_exists', 'integrity', 'file_type'])[filename])
489  else:
490  raise trfExceptions.TransformReportException(trfExit.nameToCode('TRF_INTERNAL_REPORT_ERROR'),
491  'Unknown file report type ({0}) in the file report for {1}'.format(type, self._fileArg))
492 
493  return entry
494 
495 
496 
501  def classicEltreeList(self, fast = False):
502  treeList = []
503  for fname in self._fileArg.value:
504  treeList.append(self.classicSingleEltree(fname, fast = fast))
505 
506  return treeList
507 
508 
509 
512  def classicSingleEltree(self, filename, fast = False):
513  if filename not in self._fileArg.value:
514  raise trfExceptions.TransformReportException(trfExit.nameToCode('TRF_INTERNAL_REPORT_ERROR'),
515  'Unknown file ({0}) in the file report for {1}'.format(filename, self._fileArg))
516  tree = ElementTree.Element('File', ID = str(self._fileArg.getSingleMetadata(fname = filename, metadataKey = 'file_guid', populate = not fast)))
517  for myKey, classicKey in self._internalToClassicMap.items():
518  # beam_type is tricky - we return only the first list value,
519  # (but remember, protect against funny stuff!)
520  if myKey == 'beam_type':
521  beamType = self._fileArg.getSingleMetadata(fname = filename, metadataKey = myKey, populate = not fast)
522  if isinstance(beamType, list):
523  if len(beamType) == 0:
524  ElementTree.SubElement(tree, 'metadata', att_name = classicKey, att_value = '')
525  else:
526  ElementTree.SubElement(tree, 'metadata', att_name = classicKey, att_value = str(beamType[0]))
527  else:
528  # This is really not normal, but best we can do is str conversion
529  ElementTree.SubElement(tree, 'metadata', att_name = classicKey, att_value = str(beamType))
530  else:
531  ElementTree.SubElement(tree, 'metadata', att_name = classicKey,
532  att_value = str(self._fileArg.getSingleMetadata(fname = filename, metadataKey = myKey, populate = not fast)))
533  # Now add the metadata which is stored at the whole argument level
534  ElementTree.SubElement(tree, 'metadata', att_name = 'fileType', att_value = str(self._fileArg.type))
535  if self._fileArg.dataset is not None:
536  ElementTree.SubElement(tree, 'metadata', att_name = 'dataset', att_value = self._fileArg.dataset)
537 
538  return tree
539 
540 
543  def classicPython(self, fast = False):
544  fileList = []
545  for fname in self._fileArg.value:
546  fileList.append(self.classicSinglePython(fname, fast = fast))
547  return fileList
548 
549 
552  def classicSinglePython(self, filename, fast = False):
553  if filename not in self._fileArg.value:
554  raise trfExceptions.TransformReportException(trfExit.nameToCode('TRF_INTERNAL_REPORT_ERROR'),
555  'Unknown file ({0}) in the file report for {1}'.format(filename, self._fileArg))
556  # Direct population of some keys
557  fileDict = {'lfn' : filename,
558  'dataset' : self._fileArg.dataset,
559  }
560  # Fill in the mapped 'primary' keys
561  for myKey, classicKey in self._internalToGpickleMap.items():
562  fileDict[classicKey] = self._fileArg.getSingleMetadata(fname = filename, metadataKey = myKey, populate = not fast)
563  if classicKey == 'checkSum' and fileDict[classicKey] == 'UNDEFINED':
564  # Old style is that we give back None when we don't know
565  fileDict[classicKey] = None
566  elif fileDict[classicKey] == 'UNDEFINED':
567  # Suppress things we don't generally expect to know
568  del fileDict[classicKey]
569  # Base 'more' stuff which is known by the argFile itself
570  fileDict['more'] = {'metadata' : {'fileType' : self._fileArg.type}}
571  for myKey, classicKey in self._internalToGpickleMoreMap.items():
572  value = self._fileArg.getSingleMetadata(fname = filename, metadataKey = myKey, populate = not fast)
573  if value != 'UNDEFINED':
574  fileDict['more']['metadata'][classicKey] = value
575 
576  return fileDict
577 
578 
579 
581 
582  def python(self, fast = False):
583  machine = {}
584  # Get the following from the platform module
585  attrs = ['node', 'platform']
586  for attr in attrs:
587  try:
588  machine[attr] = getattr(platform, attr).__call__()
589  except AttributeError as e:
590  msg.warning('Failed to get "{0}" attribute from platform module: {1}'.format(attr, e))
591 
592  attrs = ['linux_distribution']
593  for attr in attrs:
594  try:
595  machine[attr] = getattr(distro, attr).__call__()
596  except (AttributeError,NameError) as e:
597  msg.warning('Failed to get "{0}" attribute from platform module: {1}'.format(attr, e))
598 
599  # Now try to get processor information from /proc/cpuinfo
600  try:
601  with open('/proc/cpuinfo') as cpuinfo:
602  for line in cpuinfo:
603  try:
604  k, v = [ l.strip() for l in line.split(':') ]
605  if k == 'cpu family' and 'cpu_family' not in machine:
606  machine['cpu_family'] = v
607  elif k == 'model' and 'model' not in machine:
608  machine['model'] = v
609  elif k == 'model name' and 'model_name' not in machine:
610  machine['model_name'] = v
611  except ValueError:
612  pass
613  except Exception as e:
614  msg.warning('Unexpected error while parsing /proc/cpuinfo: {0}'.format(e))
615  try:
616  with open('/etc/machinefeatures/hs06') as hs:
617  machine['hepspec'] = hs.readlines()[0].strip()
618  except IOError:
619  pass
620  return machine
621 
622 
623 
624 def pyJobReportToFileDict(jobReport, io = 'all'):
625  dataDict = {}
626  if 'files' not in jobReport:
627  msg.warning('Job report has no "files" section')
628  return dataDict
629  for iotype in jobReport['files']:
630  if io == 'all' or io == iotype:
631  for filedata in jobReport['files'][iotype]:
632  dataDict[filedata['type']] = filedata
633  return dataDict
634 
635 
636 def exeResourceReport(exe, report):
637  exeResource = {'cpuTime': report.roundoff(exe.cpuTime),
638  'wallTime': report.roundoff(exe.wallTime),
639  'preExe': {
640  'cpuTime': report.roundoff(exe.preExeCpuTime),
641  'wallTime': report.roundoff(exe.preExeWallTime),
642  },
643  'postExe': {
644  'cpuTime': report.roundoff(exe.postExeCpuTime),
645  'wallTime': report.roundoff(exe.postExeWallTime),
646  },
647  'validation': {
648  'cpuTime': report.roundoff(exe.validationCpuTime),
649  'wallTime': report.roundoff(exe.validationWallTime),
650  },
651  'total': {
652  'cpuTime': report.roundoff(exe.cpuTimeTotal),
653  'wallTime': report.roundoff(exe.wallTimeTotal),
654  },
655  }
656 
657  if exe.memStats:
658  exeResource['memory'] = exe.memStats
659  if exe.memAnalysis:
660  exeResource['memoryAnalysis'] = exe.memAnalysis
661  if exe.eventCount:
662  exeResource['nevents'] = exe.eventCount
663  if exe.name=='ReSim':
664  exeResource['resimevents'] = exe.reSimEvent
665  if exe.athenaMP:
666  exeResource['mpworkers'] = exe.athenaMP
667  exeResource['cpuTimePerWorker'] = report.roundoff(exe.cpuTime/exe.athenaMP)
668  if exe.dbMonitor:
669  exeResource['dbData'] = exe.dbMonitor['bytes']
670  exeResource['dbTime'] = report.roundoff(exe.dbMonitor['time'])
671  report._dbDataTotal += exeResource['dbData']
672  report._dbTimeTotal += exeResource['dbTime']
673  return exeResource
python.trfReports.trfFileReport.classicSingleEltree
def classicSingleEltree(self, filename, fast=False)
Return file metadata in 'classic' POOLFILECATALOG format.
Definition: trfReports.py:512
python.trfReports.trfFileReport.singleFilePython
def singleFilePython(self, filename, fast=False, type='full', basename=True)
Return unique metadata for a single file in an argFile class.
Definition: trfReports.py:475
vtune_athena.format
format
Definition: vtune_athena.py:14
python.trfReports.trfReport._dataDictionary
_dataDictionary
Definition: trfReports.py:41
python.trfReports.trfFileReport.classicPython
def classicPython(self, fast=False)
Return file metadata in classic Tier 0 python style.
Definition: trfReports.py:543
python.trfReports.trfJobReport.classicPython
def classicPython(self, fast=False)
Classic Tier 0 metadata python object.
Definition: trfReports.py:285
python.trfReports.trfFileReport._internalToClassicMap
_internalToClassicMap
Definition: trfReports.py:396
python.trfReports.trfReport
Base (almost virtual) report from which all real transform reports derive.
Definition: trfReports.py:39
python.trfReports.trfExecutorReport
Class to contain metadata for an executor.
Definition: trfReports.py:357
python.trfUtils.prettyXML
def prettyXML(element, indent=' ', poolFileCatalogFormat=False)
XML pretty print an ElementTree.ELement object.
Definition: trfUtils.py:397
python.trfUtils.calcCpuTime
def calcCpuTime(start, stop)
Definition: trfUtils.py:1683
python.trfReports.trfJobReport.roundoff
def roundoff(self, value)
Definition: trfReports.py:352
MuonGM::round
float round(const float toRound, const unsigned int decimals)
Definition: Mdt.cxx:27
python.trfReports.trfReport.json
def json(self, fast=False)
Method which returns a JSON representation of a report.
Definition: trfReports.py:58
python.trfReports.trfJobReport.python
def python(self, fast=False, fileReport=defaultFileReport)
generate the python transform job report
Definition: trfReports.py:140
python.trfReports.trfJobReport
Class holding a transform job report.
Definition: trfReports.py:117
python.trfReports.trfJobReport._dbTimeTotal
_dbTimeTotal
Definition: trfReports.py:132
python.trfReports.trfJobReport._reportVersion
_reportVersion
This is the version counter for transform job reports any changes to the format must be reflected by ...
Definition: trfReports.py:120
python.trfReports.pyJobReportToFileDict
def pyJobReportToFileDict(jobReport, io='all')
Small helper function to extract a per-datatype dictionary from a job report of lists.
Definition: trfReports.py:624
PyJobTransforms.trfArgClasses
Transform argument class definitions.
python.trfReports.trfFileReport._fileArg
_fileArg
Definition: trfReports.py:417
python.trfReports.trfJobReport._precisionDigits
_precisionDigits
Definition: trfReports.py:130
PyJobTransforms.trfExitCodes
Module for transform exit codes.
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.trfUtils.isodate
def isodate()
Return isoformated 'now' string.
Definition: trfUtils.py:434
python.trfReports.trfJobReport.classicEltree
def classicEltree(self, fast=False)
Classic metadata.xml report.
Definition: trfReports.py:253
python.trfReports.trfJobReport._trf
_trf
Definition: trfReports.py:129
python.trfReports.trfReport.__str__
def __str__(self)
String representation of the job report.
Definition: trfReports.py:47
python.trfReports.trfReport.writeGPickleReport
def writeGPickleReport(self, filename, fast=False)
Definition: trfReports.py:100
python.trfReports.trfJobReport.__init__
def __init__(self, parentTrf)
Constructor.
Definition: trfReports.py:127
python.trfReports.trfJobReport._metadataKeyMap
_metadataKeyMap
Definition: trfReports.py:121
python.trfReports.trfReport.classicPython
def classicPython(self, fast=False)
Method which returns a python representation of a report in classic Tier 0 style.
Definition: trfReports.py:68
python.trfReports.trfFileReport.classicEltreeList
def classicEltreeList(self, fast=False)
Return an element tree representation of the classic file report @detail Returns a list of eltree ent...
Definition: trfReports.py:501
getMetadata
Definition: getMetadata.py:1
python.trfReports.trfJobReport._dbDataTotal
_dbDataTotal
Definition: trfReports.py:131
python.trfArgClasses.argBSFile
ByteStream file class.
Definition: trfArgClasses.py:1358
python
Definition: AtlasTest/TestTools/python/__init__.py:1
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.trfReports.trfReport.writePilotPickleReport
def writePilotPickleReport(self, filename, fast=False, fileReport=defaultFileReport)
Definition: trfReports.py:108
python.trfReports.trfExecutorReport._exe
_exe
Definition: trfReports.py:359
python.trfReports.machineReport.python
def python(self, fast=False)
Definition: trfReports.py:582
python.trfReports.trfJobReport._maxMsgLen
_maxMsgLen
Definition: trfReports.py:122
python.trfReports.machineReport
Report on the machine where we ran.
Definition: trfReports.py:580
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:224
python.trfReports.trfFileReport.classicSinglePython
def classicSinglePython(self, filename, fast=False)
Return single file metadata in classic Tier 0 python style.
Definition: trfReports.py:552
python.trfReports.exeResourceReport
def exeResourceReport(exe, report)
Definition: trfReports.py:636
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.trfReports.trfFileReport._internalToGpickleMap
_internalToGpickleMap
Definition: trfReports.py:403
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
python.trfReports.trfJobReport._truncationMsg
_truncationMsg
Definition: trfReports.py:123
python.trfReports.trfReport.__init__
def __init__(self)
Definition: trfReports.py:40
python.trfUtils.calcWallTime
def calcWallTime(start, stop)
Definition: trfUtils.py:1691
python.trfReports.trfFileReport._internalToGpickleMoreMap
_internalToGpickleMoreMap
Definition: trfReports.py:409
Trk::open
@ open
Definition: BinningType.h:40
python.trfReports.trfReport.writeTxtReport
def writeTxtReport(self, filename, dumpEnv=True, fast=False, fileReport=defaultFileReport)
Definition: trfReports.py:84
python.trfReports.trfExecutorReport.__init__
def __init__(self, executor)
Definition: trfReports.py:358
python.trfReports.trfReport.python
def python(self, fast=False, fileReport=defaultFileReport)
Method which returns a python representation of a report.
Definition: trfReports.py:52
PyJobTransforms.trfUtils
Transform utility functions.
python.trfReports.trfExecutorReport.python
def python(self, fast=False)
Get a python representation of executor report.
Definition: trfReports.py:364
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
pickleTool.object
object
Definition: pickleTool.py:30
str
Definition: BTagTrackIpAccessor.cxx:11
python.trfExceptions.TransformReportException
Group of validation exceptions.
Definition: trfExceptions.py:54
python.trfUtils.shQuoteStrings
def shQuoteStrings(strArray=sys.argv)
Quote a string array so that it can be echoed back on the command line in a cut 'n' paste safe way.
Definition: trfUtils.py:361
python.trfReports.trfFileReport.__init__
def __init__(self, fileArg)
Constructor.
Definition: trfReports.py:416
python.trfReports.trfReport.writeClassicXMLReport
def writeClassicXMLReport(self, filename, fast=False)
Definition: trfReports.py:104
python.trfReports.trfReport.classicEltree
def classicEltree(self, fast=False)
Method which returns an ElementTree.Element representation of the old POOLFILECATALOG report.
Definition: trfReports.py:63
xAOD::bool
setBGCode setTAP setLVL2ErrorBits bool
Definition: TrigDecision_v1.cxx:60
python.trfReports.trfReport.writeJSONReport
def writeJSONReport(self, filename, sort_keys=True, indent=2, fast=False, fileReport=defaultFileReport)
Definition: trfReports.py:71
python.trfReports.trfFileReport.python
def python(self, fast=False, type='full')
Get a python representation of file report @detail Returns the python representation of this file rep...
Definition: trfReports.py:424
python.trfReports.trfFileReport
Class to contain metadata for file types.
Definition: trfReports.py:393