CMS 3D CMS Logo

cmsPerfRegress.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 from __future__ import print_function
4 import time, os, sys, math, re, gzip
5 import tempfile as tmp
6 import optparse as opt
7 from cmsPerfCommons import CandFname
8 #from ROOT import gROOT, TCanvas, TF1
9 import ROOT
10 from array import array
11 #Substitute popen with subprocess.Popen! popen obsolete...
12 import subprocess
13 from functools import reduce
14 
15 _cmsver = os.environ['CMSSW_VERSION']
16 values_set=('vsize','delta_vsize','rss','delta_rss')
17 
19  """Base class for exceptions in this module."""
20  pass
21 
23  """Exception raised when Could not parse TimingReport Log.
24 
25  Attributes:
26  expression -- input expression in which the error occurred
27  message -- explanation of the error
28  """
29 
30  def __init__(self, message):
31  self.message = message
32 
34  """Exception raised when Could not parse TimingReport Log.
35 
36  Attributes:
37  expression -- input expression in which the error occurred
38  message -- explanation of the error
39  """
40 
41  def __init__(self, message):
42  self.message = message
43 
45  """Exception raised when Could not parse TimingReport Log.
46 
47  Attributes:
48  expression -- input expression in which the error occurred
49  message -- explanation of the error
50  """
51 
52  def __init__(self, message):
53  self.message = message
54 
56  """Exception raised when Could not parse TimingReport Log.
57 
58  Attributes:
59  expression -- input expression in which the error occurred
60  message -- explanation of the error
61  """
62 
63  def __init__(self, message):
64  self.message = message
65 
66 #############
67 # Option parser
68 #
70  parser = opt.OptionParser()
71  #
72  # Options
73  #
74  parser.add_option('-n',
75  type="int",
76  help='Number of secs per bin. Default is 1.' ,
77  default=1,
78  dest='startevt')
79  parser.add_option('-t',
80  '--report-type',
81  type="choice",
82  choices= ("timing", "simplememory","edmsize","igprof","callgrind",""),
83  help='Type of report to perform regrssion on. Default is TimingReport.' ,
84  default="timing",
85  dest='reporttype')
86  parser.add_option('-i',
87  '--IgProfMemOption',
88  type="choice",
89  choices= ("-y MEM_TOTAL", "-y MEM_LIVE",""),
90  help='Eventual IgProfMem counter to use for the regression. Default is no argument (IgProfPerf).' ,
91  default="",
92  dest='igprofmem')
93  (options,args) = parser.parse_args()
94  if len(args) < 2:
95  parser.error("ERROR: Not enough arguments")
96  sys.exit()
97 
98 
99  path1 = os.path.abspath(args[0])
100  path2 = os.path.abspath(args[1])
101  if os.path.exists(path1) and os.path.exists(path2):
102  return (path1, path2, options.startevt, options.reporttype, options.igprofmem)
103  else:
104  print("Error: one of the paths does not exist")
105  sys.exit()
106 
107 ###########
108 # Get max value in data set
109 #
110 def get_max(data,index=1):
111  max_time=-1
112  for el in data:
113  sec=el[index]
114  if max_time<sec:
115  max_time=sec
116  return max_time
117 
118 ###########
119 # Get min value in data set
120 #
121 def get_min(data,index=1):
122  min_time=1e20
123  for el in data:
124  sec=el[index]
125  if min_time>sec:
126  min_time=sec
127  return min_time
128 
129 ###########
130 # Setup PyRoot as a batch run
131 #
132 def setBatch():
133  __argv=sys.argv # trick for a strange behaviour of the TApp..
134  sys.argv=sys.argv[:1]
135  ROOT.gROOT.SetStyle("Plain") # style paranoia
136  sys.argv=__argv
137  #Cannot use this option when the logfile includes
138  #a large number of events... PyRoot seg-faults.
139  #Set ROOT in batch mode to avoid canvases popping up!
140  ROOT.gROOT.SetBatch(1)
141 
142 ##########
143 # Create the root file to save the graphs in
144 #
145 def createROOT(outdir,filename):
146 
147  # Save in file
148  rootfilename = os.path.join(outdir,filename)
149  myfile = None
150  exists = os.path.exists(rootfilename)
151  if exists:
152  myfile=ROOT.TFile(rootfilename,'UPDATE')
153  else:
154  myfile=ROOT.TFile(rootfilename,'RECREATE')
155  return myfile
156 
157 ##########
158 # Parse timing data from log file
159 #
160 def getTimingLogData(logfile_name):
161  data=[]
162 
163  # open file and read it and fill the structure!
164  logfile=open(logfile_name,'r')
165  logfile_lines=logfile.readlines()
166  logfile.close()
167 
168  # we get the info we need!
169  i=0
170  while i < len(logfile_lines):
171  line=logfile_lines[i]
172  if 'TimeEvent>' in line:
173  line=line.strip()
174  line_content_list = line.split(' ')[0:]
175  event_number = int(line_content_list[1])
176  seconds = float(line_content_list[3])
177  data.append((event_number,seconds))
178  i+=1
179 
180  return data
181 
182 ###########
183 # Parse memory check data from log file
184 #
185 def getSimpleMemLogData(logfile_name,startevt, candle):
186  data=[]
187  values_set=('vsize','delta_vsize','rss','delta_rss')
188 
189  # open file and read it and fill the structure!
190  logfile=open(logfile_name,'r')
191  logfile_lines=logfile.readlines()
192  logfile.close()
193 
194  step = ""
195  steps = []
196  #print candle
197  #print CandFname[candle]
198  #Get the step from log filename:
199 
200  #Catching the case of command line use in which the candle is unknown (and the log name does not match the perfsuite naming convention
201  if candle:
202  stepreg = re.compile("%s_([^_]*(_PILEUP)?)_%s((.log)|(.gz))?" % (CandFname[candle],"TimingReport"))
203  else:
204  stepreg = re.compile("([^_]*(_PILEUP)?)_%s((.log)|(.gz))?"%"TimingReport")
205 
206  #print logfile_name
207  found=stepreg.search(logfile_name)
208  if found:
209  step=found.groups()[0]
210  print("Determined step from log filename to be %s"%step)
211  else:
212  print("Could not determine step from log filename")
213 
214  #steps.append((step,data))
215 
216  #Rewriting the following code... what a mess!
217  # we get the info we need!
218  #i=0
219  #while i < len(logfile_lines):
220  #line=logfile_lines[i]
221  #Format has changed... we use the output of individual steps, so no need to read into the logfile to find which step we are
222  #referring too (by the way it would not work now!)... the step info comes from the logfilename done above...
223  #if "RelValreport" in line and "cmsDriver" in line and "step" in line:
224  # stepreg = re.compile("--step=([^ ]*)")
225  # found = stepreg.search(line)
226  # if found:
227  # if step == "":
228  # step = found.groups()[0]
229  # else:
230  # steps.append((step,data))
231  # step = found.groups()[0]
232  # data = []
233  #if '%MSG-w MemoryCheck:' in line:
234  # line=line[:-1] #no \n!
235  # line_content_list=line.split(' ')
236  # event_number=int(line_content_list[-1])
237  # if event_number<startevt:
238  # i+=1
239  # continue
240  # i+=1 # we inspect the following line
241  # try:
242  # line=logfile_lines[i]
243  # except IndexError:
244  # continue
245  # line=line[:-1] #no \n!
246  # line_content_list=line.split(' ')
247  # vsize=float(line_content_list[4])
248  # delta_vsize=float(line_content_list[5])
249  # rss=float(line_content_list[7])
250  # delta_rss=float(line_content_list[8])
251  #
252  # data.append((event_number,{'vsize':vsize,
253  # 'delta_vsize':delta_vsize,
254  # 'rss':rss,
255  # 'delta_rss':delta_rss}))
256  #i += 1
257  event_number= startevt
258  for line in logfile_lines:
259  #Match SimpleMemoryCheck output to get the event number
260  if '%MSG-w MemoryCheck:' in line: #Harcoded based on format: %MSG-w MemoryCheck: PostModule 12-Jan-2009 16:00:29 CET Run: 1 Event: 1
261  tokens=line.split()
262  if int(tokens[-1])>= startevt:
263  event_number=int(tokens[-1])
264  if 'VSIZE' in line: #Harcoded based on format: MemoryCheck: event : VSIZE 648.426 0 RSS 426.332 0
265  tokens=line.split()
266  vsize=float(tokens[4])
267  delta_vsize=float(tokens[5])
268  rss=float(tokens[7])
269  delta_rss=float(tokens[8])
270  data.append((event_number,{'vsize':vsize,
271  'delta_vsize':delta_vsize,
272  'rss':rss,
273  'delta_rss':delta_rss
274  }
275  )
276  )
277 
278  if not len(data) == 0:
279  #print "!!!!!!!!!!!!!Adding step %s and data!"%step
280  steps.append((step,data))
281 
282  #print "PRINTOUT@@@@@@"
283  #print steps
284  return steps
285 
286 ###############
287 #
288 # Create a new timing graph and histogram
289 #
290 def newGraphAndHisto(histoleg,leg,npoints,nbins,min_val,max_val,data,graph_num,prevrev=""):
291 
292  colors = [2,4]
293  prevRevName = "???"
294  if not prevrev == "":
295  (head, tail) = os.path.split(prevrev)
296  prevRevName = os.path.basename(tail)
297  releaseNames = ["Previous (%s)" % prevRevName,_cmsver]
298 
299  histo=ROOT.TH1F("Seconds per event (histo: %s)" % graph_num,'Seconds per event',nbins,min_val,max_val)
300 
301  graph=ROOT.TGraph(npoints)
302 
303  evt_counter=0
304  peak = data[0][1]
305  for evt_num,secs in data:
306  if secs > peak:
307  peak = secs
308  graph.SetPoint(evt_counter,evt_num,secs)
309  histo.Fill(secs)
310  evt_counter+=1
311 
312  allsecs = []
313  map(lambda x: allsecs.append(x[1]),data)
314  total = reduce(lambda x,y: x + y,allsecs)
315  mean = total / evt_counter
316  rms = math.sqrt(reduce(lambda x,y: x + y,map(lambda x: x * x,allsecs)) / evt_counter)
317 
318 
319  graph.SetMarkerStyle(8)
320  graph.SetMarkerSize(.7)
321  graph.SetMarkerColor(1)
322  graph.SetLineWidth(3)
323  graph.SetLineColor(colors[graph_num]) # for each iterate through colors
324  histo.SetLineColor(colors[graph_num])
325  histo.SetStats(1)
326  histoleg.AddEntry(histo, "%s release" % releaseNames[graph_num], "l")
327  leg.AddEntry(graph , "%s release" % releaseNames[graph_num], "l")
328  leg.AddEntry(graph , "Mean: %s" % str(mean) , "l")
329  leg.AddEntry(graph , "RMS : %s" % str(rms) , "l")
330  leg.AddEntry(graph , "Peak: %s" % str(peak) , "l")
331  leg.AddEntry(graph , "Total time: %s" % str(total) , "l")
332  if graph_num == 0 :
333  histo.SetFillColor(colors[graph_num])
334 
335  return (graph,histo,mean)
336 
337 ###########
338 # Get limits to plot the graph
339 #
340 def getLimits(data,secsperbin):
341  min_val=get_min(data,1)
342  max_val=get_max(data,1)
343  interval=int(max_val-min_val)
344 
345  min_val=min_val-interval*0.2
346  max_val=max_val+interval*0.2
347  interval=int(max_val-min_val)
348 
349  nbins=int(interval/secsperbin)
350 
351  npoints=len(data)
352 
353  last_event=data[-1][0]
354 
355  return (min_val,max_val,interval,npoints,last_event)
356 
357 ##############
358 # Setup graph information for one graph (no need to it on both if they are superimposed)
359 #
360 def setupSuperimpose(graph1,graph2,last_event,max_val,reporttype=0, title = ""):
361  name = ""
362  xtitle = ""
363  ytitle = ""
364  if reporttype == 0:
365  title = 'Seconds per event'
366  name = 'SecondsPerEvent'
367  xtitle = "Event"
368  ytitle = "Processing time for each event (s)"
369  graph1.GetYaxis().SetRangeUser(0,max_val)
370  graph2.GetYaxis().SetRangeUser(0,max_val)
371  elif reporttype == 1:
372  name = "%s_graph" % title
373  xtitle = "Event"
374  ytitle = "MB"
375 
376  graph1.SetTitle(title)
377  graph1.SetName(name)
378  graph1.GetXaxis().SetTitle(xtitle)
379  graph1.GetYaxis().SetTitleOffset(1.3)
380  graph1.GetYaxis().SetTitle(ytitle)
381  graph1.GetXaxis().SetLimits(0,last_event)
382  # Do I need to set limits on graph 2? I don't know
383  # I'm doing it anyway, can't hurt.
384  graph2.GetXaxis().SetLimits(0,last_event)
385 
386 #############
387 # Plot the mean line on a graph
388 #
389 def getMeanLines(avg,last_event,graph_num):
390  colors = [2,4]
391  avg_line=ROOT.TLine(1,avg,last_event,avg)
392  avg_line.SetLineColor(colors[graph_num])
393  avg_line.SetLineWidth(2)
394 
395  return avg_line
396 
397 ############
398 # Get the difference in timing data (for comparison of two releases)
399 #
400 def getTimingDiff(data1,data2,npoints,last_event,orig_max_val):
401  data3 = []
402  for x in range(len(data2)):
403  try:
404  if data2[x][0] == data1[x][0]:
405  avgEventNum = data2[x][0]
406  diffSecs = data2[x][1] - data1[x][1]
407  data3.append((avgEventNum,diffSecs))
408  except IndexError:
409  pass
410  except ValueError:
411  pass
412 
413  graph=ROOT.TGraph(npoints)
414 
415  evt_counter=0
416  peak = data3[0][1]
417  for evt_num,secs in data3:
418  if secs > peak:
419  peak = secs
420  graph.SetPoint(evt_counter,evt_num,secs)
421  evt_counter+=1
422 
423  allsecs = []
424  map(lambda x: allsecs.append(x[1]),data3)
425  total = reduce(lambda x,y: x + y,allsecs)
426  mean = total / evt_counter
427  rms = math.sqrt(reduce(lambda x,y: x + y,map(lambda x: x * x,allsecs)) / evt_counter)
428 
429  min_val=get_min(data3,1)
430  max_val=get_max(data3,1)
431  interval=int(max_val-min_val)
432 
433  min_val=min_val-interval*0.2
434  max_val=max_val+interval*0.2
435  interval=int(max_val-min_val)
436 
437  # Determine the max value to be something that makes the scale similar to what
438  # the original graph had. Unless we can't seem the maximum value.
439 
440  new_max = min_val + orig_max_val
441  if new_max < max_val:
442  pass
443  else :
444  max_val = new_max
445 
446  graph.SetTitle('Change in processing time for each event between revs')
447  graph.SetName('SecondsPerEvent')
448  graph.GetXaxis().SetTitle("Event Number")
449  graph.GetYaxis().SetTitle("Change in processing time between revs (s)")
450  graph.GetYaxis().SetTitleOffset(1.3)
451  graph.SetLineColor(2)
452  graph.SetMarkerStyle(8)
453  graph.SetMarkerSize(.7)
454  graph.SetMarkerColor(1)
455  graph.SetLineWidth(3)
456  graph.GetXaxis().SetLimits(0,last_event)
457  graph.GetYaxis().SetRangeUser(min_val,max_val)
458  leg = ROOT.TLegend(0.5,0.7,0.89,0.89)
459  leg.AddEntry(graph, "Mean: %s" % str(mean), "l")
460  leg.AddEntry(graph, "RMS : %s" % str(rms) , "l")
461  leg.AddEntry(graph, "Peak: %s" % str(peak), "l")
462  leg.AddEntry(graph, "Total time change: %s" % str(total) , "l")
463 
464  return (graph,leg)
465 
466 ##########
467 # Draw superimposed graphs on a separate canvas
468 #
469 def drawGraphs(graph1,graph2,avg1,avg2,leg):
470  graph_canvas = ROOT.TCanvas("graph_canvas")
471  graph_canvas.cd()
472  graph1.Draw("ALP")
473  graph2.Draw("LP")
474  avg1.Draw("Same")
475  avg2.Draw("Same")
476  leg.Draw()
477  return graph_canvas
478 
479 ##########
480 # Draw superimposed histograms on a separate canvas
481 #
482 def drawHistos(histo_stack,hstleg):
483  histo_canvas = ROOT.TCanvas("histo_canvas")
484  histo_canvas.cd()
485  histo_stack.Draw("nostack")
486  hstleg.Draw()
487  return histo_canvas
488 
489 ##########
490 # Draw data differences (comparison between two data sets or releases) on a separate canvas
491 #
492 def drawChanges(graph,chgleg):
493  graph_canvas = ROOT.TCanvas("change_canvas")
494  graph_canvas.cd()
495  graph.Draw("ALP")
496  chgleg.Draw()
497  return graph_canvas
498 
499 ###########
500 # Get limits for two graphs that will be superimposed upon one another
501 #
502 def getTwoGraphLimits(last_event1,max_val1,last_event2,max_val2,min_val1=-1,min_val2=-1):
503  biggestLastEvt = last_event1
504  biggestMaxval = max_val1
505  lowest_val = min_val1
506 
507  if min_val2 < lowest_val:
508  lowest_val = min_val2
509  if last_event2 > biggestLastEvt:
510  biggestLastEvt = last_event2
511  if max_val2 > biggestMaxval:
512  biggestMaxval = max_val2
513  return (biggestLastEvt,biggestMaxval,lowest_val)
514 
515 def getNpoints(data):
516  new_data=[]
517  try:
518  #This was necessary due to a bug in the getSimpleMemLogData parsing!!! no more necessary!
519  if data[0][0]==data[1][0]:
520  print('Two modules seem to have some output.\nCollapsing ...')
521  i=0
522  while True:
523  dataline1=data[i]
524  i+=1
525  dataline2=data[i]
526  new_eventnumber=dataline1[0]
527  new_vsize=dataline2[1]['vsize']
528  new_delta_vsize=dataline1[1]['delta_vsize']+dataline2[1]['delta_vsize']
529  new_rss=dataline2[1]['rss']
530  new_delta_rss=dataline1[1]['delta_rss']+dataline2[1]['delta_rss']
531 
532  new_data.append((new_eventnumber,{'vsize':new_vsize,
533  'delta_vsize':new_delta_vsize,
534  'rss':new_rss,
535  'delta_rss':new_delta_rss}))
536  i+=1
537  if i==len(data): break
538 
539  data=new_data
540  print('Collapsing: Done!')
541  except IndexError:
542  pass
543 
544  return (data,len(data))
545 
546 ###########
547 # Create simple memory check graphs
548 #
549 def createSimplMemGraphs(data,npoints,graph_num,legs,prevrev=""):
550  colors = [2,4]
551  values = ["vsize", "rss"]
552 
553  prevRevName = "???"
554  if not prevrev == "":
555  (head, tail) = os.path.split(prevrev)
556  prevRevName = os.path.basename(tail)
557 
558  releaseNames = ["Previous (%s)" % prevRevName,_cmsver]
559 
560  #fill the graphs
561  graphs = []
562  minim = -1
563  peak = -1
564  peaks = []
565  minims = []
566  idx = 0
567  for value in values:
568  leg = legs[idx]
569 
570  graph = ROOT.TGraph(npoints)
571  graph.SetTitle(value)
572  graph.SetLineColor(colors[graph_num])
573  graph.SetMarkerStyle(8)
574  graph.SetMarkerSize(.7)
575  graph.SetMarkerColor(1)
576  graph.SetLineWidth(3)
577  graph.GetXaxis().SetTitle("Event")
578  graph.GetYaxis().SetTitleOffset(1.3)
579  graph.GetYaxis().SetTitle("MB")
580 
581  total = 0
582  point_counter=0
583  rms = 0
584  first = True
585  for event_number,vals_dict in data:
586  if first:
587  minim = vals_dict[value]
588  peak = vals_dict[value]
589  first = False
590  if vals_dict[value] > peak:
591  peak = vals_dict[value]
592  if vals_dict[value] < minim:
593  minim = vals_dict[value]
594  graph.SetPoint(point_counter, event_number, vals_dict[value])
595  total += vals_dict[value]
596  rms += vals_dict[value] * vals_dict[value]
597  point_counter+=1
598 
599  rms = math.sqrt(rms / float(point_counter))
600  mean = total / float(point_counter)
601  last_event=data[-1][0]
602  peaks.append(peak)
603  minims.append(minim)
604  graph.GetXaxis().SetRangeUser(0,last_event+1)
605  leg.AddEntry(graph , "%s release" % releaseNames[graph_num], "l")
606  leg.AddEntry(graph , "Mean: %s" % str(mean) , "l")
607  leg.AddEntry(graph , "RMS : %s" % str(rms) , "l")
608  leg.AddEntry(graph , "Peak: %s" % str(peak) , "l")
609  graphs.append(graph)
610  idx += 1
611 
612  return (graphs[0] , last_event, peaks[0], minims[0], graphs[1], last_event, peaks[1], minims[1])
613 
614 #############
615 # Produce the difference of two memory data sets
616 #
617 def getMemDiff(data1,data2,npoints,last_event,orig_max_val,stepname,rss=False):
618  data3 = []
619  memtype = "vsize"
620  if rss:
621  memtype = "rss"
622 
623  graph=ROOT.TGraph(npoints)
624  total = 0
625  rms = 0
626  evt_counter=0
627  peak = -1
628  minum = -1
629  first = True
630  for x in range(len(data2)):
631  try:
632  (evtnum2,valdict2) = data2[x]
633  (evtnum1,valdict1) = data1[x]
634  if evtnum2 == evtnum1:
635  diffMBytes = valdict2[memtype] - valdict1[memtype]
636 
637  if first:
638  peak = diffMBytes
639  minum = diffMBytes
640  first = False
641  if diffMBytes > peak:
642  peak = diffMBytes
643  if diffMBytes < minum:
644  minum = diffMBytes
645  graph.SetPoint(evt_counter,evtnum2,diffMBytes)
646  evt_counter+=1
647  total += diffMBytes
648  rms += (diffMBytes * diffMBytes)
649  except IndexError:
650  pass
651  except ValueError:
652  pass
653 
654  mean = total / evt_counter
655  rms = math.sqrt(rms / float(evt_counter))
656 
657  min_val = minum
658  max_val = peak
659  interval = int(max_val-min_val)
660 
661  min_val=min_val-interval*0.2
662  max_val=max_val+interval*0.2
663  interval=int(max_val-min_val)
664  # Determine the max value to be something that makes the scale similar to what
665  # the original graph had. Unless we can't seem the maximum value.
666 
667  new_max = min_val + orig_max_val
668  if new_max < max_val:
669  pass
670  else :
671  max_val = new_max
672 
673  graph.SetTitle("Change in %s memory usage for each event between revs for step %s" % (memtype,stepname))
674  graph.SetName('MemoryUsagePerEvent')
675  graph.GetXaxis().SetTitle("Event Number")
676  graph.GetYaxis().SetTitle("Change in memory usage between revs (MBs)")
677  graph.GetYaxis().SetTitleOffset(1.3)
678  graph.SetLineColor(2)
679  graph.SetMarkerStyle(8)
680  graph.SetMarkerSize(.7)
681  graph.SetMarkerColor(1)
682  graph.SetLineWidth(3)
683  graph.GetXaxis().SetLimits(0,last_event)
684  graph.GetYaxis().SetRangeUser(min_val,max_val)
685  leg = ROOT.TLegend(0.5,0.7,0.89,0.89)
686  leg.AddEntry(graph, "Mean: %s" % str(mean), "l")
687  leg.AddEntry(graph, "RMS : %s" % str(rms) , "l")
688  leg.AddEntry(graph, "Peak: %s" % str(peak), "l")
689  leg.AddEntry(graph, "Trough: %s" % str(minum) , "l")
690 
691  return (graph,leg)
692 
693 ############
694 # Draw two memory graphs superimposed on one another
695 #
696 def drawMemGraphs(graph1,graph2,min_val,max_val,last_event,leg,memtype,stepname):
697  graph_canvas=ROOT.TCanvas("%s_%s_canvas" % (memtype,stepname))
698  graph_canvas.cd()
699  graph1.GetYaxis().SetRangeUser(min_val,max_val)
700  graph1.GetXaxis().SetRangeUser(0,last_event)
701  graph1.Draw("ALP")
702  graph2.Draw("LP" )
703  leg.Draw()
704  graph_canvas.ForceUpdate()
705  graph_canvas.Flush()
706  return graph_canvas
707 
708 ###########
709 # Draw the comparison graphs of two memory graphs
710 #
711 def drawMemChangeGraphs(graph,leg,memtype,stepname):
712  graph_canvas=ROOT.TCanvas("%s_%s_change_canvas" % (memtype,stepname))
713  graph_canvas.cd()
714  graph.Draw("ALP" )
715  leg.Draw()
716  graph_canvas.ForceUpdate()
717  graph_canvas.Flush()
718  return graph_canvas
719 
720 def getMemOrigScale(fst_min,snd_min,fst_max,snd_max):
721  minim = fst_min
722  if snd_min < minim:
723  minim = snd_min
724  maxim = fst_max
725  if snd_max > maxim:
726  maxim = snd_max
727  return (minim,maxim)
728 
729 def cmpSimpMemReport(rootfilename,outdir,oldLogfile,newLogfile,startevt,batch=True,candle="",prevrev=""):
730  if batch:
731  setBatch()
732  # the fundamental structure: the key is the evt number the value is a list containing
733  # VSIZE deltaVSIZE RSS deltaRSS
734  print("#####LOGFILES in cmsPErfRegress:")
735  print(oldLogfile)
736  print(newLogfile)
737  try:
738  info1 = getSimpleMemLogData(oldLogfile,startevt, candle)
739  if len(info1) == 0:
740  raise IndexError
741  except IndexError:
742  raise SimpMemParseErr(oldLogfile)
743  except IOError:
744  raise SimpMemParseErr(oldLogfile)
745 
746  try:
747  info2 = getSimpleMemLogData(newLogfile,startevt, candle)
748  if len(info2) == 0:
749  raise IndexError
750  except IndexError:
751  raise SimpMemParseErr(newLogfile)
752  except IOError:
753  raise SimpMemParseErr(newLogfile)
754  #print "DDDDD info1 %s"%info1 #Format is of the type:[('GEN,SIM', [(1, {'delta_vsize': 0.0, 'delta_rss': 0.0, 'vsize': 648.42600000000004, 'rss': 426.33199999999999}), (2,{...
755  #print "DDDDD info2 %s"%info2
756  canvases = []
757  # skim the second entry when the event number is the same BUG!!!!!!!
758  # i take elements in couples!
759 
760  candreg = re.compile("(.*)(?:\.log)")
761  vsize_canvas = None
762  rss_canvas = None
763  i = 0
764  firstRoot = True
765  newrootfile = None
766  #The following whileis confusing and not necessary info1 and info2 are supposed to only contain one set of simplememorycheck numbers
767  #in a tuple as seen above ('GEN,SIM',[(event_number,{'delta_vsize': 0.0, etc
768  #The data structure is questionable... but it is a remnant of past format of the log files I guess.
769  #while ( i < len(info1) and i < len(info2)):
770  #curinfo1 = info1[i]
771  #curinfo2 = info2[i]
772  (stepname1, data1) = info1[0]
773  (stepname2, data2) = info2[0]
774 
775  #Again this is not necessary anymore...
776  #if not stepname1 == stepname2:
777  #print "WARNING: Could not compare %s step and %s step because they are not the same step" % (stepname1, stepname2)
778  # print " Searching for next occurence"
779  # x = 1
780  # if not (i + 1) > len(info1):
781  # found = False
782  # for info in info1[i + 1:]:
783  # (stepname,trash) = info
784  # if stepname == stepname2:
785  # i += x
786  #print " Next occurence found, skipping in-between steps"
787  # assert i < len(info1)
788  # found = True
789  # break
790  # x += 1
791  # if found:
792  # continue
793  # print " No more occurences of this step, continuing"
794  # i += 1
795  # continue
796 
797  #Not sure what this is for!
798  #OK it was due to the bug of duplicated info in getSimpleMemLogData parsing!
799  #No need!
800  #(data1,npoints1) = getNpoints(data1)
801  #(data2,npoints2) = getNpoints(data2)
802  npoints1=len(data1)
803  npoints2=len(data2)
804 
805  legs = []
806  leg = ROOT.TLegend(0.6,0.99,0.89,0.8)
807  legs.append(leg)
808  leg = ROOT.TLegend(0.6,0.99,0.89,0.8)
809  legs.append(leg)
810 
811  try:
812  if len(data1) == 0:
813  raise IndexError
814  (vsize_graph1,
815  vsize_lstevt1,
816  vsize_peak1,
817  vsize_minim1,
818  rss_graph1,
819  rss_lstevt1,
820  rss_peak1,
821  rss_minim1) = createSimplMemGraphs(data1,npoints1,0,legs,prevrev=prevrev)
822  #candFilename = CandFname[candle]
823  #outputdir = "%s_%s_SimpleMemReport" % (candFilename,stepname1)
824  #outputdir = os.path.join(outdir,outputdir)
825  #print "Graph1"
826  #vsize_graph1.Print()
827  except IndexError:
828  raise SimpMemParseErr(oldLogfile)
829 
830  try:
831  if len(data2) == 0:
832  raise IndexError
833  (vsize_graph2,
834  vsize_lstevt2,
835  vsize_peak2,
836  vsize_minim2,
837  rss_graph2,
838  rss_lstevt2,
839  rss_peak2,
840  rss_minim2) = createSimplMemGraphs(data2,npoints2,1,legs,prevrev=prevrev)
841  #candFilename = CandFname[candle]
842  #outputdir = "%s_%s_SimpleMemReport" % (candFilename,stepname1)
843  #outputdir = os.path.join(outdir,outputdir)
844  #print "Graph2"
845  #vsize_graph2.Print()# os.path.join(outputdir,"vsize_graph2.png"))
846  except IndexError:
847  raise SimpMemParseErr(newLogfile)
848 
849 
850  (vsize_lstevt, vsize_max_val, vsize_min_val) = getTwoGraphLimits(vsize_lstevt1, vsize_peak1, vsize_lstevt2, vsize_peak2, vsize_minim1, vsize_minim2)
851  (rss_lstevt , rss_max_val , rss_min_val) = getTwoGraphLimits(rss_lstevt1 , rss_peak1, rss_lstevt2 , rss_peak2, rss_minim1, rss_minim2)
852 
853  (vsize_min,vsize_max) = getMemOrigScale(vsize_minim1,vsize_minim2,vsize_peak1,vsize_peak2)
854  (rss_min ,rss_max ) = getMemOrigScale(rss_minim1,rss_minim2,rss_peak1,rss_peak2)
855 
856  setupSuperimpose(vsize_graph1,
857  vsize_graph2,
858  vsize_lstevt,
859  0,
860  reporttype = 1,
861  title = "%s_vsize" % stepname1)
862  setupSuperimpose(rss_graph1 ,
863  rss_graph2 ,
864  rss_lstevt ,
865  0,
866  reporttype = 1,
867  title = "%s_rss" % stepname2)
868 
869  (vsizePerfDiffgraph, vsizeleg) = getMemDiff(data1,data2,npoints2,vsize_lstevt, (vsize_max - vsize_min), stepname1, rss=False)
870  (rssPerfDiffgraph, rssleg) = getMemDiff(data1,data2,npoints2,rss_lstevt , (rss_max - rss_min) , stepname1, rss=True )
871 
872  vsize_canvas = drawMemGraphs(vsize_graph1, vsize_graph2, vsize_min_val, vsize_max_val, vsize_lstevt, legs[0], "vsize", stepname1)
873  rss_canvas = drawMemGraphs(rss_graph1 , rss_graph2 , rss_min_val, rss_max_val, rss_lstevt, legs[1], "rss" , stepname1)
874  vsize_change_canvas = drawMemChangeGraphs(vsizePerfDiffgraph, vsizeleg, "vsize", stepname1)
875  rss_change_canvas = drawMemChangeGraphs(rssPerfDiffgraph , rssleg , "rss" , stepname1)
876 
877  if batch:
878 
879 
880  logcandle = ""
881  candname = ""
882  found = candreg.search(os.path.basename(newLogfile))
883 
884  if found:
885  logcandle = found.groups()[0]
886 
887  if candle in CandFname:
888  candFilename = CandFname[candle]
889  elif logcandle in CandFname:
890  candFilename = CandFname[logcandle]
891  else:
892  print("%s is an unknown candle!"%candle)
893  candFilename = "Unknown-candle"
894 
895  outputdir = "%s_%s_SimpleMemReport" % (candFilename,stepname1)
896  outputdir = os.path.join(outdir,outputdir)
897 
898  if not os.path.exists(outputdir):
899  os.mkdir(outputdir)
900 
901  #print the graphs as files :)
902 
903  newrootfile = createROOT(outputdir,rootfilename)
904 
905  vsize_canvas.Print( os.path.join(outputdir,"vsize_graphs.png"), "png")
906  rss_canvas.Print( os.path.join(outputdir,"rss_graphs.png" ), "png")
907  vsize_change_canvas.Print(os.path.join(outputdir,"vsize_change.png"), "png")
908  rss_change_canvas.Print( os.path.join(outputdir,"rss_change.png" ), "png")
909  # write it on file
910  map(lambda x: x.Write(), [vsize_graph1,vsize_graph2, rss_graph1, rss_graph2, vsizePerfDiffgraph, rssPerfDiffgraph])
911  map(lambda x: x.Write(), [vsize_canvas,rss_canvas,vsize_change_canvas,rss_change_canvas])
912  newrootfile.Close()
913  else:
914  # we have to do this if we are running the application standalone
915  # For some reason it will not draw some graphs at all if there is more than
916  # one step.
917  # If we wait between iterations of this loop the graphs will be drawn correctly.
918  # Perhaps a graphics buffer problem with ROOT?
919  # Perhaps a garbage collection problem in python? (Doubt it)
920  canvases.append(rss_canvas)
921  canvases.append(vsize_canvas)
922  canvases.append(vsize_change_canvas)
923  canvases.append(rss_change_canvas)
924  time.sleep(5.0)
925 
926  #Eliminated the while loop!
927  #i += 1
928 
929  #
930  # Create a one dimensional function and draw it
931  #
932 
933  if batch:
934  pass
935  else:
936  if len(canvases) > 0:
937  while reduce(lambda x,y: x or y ,canvases):
938  time.sleep(2.5)
939  return 0
940 
941 
942 def cmpTimingReport(rootfilename,outdir,oldLogfile,newLogfile,secsperbin,batch=True,prevrev=""):
943  if batch:
944  setBatch()
945 
946  data1 = getTimingLogData(oldLogfile)
947  data2 = getTimingLogData(newLogfile)
948 
949  try:
950  (min_val1,max_val1,nbins1,npoints1,last_event1) = getLimits(data1,secsperbin)
951  except IndexError as detail:
952  raise TimingParseErr(oldLogfile)
953 
954  try:
955  (min_val2,max_val2,nbins2,npoints2,last_event2) = getLimits(data2,secsperbin)
956  except IndexError as detail:
957  raise TimingParseErr(newLogfile)
958 
959  hsStack = ROOT.THStack("hsStack","Histogram Comparison")
960  leg = ROOT.TLegend(0.6,0.99,0.89,0.8)
961  histoleg = ROOT.TLegend(0.5,0.8,0.89,0.89)
962 
963  #
964 
965  (graph1,histo1,mean1) = newGraphAndHisto(histoleg,leg,npoints1,nbins1,min_val1,max_val1,data1,0,prevrev)
966  hsStack.Add(histo1)
967  (graph2,histo2,mean2) = newGraphAndHisto(histoleg,leg,npoints2,nbins2,min_val2,max_val2,data2,1,prevrev)
968  hsStack.Add(histo2)
969 
970  (biggestLastEvt,biggestMaxval, trashthis) = getTwoGraphLimits(last_event1,max_val1,last_event2,max_val2,min_val1,min_val2)
971 
972  (changegraph,chgleg) = getTimingDiff(data1,data2,npoints2,biggestLastEvt,biggestMaxval)
973  setupSuperimpose(graph1,graph2,biggestLastEvt,biggestMaxval)
974  avg_line1 = getMeanLines(mean1,last_event1,0)
975  avg_line2 = getMeanLines(mean2,last_event2,1)
976 
977  #
978  # Create a one dimensional function and draw it
979  #
980  histo1.GetXaxis().SetTitle("s")
981  graph_canvas = drawGraphs(graph1,graph2,avg_line1,avg_line2,leg)
982  changes_canvas = drawChanges(changegraph,chgleg)
983  histo_canvas = drawHistos(hsStack,histoleg)
984 
985  newrootfile = None
986  if batch:
987 
988  newrootfile = createROOT(outdir,rootfilename)
989 
990  cput = ROOT.TTree()
991  # array(typecode, initializer)
992  # typecode is i for int, f for float etc.
993  tot_a1 = array( "f", [ 0 ] )
994  tot_a2 = array( "f", [ 0 ] )
995 
996  tot_a1[0] = mean1
997  tot_a2[0] = mean2
998 
999  cput.Branch("total1",tot_a1,"total1/F")
1000  cput.Branch("total2",tot_a2,"total2/F")
1001  cput.Fill()
1002  cput.Write("cpu_time_tuple",ROOT.TObject.kOverwrite)
1003 
1004  names = ["graphs.png","changes.png","histos.png"]
1005 
1006  graph_canvas.Print( os.path.join(outdir,names[0]),"png")
1007  changes_canvas.Print(os.path.join(outdir,names[1]),"png")
1008  histo_canvas.Print( os.path.join(outdir,names[2]),"png")
1009 
1010  map(lambda x:x.Write(),[graph1,graph2,changegraph,hsStack,histo1,histo2])
1011 
1012  graph_canvas.Write() # to file
1013  changes_canvas.Write()
1014  histo_canvas.Write()
1015  newrootfile.Close()
1016 
1017  return names
1018  else:
1019 
1020  while graph_canvas or histo_canvas or changes_canvas:
1021  time.sleep(2.5)
1022  return 0
1023 
1024 def rmtree(path):
1025  try:
1026  #os.remove(path)
1027  #Brute force solution:
1028  RemoveCmd="rm -Rf %s"%path
1029  os.system(RemoveCmd)
1030  except OSError as detail:
1031  if detail.errno == 39:
1032  try:
1033  gen = os.walk(path)
1034  nodes = next(gen)
1035  nodes[0] = par
1036  nodes[1] = dirs
1037  nodes[2] = files
1038  for f in files:
1039  os.remove(os.path.join(path,f))
1040  for d in dirs:
1041  rmtree(os.path.join(path,d))
1042  except OSError as detail:
1043  print(detail)
1044  except IOError as detail:
1045  print(detail)
1046  os.remove(path)
1047 
1048 def perfreport(perftype,file1,file2,outdir,IgProfMemopt=""):
1049  src = ""
1050  try:
1051  src = os.environ["CMSSW_SEARCH_PATH"]
1052  except KeyError as detail:
1053  print("ERROR: scramv1 environment could not be located", detail)
1054 
1055  vars = src.split(":")
1056  loc = vars[0]
1057 
1058  proftype = ""
1059  if perftype == 0: # EdmSize
1060  proftype = "-fe"
1061  elif perftype == 1: # IgProf
1062  proftype = "-fi"
1063  else: # Callgrind
1064  proftype = "-ff"
1065 
1066  cmssw_release_base = ""
1067  cmssw_data = ""
1068  try:
1069  cmssw_release_base = os.environ['CMSSW_RELEASE_BASE']
1070  cmssw_data = os.environ['CMSSW_DATA_PATH']
1071  except KeyError as detail:
1072  raise PerfReportErr
1073 
1074  xmlfile = os.path.join(cmssw_release_base,"src","Validation","Performance","doc","regress.xml")
1075 
1076  prRoot = "/afs/cern.ch/user/g/gbenelli/public/PerfReport2/2.0.1"
1077 
1078  # this might be useful at some point
1079  #cd %s ; eval `scramv1 runtime -csh` ; source $CMSSW_DATA_PATH/perfreport/2.0.0/etc/profile.d/init.csh; cd - ; %s\"" % (loc,perfcmd)
1080 
1081  # Before adding Danilo's 2.1 we did this
1082  #perfcmd = "perfreport -tmp %s -i %s -r %s -o %s" % (proftype,file2,file1,outdir)
1083  #cmd = "tcsh -c \"source %s/perfreport/2.0.0/etc/profile.d/init.csh; cd - ; %s\"" % (cmssw_data,perfcmd)
1084 
1085  # now we do
1086 
1087  tmpdir = tmp.mkdtemp(prefix=os.path.join(outdir,"tmp"))
1088 
1089  perfcmd = "%s %s %s -c %s -t%s -i %s -r %s -o %s" % (os.path.join(prRoot,"bin","perfreport"),proftype,IgProfMemopt,xmlfile,tmpdir,file2,file1,outdir)
1090  cmd = "tcsh -c \"cd %s ; eval `scramv1 runtime -csh` ; cd - ;source %s/etc/profile.d/init.csh ; %s\"" % (loc,prRoot,perfcmd)
1091  #Obsolete popen4-> subprocess.Popen
1092  #process = os.popen(cmd)
1093  process = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
1094  exitstat = process.wait()
1095  cmdout = process.stdout.read()
1096  exitstat = process.returncode
1097 
1098  try:
1099  rmtree(tmpdir) #Brute force solution rm -RF tmpdir done in rmtree()
1100  #os.rmdir(tmpdir)
1101  except IOError as detail:
1102  print("WARNING: Could not remove dir because IO%s" % detail)
1103  except OSError as detail:
1104  print("WARNING: Could not remove dir because %s" % detail)
1105 
1106  if True:
1107  print(cmd)
1108  print(cmdout)
1109 
1110  if not exitstat == None:
1111  sig = exitstat >> 16 # Get the top 16 bits
1112  xstatus = exitstat & 0xffff # Mask out all bits except the bottom 16
1113  raise PerfReportErr("ERROR: PerfReport returned a non-zero exit status (%s, SIG_INT = %s) run %s. Dump follows: \n%s" % (perfcmd,xstatus,sig,cmdout))
1114 
1115 
1116 def cmpEdmSizeReport(outdir,file1,file2):
1117  perfreport(0,file1,file2,outdir)
1118 
1119 def ungzip(inf,outh):
1120  gzf = gzip.open(inf,"r")
1121  print("ungzipping")
1122  for char in gzf:
1123  os.write(outh,char)
1124  os.close(outh)
1125  print("finish ungzipping")
1126  gzf.close()
1127 
1128 def ungzip2(inf,out):
1129  os.system("gzip -c -d %s > %s" % (inf,out))
1130 
1131 def cmpIgProfReport(outdir,file1,file2,IgProfMemOpt=""):
1132  (tfile1, tfile2) = ("", "")
1133  try:
1134  # don't make temp files in /tmp because it's never bloody big enough
1135  (th1, tfile1) = tmp.mkstemp(prefix=os.path.join(outdir,"igprofRegressRep."))
1136  (th2, tfile2) = tmp.mkstemp(prefix=os.path.join(outdir,"igprofRegressRep."))
1137  os.close(th1)
1138  os.close(th2)
1139  os.remove(tfile1)
1140  os.remove(tfile2)
1141  ungzip2(file1,tfile1)
1142  ungzip2(file2,tfile2)
1143 
1144  perfreport(1,tfile1,tfile2,outdir,IgProfMemOpt)
1145 
1146  os.remove(tfile1)
1147  os.remove(tfile2)
1148  except OSError as detail:
1149  raise PerfReportErr("WARNING: The OS returned the following error when comparing %s and %s\n%s" % (file1,file2,str(detail)))
1150  if os.path.exists(tfile1):
1151  os.remove(tfile1)
1152  if os.path.exists(tfile2):
1153  os.remove(tfile2)
1154  except IOError as detail:
1155  raise PerfReportErr("IOError: When comparing %s and %s using temporary files %s and %s. Error message:\n%s" % (file1,file2,tfile1,tfile2,str(detail)))
1156  if os.path.exists(tfile1):
1157  os.remove(tfile1)
1158  if os.path.exists(tfile2):
1159  os.remove(tfile2)
1160 
1161 
1162 def cmpCallgrindReport(outdir,file1,file2):
1163  perfreport(2,file1,file2,outdir)
1164 
1165 def _main():
1166  outdir = os.getcwd()
1167 
1168  (file1,file2,secsperbin,reporttype,IgProfMemOptions) = getParameters()
1169 
1170  try:
1171  if reporttype == "timing":
1172  rootfilename = "timingrep-regression.root"
1173  cmpTimingReport(rootfilename ,outdir,file1,file2,secsperbin,True)
1174  elif reporttype == "simplememory":
1175  rootfilename = "simpmem-regression.root"
1176  cmpSimpMemReport(rootfilename,outdir,file1,file2,secsperbin,True)
1177  elif reporttype == "edmsize":
1178  cmpEdmSizeReport(outdir,file1,file2)
1179  elif reporttype == "callgrind":
1180  cmpCallgrindReport(outdir,file1,file2)
1181  elif reporttype == "igprof":
1182  cmpIgProfReport(outdir,file1,file2,IgProfMemOptions)
1183  except TimingParseErr as detail:
1184  print("WARNING: Could not parse data from Timing report file %s; not performing regression" % detail.message)
1185  except SimpMemParseErr as detail:
1186  print("WARNING: Could not parse data from Memory report file %s; not performing regression" % detail.message)
1187  except PerfReportErr as detail:
1188  print("WARNING: Could not parse data from Edm file %s; not performing regression" % detail.message)
1189  except IOError as detail:
1190  print(detail)
1191  except OSError as detail:
1192  print(detail)
1193 
1194 if __name__ == "__main__":
1195  _main()
1196 
def cmpEdmSizeReport(outdir, file1, file2)
def cmpTimingReport(rootfilename, outdir, oldLogfile, newLogfile, secsperbin, batch=True, prevrev="")
def perfreport(perftype, file1, file2, outdir, IgProfMemopt="")
def getMeanLines(avg, last_event, graph_num)
Plot the mean line on a graph.
def createSimplMemGraphs(data, npoints, graph_num, legs, prevrev="")
Create simple memory check graphs.
def getMemOrigScale(fst_min, snd_min, fst_max, snd_max)
def getSimpleMemLogData(logfile_name, startevt, candle)
Parse memory check data from log file.
def drawHistos(histo_stack, hstleg)
Draw superimposed histograms on a separate canvas.
def __init__(self, message)
def getMemDiff(data1, data2, npoints, last_event, orig_max_val, stepname, rss=False)
Produce the difference of two memory data sets.
def setBatch()
Setup PyRoot as a batch run.
def getNpoints(data)
def drawMemGraphs(graph1, graph2, min_val, max_val, last_event, leg, memtype, stepname)
Draw two memory graphs superimposed on one another.
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:65
def drawGraphs(graph1, graph2, avg1, avg2, leg)
Draw superimposed graphs on a separate canvas.
def get_min(data, index=1)
Get min value in data set.
def cmpCallgrindReport(outdir, file1, file2)
def __init__(self, message)
def cmpSimpMemReport(rootfilename, outdir, oldLogfile, newLogfile, startevt, batch=True, candle="", prevrev="")
def createROOT(outdir, filename)
Create the root file to save the graphs in.
def newGraphAndHisto(histoleg, leg, npoints, nbins, min_val, max_val, data, graph_num, prevrev="")
Create a new timing graph and histogram.
def cmpIgProfReport(outdir, file1, file2, IgProfMemOpt="")
def getLimits(data, secsperbin)
Get limits to plot the graph.
def ungzip(inf, outh)
def __init__(self, message)
def getTimingLogData(logfile_name)
Parse timing data from log file.
def getTwoGraphLimits(last_event1, max_val1, last_event2, max_val2, min_val1=-1, min_val2=-1)
Get limits for two graphs that will be superimposed upon one another.
def get_max(data, index=1)
Get max value in data set.
def drawChanges(graph, chgleg)
Draw data differences (comparison between two data sets or releases) on a separate canvas...
def ungzip2(inf, out)
def drawMemChangeGraphs(graph, leg, memtype, stepname)
Draw the comparison graphs of two memory graphs.
#define str(s)
def getTimingDiff(data1, data2, npoints, last_event, orig_max_val)
Get the difference in timing data (for comparison of two releases)
def setupSuperimpose(graph1, graph2, last_event, max_val, reporttype=0, title="")
Setup graph information for one graph (no need to it on both if they are superimposed) ...