ATLAS Offline Software
Loading...
Searching...
No Matches
trfReports.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
10
11__version__ = '$Revision: 784023 $'
12
13import pickle as pickle
14import json
15import os.path
16import platform
17import pprint
18import sys
19try:
20 import distro
21except ImportError:
22 pass
23
24from xml.etree import ElementTree
25
26import logging
27msg = logging.getLogger(__name__)
28
29import PyJobTransforms.trfExceptions as trfExceptions
30import PyJobTransforms.trfArgClasses as trfArgClasses
31
32from PyJobTransforms.trfExitCodes import trfExit
33from PyJobTransforms.trfUtils import shQuoteStrings, isodate, prettyXML, calcCpuTime, calcWallTime
34
35
36defaultFileReport = {'input': 'name', 'temporary': None, 'output': 'full'}
37
38
40 def __init__(self):
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
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
624def 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
636def 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
nlohmann::json json
void print(char *figname, TCanvas *c1)
Report on the machine where we ran.
Class to contain metadata for an executor.
Class to contain metadata for file types.
Class holding a transform job report.
str _reportVersion
This is the version counter for transform job reports any changes to the format must be reflected by ...
classicPython(self, fast=False)
Classic Tier 0 metadata python object.
classicEltree(self, fast=False)
Classic metadata.xml report.
__init__(self, parentTrf)
Constructor.
Base (almost virtual) report from which all real transform reports derive.
Definition trfReports.py:39
classicEltree(self, fast=False)
Method which returns an ElementTree.Element representation of the old POOLFILECATALOG report.
Definition trfReports.py:63
__str__(self)
String representation of the job report.
Definition trfReports.py:47
classicPython(self, fast=False)
Method which returns a python representation of a report in classic Tier 0 style.
Definition trfReports.py:68
writeGPickleReport(self, filename, fast=False)
writeClassicXMLReport(self, filename, fast=False)
writeTxtReport(self, filename, dumpEnv=True, fast=False, fileReport=defaultFileReport)
Definition trfReports.py:84
writeJSONReport(self, filename, sort_keys=True, indent=2, fast=False, fileReport=defaultFileReport)
Definition trfReports.py:71
writePilotPickleReport(self, filename, fast=False, fileReport=defaultFileReport)
STL class.
Transform argument class definitions.
Module for transform exit codes.
Transform utility functions.
exeResourceReport(exe, report)
pyJobReportToFileDict(jobReport, io='all')
Small helper function to extract a per-datatype dictionary from a job report of lists.