4 cmsRelvalreport.py: a script to run performance tests and produce reports in a automated way.
11 PR3_BASE=
'/afs/cern.ch/user/d/dpiparo/w0/perfreport3installation/'
12 PR3=PR3_BASE+
'/bin/perfreport'
13 PERFREPORT3_PATH=PR3_BASE+
'/share/perfreport'
15 PR3_PRODUCER_PLUGIN=
'/afs/cern.ch/user/d/dpiparo/w0/pr3/perfreport/plugins/cmssw_by_producer/libcmssw_by_producer.so'
18 PR2_BASE=
'/afs/cern.ch/user/g/gbenelli/public/PerfReport2/2.0.1/'
19 PR2=
'%s' %(PR2_BASE+
'/bin/perfreport')
20 PERFREPORT2_PATH=PR2_BASE+
'/share/perfreport'
23 cmssw_base=os.environ[
"CMSSW_BASE"]
24 cmssw_release_base=os.environ[
"CMSSW_RELEASE_BASE"]
25 pyrelvallocal=cmssw_base+
"/src/Configuration/PyReleaseValidation"
27 if os.path.exists(pyrelvallocal):
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'
36 VMPARSER=
'valgrindMemcheckParser.pl'
40 VMPARSERSTYLE=
'%s/src/Utilities/ReleaseScripts/data/valgrindMemcheckParser.css' %os.environ[
'CMSSW_RELEASE_BASE']
44 IGPROFANALYS=
'cmsIgProf_Analysis.py'
48 TIMEREPORTPARSER=
'cmsTimeReport.pl'
52 SIMPLEMEMPARSER=
'cmsSimplememchecker_parser.py'
56 TIMINGPARSER=
'cmsTiming_parser.py'
59 MAKESKIMDRIVERDIR=
'%s/src/Configuration/EventContent/test' %os.environ[RELEASE]
60 MAKESKIMDRIVER=
'%s/makeSkimDriver.py'%MAKESKIMDRIVERDIR
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'
72 STDOUTPROFILERS=[
'Memcheck_Valgrind',
77 PROFILERS=[
'ValgrindFCE',
80 'Edm_Size']+STDOUTPROFILERS
91 IgProfCounters={
'IgProfPerf':[
'PERF_TICKS'],
92 'IgProfMem':[
'MEM_TOTAL',
'MEM_LIVE',
'MEM_MAX']
94 IgProfProfiles={
'PERF_TICKS':
'IgProfPerf',
95 'MEM_TOTAL':
'IgProfMem',
96 'MEM_LIVE':
'IgProfMem',
106 return '%s%s%s' %(
'\033[1;31m',string,
'\033[1;0m')
108 return '%s%s%s' %(
'\033[1;32m',string,
'\033[1;0m')
110 return '%s%s%s' %(
'\033[1;33m',string,
'\033[1;0m')
115 Trivially removes an underscore if present as last char of a string
130 It executes command if the EXEC switch is True.
131 Catches exitcodes different from 0.
135 exit_code=os.system(command)
137 logger(
red(
'*** Seems like "%s" encountered problems.' %command))
146 level=0 output, level 1 debug.
148 message=
'%s %s' %(
yellow(
'[RelValreport]'),message)
154 if level==1
and DEBUG:
163 Class to read the trivial ASCII file containing the candles
169 candlesfile=open(filename,
'r')
171 if filename[-3:]==
'xml':
178 from xml.dom
import minidom
181 xmldoc = minidom.parse(filename)
184 candles_list = xmldoc.getElementsByTagName(
'candle')
189 for candle
in candles_list:
191 for child
in candle.childNodes:
192 if not child.__dict__.has_key(
'nodeName'):
196 tag_name=child.tagName
198 data=child.firstChild.data
201 info_dict[tag_name]=data
203 candles_dict_list.append(info_dict)
207 for candle_dict
in candles_dict_list:
209 command=candle_dict[
'command']
210 profiler=candle_dict[
'profiler']
211 meta=candle_dict[
'meta']
214 db_meta=candle_dict[
'db_meta']
218 reuse=candle_dict[
'reuse']
222 self.commands_profilers_meta_list.append([command,profiler,meta,reuse,db_meta])
226 for candle
in candlesfile.readlines():
228 if candle[0]!=
'#' and candle.strip(
' \n\t')!=
'':
231 splitted_candle=candle.split(
'@@@')
234 command=splitted_candle[0]
235 profiler=splitted_candle[1].
strip(
' \t')
236 meta=splitted_candle[2].
strip(
' \t')
237 info=[command,profiler,meta]
241 len_splitted_candle=len(splitted_candle)
243 if len_splitted_candle>3:
245 if 'reuse' in splitted_candle[3]:
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)
259 self.commands_profilers_meta_list.append(info)
270 Class that represents the procedure of performance report creation
281 Launch the right function according to the profiler name.
285 elif self.profiler.find(
'IgProf')!=-1:
287 elif self.profiler.find(
'Edm_Size')!=-1:
289 elif self.
profiler==
'Memcheck_Valgrind':
291 elif self.
profiler==
'Timereport_Parser':
293 elif self.
profiler==
'Timing_Parser':
295 elif self.
profiler==
'SimpleMem_Parser':
302 raise(
'No %s profiler found!' %self.
profiler)
306 Valgrind profile launcher.
309 os.environ[
"VALGRIND_LIB"]=VFCE_LIB
312 valgrind_options=
'time valgrind '+\
313 '--tool=callgrind '+\
317 if EXECUTABLE==
'cmsRun' and self.command.find(
'cmsDriver.py')!=-1:
318 profiler_line=
'%s --prefix "%s"' %(self.
command,valgrind_options)
321 profiler_line=
'%s %s' %(valgrind_options,self.
command)
329 IgProf profiler launcher.
333 igprof_options=
'igprof -d -t %s ' \
338 igprof_options+=
'-pp '
340 igprof_options+=
'-mp '
342 raise (
'Unknown IgProf flavour: %s !'%self.
profiler)
346 if EXECUTABLE==
'cmsRun' and self.command.find(
'cmsDriver.py')!=-1:
347 profiler_line=
'%s --prefix "%s"' %(self.
command,igprof_options)
349 profiler_line=
'%s %s' %(igprof_options, self.
command)
357 Launch edm size profiler
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,
375 os.environ[
'PYTHONPATH']=oldpypath
381 profiler_line=
'edmEventSize -o %s -d %s'\
390 Valgrind Memcheck profile launcher
399 valgrind_options=
'time valgrind --tool=memcheck `cmsvgsupp` '+\
400 '--num-callers=20 '+\
402 '--xml-file=%s.xml '%self.profile_name.replace(
",",
"-")[:-4]
405 if EXECUTABLE==
'cmsRun' and self.command.find(
'cmsDriver.py')!=-1:
433 Save the output of cmsRun on a file!
447 Just Run the command!
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.
468 if not os.path.exists(outdir)
and not fill_db
and not IgProf_option:
474 if not os.path.exists(db_name):
481 tmp_switch=
' -t %s' %tmp_dir
489 perfreport_command=
''
492 os.environ[
"PERFREPORT_PATH"]=
'%s/' %PERFREPORT2_PATH
493 perfreport_command=
'%s %s -ff -i %s -o %s' %(PR2,
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' \
507 return execute(perfreport_command)
512 if self.profiler.find(
'IgProf')!=-1:
514 if not 'ANALYSE' in IgProf_option:
518 NumberOfEvents=self.command.split()[3]
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))
532 IgProfCounter=IgProf_option[1]
534 logger(
"Looking for IgProf intermediate event profile dumps")
536 IgProfDumps=glob.glob(
"IgProf.*.gz")
540 localFiles=os.listdir(
'.')
541 IgProfDumpProfilesPrevious=re.compile(
r"\w+.\d+.gz")
542 IgProfDumps=
filter(
lambda x: IgProfDumpProfilesPrevious.search(x),localFiles)
546 logger(
"Found the following IgProf intermediate event profile dumps:")
548 FirstDumpEvent=9999999
551 for dump
in IgProfDumps:
553 DumpEvent=dump.split(
".")[0].
split(
"___")[-1]
555 DumpEvent=dump.split(
".")[1]
557 DumpedProfileName=self.
profile_name[:-3]+
"___"+DumpEvent+
".gz"
558 if dump.startswith(
"IgProf"):
559 execute(
'mv %s %s'%(dump,DumpedProfileName))
561 if int(DumpEvent) < FirstDumpEvent:
562 FirstDumpEvent = int(DumpEvent)
563 if int(DumpEvent) > LastDumpEvent:
564 LastDumpEvent = int(DumpEvent)
570 logger(
"Executing the igprof-analyse analysis saving into igprof-navigator browseable SQLite3 format")
579 exit=exit+
execute(
'%s -c%s -i%s -t%s' %(IGPROFANALYS,IgProfCounter,DumpedProfileName,
"SQLite3"))
586 FirstDumpProfile=self.
profile_name[:-3]+
"___"+str(FirstDumpEvent)+
".gz"
587 LastDumpProfile=self.
profile_name[:-3]+
"___"+str(LastDumpEvent)+
".gz"
590 if len(IgProfDumps)>1:
591 logger(
"Executing the igprof-analyse regression between the first IgProf profile dump and the last one")
595 exit=exit+
execute(
'%s -c%s -i%s -r%s' %(IGPROFANALYS,IgProfCounter,LastDumpProfile,FirstDumpProfile))
597 logger(
"CANNOT execute any regressions: not enough IgProf intermediate event profile dumps!")
599 logger(
"Executing the igprof-analyse analysis merging the results by library via regexp and saving the result in igprof-navigator browseable SQLite3 format")
603 exit=exit+
execute(
'%s -c%s -i%s --library' %(IGPROFANALYS,IgProfCounter,LastDumpProfile))
606 logger(
"No IgProf intermediate event profile dumps found!")
616 perfreport_command=
''
618 os.environ[
"PERFREPORT_PATH"]=
'%s/' \
620 perfreport_command=
'%s %s -fe -i %s -o %s' \
626 os.environ[
"PERFREPORT_PATH"]=
'%s/' \
628 perfreport_command=
'%s %s -n5000 -u%s -fe -i %s -a -o %s' \
635 return execute(perfreport_command)
644 if self.
profiler==
'Memcheck_Valgrind':
646 os.environ[
'PERL5LIB']=PERL5_LIB
649 copyStyleFile=
'cp -pR %s %s'%(VMPARSERSTYLE,outdir)
651 report_commands=(
'%s --preset +prod,-prod1 %s > %s/edproduce.html'\
653 '%s --preset +prod1 %s > %s/esproduce.html'\
655 '%s -t beginJob %s > %s/beginjob.html'\
658 for command
in report_commands:
665 if self.
profiler==
'Timereport_Parser':
680 if self.
profiler==
'SimpleMem_Parser':
694 Here the objects of the Profile class are istantiated.
700 commands_profilers_meta_list=[]
703 if options.infile==
'':
704 logger(
'Single command found...')
705 commands_profilers_meta_list.append([options.command,
'',
'',
False,
''])
709 logger(
'List of commands found. Processing %s ...' %options.infile)
714 commands_profilers_meta_list=candles_file.get_commands_profilers_meta_list()
717 logger(
'Iterating through commands of executables to profile ...')
720 len_commands_profilers_meta_list=len(commands_profilers_meta_list)
723 precedent_profile_name=
''
724 precedent_reuseprofile=
False
725 for command,profiler_opt,meta,reuseprofile,db_metastring
in commands_profilers_meta_list:
729 logger(
'Processing command %d/%d' \
730 %(commands_counter,len_commands_profilers_meta_list))
731 logger(
'Process started on %s' %time.asctime())
737 reportdir=options.output
738 IgProf_counter=options.IgProf_counter
741 if options.infile!=
'':
743 reportdir=
'%s_%s' %(meta,options.output)
746 profile_name=
clean_name(
'%s_%s'%(meta,options.profile_name))
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:]
754 if profile_name[-3:]!=
'.gz':
758 elif profiler_opt
in STDOUTPROFILERS:
760 if profile_name[:-4]!=
'.log':
762 profiler=profiler_opt
766 profiler=profiler_opt
768 if precedent_reuseprofile:
769 profile_name=precedent_profile_name
771 precedent_profile_name=profile_name
776 profile_name=options.profile_name
777 reportdir=options.output
778 profiler=options.profiler
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))
790 performance_profile=
Profile(command,
797 logger(
'Saving profile name to reuse it ...')
798 precedent_profile_name=profile_name
800 precedent_profile_name=
''
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()
807 logger(
'The exit code was %s'%exit_code)
808 exitCodeSum=exitCodeSum+exit_code
809 logger(
'The exit code sum is %s'%exitCodeSum)
815 logger(
'Halting report creation procedure: unexpected exit code %s from %s ...' \
816 %(exit_code,profiler))
818 logger(
'Creating report for command %d using %s ...' \
819 %(commands_counter,profiler))
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
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
836 precedent_reuseprofile=reuseprofile
837 if not precedent_reuseprofile:
838 precedent_profile_name=
''
840 logger(
'Process ended on %s\n' %time.asctime())
842 logger(
'Procedure finished on %s' %time.asctime())
843 logger(
"Exit code sum is %s"%exitCodeSum)
848 if __name__==
"__main__":
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'+\
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'
876 parser = optparse.OptionParser(usage)
878 parser.add_option(
'-p',
'--profiler',
879 help=
'Profilers are: %s' %str(PROFILERS) ,
883 parser.add_option(
'-c',
'--command',
884 help=
'Command to profile. If specified the infile is ignored.' ,
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.',
895 parser.add_option(
'--db',
896 help=
'EXPERIMENTAL: Write results on the db.',
901 parser.add_option(
'-R',
'--Report',
902 help=
'Create a static html report. If db switch is on this is ignored.',
907 parser.add_option(
'-P',
'--Profile',
908 help=
'Create a profile for the selected profiler.',
915 parser.add_option(
'-n',
'--profile_name',
916 help=
'Profile name' ,
920 parser.add_option(
'-o',
'--output',
921 help=
'Outdir for the html report or db filename.' ,
927 parser.add_option(
'-i',
'--infile',
928 help=
'Name of the ASCII file containing the commands to profile.' ,
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.' ,
939 dest=
'IgProf_counter')
941 parser.add_option(
'--executable',
942 help=
'Specify executable to monitor if different from cmsRun. '+\
943 'Only valid for IgProf.',
948 parser.add_option(
'--noexec',
949 help=
'Do not exec commands, just display them!',
954 (options,args) = parser.parse_args()
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!')
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!")
969 if options.command!=
'' and options.infile!=
'':
970 raise(
'-c and -i options cannot coexist!')
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.')
975 if options.executable!=
'':
976 globals()[
'EXECUTABLE']=options.executable
979 globals()[
'EXEC']=
False
981 logger(
'Procedure started on %s' %time.asctime())
983 if options.infile ==
'':
985 for key,val
in options.__dict__.items():
987 logger (
'\t\t|- %s = %s' %(key, str(val)))
990 logger(
"Exit code received from principal is: %s"%exit)
commands_profilers_meta_list
def get_commands_profilers_meta_list
def _profile_Memcheck_Valgrind
def _profile_SimpleMem_Parser
def _profile_Timereport_Parser
tuple filter
USE THIS FOR SKIMMED TRACKS process.p = cms.Path(process.hltLevel1GTSeed*process.skimming*process.offlineBeamSpot*process.TrackRefitter2) OTHERWISE USE THIS.
def _profile_Timing_Parser