CMS 3D CMS Logo

edmStreamStallGrapher.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from itertools import groupby
3 from operator import attrgetter,itemgetter
4 import sys
5 from collections import defaultdict
6 
7 #----------------------------------------------
8 def printHelp():
9  s = '''
10 To Use: Add the StallMonitor Service to the cmsRun job you want to check for
11  stream stalls. Use something like this in the configuration:
12 
13  process.add_(cms.Service("StallMonitor", fileName = cms.untracked.string("stallMonitor.log")))
14 
15  After running the job, execute this script and pass the name of the
16  StallMonitor log file to the script.
17 
18  By default, the script will then print an 'ASCII art' stall graph
19  which consists of a line of text for each time a module or the
20  source stops or starts. Each line contains the name of the module
21  which either started or stopped running, and the number of modules
22  running on each stream at that moment in time. After that will be
23  the time and stream number. Then if a module just started, you
24  will also see the amount of time the module spent between finishing
25  its prefetching and starting. The state of a module is represented
26  by a symbol:
27 
28  plus ("+") the stream has just finished waiting and is starting a module
29  minus ("-") the stream just finished running a module
30 
31  If a module had to wait more than 0.1 seconds, the end of the line
32  will have "STALLED". Startup actions, e.g. reading conditions,
33  may affect results for the first few events.
34 
35  Using the command line arguments described above you can make the
36  program create a PDF file with actual graphs instead of the 'ASCII art'
37  output.
38 
39  Once the graph is completed, the program outputs the list of modules
40  which had the greatest total stall times. The list is sorted by
41  total stall time and written in descending order. In addition, the
42  list of all stall times for the module is given.
43 
44  There is an inferior alternative (an old obsolete way).
45  Instead of using the StallMonitor Service, you can use the
46  Tracer Service. Make sure to use the 'printTimestamps' option
47  cms.Service("Tracer", printTimestamps = cms.untracked.bool(True))
48  There are problems associated with this and it is not recommended.'''
49  return s
50 
51 kStallThreshold=100 #in milliseconds
52 kTracerInput=False
53 
54 #Stream states
55 kStarted=0
56 kFinished=1
57 kPrefetchEnd=2
58 kStartedAcquire=3
59 kFinishedAcquire=4
60 kStartedSource=5
61 kFinishedSource=6
62 kStartedSourceDelayedRead=7
63 kFinishedSourceDelayedRead=8
64 
65 #Special names
66 kSourceFindEvent = "sourceFindEvent"
67 kSourceDelayedRead ="sourceDelayedRead"
68 
69 #----------------------------------------------
71  processingSteps = []
72  numStreams = 0
73  maxNameSize = 0
74  moduleNames = {}
75  for rawl in f:
76  l = rawl.strip()
77  if not l or l[0] == '#':
78  if len(l) > 5 and l[0:2] == "#M":
79  (id,name)=tuple(l[2:].split())
80  moduleNames[id] = name
81  continue
82  (step,payload) = tuple(l.split(None,1))
83  payload=payload.split()
84 
85  # Ignore these
86  if step == 'E' or step == 'e':
87  continue
88 
89  # Payload format is:
90  # <stream id> <..other fields..> <time since begin job>
91  stream = int(payload[0])
92  time = int(payload[-1])
93  trans = None
94 
95  # 'S' = begin of event creation in source
96  # 's' = end of event creation in source
97  if step == 'S' or step == 's':
98  name = kSourceFindEvent
99  trans = kStartedSource
100  # The start of an event is the end of the framework part
101  if step == 's':
102  trans = kFinishedSource
103  else:
104  # moduleID is the second payload argument for all steps below
105  moduleID = payload[1]
106 
107  # 'p' = end of module prefetching
108  # 'M' = begin of module processing
109  # 'm' = end of module processing
110  if step == 'p' or step == 'M' or step == 'm':
111  trans = kStarted
112  if step == 'p':
113  trans = kPrefetchEnd
114  elif step == 'm':
115  trans = kFinished
116  name = moduleNames[moduleID]
117 
118  # 'A' = begin of module acquire function
119  # 'a' = end of module acquire function
120  elif step == 'A' or step == 'a':
121  trans = kStartedAcquire
122  if step == 'a':
123  trans = kFinishedAcquire
124  name = moduleNames[moduleID]
125 
126  # Delayed read from source
127  # 'R' = begin of delayed read from source
128  # 'r' = end of delayed read from source
129  elif step == 'R' or step == 'r':
130  trans = kStartedSourceDelayedRead
131  if step == 'r':
132  trans = kFinishedSourceDelayedRead
133  name = kSourceDelayedRead
134 
135  if trans is not None:
136  numStreams = max(numStreams, stream+1)
137  maxNameSize = max(maxNameSize, len(name))
138  processingSteps.append((name,trans,stream,time))
139 
140  f.close()
141  return (processingSteps,numStreams,maxNameSize)
142 
143 #----------------------------------------------
144 # Utility to get time out of Tracer output text format
145 def getTime(line):
146  time = line.split(" ")[1]
147  time = time.split(":")
148  time = int(time[0])*60*60+int(time[1])*60+float(time[2])
149  time = int(1000*time) # convert to milliseconds
150  return time
151 
152 #----------------------------------------------
153 # The next function parses the Tracer output.
154 # Here are some differences to consider if you use Tracer output
155 # instead of the StallMonitor output.
156 # - The time in the text of the Tracer output is not as precise
157 # as the StallMonitor (.01 s vs .001 s)
158 # - The MessageLogger bases the time on when the message printed
159 # and not when it was initially queued up to print which smears
160 # the accuracy of the times.
161 # - Both of the previous things can produce some strange effects
162 # in the output plots.
163 # - The file size of the Tracer text file is much larger.
164 # - The CPU work needed to parse the Tracer files is larger.
165 # - The Tracer log file is expected to have "++" in the first
166 # or fifth line. If there are extraneous lines at the beginning
167 # you have to remove them.
168 # - The ascii printout out will have one extraneous line
169 # near the end for the SourceFindEvent start.
170 # - The only advantage I can see is that you have only
171 # one output file to handle instead of two, the regular
172 # log file and the StallMonitor output.
173 # We might should just delete the Tracer option because it is
174 # clearly inferior ...
176  processingSteps = []
177  numStreams = 0
178  maxNameSize = 0
179  startTime = 0
180  streamsThatSawFirstEvent = set()
181  for l in f:
182  trans = None
183  # We estimate the start and stop of the source
184  # by the end of the previous event and start of
185  # the event. This is historical, probably because
186  # the Tracer output for the begin and end of the
187  # source event does not include the stream number.
188  if l.find("processing event :") != -1:
189  name = kSourceFindEvent
190  trans = kStartedSource
191  # the end of the source is estimated using the start of the event
192  if l.find("starting:") != -1:
193  trans = kFinishedSource
194  elif l.find("processing event for module") != -1:
195  trans = kStarted
196  if l.find("finished:") != -1:
197  if l.find("prefetching") != -1:
198  trans = kPrefetchEnd
199  else:
200  trans = kFinished
201  else:
202  if l.find("prefetching") != -1:
203  #skip this since we don't care about prefetch starts
204  continue
205  name = l.split("'")[1]
206  elif l.find("processing event acquire for module:") != -1:
207  trans = kStartedAcquire
208  if l.find("finished:") != -1:
209  trans = kFinishedAcquire
210  name = l.split("'")[1]
211  elif l.find("event delayed read from source") != -1:
212  trans = kStartedSourceDelayedRead
213  if l.find("finished:") != -1:
214  trans = kFinishedSourceDelayedRead
215  name = kSourceDelayedRead
216  if trans is not None:
217  time = getTime(l)
218  if startTime == 0:
219  startTime = time
220  time = time - startTime
221  streamIndex = l.find("stream = ")
222  stream = int(l[streamIndex+9:l.find(" ",streamIndex+10)])
223  maxNameSize = max(maxNameSize, len(name))
224 
225  if trans == kFinishedSource and not stream in streamsThatSawFirstEvent:
226  # This is wrong but there is no way to estimate the time better
227  # because there is no previous event for the first event.
228  processingSteps.append((name,kStartedSource,stream,time))
229  streamsThatSawFirstEvent.add(stream)
230 
231  processingSteps.append((name,trans,stream,time))
232  numStreams = max(numStreams, stream+1)
233 
234  f.close()
235  return (processingSteps,numStreams,maxNameSize)
236 
237 #----------------------------------------------
238 def chooseParser(inputFile):
239 
240  firstLine = inputFile.readline().rstrip()
241  for i in range(3):
242  inputFile.readline()
243  # Often the Tracer log file starts with 4 lines not from the Tracer
244  fifthLine = inputFile.readline().rstrip()
245  inputFile.seek(0) # Rewind back to beginning
246 
247  if firstLine.find("# Step") != -1:
248  print "> ... Parsing StallMonitor output."
249  return parseStallMonitorOutput
250 
251  if firstLine.find("++") != -1 or fifthLine.find("++") != -1:
252  global kTracerInput
253  kTracerInput = True
254  print "> ... Parsing Tracer output."
255  return parseTracerOutput
256  else:
257  inputFile.close()
258  print "Unknown input format."
259  exit(1)
260 
261 #----------------------------------------------
262 def readLogFile(inputFile):
263  parseInput = chooseParser(inputFile)
264  return parseInput(inputFile)
265 
266 #----------------------------------------------
267 #
268 # modules: The time between prefetch finished and 'start processing' is
269 # the time it took to acquire any resources which is by definition the
270 # stall time.
271 #
272 # source: The source just records how long it spent doing work,
273 # not how long it was stalled. We can get a lower bound on the stall
274 # time for delayed reads by measuring the time the stream was doing
275 # no work up till the start of the source delayed read.
276 #
277 def findStalledModules(processingSteps, numStreams):
278  streamTime = [0]*numStreams
279  streamState = [0]*numStreams
280  stalledModules = {}
281  modulesActiveOnStream = [{} for x in xrange(numStreams)]
282  for n,trans,s,time in processingSteps:
283 
284  waitTime = None
285  modulesOnStream = modulesActiveOnStream[s]
286  if trans == kPrefetchEnd:
287  modulesOnStream[n] = time
288  elif trans == kStarted or trans == kStartedAcquire:
289  if n in modulesOnStream:
290  waitTime = time - modulesOnStream[n]
291  modulesOnStream.pop(n, None)
292  streamState[s] +=1
293  elif trans == kFinished or trans == kFinishedAcquire:
294  streamState[s] -=1
295  streamTime[s] = time
296  elif trans == kStartedSourceDelayedRead:
297  if streamState[s] == 0:
298  waitTime = time - streamTime[s]
299  elif trans == kStartedSource:
300  modulesOnStream.clear()
301  elif trans == kFinishedSource or trans == kFinishedSourceDelayedRead:
302  streamTime[s] = time
303  if waitTime is not None:
304  if waitTime > kStallThreshold:
305  t = stalledModules.setdefault(n,[])
306  t.append(waitTime)
307  return stalledModules
308 
309 #----------------------------------------------
310 def createAsciiImage(processingSteps, numStreams, maxNameSize):
311  streamTime = [0]*numStreams
312  streamState = [0]*numStreams
313  modulesActiveOnStreams = [{} for x in xrange(numStreams)]
314  for n,trans,s,time in processingSteps:
315  waitTime = None
316  modulesActiveOnStream = modulesActiveOnStreams[s]
317  if trans == kPrefetchEnd:
318  modulesActiveOnStream[n] = time
319  continue
320  elif trans == kStartedAcquire or trans == kStarted:
321  if n in modulesActiveOnStream:
322  waitTime = time - modulesActiveOnStream[n]
323  modulesActiveOnStream.pop(n, None)
324  streamState[s] +=1
325  elif trans == kFinishedAcquire or trans == kFinished:
326  streamState[s] -=1
327  streamTime[s] = time
328  elif trans == kStartedSourceDelayedRead:
329  if streamState[s] == 0:
330  waitTime = time - streamTime[s]
331  elif trans == kStartedSource:
332  modulesActiveOnStream.clear()
333  elif trans == kFinishedSource or trans == kFinishedSourceDelayedRead:
334  streamTime[s] = time
335  states = "%-*s: " % (maxNameSize,n)
336  if trans == kStartedAcquire or trans == kStarted or trans == kStartedSourceDelayedRead or trans == kStartedSource:
337  states +="+ "
338  else:
339  states +="- "
340  for index, state in enumerate(streamState):
341  if n==kSourceFindEvent and index == s:
342  states +="* "
343  else:
344  states +=str(state)+" "
345  states += " -- " + str(time/1000.) + " " + str(s) + " "
346  if waitTime is not None:
347  states += " %.2f"% (waitTime/1000.)
348  if waitTime > kStallThreshold:
349  states += " STALLED"
350 
351  print states
352 
353 #----------------------------------------------
354 def printStalledModulesInOrder(stalledModules):
355  priorities = []
356  maxNameSize = 0
357  for name,t in stalledModules.iteritems():
358  maxNameSize = max(maxNameSize, len(name))
359  t.sort(reverse=True)
360  priorities.append((name,sum(t),t))
361 
362  def sumSort(i,j):
363  return cmp(i[1],j[1])
364  priorities.sort(cmp=sumSort, reverse=True)
365 
366  nameColumn = "Stalled Module"
367  maxNameSize = max(maxNameSize, len(nameColumn))
368 
369  stallColumn = "Tot Stall Time"
370  stallColumnLength = len(stallColumn)
371 
372  print "%-*s" % (maxNameSize, nameColumn), "%-*s"%(stallColumnLength,stallColumn), " Stall Times"
373  for n,s,t in priorities:
374  paddedName = "%-*s:" % (maxNameSize,n)
375  print paddedName, "%-*.2f"%(stallColumnLength,s/1000.), ", ".join([ "%.2f"%(x/1000.) for x in t])
376 
377 #--------------------------------------------------------
378 class Point:
379  def __init__(self, x_, y_):
380  self.x = x_
381  self.y = y_
382 
383  def __str__(self):
384  return "(x: {}, y: {})".format(self.x,self.y)
385 
386  def __repr__(self):
387  return self.__str__()
388 
389 #--------------------------------------------------------
391  if len(ps) < 2:
392  return ps
393  reducedPoints = []
394  tmp = Point(ps[0].x, ps[0].y)
395  for p in ps[1:]:
396  if tmp.x == p.x:
397  tmp.y += p.y
398  else:
399  reducedPoints.append(tmp)
400  tmp = Point(p.x, p.y)
401  reducedPoints.append(tmp)
402  reducedPoints = [p for p in reducedPoints if p.y != 0]
403  return reducedPoints
404 
405 # -------------------------------------------
406 def adjacentDiff(*pairLists):
407  points = []
408  for pairList in pairLists:
409  points += [Point(x[0], 1) for x in pairList if x[1] != 0]
410  points += [Point(sum(x),-1) for x in pairList if x[1] != 0]
411  points.sort(key=attrgetter('x'))
412  return points
413 
414 stackType = 'stack'
415 
416 # --------------------------------------------
417 class Stack:
418  def __init__(self):
419  self.data = []
420 
421  def update(self, graphType, points):
422  tmp = points
423  if len(self.data) != 0:
424  tmp += self.data[-1][1]
425 
426  tmp.sort(key=attrgetter('x'))
427  tmp = reduceSortedPoints(tmp)
428  self.data.append((graphType, tmp))
429 
430 #---------------------------------------------
431 # StreamInfoElement
433  def __init__(self, begin_, delta_, color_):
434  self.begin=begin_
435  self.delta=delta_
436  self.color=color_
437 
438  def unpack(self):
439  return self.begin, self.delta, self.color
440 
441 #----------------------------------------------
442 # Consolidating contiguous blocks with the same color
443 # drastically reduces the size of the pdf file.
444 def consolidateContiguousBlocks(numStreams, streamInfo):
445  oldStreamInfo = streamInfo
446  streamInfo = [[] for x in xrange(numStreams)]
447 
448  for s in xrange(numStreams):
449  if oldStreamInfo[s]:
450  lastStartTime,lastTimeLength,lastColor = oldStreamInfo[s][0].unpack()
451  for info in oldStreamInfo[s][1:]:
452  start,length,color = info.unpack()
453  if color == lastColor and lastStartTime+lastTimeLength == start:
454  lastTimeLength += length
455  else:
456  streamInfo[s].append(StreamInfoElement(lastStartTime,lastTimeLength,lastColor))
457  lastStartTime = start
458  lastTimeLength = length
459  lastColor = color
460  streamInfo[s].append(StreamInfoElement(lastStartTime,lastTimeLength,lastColor))
461 
462  return streamInfo
463 
464 #----------------------------------------------
465 # Consolidating contiguous blocks with the same color drastically
466 # reduces the size of the pdf file. Same functionality as the
467 # previous function, but with slightly different implementation.
469  oldBlocks = blocks
470 
471  blocks = []
472  if not oldBlocks:
473  return blocks
474 
475  lastStartTime,lastTimeLength,lastHeight = oldBlocks[0]
476  for start,length,height in oldBlocks[1:]:
477  if height == lastHeight and lastStartTime+lastTimeLength == start:
478  lastTimeLength += length
479  else:
480  blocks.append((lastStartTime,lastTimeLength,lastHeight))
481  lastStartTime = start
482  lastTimeLength = length
483  lastHeight = height
484  blocks.append((lastStartTime,lastTimeLength,lastHeight))
485 
486  return blocks
487 
488 #----------------------------------------------
489 def plotPerStreamAboveFirstAndPrepareStack(points, allStackTimes, ax, stream, height, streamHeightCut, doPlot, addToStackTimes, color, threadOffset):
490  points = sorted(points, key=attrgetter('x'))
491  points = reduceSortedPoints(points)
492  streamHeight = 0
493  preparedTimes = []
494  for t1,t2 in zip(points, points[1:]):
495  streamHeight += t1.y
496  # We make a cut here when plotting because the first row for
497  # each stream was already plotted previously and we do not
498  # need to plot it again. And also we want to count things
499  # properly in allStackTimes. We want to avoid double counting
500  # or missing running modules and this is complicated because
501  # we counted the modules in the first row already.
502  if streamHeight < streamHeightCut:
503  continue
504  preparedTimes.append((t1.x,t2.x-t1.x, streamHeight))
505  preparedTimes.sort(key=itemgetter(2))
506  preparedTimes = mergeContiguousBlocks(preparedTimes)
507 
508  for nthreads, ts in groupby(preparedTimes, itemgetter(2)):
509  theTS = [(t[0],t[1]) for t in ts]
510  if doPlot:
511  theTimes = [(t[0]/1000.,t[1]/1000.) for t in theTS]
512  yspan = (stream-0.4+height,height*(nthreads-1))
513  ax.broken_barh(theTimes, yspan, facecolors=color, edgecolors=color, linewidth=0)
514  if addToStackTimes:
515  allStackTimes[color].extend(theTS*(nthreads-threadOffset))
516 
517 #----------------------------------------------
518 def createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModuleInfo, displayExternalWork, checkOrder):
519 
520  stalledModuleNames = set([x for x in stalledModuleInfo.iterkeys()])
521  streamLowestRow = [[] for x in xrange(numStreams)]
522  modulesActiveOnStreams = [set() for x in xrange(numStreams)]
523  acquireActiveOnStreams = [set() for x in xrange(numStreams)]
524  externalWorkOnStreams = [set() for x in xrange(numStreams)]
525  previousFinishTime = [None for x in xrange(numStreams)]
526  streamRunningTimes = [[] for x in xrange(numStreams)]
527  streamExternalWorkRunningTimes = [[] for x in xrange(numStreams)]
528  maxNumberOfConcurrentModulesOnAStream = 1
529  previousTime = [0 for x in xrange(numStreams)]
530 
531  # The next five variables are only used to check for out of order transitions
532  finishBeforeStart = [set() for x in xrange(numStreams)]
533  finishAcquireBeforeStart = [set() for x in xrange(numStreams)]
534  countSource = [0 for x in xrange(numStreams)]
535  countDelayedSource = [0 for x in xrange(numStreams)]
536  countExternalWork = [defaultdict(int) for x in xrange(numStreams)]
537 
538  for n,trans,s,time in processingSteps:
539 
540  startTime = None
541 
542  # force the time to monotonically increase on each stream
543  if time < previousTime[s]:
544  time = previousTime[s]
545  previousTime[s] = time
546 
547  activeModules = modulesActiveOnStreams[s]
548  acquireModules = acquireActiveOnStreams[s]
549  externalWorkModules = externalWorkOnStreams[s]
550 
551  if trans == kStarted or trans == kStartedSourceDelayedRead or trans == kStartedAcquire or trans == kStartedSource :
552  if checkOrder:
553  # Note that the code which checks the order of transitions assumes that
554  # all the transitions exist in the input. It is checking only for order
555  # problems, usually a start before a finish. Problems are fixed and
556  # silently ignored. Nothing gets plotted for transitions that are
557  # in the wrong order.
558  if trans == kStarted:
559  countExternalWork[s][n] -= 1
560  if n in finishBeforeStart[s]:
561  finishBeforeStart[s].remove(n)
562  continue
563  elif trans == kStartedAcquire:
564  if n in finishAcquireBeforeStart[s]:
565  finishAcquireBeforeStart[s].remove(n)
566  continue
567 
568  if trans == kStartedSourceDelayedRead:
569  countDelayedSource[s] += 1
570  if countDelayedSource[s] < 1:
571  continue
572  elif trans == kStartedSource:
573  countSource[s] += 1
574  if countSource[s] < 1:
575  continue
576 
577  moduleNames = activeModules.copy()
578  moduleNames.update(acquireModules)
579  if trans == kStartedAcquire:
580  acquireModules.add(n)
581  else:
582  activeModules.add(n)
583  streamRunningTimes[s].append(Point(time,1))
584  if moduleNames or externalWorkModules:
585  startTime = previousFinishTime[s]
586  previousFinishTime[s] = time
587 
588  if trans == kStarted and n in externalWorkModules:
589  externalWorkModules.remove(n)
590  streamExternalWorkRunningTimes[s].append(Point(time, -1))
591  else:
592  nTotalModules = len(activeModules) + len(acquireModules) + len(externalWorkModules)
593  maxNumberOfConcurrentModulesOnAStream = max(maxNumberOfConcurrentModulesOnAStream, nTotalModules)
594  elif trans == kFinished or trans == kFinishedSourceDelayedRead or trans == kFinishedAcquire or trans == kFinishedSource :
595  if checkOrder:
596  if trans == kFinished:
597  if n not in activeModules:
598  finishBeforeStart[s].add(n)
599  continue
600 
601  if trans == kFinishedSourceDelayedRead:
602  countDelayedSource[s] -= 1
603  if countDelayedSource[s] < 0:
604  continue
605  elif trans == kFinishedSource:
606  countSource[s] -= 1
607  if countSource[s] < 0:
608  continue
609 
610  if trans == kFinishedAcquire:
611  if checkOrder:
612  countExternalWork[s][n] += 1
613  if displayExternalWork:
614  if (not checkOrder) or countExternalWork[s][n] > 0:
615  externalWorkModules.add(n)
616  streamExternalWorkRunningTimes[s].append(Point(time,+1))
617  if checkOrder and n not in acquireModules:
618  finishAcquireBeforeStart[s].add(n)
619  continue
620  streamRunningTimes[s].append(Point(time,-1))
621  startTime = previousFinishTime[s]
622  previousFinishTime[s] = time
623  moduleNames = activeModules.copy()
624  moduleNames.update(acquireModules)
625 
626  if trans == kFinishedAcquire:
627  acquireModules.remove(n)
628  elif trans == kFinishedSourceDelayedRead:
629  if countDelayedSource[s] == 0:
630  activeModules.remove(n)
631  elif trans == kFinishedSource:
632  if countSource[s] == 0:
633  activeModules.remove(n)
634  else:
635  activeModules.remove(n)
636 
637  if startTime is not None:
638  c="green"
639  if not moduleNames:
640  c = "darkviolet"
641  elif (kSourceDelayedRead in moduleNames) or (kSourceFindEvent in moduleNames):
642  c = "orange"
643  else:
644  for n in moduleNames:
645  if n in stalledModuleNames:
646  c="red"
647  break
648  streamLowestRow[s].append(StreamInfoElement(startTime, time-startTime, c))
649  streamLowestRow = consolidateContiguousBlocks(numStreams, streamLowestRow)
650 
651  nr = 1
652  if shownStacks:
653  nr += 1
654  fig, ax = plt.subplots(nrows=nr, squeeze=True)
655  axStack = None
656  if shownStacks:
657  [xH,yH] = fig.get_size_inches()
658  fig.set_size_inches(xH,yH*4/3)
659  ax = plt.subplot2grid((4,1),(0,0), rowspan=3)
660  axStack = plt.subplot2grid((4,1),(3,0))
661 
662  ax.set_xlabel("Time (sec)")
663  ax.set_ylabel("Stream ID")
664  ax.set_ylim(-0.5,numStreams-0.5)
665  ax.yaxis.set_ticks(xrange(numStreams))
666 
667  height = 0.8/maxNumberOfConcurrentModulesOnAStream
668  allStackTimes={'green': [], 'red': [], 'blue': [], 'orange': [], 'darkviolet': []}
669  for iStream,lowestRow in enumerate(streamLowestRow):
670  times=[(x.begin/1000., x.delta/1000.) for x in lowestRow] # Scale from msec to sec.
671  colors=[x.color for x in lowestRow]
672  # for each stream, plot the lowest row
673  ax.broken_barh(times,(iStream-0.4,height),facecolors=colors,edgecolors=colors,linewidth=0)
674  # record them also for inclusion in the stack plot
675  # the darkviolet ones get counted later so do not count them here
676  for info in lowestRow:
677  if not info.color == 'darkviolet':
678  allStackTimes[info.color].append((info.begin, info.delta))
679 
680  # Now superimpose the number of concurrently running modules on to the graph.
681  if maxNumberOfConcurrentModulesOnAStream > 1:
682 
683  for i,perStreamRunningTimes in enumerate(streamRunningTimes):
684 
685  perStreamTimesWithExtendedWork = list(perStreamRunningTimes)
686  perStreamTimesWithExtendedWork.extend(streamExternalWorkRunningTimes[i])
687 
688  plotPerStreamAboveFirstAndPrepareStack(perStreamTimesWithExtendedWork,
689  allStackTimes, ax, i, height,
690  streamHeightCut=2,
691  doPlot=True,
692  addToStackTimes=False,
693  color='darkviolet',
694  threadOffset=1)
695 
696  plotPerStreamAboveFirstAndPrepareStack(perStreamRunningTimes,
697  allStackTimes, ax, i, height,
698  streamHeightCut=2,
699  doPlot=True,
700  addToStackTimes=True,
701  color='blue',
702  threadOffset=1)
703 
704  plotPerStreamAboveFirstAndPrepareStack(streamExternalWorkRunningTimes[i],
705  allStackTimes, ax, i, height,
706  streamHeightCut=1,
707  doPlot=False,
708  addToStackTimes=True,
709  color='darkviolet',
710  threadOffset=0)
711 
712  if shownStacks:
713  print "> ... Generating stack"
714  stack = Stack()
715  for color in ['green','blue','red','orange','darkviolet']:
716  tmp = allStackTimes[color]
717  tmp = reduceSortedPoints(adjacentDiff(tmp))
718  stack.update(color, tmp)
719 
720  for stk in reversed(stack.data):
721  color = stk[0]
722 
723  # Now arrange list in a manner that it can be grouped by the height of the block
724  height = 0
725  xs = []
726  for p1,p2 in zip(stk[1], stk[1][1:]):
727  height += p1.y
728  xs.append((p1.x, p2.x-p1.x, height))
729  xs.sort(key = itemgetter(2))
730  xs = mergeContiguousBlocks(xs)
731 
732  for height, xpairs in groupby(xs, itemgetter(2)):
733  finalxs = [(e[0]/1000.,e[1]/1000.) for e in xpairs]
734  # plot the stacked plot, one color and one height on each call to broken_barh
735  axStack.broken_barh(finalxs, (0, height), facecolors=color, edgecolors=color, linewidth=0)
736 
737  axStack.set_xlabel("Time (sec)");
738  axStack.set_ylabel("# threads");
739  axStack.set_xlim(ax.get_xlim())
740  axStack.tick_params(top='off')
741 
742  fig.text(0.1, 0.95, "modules running", color = "green", horizontalalignment = 'left')
743  fig.text(0.5, 0.95, "stalled module running", color = "red", horizontalalignment = 'center')
744  fig.text(0.9, 0.95, "read from input", color = "orange", horizontalalignment = 'right')
745  fig.text(0.5, 0.92, "multiple modules running", color = "blue", horizontalalignment = 'center')
746  if displayExternalWork:
747  fig.text(0.9, 0.92, "external work", color = "darkviolet", horizontalalignment = 'right')
748  print "> ... Saving to file: '{}'".format(pdfFile)
749  plt.savefig(pdfFile)
750 
751 #=======================================
752 if __name__=="__main__":
753  import argparse
754  import re
755  import sys
756 
757  # Program options
758  parser = argparse.ArgumentParser(description='Convert a text file created by cmsRun into a stream stall graph.',
759  formatter_class=argparse.RawDescriptionHelpFormatter,
760  epilog=printHelp())
761  parser.add_argument('filename',
762  type=argparse.FileType('r'), # open file
763  help='file to process')
764  parser.add_argument('-g', '--graph',
765  nargs='?',
766  metavar="'stall.pdf'",
767  const='stall.pdf',
768  dest='graph',
769  help='''Create pdf file of stream stall graph. If -g is specified
770  by itself, the default file name is \'stall.pdf\'. Otherwise, the
771  argument to the -g option is the filename.''')
772  parser.add_argument('-s', '--stack',
773  action='store_true',
774  help='''Create stack plot, combining all stream-specific info.
775  Can be used only when -g is specified.''')
776  parser.add_argument('-e', '--external',
777  action='store_false',
778  help='''Suppress display of external work in graphs.''')
779  parser.add_argument('-o', '--order',
780  action='store_true',
781  help='''Enable checks for and repair of transitions in the input that are in the wrong order (for example a finish transition before a corresponding start). This is always enabled for Tracer input, but is usually an unnecessary waste of CPU time and memory with StallMonitor input and by default not enabled.''')
782  args = parser.parse_args()
783 
784  # Process parsed options
785  inputFile = args.filename
786  pdfFile = args.graph
787  shownStacks = args.stack
788  displayExternalWork = args.external
789  checkOrder = args.order
790 
791  doGraphic = False
792  if pdfFile is not None:
793  doGraphic = True
794  import matplotlib
795  # Need to force display since problems with CMSSW matplotlib.
796  matplotlib.use("PDF")
797  import matplotlib.pyplot as plt
798  if not re.match(r'^[\w\.]+$', pdfFile):
799  print "Malformed file name '{}' supplied with the '-g' option.".format(pdfFile)
800  print "Only characters 0-9, a-z, A-Z, '_', and '.' are allowed."
801  exit(1)
802 
803  if '.' in pdfFile:
804  extension = pdfFile.split('.')[-1]
805  supported_filetypes = plt.figure().canvas.get_supported_filetypes()
806  if not extension in supported_filetypes:
807  print "A graph cannot be saved to a filename with extension '{}'.".format(extension)
808  print "The allowed extensions are:"
809  for filetype in supported_filetypes:
810  print " '.{}'".format(filetype)
811  exit(1)
812 
813  if pdfFile is None and shownStacks:
814  print "The -s (--stack) option can be used only when the -g (--graph) option is specified."
815  exit(1)
816 
817  sys.stderr.write(">reading file: '{}'\n".format(inputFile.name))
818  processingSteps,numStreams,maxNameSize = readLogFile(inputFile)
819  if kTracerInput:
820  checkOrder = True
821  sys.stderr.write(">processing data\n")
822  stalledModules = findStalledModules(processingSteps, numStreams)
823  if not doGraphic:
824  sys.stderr.write(">preparing ASCII art\n")
825  createAsciiImage(processingSteps, numStreams, maxNameSize)
826  else:
827  sys.stderr.write(">creating PDF\n")
828  createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModules, displayExternalWork, checkOrder)
829  printStalledModulesInOrder(stalledModules)
def createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModuleInfo, displayExternalWork, checkOrder)
def update(self, graphType, points)
def findStalledModules(processingSteps, numStreams)
def consolidateContiguousBlocks(numStreams, streamInfo)
def __init__(self, begin_, delta_, color_)
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def printStalledModulesInOrder(stalledModules)
def createAsciiImage(processingSteps, numStreams, maxNameSize)
void add(std::map< std::string, TH1 * > &h, TH1 *hist)
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def remove(d, key, TELL=False)
Definition: MatrixUtil.py:210
def plotPerStreamAboveFirstAndPrepareStack(points, allStackTimes, ax, stream, height, streamHeightCut, doPlot, addToStackTimes, color, threadOffset)
double split
Definition: MVATrainer.cc:139
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run