1 # -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2023 CEA, EDF
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # Lesser General Public License for more details.
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 from collections import defaultdict
27 def GetRepresentationOfTimeDelta(cls,endTime, startTime):
28 if endTime is None and startTime is None:
30 td = endTime - startTime
32 ts_of_td = time.gmtime(td.total_seconds())
33 return "{}.{:06d}".format(time.strftime("%H:%M:%S",ts_of_td),td.microseconds)
36 def MemRepr(cls,memInByte):
38 UNITS=["B","kB","MB","GB"]
41 for i in range( len(UNITS) ):
43 oss = "{:03d}".format( int( (remain/1024)*1000 ) )
44 oss = "{}.{} {}".format(m,oss,UNITS[i])
50 return "{} {}".format(m,UNITS[3])
53 def SpeedRepr(cls,memInBytePerS):
54 return "{}/s".format( cls.MemRepr(memInBytePerS) )
57 self._measure_time_resolution_ms = None
58 self._cpu_mem_during_exec = None
59 self._start_exec_time = None
60 self._end_exec_time = None
61 self._start_input_time = None
62 self._end_input_time = None
63 self._start_output_time = None
64 self._end_output_time = None
66 self._input_hdd_mem = None
68 self._output_hdd_mem = None
69 self._start_pos_log = None
70 self._stop_pos_log = None
71 self._freestyle_log = []
75 return self._freestyle_log
78 def freestyle(self, value):
79 self._freestyle_log.append( value )
82 def measureTimeResolution(self):
83 return self._measure_time_resolution_ms
85 @measureTimeResolution.setter
86 def measureTimeResolution(self, value):
87 self._measure_time_resolution_ms = value
90 def tracePosStart(self):
91 return self._start_pos_log
94 def tracePosStart(self,value):
95 self._start_pos_log = value
98 def tracePosStop(self):
99 return self._stop_pos_log
102 def tracePosStop(self,value):
103 self._stop_pos_log = value
106 def CPUMemDuringExec(self):
107 return self._cpu_mem_during_exec
109 @CPUMemDuringExec.setter
110 def CPUMemDuringExec(self,value):
111 self._cpu_mem_during_exec = value
114 def CPUMemDuringExecStr(self):
115 cpu = self._cpu_mem_during_exec[::2]
116 mem_rss = self._cpu_mem_during_exec[1::2]
117 return [(a,ScriptExecInfo.MemRepr(b)) for a,b in self._cpu_mem_during_exec]
121 return self._input_mem
124 def inputMem(self,value):
125 self._input_mem = value
128 def inputMemStr(self):
129 return ScriptExecInfo.MemRepr( self.inputMem )
133 return self._output_mem
136 def outputMem(self,value):
137 self._output_mem = value
140 def outputMemStr(self):
141 return ScriptExecInfo.MemRepr( self.outputMem )
143 def inputReadHDDSize(self):
144 return self.inputHDDMem.getSizeOfFileRead()
146 def inputReadHDDSizeRepr(self):
147 return ScriptExecInfo.MemRepr( self.inputReadHDDSize() )
149 def inputReadHDDSpeed(self):
150 return self.inputReadHDDSize() / ( self.endInputTime - self.startInputTime ).total_seconds()
152 def inputReadHDDSpeedRepr(self):
153 return ScriptExecInfo.SpeedRepr( self.inputReadHDDSpeed() )
155 def outputWriteHDDSize(self):
156 return self.outputHDDMem.getSizeOfFileRead()
158 def outputWriteHDDSizeRepr(self):
159 return ScriptExecInfo.MemRepr( self.outputWriteHDDSize() )
161 def outputWriteHDDSpeed(self):
162 return self.outputWriteHDDSize() / ( self.endOutputTime - self.startOutputTime ).total_seconds()
164 def outputWriteHDDSpeedRepr(self):
165 return ScriptExecInfo.SpeedRepr( self.outputWriteHDDSpeed() )
168 def inputHDDMem(self):
169 return self._input_hdd_mem
172 def inputHDDMem(self,value):
173 self._input_hdd_mem = value
176 def inputHDDMemStr(self):
177 if self._input_hdd_mem is None:
178 return "not computed"
179 return " ".join( [ ScriptExecInfo.MemRepr( elt.getSizeOfFileRead() ) for elt in self._input_hdd_mem] )
182 def outputHDDMem(self):
183 return self._output_hdd_mem
186 def outputHDDMem(self,value):
187 self._output_hdd_mem = value
190 def outputHDDMemStr(self):
191 if self._output_hdd_mem is None:
192 return "not computed"
193 return " ".join( [ ScriptExecInfo.MemRepr( elt.getSizeOfFileRead() ) for elt in self._output_hdd_mem] )
196 def startInputTime(self):
197 return self._start_input_time
199 @startInputTime.setter
200 def startInputTime(self,value):
201 self._start_input_time = value
204 def endInputTime(self):
205 return self._end_input_time
208 def endInputTime(self,value):
209 self._end_input_time = value
212 def startExecTime(self):
213 return self._start_exec_time
215 @startExecTime.setter
216 def startExecTime(self,value):
217 self._start_exec_time = value
220 def endExecTime(self):
221 return self._end_exec_time
224 def endExecTime(self,value):
225 self._end_exec_time = value
229 if ( self.endExecTime is not None ) and (self.startExecTime is not None):
230 return self.endExecTime - self.startExecTime
234 def fullExecTime(self):
235 return self.endOutputTime - self.startInputTime
238 def startOutputTime(self):
239 return self._start_output_time
241 @startOutputTime.setter
242 def startOutputTime(self,value):
243 self._start_output_time = value
246 def endOutputTime(self):
247 return self._end_output_time
249 @endOutputTime.setter
250 def endOutputTime(self,value):
251 self._end_output_time = value
254 def execTimeStr(self):
255 return ScriptExecInfo.GetRepresentationOfTimeDelta(self.endExecTime,self.startExecTime)
258 def inputTimeStr(self):
259 return ScriptExecInfo.GetRepresentationOfTimeDelta(self.endInputTime,self.startInputTime)
262 def outputTimeStr(self):
263 return ScriptExecInfo.GetRepresentationOfTimeDelta(self.endOutputTime,self.startOutputTime)
266 CPUMemDuringExecForStr = self.CPUMemDuringExecStr
267 if len( CPUMemDuringExecForStr ) > 30:
268 CPUMemDuringExecForStr = "{} ...".format( str(CPUMemDuringExecForStr[:30]) )
270 CPUMemDuringExecForStr = str( CPUMemDuringExecForStr )
271 return """start exec time = {self.startExecTime}
272 end exec time = {self.endExecTime}
273 exec_time = {self.execTimeStr}
274 Measure time resolution = {self.measureTimeResolution} ms
275 CPU and mem monitoring = {CPUMemDuringExecForStr}
276 input unpickling and ev load from disk time = {self.inputTimeStr}
277 output serialization and ev write to disk time = {self.outputTimeStr}
278 input memory size before exec (MemoryPeak 2x) = {self.inputMemStr}
279 input memory size from HDD = {self.inputHDDMemStr}
280 output memory size after exec (MemoryPeak 2x) = {self.outputMemStr}
281 output memory size from HDD = {self.outputHDDMemStr}
282 Start position in log = {self.tracePosStart}
283 End position in log = {self.tracePosStop}""".format(**locals())
285 class ScriptExecInfoDeco:
286 def __init__(self, eff, father):
288 self._father = father
294 def __getitem__(self,i):
297 return self._eff.__str__()
299 return self._eff.__repr__()
301 with open(self.father.father.logfile,"rb") as f:
303 return cont[self._eff.tracePosStart:self._eff.tracePosStop].decode()
306 return self.father.get().nodeName
309 return self.father.code
312 return self.father.father.ns_entry
314 def computingNode(self):
315 return self.father.computingNode
319 return self.get().freestyle
322 def measureTimeResolution(self):
323 return self.get().measureTimeResolution
326 def CPUMemDuringExec(self):
327 return self.get().CPUMemDuringExec
331 return self.get().inputMem
335 return self.get().outputMem
338 def inputHDDMem(self):
339 return self.get().inputHDDMem
342 def outputHDDMem(self):
343 return self.get().outputHDDMem
345 def inputReadHDDSize(self):
346 return self.get().inputReadHDDSize()
348 def inputReadHDDSizeRepr(self):
349 return self.get().inputReadHDDSizeRepr()
351 def inputReadHDDSpeed(self):
352 return self.get().inputReadHDDSpeed()
354 def inputReadHDDSpeedRepr(self):
355 return self.get().inputReadHDDSpeedRepr()
357 def outputWriteHDDSize(self):
358 return self.get().outputWriteHDDSize()
360 def outputWriteHDDSizeRepr(self):
361 return self.get().outputWriteHDDSizeRepr()
363 def outputWriteHDDSpeed(self):
364 return self.get().outputWriteHDDSpeed()
366 def outputWriteHDDSpeedRepr(self):
367 return self.get().outputWriteHDDSpeedRepr()
370 def startInputTime(self):
371 return self.get().startInputTime
374 def endInputTime(self):
375 return self.get().endInputTime
378 def startExecTime(self):
379 return self.get().startExecTime
382 def endExecTime(self):
383 return self.get().endExecTime
387 return self.get().execTime
390 def fullExecTime(self):
391 return self.get().fullExecTime
394 def startOutputTime(self):
395 return self.get().startOutputTime
398 def endOutputTime(self):
399 return self.get().endOutputTime
401 class ScriptInfoAbstract:
402 def __init__(self, scriptPtr):
403 self._node_name = scriptPtr.getName()
404 self._code = scriptPtr.getCode()
405 self._exec = [pickle.loads(elt.getObj()) for elt in scriptPtr.listOfExecs()]
413 return self._node_name
420 def code(self,value):
424 return len( self._exec )
426 def __getitem__(self,i):
430 return """name = {self.nodeName}\ncode = {self.code}\nexecs = {self.execs}""".format(**locals())
433 return """ScriptInfo \"{self.nodeName}\"""".format(**locals())
435 class ScriptInfoClt(ScriptInfoAbstract):
436 def __init__(self, scriptPtr):
437 self._node_name = scriptPtr.getName()
438 self._code = scriptPtr.getCode()
439 self._exec = [pickle.loads(elt.getObj()) for elt in scriptPtr.listOfExecs()]
441 class ScriptInfo(ScriptInfoAbstract):
442 def __init__(self, nodeName, code, execs):
443 self._node_name = nodeName
447 class ScriptInfoDeco:
448 def __init__(self, eff, father):
450 self._father = father
458 return self.get().nodeName
461 return self.get().code
464 return self.father.ns_entry
466 def computingNode(self):
467 return self.father.computingNode
468 def __getitem__(self,i):
469 return ScriptExecInfoDeco( self._eff[i], self )
471 return self._eff.__len__()
473 return self._eff.__str__()
475 return self._eff.__repr__()
477 class ContainerLogInfoAbstract:
481 with open(self.logfile,"rb") as f:
487 return self._ns_entry
491 return self._log_file
494 def computingNode(self):
495 return ContainerLogInfoAbstract.ComputingNodeFromNSEntry( self.ns_entry )
498 return len( self._scripts )
500 def __getitem__(self,i):
501 return ScriptInfoDeco( self._scripts[i], self)
504 return """NS entry = {self.ns_entry} LogFile = {self.logfile}""".format(**locals())
507 def ComputingNodeFromNSEntry(cls, nsEntry):
508 return nsEntry.split("/")[2]
510 class ContainerLogInfoClt(ContainerLogInfoAbstract):
511 def __init__(self,contLogPtr):
512 self._log_file = contLogPtr.getLogFile()
513 self._ns_entry = contLogPtr.getContainerEntryInNS()
514 self._scripts = [ScriptInfoClt(elt) for elt in contLogPtr.listOfScripts()]
516 class ContainerLogInfo(ContainerLogInfoAbstract):
517 def __init__(self, nsEntry, logFile, scripts):
518 self._log_file = logFile
519 self._ns_entry = nsEntry
520 self._scripts = scripts
522 from abc import ABC, abstractmethod
524 class InOutputObjVisitorAbstract(ABC):
530 self._cur_obj = ObjMemModel()
534 self._data.append( self._cur_obj )
537 def getSizeOfFileRead(self):
538 return sum( [elt.getSizeOfFileRead() for elt in self._data] )
543 def setHDDMem(self, v):
546 def setFileName(self, fileName):
553 class InOutputObjVisitorIter:
554 def __init__(self, visitor):
555 self._visitor = visitor
559 if self._current >= len(self._visitor._data):
562 ret = self._visitor._data[ self._current ]
566 class InOutputObjVisitor(InOutputObjVisitorAbstract):
571 return self.getSizeOfFileRead()
574 return InOutputObjVisitorIter(self)
576 class ObjMemModel(InOutputObjVisitorAbstract):
580 self._file_name = None
582 def setHDDMem(self, v):
586 def setFileName(self, fileName):
587 self._file_name = fileName
590 def getSizeOfFileRead(self):
591 if hasattr(self,"_data"):
592 return super().getSizeOfFileRead()
597 return self.getSizeOfFileRead()
599 class FakeObjVisitor:
600 def setHDDMem(self, v):
603 def setFileName(self, fileName):
609 class InOutputObjVisitorCM:
610 def __init__(self, visitor):
611 self._visitor = visitor
614 r = self._visitor.enter()
617 return FakeObjVisitor()
618 def __exit__(self,exctype, exc, tb):
620 self._visitor.leave()
624 def __init__(self,i):
628 def __iadd__(self,delta):
632 def unserializeInt(structData, offset):
633 from ctypes import c_int
635 sz = c_int.from_buffer_copy( structData[int(offset):int(offset)+sz_of_cint] ).value
639 def unserializeString(structData,offset):
640 sz = unserializeInt(structData,offset)
641 ret = structData[int(offset):int(offset)+sz].decode()
645 def unserializeContainerScriptExecPerfLog(structData, offset):
647 sz = unserializeInt(structData,offset)
650 inst = pickle.loads( structData[int(offset):int(offset)+sz] )
654 def unserializeContainerScriptPerfLog(structData, offset):
655 name = unserializeString(structData,offset)
656 code = unserializeString(structData,offset)
657 numberOfSessions = unserializeInt(structData,offset)
659 for _ in range(numberOfSessions):
660 session = unserializeContainerScriptExecPerfLog(structData,offset)
661 sessions.append( session )
662 return ScriptInfo(name,code,sessions)
664 def unserializeContainerPerfLog(structData, offset):
665 nsEntry = unserializeString(structData,offset)
666 logFile = unserializeString(structData,offset)
668 nbScripts = unserializeInt(structData,offset)
669 for _ in range(nbScripts):
670 script = unserializeContainerScriptPerfLog(structData,offset)
671 scripts.append( script )
672 return ContainerLogInfo(nsEntry,logFile,scripts)
674 def unserializeLogManager(structData):
675 offset = OffsetType(0)
676 numberOfScripts = unserializeInt(structData,offset)
678 for _ in range(numberOfScripts):
679 containerPerfLogInst = unserializeContainerPerfLog(structData,offset)
680 logManagerInst.append( containerPerfLogInst )
681 if int(offset) != len(structData):
682 raise RuntimeError("Something went wrong during unserialization phase.")
683 return logManagerInst
685 def ListAllExecContainIn( listOfContainerLogInfo ):
687 For all ContainerLogInfo contained in listOfContainerLogInfo extract all ScriptExecInfo contained recursively
688 in it. This method filters all "side" executions like those positionning environment for exemple.
690 See also : ListAllExecFinishedContainIn
695 listOfContainerLogInfo (list<ContainerLogInfo>) : instance typically returned by salome.LogManagerLoadFromFile
700 list<ScriptExecInfoDeco> : all ScriptExecInfoDeco instance contained recursively in all input ContainerLogInfo instances
703 allexecs = sum( [sum( [[myexec for myexec in ps] for ps in cont],[] ) for cont in listOfContainerLogInfo], [] )
704 return [elt for elt in allexecs if elt.get() is not None]
706 def ListAllExecFinishedContainIn( listOfContainerLogInfo ):
708 For all ContainerLogInfo contained in listOfContainerLogInfo extract all ScriptExecInfo contained recursively
709 in it. This method filters all "side" executions like those positionning environment for exemple and those not finished.
711 See also : ListAllExecContainIn
716 listOfContainerLogInfo (list<ContainerLogInfo>) : instance typically returned by salome.LogManagerLoadFromFile
721 list<ScriptExecInfoDeco> : all ScriptExecInfoDeco instance contained recursively in all input ContainerLogInfo instances
724 beforeFiltering = ListAllExecContainIn( listOfContainerLogInfo )
725 return [elt for elt in beforeFiltering if elt.get().execTime is not None]
727 def IsExecTimeHigherThan( execInstDeco, limitDuration ):
731 [elt for elt in allexecs if IsExecTimeHigherThan(elt,datetime.timedelta(hours=1))]
736 execInstDeco (ScriptExecInfoDeco)
737 limitDuration (datetime.timedelta) Ex (datetime.timedelta(hours=1))
740 if execInstDeco.get() is not None:
741 return execInstDeco.get().execTime > limitDuration
745 def GetMaxTimeExec( listOfFinishedExecs ):
747 Returns the instance among listOfFinishedExecs that spends the max time to be executed
752 listOfFinishedExecs ( list<ScriptExecInfoDeco> ) : instance of ScriptExecInfoDeco that have finished to be executed
753 Typically returned by ListAllExecFinishedContainIn
760 return listOfFinishedExecs[ max([(i,elt.execTime) for i,elt in enumerate( listOfFinishedExecs) ],key = lambda x : x[1])[0] ]
762 def GetMinTimeExec( listOfFinishedExecs ):
764 Returns the instance among listOfFinishedExecs that spends the min time to be executed
769 listOfFinishedExecs ( list<ScriptExecInfoDeco> ) : instance of ScriptExecInfoDeco that have finished to be executed
770 Typically returned by ListAllExecFinishedContainIn
777 return listOfFinishedExecs[ min([(i,elt.execTime) for i,elt in enumerate( listOfFinishedExecs) ],key = lambda x : x[1])[0] ]
779 class DistriutionClass:
780 def __init__(self, begin, end, listOfExecs):
783 self._list_of_execs = sorted( listOfExecs, key = lambda x : x.execTime)
784 def __getitem__(self, *args):
785 return self._list_of_execs.__getitem__( *args )
793 def listOfExecs(self):
794 return self._list_of_execs
797 return len( self.listOfExecs )
799 def reprGlobal(self):
800 return f"[{self.begin}->{self.end}] : {self.size} execs"
802 class DistributionOfExecs:
804 Class in charge to distribute execution log instance into equi-classes
806 def __init__(self, listOfFinishedExecs, nbOfClasses):
807 self._list_of_finished_execs = listOfFinishedExecs
808 self._nb_of_classes = nbOfClasses
809 self._classes = DistributionOfExecs.ComputeDistributionOfExecTime(self._list_of_finished_execs,self._nb_of_classes)
811 def __getitem__(self, *args):
812 return self._classes.__getitem__( *args )
813 def reprOfExecs(self):
815 cs = np.cumsum( [elt.size for elt in self._classes] )
817 for i,(elt,cum_nb) in enumerate( zip(self._classes,cs) ):
818 ratio = (cum_nb / cs[-1])*100.0
819 ret.append( f"- For class {i} - {elt.reprGlobal} ( {ratio:.1f}% )" )
820 return "\n".join( ret )
822 def ComputeDistributionOfExecTime( cls, listOfFinishedExecs, nbOfClasses ):
823 maxt = GetMaxTimeExec( listOfFinishedExecs )
824 mint = GetMinTimeExec( listOfFinishedExecs )
825 deltaTime = ( maxt.execTime - mint.execTime ) / nbOfClasses
827 for i in range( nbOfClasses ):
828 begin = mint.execTime + i * deltaTime
829 end = mint.execTime + (i+1) * deltaTime
830 res.append( DistriutionClass(begin,end,[elt for elt in listOfFinishedExecs if elt.execTime >=begin and elt.execTime <= end]) )