CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
cmsRelvalreport.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 
3 r'''
4 cmsRelvalreport.py: a script to run performance tests and produce reports in a automated way.
5 '''
6 
7 import glob,re #Used for IgProf Analyse work with IgProf Mem profile dumps
8 # Configuration parameters:#############################################################
9 
10 # Perfreport 3 and 2 coordinates:
11 PR3_BASE='/afs/cern.ch/user/d/dpiparo/w0/perfreport3installation/'
12 PR3=PR3_BASE+'/bin/perfreport'# executable
13 PERFREPORT3_PATH=PR3_BASE+'/share/perfreport' #path to xmls
14 #PR3_PRODUCER_PLUGIN=PR3_BASE+'/lib/libcmssw_by_producer.so' #plugin for fpage
15 PR3_PRODUCER_PLUGIN='/afs/cern.ch/user/d/dpiparo/w0/pr3/perfreport/plugins/cmssw_by_producer/libcmssw_by_producer.so'
16 
17 #PR2_BASE='/afs/cern.ch/user/d/dpiparo/w0/perfreport2.1installation/'
18 PR2_BASE='/afs/cern.ch/user/g/gbenelli/public/PerfReport2/2.0.1/'
19 PR2='%s' %(PR2_BASE+'/bin/perfreport')# executable
20 PERFREPORT2_PATH=PR2_BASE+'/share/perfreport' #path to xmls
21 
22 import os
23 cmssw_base=os.environ["CMSSW_BASE"]
24 cmssw_release_base=os.environ["CMSSW_RELEASE_BASE"]
25 pyrelvallocal=cmssw_base+"/src/Configuration/PyReleaseValidation"
26 #Set the path depending on the presence of a locally checked out version of PyReleaseValidation
27 if os.path.exists(pyrelvallocal):
28  RELEASE='CMSSW_BASE'
29  print "Using LOCAL version of Configuration/PyReleaseValidation instead of the RELEASE version"
30 elif not os.path.exists(pyrelvallocal):
31  RELEASE='CMSSW_RELEASE_BASE'
32 
33 #Eliminating the full paths to all the scripts used they are assumed to be in the release by default, being in the scripts/ directory of their package
34 # Valgrind Memcheck Parser coordinates:
35 #VMPARSER='%s/src/Utilities/ReleaseScripts/scripts/valgrindMemcheckParser.pl' %os.environ['CMSSW_RELEASE_BASE']#This has to always point to 'CMSSW_RELEASE_BASE'
36 VMPARSER='valgrindMemcheckParser.pl'
37 
38 #Not a script... need to keep the full path to the release
39 # Valgrind Memcheck Parser output style file coordinates:
40 VMPARSERSTYLE='%s/src/Utilities/ReleaseScripts/data/valgrindMemcheckParser.css' %os.environ['CMSSW_RELEASE_BASE']#This has to always point to 'CMSSW_RELEASE_BASE'
41 
42 # IgProf_Analysis coordinates:
43 #IGPROFANALYS='%s/src/Validation/Performance/scripts/cmsIgProf_Analysis.py'%os.environ[RELEASE]
44 IGPROFANALYS='cmsIgProf_Analysis.py'
45 
46 # Timereport parser
47 #TIMEREPORTPARSER='%s/src/Validation/Performance/scripts/cmsTimeReport.pl'%os.environ[RELEASE]
48 TIMEREPORTPARSER='cmsTimeReport.pl'
49 
50 # Simple memory parser
51 #SIMPLEMEMPARSER='%s/src/Validation/Performance/scripts/cmsSimplememchecker_parser.py' %os.environ[RELEASE]
52 SIMPLEMEMPARSER='cmsSimplememchecker_parser.py'
53 
54 # Timing Parser
55 #TIMINGPARSER='%s/src/Validation/Performance/scripts/cmsTiming_parser.py' %os.environ[RELEASE]
56 TIMINGPARSER='cmsTiming_parser.py'
57 
58 # makeSkimDriver
59 MAKESKIMDRIVERDIR='%s/src/Configuration/EventContent/test' %os.environ[RELEASE]
60 MAKESKIMDRIVER='%s/makeSkimDriver.py'%MAKESKIMDRIVERDIR
61 
62 ########################################################################################
63 
64 
65 # Library to include to run valgrind fce
66 VFCE_LIB='/afs/cern.ch/user/m/moserro/public/vgfcelib'
67 PERL5_LIB='/afs/cern.ch/user/d/dpiparo/w0/PERLlibs/5.8.0'
68 
69 
70 
71 # the profilers that use the stout of the app..
72 STDOUTPROFILERS=['Memcheck_Valgrind',
73  'Timereport_Parser',
74  'Timing_Parser',
75  'SimpleMem_Parser']
76 # Profilers list
77 PROFILERS=['ValgrindFCE',
78  'IgProf_perf',
79  'IgProf_mem',
80  'Edm_Size']+STDOUTPROFILERS
81 
82 
83 # name of the executable to benchmark. It can be different from cmsRun in future
84 EXECUTABLE='cmsRun'
85 
86 # Command execution and debug switches
87 EXEC=True
88 DEBUG=True
89 
90 #Handy dictionaries to handle the mapping between IgProf Profiles and counters:
91 IgProfCounters={'IgProfPerf':['PERF_TICKS'],
92  'IgProfMem':['MEM_TOTAL','MEM_LIVE','MEM_MAX']
93  }
94 IgProfProfiles={'PERF_TICKS':'IgProfPerf',
95  'MEM_TOTAL':'IgProfMem',
96  'MEM_LIVE':'IgProfMem',
97  'MEM_MAX':'IgProfMem'
98  }
99 import time
100 import optparse
101 import sys
102 
103 
104 #######################################################################
105 def red(string):
106  return '%s%s%s' %('\033[1;31m',string,'\033[1;0m')
107 def green(string):
108  return '%s%s%s' %('\033[1;32m',string,'\033[1;0m')
109 def yellow(string):
110  return '%s%s%s' %('\033[1;33m',string,'\033[1;0m')
111 #######################################################################
112 
113 def clean_name(name):
114  '''
115  Trivially removes an underscore if present as last char of a string
116  '''
117  i=-1
118  is_dirty=True
119  while(is_dirty):
120  if name[i]=='_':
121  name=name[:-1]
122  else:
123  return name
124  i-=1
125 
126 #######################################################################
127 
128 def execute(command):
129  '''
130  It executes command if the EXEC switch is True.
131  Catches exitcodes different from 0.
132  '''
133  logger('%s %s ' %(green('[execute]'),command))
134  if EXEC:
135  exit_code=os.system(command)
136  if exit_code!=0:
137  logger(red('*** Seems like "%s" encountered problems.' %command))
138  return exit_code
139  else:
140  return 0
141 
142 #######################################################################
143 
144 def logger(message,level=0):
145  '''
146  level=0 output, level 1 debug.
147  '''
148  message='%s %s' %(yellow('[RelValreport]'),message)
149 
150  sys.stdout.flush()
151 
152  if level==0:
153  print message
154  if level==1 and DEBUG:
155  print message
156 
157  sys.stdout.flush()
158 
159 #######################################################################
160 
162  '''
163  Class to read the trivial ASCII file containing the candles
164  '''
165  def __init__(self, filename):
166 
168 
169  candlesfile=open(filename,'r')
170 
171  if filename[-3:]=='xml':
172  command=''
173  profiler=''
174  meta=''
175  db_meta=''
176  reuse=False
177 
178  from xml.dom import minidom
179 
180  # parse the config
181  xmldoc = minidom.parse(filename)
182 
183  # get the candles
184  candles_list = xmldoc.getElementsByTagName('candle')
185 
186  # a list of dictionaries to store the info
187  candles_dict_list=[]
188 
189  for candle in candles_list:
190  info_dict={}
191  for child in candle.childNodes:# iteration over candle node children
192  if not child.__dict__.has_key('nodeName'):# if just a text node skip!
193  #print 'CONTINUE!'
194  continue
195  # We pick the info from the node
196  tag_name=child.tagName
197  #print 'Manipulating a %s ...'%tag_name
198  data=child.firstChild.data
199  #print 'Found the data: %s !' %data
200  # and we put it in the dictionary
201  info_dict[tag_name]=data
202  # to store it in a list
203  candles_dict_list.append(info_dict)
204 
205  # and now process what was parsed
206 
207  for candle_dict in candles_dict_list:
208  # compulsory params!!
209  command=candle_dict['command']
210  profiler=candle_dict['profiler']
211  meta=candle_dict['meta']
212  # other params
213  try:
214  db_meta=candle_dict['db_meta']
215  except:
216  db_meta=None
217  try:
218  reuse=candle_dict['reuse']
219  except:
220  reuse=False
221 
222  self.commands_profilers_meta_list.append([command,profiler,meta,reuse,db_meta])
223 
224  # The file is a plain ASCII
225  else:
226  for candle in candlesfile.readlines():
227  # Some parsing of the file
228  if candle[0]!='#' and candle.strip(' \n\t')!='': # if not a comment or an empty line
229  if candle[-1]=='\n': #remove trail \n if it's there
230  candle=candle[:-1]
231  splitted_candle=candle.split('@@@') #separate
232 
233  # compulsory fields
234  command=splitted_candle[0]
235  profiler=splitted_candle[1].strip(' \t')
236  meta=splitted_candle[2].strip(' \t')
237  info=[command,profiler,meta]
238 
239  # FIXME: AN .ini or xml config??
240  # do we have something more?
241  len_splitted_candle=len(splitted_candle)
242  reuse=False
243  if len_splitted_candle>3:
244  # is it a reuse statement?
245  if 'reuse' in splitted_candle[3]:
246  reuse=True
247  info.append(reuse)
248  else:
249  info.append(reuse)
250 
251  # we have one more field or we hadn't a reuse in the last one
252  if len_splitted_candle>4 or (len_splitted_candle>3 and not reuse):
253  cmssw_scram_version_string=splitted_candle[-1].strip(' \t')
254  info.append(cmssw_scram_version_string)
255  else:
256  info.append(None)
257 
258 
259  self.commands_profilers_meta_list.append(info)
260 
261  #----------------------------------------------------------------------
262 
264  return self.commands_profilers_meta_list
265 
266 #######################################################################
267 
268 class Profile:
269  '''
270  Class that represents the procedure of performance report creation
271  '''
272  def __init__(self,command,profiler,profile_name):
273  self.command=command
274  self.profile_name=profile_name
275  self.profiler=profiler
276 
277  #------------------------------------------------------------------
278  # edit here if more profilers added
279  def make_profile(self):
280  '''
281  Launch the right function according to the profiler name.
282  '''
283  if self.profiler=='ValgrindFCE':
284  return self._profile_valgrindfce()
285  elif self.profiler.find('IgProf')!=-1:
286  return self._profile_igprof()
287  elif self.profiler.find('Edm_Size')!=-1:
288  return self._profile_edmsize()
289  elif self.profiler=='Memcheck_Valgrind':
290  return self._profile_Memcheck_Valgrind()
291  elif self.profiler=='Timereport_Parser':
292  return self._profile_Timereport_Parser()
293  elif self.profiler=='Timing_Parser':
294  return self._profile_Timing_Parser()
295  elif self.profiler=='SimpleMem_Parser':
296  return self._profile_SimpleMem_Parser()
297  elif self.profiler=='':
298  return self._profile_None()
299  elif self.profiler=='None': #adding this for the case of candle ASCII file non-profiling commands
300  return self._profile_None()
301  else:
302  raise('No %s profiler found!' %self.profiler)
303  #------------------------------------------------------------------
305  '''
306  Valgrind profile launcher.
307  '''
308  # ValgrindFCE needs a special library to run
309  os.environ["VALGRIND_LIB"]=VFCE_LIB
310 
311  profiler_line=''
312  valgrind_options= 'time valgrind '+\
313  '--tool=callgrind '+\
314  '--fce=%s ' %(self.profile_name)
315 
316  # If we are using cmsDriver we should use the prefix switch
317  if EXECUTABLE=='cmsRun' and self.command.find('cmsDriver.py')!=-1:
318  profiler_line='%s --prefix "%s"' %(self.command,valgrind_options)
319 
320  else:
321  profiler_line='%s %s' %(valgrind_options,self.command)
322  #'--trace-children=yes '+\
323 
324  return execute(profiler_line)
325 
326  #------------------------------------------------------------------
327  def _profile_igprof(self):
328  '''
329  IgProf profiler launcher.
330  '''
331  profiler_line=''
332 
333  igprof_options='igprof -d -t %s ' \
334  %EXECUTABLE # IgProf profile not general only for CMSRUN!
335 
336  # To handle Igprof memory and performance profiler in one function
337  if self.profiler=='IgProf_perf':
338  igprof_options+='-pp '
339  elif self.profiler=='IgProf_mem':
340  igprof_options+='-mp '
341  else:
342  raise ('Unknown IgProf flavour: %s !'%self.profiler)
343  igprof_options+='-z -o %s' %(self.profile_name)
344 
345  # If we are using cmsDriver we should use the prefix switch
346  if EXECUTABLE=='cmsRun' and self.command.find('cmsDriver.py')!=-1:
347  profiler_line='%s --prefix "%s"' %(self.command,igprof_options)
348  else:
349  profiler_line='%s %s' %(igprof_options, self.command)
350 
351  return execute(profiler_line)
352 
353  #------------------------------------------------------------------
354 
355  def _profile_edmsize(self):
356  '''
357  Launch edm size profiler
358  '''
359  # In this case we replace the name to be clear
360  input_rootfile=self.command
361 
362  # Skim the content if requested!
363  if '.' in self.profiler:
364 
365  clean_profiler_name,options=self.profiler.split('.')
366  content,nevts=options.split(',')
367  outfilename='%s_%s.root'%(os.path.basename(self.command)[:-6],content)
368  oldpypath=os.environ['PYTHONPATH']
369  os.environ['PYTHONPATH']+=':%s' %MAKESKIMDRIVERDIR
370  execute('%s -i %s -o %s --outputcommands %s -n %s' %(MAKESKIMDRIVER,
371  self.command,
372  outfilename,
373  content,
374  nevts))
375  os.environ['PYTHONPATH']=oldpypath
376  #execute('rm %s' %outfilename)
377  self.command=outfilename
378  self.profiler=clean_profiler_name
379 
380 
381  profiler_line='edmEventSize -o %s -d %s'\
382  %(self.profile_name,self.command)
383 
384  return execute(profiler_line)
385 
386  #------------------------------------------------------------------
387 
389  '''
390  Valgrind Memcheck profile launcher
391  '''
392  profiler_line=''
393  #Adding cms suppression of useless messages (cmsvgsupp)
394  #Removing leak-checking (done with igprof)
395  #'--leak-check=no '+\ (no is the default)
396  #'--show-reachable=yes '+\
397  #'--track-fds=yes '
398  #Adding xml logging
399  valgrind_options='time valgrind --tool=memcheck `cmsvgsupp` '+\
400  '--num-callers=20 '+\
401  '--xml=yes '+\
402  '--xml-file=%s.xml '%self.profile_name.replace(",","-")[:-4]
403 
404  # If we are using cmsDriver we should use the prefix switch
405  if EXECUTABLE=='cmsRun' and self.command.find('cmsDriver.py')!=-1:
406  #Replacing 2>&1 |tee with >& in the shell command to preserve the return code significance:
407  # using tee return would be 0 even if the command failed before the pipe:
408  profiler_line='%s --prefix "%s" >& %s' %(self.command,valgrind_options,self.profile_name)
409 
410  else:
411  profiler_line='%s %s >& %s' %(valgrind_options,self.command,self.profile_name)
412  #'--trace-children=yes '+\
413  return execute(profiler_line)
414  #-------------------------------------------------------------------
415 
417  return self._save_output()
418 
419  #-------------------------------------------------------------------
420 
422  return self._save_output()
423 
424  #-------------------------------------------------------------------
425 
427  return self._save_output()
428 
429  #-------------------------------------------------------------------
430 
431  def _save_output(self):
432  '''
433  Save the output of cmsRun on a file!
434  '''
435 # # a first maquillage about the profilename:
436 # if self.profile_name[-4:]!='.log':
437 # self.profile_name+='.log'
438  #Replacing 2>&1 |tee with >& in the shell command to preserve the return code significance:
439  # using tee return would be 0 even if the command failed before the pipe:
440  profiler_line='%s >& %s' %(self.command,self.profile_name)
441  return execute(profiler_line)
442 
443  #-------------------------------------------------------------------
444 
445  def _profile_None(self):
446  '''
447  Just Run the command!
448  '''
449  return execute(self.command)
450 
451  #-------------------------------------------------------------------
452 
453  def make_report(self,
454  fill_db=False,
455  db_name=None,
456  tmp_dir=None,
457  outdir=None,
458  IgProf_option=None,
459  metastring=None):
460  '''
461  Make a performance report with CMSSW scripts for CMSSW internal profiling (Timing/SimpleMemoryCheck) and Memcheck, PR2 for edmEventSize and Callgrind (NOTE PR2 is not supported anymore and is not currently in the CMSSW external, running froma privat AFS!), igprof-analyse for all IgProf profiling.
462  '''
463 
464  if outdir==None or outdir==self.profile_name:
465  outdir=self.profile_name+'_outdir'
466 
467  #Create the directory where the report will be stored:
468  if not os.path.exists(outdir) and not fill_db and not IgProf_option:
469  #Added an IgProf condition to avoid the creation of a directory that we will not need anymore, since we will put all info in the filenames
470  execute('mkdir %s' %outdir)
471 
472  if fill_db:
473  db_option='-a'
474  if not os.path.exists(db_name):
475  db_option='-A'
476 
477  # temp in the local dir for PR
478  tmp_switch=''
479  if tmp_dir!='':
480  execute('mkdir %s' %tmp_dir)
481  tmp_switch=' -t %s' %tmp_dir
482 
483  #Handle the various profilers:
484 
485  #####################################################################
486 
487  # Profiler is ValgrindFCE:
488  if self.profiler=='ValgrindFCE':
489  perfreport_command=''
490  # Switch for filling the db
491  if not fill_db:
492  os.environ["PERFREPORT_PATH"]='%s/' %PERFREPORT2_PATH
493  perfreport_command='%s %s -ff -i %s -o %s' %(PR2,
494  tmp_switch,
495  self.profile_name,
496  outdir)
497  else:
498  os.environ["PERFREPORT_PATH"]='%s/' %PERFREPORT3_PATH
499  perfreport_command='%s %s -n5000 -u%s -ff -m \'scram_cmssw_version_string,%s\' -i %s %s -o %s' \
500  %(PR3,
501  tmp_switch,
502  PR3_PRODUCER_PLUGIN,
503  metastring,
504  self.profile_name,
505  db_option,
506  db_name)
507  return execute(perfreport_command)
508 
509  #####################################################################
510 
511  # Profiler is IgProf:
512  if self.profiler.find('IgProf')!=-1:
513  #First the case of IgProf PERF and MEM reporting:
514  if not 'ANALYSE' in IgProf_option:
515  #Switch to the use of igprof-analyse instead of PerfReport!
516  #Will use the ANALYSE case for regressions between early event dumps and late event dumps of the profiles
517  #Following Andreas suggestion, add the number of events for the EndOfJob report
518  NumberOfEvents=self.command.split()[3] #FIXME: this is quite hardcoded... but should be stable...
519  sqlite_outputfile=self.profile_name.split(".")[0].replace(IgProfProfiles[IgProf_option[0]],IgProf_option[0])+'___'+NumberOfEvents+'_EndOfJob.sql3'
520  logger("Executing the report of the IgProf end of job profile")
521  exit=execute('igprof-analyse --sqlite -d -v -g -r %s %s | sqlite3 %s'%(IgProf_option[0],self.profile_name,sqlite_outputfile))
522  return exit
523  #Then the "ANALYSE" case that we want to use to add to the same directories (Perf, MemTotal, MemLive)
524  #also some other analyses and in particular:
525  #1-the first 7 lines of the ASCII analysis of the IgProf profile dumps (total of the counters)
526  #2-the dump at different event numbers,
527  #3-the diff between the first and last dump,
528  #4-the counters grouped by library using regexp at the last dump:
529  else: #We use IgProf Analysis
530  #Set the IgProfCounter from the ANALYSE.MEM_TOT style IgProf_option
531  #print IgProf_option
532  IgProfCounter=IgProf_option[1]
533  #Add here the handling of the new IgProf.N.gz files so that they will get preserved and not overwritten:
534  logger("Looking for IgProf intermediate event profile dumps")
535  #Check if there are IgProf.N.gz dump files:
536  IgProfDumps=glob.glob("IgProf.*.gz")
537  #in case there are none check if they have already been mv'd to another name to avoid overwriting
538  #(MEM_LIVE usually re-uses MEM_TOTAL, so the IgProf.N.gz files will already have a MemTotal name...)
539  if not IgProfDumps:
540  localFiles=os.listdir('.')
541  IgProfDumpProfilesPrevious=re.compile(r"\w+.\d+.gz")
542  IgProfDumps=filter(lambda x: IgProfDumpProfilesPrevious.search(x),localFiles)
543  #Now if there are dumps execute the following analyses:
544  if IgProfDumps:
545  IgProfDumps.sort()
546  logger("Found the following IgProf intermediate event profile dumps:")
547  logger(IgProfDumps)
548  FirstDumpEvent=9999999
549  LastDumpEvent=0
550  exit=0
551  for dump in IgProfDumps:
552  if "___" in dump:
553  DumpEvent=dump.split(".")[0].split("___")[-1]
554  else:
555  DumpEvent=dump.split(".")[1]
556  #New naming convention using ___ as separator
557  DumpedProfileName=self.profile_name[:-3]+"___"+DumpEvent+".gz"
558  if dump.startswith("IgProf"):
559  execute('mv %s %s'%(dump,DumpedProfileName))
560  #Keep a tab of the first and last dump event for the diff analysis
561  if int(DumpEvent) < FirstDumpEvent:
562  FirstDumpEvent = int(DumpEvent)
563  if int(DumpEvent) > LastDumpEvent:
564  LastDumpEvent = int(DumpEvent)
565  #Eliminating the ASCII analysis to get the totals, Giulio will provide this information in igprof-navigator with a special query
566  #First type of analysis: dump first 7 lines of ASCII analysis:
567  #logger("Executing the igprof-analyse analysis to dump the ASCII 7 lines output with the totals for the IgProf counter")
568  #exit=execute('%s -o%s -i%s -t%s' %(IGPROFANALYS,outdir,DumpedProfileName,"ASCII"))
569  #Second type of analysis: dump the report in sqlite format to be ready to be browsed with igprof-navigator
570  logger("Executing the igprof-analyse analysis saving into igprof-navigator browseable SQLite3 format")
571  #exit=exit+execute('%s -o%s -i%s -t%s' %(IGPROFANALYS,outdir,DumpedProfileName,"SQLite3"))
572  #Execute all types of analyses available with the current profile (using the dictionary IgProfProfile):
573  #To avoid this we should use a further input in SimulationCandles.txt IgProfMem.ANALYSE.MEM_TOTAL maybe the cleanest solution.
574  #for IgProfile in IgProfCounters.keys():
575  # if DumpedProfileName.find(IgProfile)>0:
576  # for counter in IgProfCounters[IgProfile]:
577  #Check that the file does not exist:
578  #if not os.path.exists(DumpedProfileName.split(".")[0].replace(IgProfProfiles[counter],counter)+".sql3"):
579  exit=exit+execute('%s -c%s -i%s -t%s' %(IGPROFANALYS,IgProfCounter,DumpedProfileName,"SQLite3"))
580  #else:
581  # print "File %s already exists will not process profile"%DumpedProfileName.split(".")[0].replace(IgProfProfiles[counter],counter)+".sql3"
582  #FIXME:
583  #Issue with multiple profiles in the same dir: assuming Perf and Mem will always be in separate dirs
584  #Potential ssue with multiple steps?
585  #Adapting to new igprof naming scheme:
586  FirstDumpProfile=self.profile_name[:-3]+"___"+str(FirstDumpEvent)+".gz"
587  LastDumpProfile=self.profile_name[:-3]+"___"+str(LastDumpEvent)+".gz"
588  #Third type of analysis: execute the diff analysis:
589  #Check there are at least 2 IgProf intermediate event dump profiles to do a regression!
590  if len(IgProfDumps)>1:
591  logger("Executing the igprof-analyse regression between the first IgProf profile dump and the last one")
592  #for IgProfile in IgProfCounters.keys():
593  # if DumpedProfileName.find(IgProfile)>0:
594  # IgProfCounter=IgProfCounters[IgProfile]
595  exit=exit+execute('%s -c%s -i%s -r%s' %(IGPROFANALYS,IgProfCounter,LastDumpProfile,FirstDumpProfile))
596  else:
597  logger("CANNOT execute any regressions: not enough IgProf intermediate event profile dumps!")
598  #Fourth type of analysis: execute the grouped by library igprof-analyse:
599  logger("Executing the igprof-analyse analysis merging the results by library via regexp and saving the result in igprof-navigator browseable SQLite3 format")
600  #for IgProfile in IgProfCounters.keys():
601  # if DumpedProfileName.find(IgProfile)>0:
602  # IgProfCounter=IgProfCounters[IgProfile]
603  exit=exit+execute('%s -c%s -i%s --library' %(IGPROFANALYS,IgProfCounter,LastDumpProfile))
604  #If they are not there at all (no dumps)
605  else:
606  logger("No IgProf intermediate event profile dumps found!")
607  exit=0
608 
609  return exit
610 
611 
612  #####################################################################
613 
614  # Profiler is EdmSize:
615  if 'Edm_Size' in self.profiler:
616  perfreport_command=''
617  if not fill_db:
618  os.environ["PERFREPORT_PATH"]='%s/' \
619  %PERFREPORT2_PATH
620  perfreport_command='%s %s -fe -i %s -o %s' \
621  %(PR2,
622  tmp_switch,
623  self.profile_name,
624  outdir)
625  else:
626  os.environ["PERFREPORT_PATH"]='%s/' \
627  %PERFREPORT3_PATH
628  perfreport_command='%s %s -n5000 -u%s -fe -i %s -a -o %s' \
629  %(PR3,
630  tmp_switch,
631  PR3_PRODUCER_PLUGIN,
632  self.profile_name,
633  db_name)
634 
635  return execute(perfreport_command)
636 
637  #FIXME: probably need to move this somewhere else now that we use return statements
638  if tmp_dir!='':
639  execute('rm -r %s' %tmp_dir)
640 
641  #####################################################################
642 
643  # Profiler is Valgrind Memcheck
644  if self.profiler=='Memcheck_Valgrind':
645  # Three pages will be produced:
646  os.environ['PERL5LIB']=PERL5_LIB
647  report_coordinates=(VMPARSER,self.profile_name,outdir)
648  # Copy the Valgrind Memcheck parser style file in the outdir
649  copyStyleFile='cp -pR %s %s'%(VMPARSERSTYLE,outdir)
650  execute(copyStyleFile)
651  report_commands=('%s --preset +prod,-prod1 %s > %s/edproduce.html'\
652  %report_coordinates,
653  '%s --preset +prod1 %s > %s/esproduce.html'\
654  %report_coordinates,
655  '%s -t beginJob %s > %s/beginjob.html'\
656  %report_coordinates)
657  exit=0
658  for command in report_commands:
659  exit= exit + execute(command)
660  return exit
661  #####################################################################
662 
663  # Profiler is TimeReport parser
664 
665  if self.profiler=='Timereport_Parser':
666  return execute('%s %s %s' %(TIMEREPORTPARSER,self.profile_name,outdir))
667 
668  #####################################################################
669 
670  # Profiler is Timing Parser
671 
672  if self.profiler=='Timing_Parser':
673  return execute('%s -i %s -o %s' %(TIMINGPARSER,self.profile_name,outdir))
674 
675 
676  #####################################################################
677 
678  # Profiler is Simple memory parser
679 
680  if self.profiler=='SimpleMem_Parser':
681  return execute('%s -i %s -o %s' %(SIMPLEMEMPARSER,self.profile_name,outdir))
682 
683  #####################################################################
684 
685  # no profiler
686 
687  if self.profiler=='' or self.profiler=='None': #Need to catch the None case, since otherwise we get no return code (crash for pre-requisite step running).
688  return 0 #Used to be pass, but we need a return 0 to handle exit code properly!
689 
690 #############################################################################################
691 
692 def principal(options):
693  '''
694  Here the objects of the Profile class are istantiated.
695  '''
696  #Add a global exit code variable, that is the sum of all exit codes, to return it at the end:
697  exitCodeSum=0
698  # Build a list of commands for programs to benchmark.
699  # It will be only one if -c option is selected
700  commands_profilers_meta_list=[]
701 
702  # We have only one
703  if options.infile=='':
704  logger('Single command found...')
705  commands_profilers_meta_list.append([options.command,'','',False,''])
706 
707  # We have more: we parse the list of candles
708  else:
709  logger('List of commands found. Processing %s ...' %options.infile)
710 
711  # an object that represents the candles file:
712  candles_file = Candles_file(options.infile)
713 
714  commands_profilers_meta_list=candles_file.get_commands_profilers_meta_list()
715 
716 
717  logger('Iterating through commands of executables to profile ...')
718 
719  # Cycle through the commands
720  len_commands_profilers_meta_list=len(commands_profilers_meta_list)
721 
722  commands_counter=1
723  precedent_profile_name=''
724  precedent_reuseprofile=False
725  for command,profiler_opt,meta,reuseprofile,db_metastring in commands_profilers_meta_list:
726 
727  exit_code=0
728 
729  logger('Processing command %d/%d' \
730  %(commands_counter,len_commands_profilers_meta_list))
731  logger('Process started on %s' %time.asctime())
732 
733  # for multiple directories and outputs let's put the meta
734  # just before the output profile and the outputdir
735  profile_name=''
736  profiler=''
737  reportdir=options.output
738  IgProf_counter=options.IgProf_counter
739 
740 
741  if options.infile!='': # we have a list of commands
742 
743  reportdir='%s_%s' %(meta,options.output) #Usually options.output is not used
744  reportdir=clean_name(reportdir) #Remove _
745 
746  profile_name=clean_name('%s_%s'%(meta,options.profile_name)) #Also options.profile_name is usually not used... should clean up...
747 
748  # profiler is igprof: we need to disentangle the profiler and the counter
749 
750  if profiler_opt.find('.')!=-1 and profiler_opt.find('IgProf')!=-1:
751  profiler_opt_split=profiler_opt.split('.')
752  profiler=profiler_opt_split[0]
753  IgProf_counter=profiler_opt_split[1:] #This way can handle IgProfMem.ANALYSE.MEM_TOT etc.
754  if profile_name[-3:]!='.gz':
755  profile_name+='.gz'
756 
757  # Profiler is Timereport_Parser
758  elif profiler_opt in STDOUTPROFILERS:
759  # a first maquillage about the profilename:
760  if profile_name[:-4]!='.log':
761  profile_name+='.log'
762  profiler=profiler_opt
763 
764  # profiler is not igprof
765  else:
766  profiler=profiler_opt
767 
768  if precedent_reuseprofile:
769  profile_name=precedent_profile_name
770  if reuseprofile:
771  precedent_profile_name=profile_name
772 
773 
774 
775  else: # we have a single command: easy job!
776  profile_name=options.profile_name
777  reportdir=options.output
778  profiler=options.profiler
779 
780 
781 
782  # istantiate a Profile object
783  if precedent_profile_name!='':
784  if os.path.exists(precedent_profile_name):
785  logger('Reusing precedent profile: %s ...' %precedent_profile_name)
786  if profile_name!=precedent_profile_name:
787  logger('Copying the old profile to the new name %s ...' %profile_name)
788  execute('cp %s %s' %(precedent_profile_name, profile_name))
789 
790  performance_profile=Profile(command,
791  profiler,
792  profile_name)
793 
794  # make profile if needed
795  if options.profile:
796  if reuseprofile:
797  logger('Saving profile name to reuse it ...')
798  precedent_profile_name=profile_name
799  else:
800  precedent_profile_name=''
801 
802  if not precedent_reuseprofile:
803  logger('Creating profile for command %d using %s ...' \
804  %(commands_counter,profiler))
805  exit_code=performance_profile.make_profile()
806  print exit_code
807  logger('The exit code was %s'%exit_code)
808  exitCodeSum=exitCodeSum+exit_code #Add all exit codes into the global exitCodeSum in order to return it on cmsRelvareport.py exit.
809  logger('The exit code sum is %s'%exitCodeSum)
810 
811 
812  # make report if needed
813  if options.report:
814  if exit_code!=0:
815  logger('Halting report creation procedure: unexpected exit code %s from %s ...' \
816  %(exit_code,profiler))
817  else:
818  logger('Creating report for command %d using %s ...' \
819  %(commands_counter,profiler))
820 
821  # Write into the db instead of producing html if this is the case:
822  if options.db:
823  exit_code=performance_profile.make_report(fill_db=True,
824  db_name=options.output,
825  metastring=db_metastring,
826  tmp_dir=options.pr_temp,
827  IgProf_option=IgProf_counter)
828  exitCodeSum=exitCodeSum+exit_code #this is to also check that the reporting works... a little more ambitious testing... could do without for release integration
829  else:
830  exit_code=performance_profile.make_report(outdir=reportdir,
831  tmp_dir=options.pr_temp,
832  IgProf_option=IgProf_counter)
833  exitCodeSum=exitCodeSum+exit_code #this is to also check that the reporting works... a little more ambitious testing... could do without for release integration
834 
835  commands_counter+=1
836  precedent_reuseprofile=reuseprofile
837  if not precedent_reuseprofile:
838  precedent_profile_name=''
839 
840  logger('Process ended on %s\n' %time.asctime())
841 
842  logger('Procedure finished on %s' %time.asctime())
843  logger("Exit code sum is %s"%exitCodeSum)
844  return exitCodeSum
845 
846 ###################################################################################################
847 
848 if __name__=="__main__":
849 
850  usage='\n'+\
851  '----------------------------------------------------------------------------\n'+\
852  ' RelValreport: a tool for automation of benchmarking and report generation. \n'+\
853  '----------------------------------------------------------------------------\n\n'+\
854  'relvalreport.py <options>\n'+\
855  'relvalreport.py -i candles_150.txt -R -P -n 150.out -o 150_report\n'+\
856  ' - Executes the candles contained in the file candles_150.txt, create\n'+\
857  ' profiles, specified by -n, and reports, specified by -o.\n\n'+\
858  'Candles file grammar:\n'+\
859  'A candle is specified by the syntax:\n'+\
860  'executable_name @@@ profiler_name @@@ meta\n'+\
861  ' - executable_name: the name of the executable to benchmark.\n'+\
862  ' - profiler_name: the name of the profiler to use. The available are: %s.\n' %str(PROFILERS)+\
863  ' In case you want to use IgProf_mem or IgProf_perf, the counter (MEM_TOTAL,PERF_TICKS...)\n'+\
864  ' must be added with a ".": IgProf_mem.MEM_TOTAL.\n'+\
865  ' - meta: metastring that is used to change the name of the names specified in the command line\n'+\
866  ' in case of batch execution.'+\
867  'An example of candle file:\n\n'+\
868  ' ># My list of candles:\n'+\
869  ' >\n'+\
870  ' >cmsDriver.py MU- -sSIM -e10_20 @@@ IgProf_perf.PERF_TICKS @@@ QCD_sim_IgProfperf\n'+\
871  ' >cmsDriver.py MU- -sRECO -e10_20 @@@ ValgrindFCE @@@ QCD_reco_Valgrind\n'+\
872  ' >cmsRun mycfg.cfg @@@ IgProf_mem.MEM_TOTAL @@@ Mycfg\n'
873 
874 
875 
876  parser = optparse.OptionParser(usage)
877 
878  parser.add_option('-p', '--profiler',
879  help='Profilers are: %s' %str(PROFILERS) ,
880  default='',
881  dest='profiler')
882 
883  parser.add_option('-c', '--command',
884  help='Command to profile. If specified the infile is ignored.' ,
885  default='',
886  dest='command')
887 
888  parser.add_option('-t',
889  help='The temp directory to store the PR service files. Default is PR_TEMP Ignored if PR is not used.',
890  default='',
891  dest='pr_temp')
892 
893  #Flags
894 
895  parser.add_option('--db',
896  help='EXPERIMENTAL: Write results on the db.',
897  action='store_true',
898  default=False,
899  dest='db')
900 
901  parser.add_option('-R','--Report',
902  help='Create a static html report. If db switch is on this is ignored.',
903  action='store_true',
904  default=False,
905  dest='report')
906 
907  parser.add_option('-P','--Profile',
908  help='Create a profile for the selected profiler.',
909  action='store_true',
910  default=False,
911  dest='profile')
912 
913  # Output location for profile and report
914 
915  parser.add_option('-n', '--profile_name',
916  help='Profile name' ,
917  default='',
918  dest='profile_name')
919 
920  parser.add_option('-o', '--output',
921  help='Outdir for the html report or db filename.' ,
922  default='',
923  dest='output')
924 
925  #Batch functionality
926 
927  parser.add_option('-i', '--infile',
928  help='Name of the ASCII file containing the commands to profile.' ,
929  default='',
930  dest='infile')
931 
932  # ig prof options
933 
934  parser.add_option('-y',
935  help='Specify the IgProf counter or the CMSSW. '+\
936  'If a profiler different from '+\
937  'IgProf is selected this is ignored.' ,
938  default=None,
939  dest='IgProf_counter')
940 
941  parser.add_option('--executable',
942  help='Specify executable to monitor if different from cmsRun. '+\
943  'Only valid for IgProf.',
944  default='',
945  dest='executable')
946 
947  # Debug options
948  parser.add_option('--noexec',
949  help='Do not exec commands, just display them!',
950  action='store_true',
951  default=False,
952  dest='noexec')
953 
954  (options,args) = parser.parse_args()
955 
956  # FAULT CONTROLS
957  if options.infile=='' and options.command=='' and not (options.report and not options.profile):
958  raise('Specify at least one command to profile!')
959  if options.profile_name=='' and options.infile=='':
960  raise('Specify a profile name!')
961  if not options.db and options.output=='' and options.infile=='':
962  raise('Specify a db name or an output dir for the static report!')
963 
964  if not options.profile:
965  if not os.path.exists(options.profile_name) and options.infile=='':
966  raise('Profile %s does not exist!' %options.profile_name)
967  logger("WARNING: No profile will be generated. An existing one will be processed!")
968 
969  if options.command!='' and options.infile!='':
970  raise('-c and -i options cannot coexist!')
971 
972  if options.profiler=='Memcheck_Valgrind' and not os.path.exists(VMPARSER):
973  raise('Couldn\'t find Valgrind Memcheck Parser Script! Please install it from Utilities/ReleaseScripts.')
974 
975  if options.executable!='':
976  globals()['EXECUTABLE']=options.executable
977 
978  if options.noexec:
979  globals()['EXEC']=False
980 
981  logger('Procedure started on %s' %time.asctime())
982 
983  if options.infile == '':
984  logger('Script options:')
985  for key,val in options.__dict__.items():
986  if val!='':
987  logger ('\t\t|- %s = %s' %(key, str(val)))
988  logger ('\t\t|')
989  exit=principal(options)
990  logger("Exit code received from principal is: %s"%exit)
991  #Mind you! exit codes in Linux are all 0 if they are even! We can easily make the code 1
992  if exit: #This is different than 0 only if there have been at least one non-zero exit(return) code in the cmsRelvalreport.py
993  exit=1
994  sys.exit(exit)
995 
void strip(std::string &input, const std::string &blanks=" \n\t")
Definition: stringTools.cc:16
tuple filter
USE THIS FOR SKIMMED TRACKS process.p = cms.Path(process.hltLevel1GTSeed*process.skimming*process.offlineBeamSpot*process.TrackRefitter2) OTHERWISE USE THIS.
Definition: align_tpl.py:86
double split
Definition: MVATrainer.cc:139