CMS 3D CMS Logo

/data/doxygen/doxygen-1.7.3/gen/CMSSW_4_2_8/src/Validation/Performance/scripts/cmsPerfSuite.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 import os, time, sys, re, glob, exceptions
00003 import optparse as opt
00004 import cmsRelRegress as crr
00005 from cmsPerfCommons import Candles, KeywordToCfi, CandFname, cmsDriverPileUpOption, getVerFromLog
00006 import cmsRelValCmd,cmsCpuInfo
00007 import threading #Needed in threading use for Valgrind
00008 import subprocess #Nicer subprocess management than os.popen
00009 import datetime #Used to time the running of the performance suite
00010 import pickle #Used to dump the running timing information
00011 
00012 #Redefine _cleanup() function not to poll active processes
00013 #[This is necessary to avoid issues when threading]
00014 #So let's have it do nothing:
00015 def _cleanup():
00016    pass
00017 #Override the function in subprocess
00018 subprocess._cleanup=_cleanup
00019 
00020 class PerfThread(threading.Thread):
00021     def __init__(self,**args):
00022         self.args=args
00023         threading.Thread.__init__(self)
00024     def run(self):
00025         self.suite=PerfSuite()
00026         #print "Arguments inside the thread instance:"
00027         #print type(self.args)
00028         #print self.args
00029         self.suite.runPerfSuite(**(self.args))#self.args)
00030 
00031 class PerfSuiteTimer:
00032    """A class defining timing objects to time the running of the various parts of the performance suite. The class depends on module datetime."""
00033    def __init__(self,start=None):
00034       """Initialize the start time and set the end time to some indefinite time in the future"""
00035       self.start = start
00036       self.end = datetime.datetime.max
00037       self.duration = self.start - self.end
00038 
00039    #Setters:
00040    def set_start(self,start=None):
00041       self.start = start
00042    def set_end(self,end=None):
00043       #print "Setting end time to %s"%end.ctime()
00044       self.end = end
00045       self.duration = self.end - self.start
00046    #Getters
00047    def get_start(self):
00048       """Return the start time in ctime timestamp format"""
00049       return self.start.ctime()
00050    def get_end(self):
00051       """Return the end time in ctime timestamp format"""
00052       return self.end.ctime()
00053    def get_duration(self):
00054       """Return the duration between start and end as a dictionary with keys 'hours', 'minutes', 'seconds' to express the total duration in the favourite (most appropriate) unit. The function returns truncated integers."""
00055       self.duration_seconds = self.duration.days*86400 + self.duration.seconds
00056       self.duration_minutes = self.duration_seconds/60
00057       self.duration_hours = self.duration_seconds/3600
00058       return {'hours':self.duration_hours, 'minutes':self.duration_minutes, 'seconds':self.duration_seconds}
00059 
00060 class PerfSuite:
00061     def __init__(self):
00062         
00063         self.ERRORS = 0
00064         self._CASTOR_DIR = "/castor/cern.ch/cms/store/relval/performance/"
00065         self._dryrun   = False
00066         self._debug    = False
00067         self._unittest = False
00068         self._noexec   = False
00069         self._verbose  = True
00070         self.logh = sys.stdout
00071     
00072         #Get some environment variables to use
00073         try:
00074             self.cmssw_arch   = os.environ["SCRAM_ARCH"]
00075             self.cmssw_version= os.environ["CMSSW_VERSION"]
00076             self.host         = os.environ["HOST"]
00077             self.user              = os.environ["USER"]
00078         except KeyError:
00079             self.logh.write('Error: An environment variable either SCRAM_ARCH, CMSSW_VERSION, HOST or USER is not available.\n')
00080             self.logh.write('       Please run eval `scramv1 runtime -csh` to set your environment variables\n')
00081             self.logh.flush()
00082             sys.exit()
00083            
00084         #Scripts used by the suite:
00085         self.Scripts         =["cmsDriver.py","cmsRelvalreport.py","cmsRelvalreportInput.py","cmsScimark2"]
00086         self.AuxiliaryScripts=["cmsScimarkLaunch.csh","cmsScimarkParser.py","cmsScimarkStop.py"]
00087 
00088         
00089     #Threading the execution of IgProf, Memcheck and Callgrind using the same model used to thread the whole performance suite:
00090     #1-Define a class simpleGenReportThread() that has relevant methods needed to handle PerfTest()
00091     #2-Instantiate one with the necessary arguments to run simpleGenReport on core N
00092     #3-Execute its "run" method by starting the thread
00093     #Simplest way maybe is to keep 2 global lists:
00094     #AvailableCores
00095     #TestsToDo
00096     #PerfSuite will fill the TestsToDo list with dictionaries, to be used as keyword arguments to instantiate a relevant thread.
00097     #Once all the TestsToDo are "scheduled" into the list (FirstInLastOut buffer since we use pop()) PerfSuite will look into the
00098     #AvailableCores list and start popping cores onto which to instantiate the relevant threads, then it will start the thread,
00099     #appending it to the activePerfTestThread{},a dictionary with core as key and thread object as value, to facilitate bookkeeping.
00100     #An infinite loop will take care of checking for AvailableCores as long as there are TestsToDo and keep submitting.
00101     #In the same loop the activePerfTestThread{} will be checked for finished threads and it will re-append the relevant cpu back
00102     #to the AvailableCores list.
00103     #In the same loop a check for the case of all cores being back into AvailableCores with no more TestsToDo will break the infinite loop
00104     #and declare the end of all tests.As else to this if a sleep statement of 5 seconds will delay the repetition of the loop.
00105  
00106     class simpleGenReportThread(threading.Thread):
00107        def __init__(self,cpu,perfsuiteinstance,**simpleGenReportArgs): #Passing around the perfsuite object to be able to access simpleGenReport
00108           self.cpu=cpu
00109           self.simpleGenReportArgs=simpleGenReportArgs
00110           self.perfsuiteinstance=perfsuiteinstance
00111           threading.Thread.__init__(self)
00112        def run(self):
00113           self.PerfTest=self.perfsuiteinstance.PerfTest(self.cpu,self.perfsuiteinstance,**(self.simpleGenReportArgs))
00114           self.PerfTest.runPerfTest()
00115     
00116     class PerfTest:
00117        def __init__(self,cpu,perfsuiteinstance,**simpleGenReportArgs):
00118           self.cpu=cpu
00119           self.simpleGenReportArgs=simpleGenReportArgs
00120           self.perfsuiteinstance=perfsuiteinstance
00121        def runPerfTest(self):
00122 #          self.PerfTestTotalTimer=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
00123 #          TimerInfo.update({self.simpleGenReportArgs['Name']:{'TotalTime':self.PerfTestTotalTimer}}) #Add the TimeSize timer to the dictionary  
00124           if "--pileup" in self.simpleGenReportArgs['cmsdriverOptions']:
00125              self.perfsuiteinstance.logh.write("Launching the PILE UP %s tests on cpu %s with %s events each\n"%(self.simpleGenReportArgs['Name'],self.cpu,self.simpleGenReportArgs['NumEvents']))
00126              self.PerfTestPUTimer=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
00127              TimerInfo[self.simpleGenReportArgs['Name']].update({'PileUpTime':self.PerfTestPUTimer}) #Add the TimeSize timer to the dictionary
00128              
00129           else:
00130              self.perfsuiteinstance.logh.write("Launching the %s tests on cpu %s with %s events each\n"%(self.simpleGenReportArgs['Name'],self.cpu,self.simpleGenReportArgs['NumEvents']))
00131              self.PerfTestTimer=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
00132              TimerInfo[self.simpleGenReportArgs['Name']].update({'NoPileUpTime':self.PerfTestTimer}) #Add the TimeSize timer to the dictionary
00133           self.perfsuiteinstance.logh.flush()
00134           #Cut and paste in bulk, should see if this works...
00135           self.perfsuiteinstance.printDate()
00136           self.perfsuiteinstance.logh.flush()
00137           self.exitcode=self.perfsuiteinstance.simpleGenReport([self.cpu],**(self.simpleGenReportArgs)) #Returning ReportExit code
00138           #Stop the timers on the threaded PileUp and NoPileUp tests:
00139           if "--pileup" in self.simpleGenReportArgs['cmsdriverOptions']:
00140              self.PerfTestPUTimer.set_end(datetime.datetime.now())
00141           else:
00142              self.PerfTestTimer.set_end(datetime.datetime.now())
00143           return self.exitcode
00144        
00145     #Options handling
00146     def optionParse(self,argslist=None):
00147         parser = opt.OptionParser(usage='''./cmsPerfSuite.py [options]
00148            
00149     Examples:
00150     
00151     cmsPerfSuite.py --step GEN-HLT -t 5 -i 2 -c 1 -m 5 --RunTimeSize MinBias,TTbar --RunIgProf TTbar --RunCallgrind TTbar --RunMemcheck TTbar --RunDigiPileUp TTbar --PUInputFile /store/relval/CMSSW_2_2_1/RelValMinBias/GEN-SIM-DIGI-RAW-HLTDEBUG/IDEAL_V9_v2/0001/101C84AF-56C4-DD11-A90D-001D09F24EC0.root --cmsdriver="--eventcontent FEVTDEBUGHLT --conditions FrontierConditions_GlobalTag,IDEAL_V9::All"
00152     (this will run the suite with 5 events for TimeSize tests on MinBias and TTbar, 2 for IgProf tests on TTbar only, 1 for Callgrind tests on TTbar only, 5 for Memcheck on MinBias and TTbar, it will also run DIGI PILEUP for all TTbar tests defined, i.e. 5 TimeSize, 2 IgProf, 1 Callgrind, 5 Memcheck. The file /store/relval/CMSSW_2_2_1/RelValMinBias/GEN-SIM-DIGI-RAW-HLTDEBUG/IDEAL_V9_v2/0001/101C84AF-56C4-DD11-A90D-001D09F24EC0.root will be copied locally as INPUT_PILEUP_EVENTS.root and it will be used as the input file for the MixingModule pile up events. All these tests will be done for the step GEN-HLT, i.e. GEN,SIM,DIGI,L1,DIGI2RAW,HLT at once)
00153     OR
00154     cmsPerfSuite.py --step GEN-HLT -t 5 -i 2 -c 1 -m 5 --RunTimeSize MinBias,TTbar --RunIgProf TTbar --RunCallgrind TTbar --RunMemcheck TTbar --RunTimeSizePU TTbar --PUInputFile /store/relval/CMSSW_2_2_1/RelValMinBias/GEN-SIM-DIGI-RAW-HLTDEBUG/IDEAL_V9_v2/0001/101C84AF-56C4-DD11-A90D-001D09F24EC0.root
00155     (this will run the suite with 5 events for TimeSize tests on MinBias and TTbar, 2 for IgProf tests on TTbar only, 1 for Callgrind tests on TTbar only, 5 for Memcheck on MinBias and TTbar, it will also run DIGI PILEUP on TTbar but only for 5 TimeSize events. All these tests will be done for the step GEN-HLT, i.e. GEN,SIM,DIGI,L1,DIGI2RAW,HLT at once)
00156     OR
00157     cmsPerfSuite.py --step GEN-HLT -t 5 -i 2 -c 1 -m 5 --RunTimeSize MinBias,TTbar --RunIgProf TTbar --RunCallgrind TTbar --RunMemcheck TTbar --RunTimeSizePU TTbar --PUInputFile /store/relval/CMSSW_2_2_1/RelValMinBias/GEN-SIM-DIGI-RAW-HLTDEBUG/IDEAL_V9_v2/0001/101C84AF-56C4-DD11-A90D-001D09F24EC0.root --cmsdriver="--eventcontent RAWSIM --conditions FrontierConditions_GlobalTag,IDEAL_V9::All"
00158     (this will run the suite with 5 events for TimeSize tests on MinBias and TTbar, 2 for IgProf tests on TTbar only, 1 for Callgrind tests on TTbar only, 5 for Memcheck on MinBias and TTbar, it will also run DIGI PILEUP on TTbar but only for 5 TimeSize events. All these tests will be done for the step GEN-HLT, i.e. GEN,SIM,DIGI,L1,DIGI2RAW,HLT at once. It will also add the options "--eventcontent RAWSIM --conditions FrontierConditions_GlobalTag,IDEAL_V9::All" to all cmsDriver.py commands executed by the suite. In addition it will run only 2 cmsDriver.py "steps": "GEN,SIM" and "DIGI". Note the syntax GEN-SIM for combined cmsDriver.py steps)
00159     
00160     Legal entries for individual candles (--RunTimeSize, --RunIgProf, --RunCallgrind, --RunMemcheck options):
00161     %s
00162     ''' % ("\n".join(Candles)))
00163     
00164         parser.set_defaults(TimeSizeEvents   = 0        ,
00165                             IgProfEvents     = 0          ,
00166                             CallgrindEvents  = 0          ,
00167                             MemcheckEvents   = 0          ,
00168                             cmsScimark       = 10         ,
00169                             cmsScimarkLarge  = 10         ,  
00170                             cmsdriverOptions = "--eventcontent FEVTDEBUGHLT", # Decided to avoid using the automatic parsing of cmsDriver_highstats_hlt.txt: cmsRelValCmd.get_cmsDriverOptions(), #Get these options automatically now!
00171                             #"Release Integrators" will create another file relative to the performance suite and the operators will fetch from that file the --cmsdriver option... for now just set the eventcontent since that is needed in order for things to run at all now...
00172                             stepOptions      = ""         ,
00173                             profilers        = ""         ,
00174                             outputdir        = ""         ,
00175                             logfile          = os.path.join(os.getcwd(),"cmsPerfSuite.log"),
00176                             runonspare       = True       ,
00177                             bypasshlt        = False      ,
00178                             quicktest        = False      ,
00179                             unittest         = False      ,
00180                             noexec           = False      ,
00181                             dryrun           = False      ,
00182                             verbose          = True       ,
00183                             previousrel      = ""         ,
00184                             castordir        = self._CASTOR_DIR,
00185                             cores            = cmsCpuInfo.get_NumOfCores(), #Get Number of cpu cores on the machine from /proc/cpuinfo
00186                             cpu              = "1"        , #Cpu core on which the suite is run:
00187                             RunTimeSize      = ""         ,
00188                             RunIgProf        = ""         ,
00189                             RunCallgrind     = ""         ,
00190                             RunMemcheck      = ""         ,
00191                             RunDigiPileUP    = ""         ,
00192                             RunTimeSizePU    = ""         ,
00193                             RunIgProfPU      = ""         ,
00194                             RunCallgrindPU   = ""         ,
00195                             RunMemcheckPU    = ""         ,
00196                             PUInputFile      = ""         ,
00197                             userInputFile    = ""         )
00198         parser.add_option('-q', '--quiet'      , action="store_false", dest='verbose'   ,
00199             help = 'Output less information'                  )
00200         parser.add_option('-b', '--bypass-hlt' , action="store_true" , dest='bypasshlt' ,
00201             help = 'Bypass HLT root file as input to RAW2DIGI')
00202         parser.add_option('-n', '--notrunspare', action="store_false", dest='runonspare',
00203             help = 'Do not run cmsScimark on spare cores')        
00204         parser.add_option('-t', '--timesize'  , type='int'   , dest='TimeSizeEvents'  , metavar='<#EVENTS>'   ,
00205             help = 'specify the number of events for the TimeSize tests'                   )
00206         parser.add_option('-i', '--igprof'    , type='int'   , dest='IgProfEvents'    , metavar='<#EVENTS>'   ,
00207             help = 'specify the number of events for the IgProf tests'                     )
00208         parser.add_option('-c', '--callgrind'  , type='int'   , dest='CallgrindEvents'  , metavar='<#EVENTS>'   ,
00209             help = 'specify the number of events for the Callgrind tests'                   )
00210         parser.add_option('-m', '--memcheck'  , type='int'   , dest='MemcheckEvents'  , metavar='<#EVENTS>'   ,
00211             help = 'specify the number of events for the Memcheck tests'                   )
00212         parser.add_option('--cmsScimark'      , type='int'   , dest='cmsScimark'      , metavar=''            ,
00213             help = 'specify the number of times the cmsScimark benchmark is run before and after the performance suite on cpu1')
00214         parser.add_option('--cmsScimarkLarge' , type='int'   , dest='cmsScimarkLarge' , metavar=''            ,
00215             help = 'specify the number of times the cmsScimarkLarge benchmark is run before and after the performance suite on cpu1')
00216         parser.add_option('--cores'           , type='int', dest='cores'              , metavar='<CORES>'     ,
00217             help = 'specify the number of cores of the machine (can be used with 0 to stop cmsScimark from running on the other cores)')        
00218         parser.add_option('--cmsdriver' , type='string', dest='cmsdriverOptions', metavar='<OPTION_STR>',
00219             help = 'specify special options to use with the cmsDriver.py commands (designed for integration build use')        
00220         parser.add_option('-a', '--archive'   , type='string', dest='castordir'       , metavar='<DIR>'       ,
00221             help = 'specify the wanted CASTOR directory where to store the results tarball')
00222         parser.add_option('-L', '--logfile'   , type='string', dest='logfile'         , metavar='<FILE>'      ,
00223             help = 'file to store log output of the script')                
00224         parser.add_option('-o', '--output'    , type='string', dest='outputdir'       , metavar='<DIR>'       ,
00225             help = 'specify the directory where to store the output of the script')        
00226         parser.add_option('-r', '--prevrel'   , type='string', dest='previousrel'     , metavar='<DIR>'       ,
00227             help = 'Top level dir of previous release for regression analysis')        
00228         parser.add_option('--step'            , type='string', dest='stepOptions'     , metavar='<STEPS>'     ,
00229             help = 'specify the processing steps intended (instead of the default ones)' )
00230         parser.add_option('--cpu'             , type='string', dest='cpu'             , metavar='<CPU>'       ,
00231             help = 'specify the core on which to run the performance suite')
00232 
00233         #Adding new options to put everything configurable at command line:
00234         parser.add_option('--RunTimeSize'             , type='string', dest='RunTimeSize' , metavar='<CANDLES>'       ,
00235             help = 'specify on which candles to run the TimeSize tests')
00236         parser.add_option('--RunIgProf'             , type='string', dest='RunIgProf' , metavar='<CANDLES>'       ,
00237             help = 'specify on which candles to run the IgProf tests')
00238         parser.add_option('--RunCallgrind'             , type='string', dest='RunCallgrind' , metavar='<CANDLES>'       ,
00239             help = 'specify on which candles to run the Callgrind tests')
00240         parser.add_option('--RunMemcheck'             , type='string', dest='RunMemcheck' , metavar='<CANDLES>'       ,
00241             help = 'specify on which candles to run the Memcheck tests')
00242         parser.add_option('--RunDigiPileUp'             , type='string', dest='RunDigiPileUp' , metavar='<CANDLES>'       ,
00243             help = 'specify the candle on which to run DIGI PILE UP and repeat all the tests set to run on that candle with PILE UP')
00244         parser.add_option('--PUInputFile'             , type='string', dest='PUInputFile' , metavar='<FILE>'       ,
00245             help = 'specify the root file to pick the pile-up events from')
00246         parser.add_option('--RunTimeSizePU'             , type='string', dest='RunTimeSizePU' , metavar='<CANDLES>'       ,
00247             help = 'specify on which candles to run the TimeSize tests with PILE UP')
00248         parser.add_option('--RunIgProfPU'             , type='string', dest='RunIgProfPU' , metavar='<CANDLES>'       ,
00249             help = 'specify on which candles to run the IgProf tests with PILE UP')
00250         parser.add_option('--RunCallgrindPU'             , type='string', dest='RunCallgrindPU' , metavar='<CANDLES>'       ,
00251             help = 'specify on which candles to run the Callgrind tests with PILE UP')
00252         parser.add_option('--RunMemcheckPU'             , type='string', dest='RunMemcheckPU' , metavar='<CANDLES>'       ,
00253             help = 'specify on which candles to run the Memcheck tests with PILE UP')
00254 
00255         #Adding a filein option to use pre-processed RAW file for RECO and HLT:
00256         parser.add_option('--filein'             , type='string', dest='userInputFile' , metavar='<FILE>', #default="",
00257             help = 'specify input RAW root file for HLT and RAW2DIGI-RECO (list the files in the same order as the candles for the tests)')
00258 
00259         #Adding an option to handle additional (to the default user) email addresses to the email notification list (that sends the cmsPerfSuite.log once the performance suite is done running):
00260         parser.add_option('--mail', type='string', dest='MailLogRecipients', metavar='<EMAIL ADDRESS>', default=self.user, help='specify valid email address(es) name@domain in order to receive notification at the end of the performance suite running with the cmsPerfSuite.log file')
00261 
00262         #Adding option to turn off tarball creation at the end of the execution of the performance suite:
00263         parser.add_option('--no_tarball', action="store_false", dest='tarball', default=True, help='Turn off automatic tarball creation at the end of the performance suite execution')
00264                 
00265         #####################
00266         #    
00267         # Developer options
00268         #
00269     
00270         devel  = opt.OptionGroup(parser, "Developer Options",
00271                                          "Caution: use these options at your own risk."
00272                                          "It is believed that some of them bite.\n")
00273     
00274         devel.add_option('-p', '--profile'  , type="str" , dest='profilers', metavar="<PROFILERS>" ,
00275             help = 'Profile codes to use for cmsRelvalInput' )
00276         devel.add_option('-f', '--false-run', action="store_true", dest='dryrun'   ,
00277             help = 'Dry run'                                                                                           )            
00278         devel.add_option('-d', '--debug'    , action='store_true', dest='debug'    ,
00279             help = 'Debug'                                                                                             )
00280         devel.add_option('--quicktest'      , action="store_true", dest='quicktest',
00281             help = 'Quick overwrite all the defaults to small numbers so that we can run a quick test of our chosing.' )  
00282         devel.add_option('--test'           , action="store_true", dest='unittest' ,
00283             help = 'Perform a simple test, overrides other options. Overrides verbosity and sets it to false.'         )            
00284         devel.add_option('--no_exec'           , action="store_true", dest='noexec' ,
00285             help = 'Run the suite without executing the cmsRelvalreport.py commands in the various directories. This is a useful debugging tool.'         )
00286         parser.add_option_group(devel)
00287         (options, args) = parser.parse_args(argslist)
00288     
00289     
00290         self._debug           = options.debug
00291         self._unittest        = options.unittest
00292         self._noexec          = options.noexec
00293         self._verbose         = options.verbose
00294         self._dryrun          = options.dryrun    
00295         castordir        = options.castordir
00296         TimeSizeEvents   = options.TimeSizeEvents
00297         IgProfEvents     = options.IgProfEvents
00298         CallgrindEvents  = options.CallgrindEvents
00299         MemcheckEvents   = options.MemcheckEvents
00300         cmsScimark       = options.cmsScimark
00301         cmsScimarkLarge  = options.cmsScimarkLarge
00302         cmsdriverOptions = options.cmsdriverOptions
00303         stepOptions      = options.stepOptions
00304         quicktest        = options.quicktest
00305         #candleoption     = options.candleOptions
00306         runonspare       = options.runonspare
00307         profilers        = options.profilers.strip()
00308         cpu              = options.cpu.strip()
00309         bypasshlt        = options.bypasshlt
00310         cores            = options.cores
00311         logfile          = options.logfile
00312         prevrel          = options.previousrel
00313         outputdir        = options.outputdir
00314         RunTimeSize      = options.RunTimeSize
00315         RunIgProf        = options.RunIgProf
00316         RunCallgrind     = options.RunCallgrind
00317         RunMemcheck      = options.RunMemcheck
00318         RunDigiPileUp    = options.RunDigiPileUp
00319         RunTimeSizePU    = options.RunTimeSizePU
00320         RunIgProfPU      = options.RunIgProfPU
00321         RunCallgrindPU   = options.RunCallgrindPU
00322         RunMemcheckPU    = options.RunMemcheckPU
00323         PUInputFile      = options.PUInputFile
00324         userInputFile    = options.userInputFile
00325         if options.MailLogRecipients !="" and self.user not in options.MailLogRecipients: #To allow for the --mail "" case of suppressing the email and the default user case
00326            MailLogRecipients= self.user+","+options.MailLogRecipients #Add the user by default if there is a mail report
00327         else:
00328            MailLogRecipients=options.MailLogRecipients
00329         tarball          = options.tarball
00330     
00331         #################
00332         # Check logfile option
00333         #
00334         if not logfile == None:
00335             logfile = os.path.abspath(logfile)
00336             logdir = os.path.dirname(logfile)
00337             if not os.path.exists(logdir):
00338                 parser.error("Directory to output logfile does not exist")
00339                 sys.exit()
00340             logfile = os.path.abspath(logfile)
00341     
00342         #############
00343         # Check step Options
00344         #
00345         if "GEN,SIM" in stepOptions:
00346             self.logh.write("WARNING: Please use GEN-SIM with a hypen not a \",\"!\n")
00347         #Using the step option as a switch between different dictionaries for:
00348         #RunTimeSize,RunIgProf,RunCallgrind,RunMemCheck,RunDigiPileUp:
00349         if stepOptions == "" or stepOptions == 'Default':
00350             pass
00351         else:
00352             stepOptions='--usersteps=%s' % (stepOptions)        
00353     
00354         ###############
00355         # Check profile option
00356         #
00357         isnumreg = re.compile("^-?[0-9]*$")
00358         found    = isnumreg.search(profilers)
00359         if not found :
00360             parser.error("profile codes option contains non-numbers")
00361             sys.exit()
00362     
00363         ###############
00364         # Check output directory option
00365         #
00366         if outputdir == "":
00367             outputdir = os.getcwd()
00368         else:
00369             outputdir = os.path.abspath(outputdir)
00370     
00371         if not os.path.isdir(outputdir):
00372             parser.error("%s is not a valid output directory" % outputdir)
00373             sys.exit()
00374             
00375         ################
00376         # Check cpu option
00377         # 
00378         numetcomreg = re.compile("^[0-9,]*")
00379         if not numetcomreg.search(cpu):
00380             parser.error("cpu option needs to be a comma separted list of ints or a single int")
00381             sys.exit()
00382     
00383         cpustr = cpu
00384         cpu = []
00385         if "," in cpustr:
00386             cpu = map(lambda x: int(x),cpustr.split(","))
00387         else:
00388             cpu = [ int(cpustr)  ]
00389     
00390         ################
00391         # Check previous release directory
00392         #
00393         if not prevrel == "":
00394             prevrel = os.path.abspath(prevrel)
00395             if not os.path.exists(prevrel):
00396                 self.logh.write("ERROR: Previous release dir %s could not be found" % prevrel)
00397                 sys.exit()
00398     
00399         #############
00400         # Setup quicktest option
00401         #
00402         if quicktest:
00403             TimeSizeEvents = 1
00404             IgProfEvents = 1
00405             CallgrindEvents = 0
00406             MemcheckEvents = 0
00407             cmsScimark = 1
00408             cmsScimarkLarge = 1
00409     
00410         #############
00411         # Setup unit test option
00412         #
00413         if self._unittest:
00414             self._verbose = False
00415             if stepOptions == "":
00416                 stepOptions = "GEN-SIM,DIGI,L1,DIGI2RAW,HLT,RAW2DIGI-RECO"
00417             cmsScimark      = 0
00418             cmsScimarkLarge = 0
00419             CallgrindEvents  = 0
00420             MemcheckEvents  = 0
00421             IgProfEvents    = 0
00422             TimeSizeEvents  = 1
00423         
00424         #Split all the RunTimeSize etc candles in lists:
00425         TimeSizeCandles=[]
00426         IgProfCandles=[]
00427         CallgrindCandles=[]
00428         MemcheckCandles=[]
00429         TimeSizePUCandles=[]
00430         IgProfPUCandles=[]
00431         CallgrindPUCandles=[]
00432         MemcheckPUCandles=[]
00433         userInputRootFiles=[]
00434         if RunTimeSize:
00435             TimeSizeCandles = RunTimeSize.split(",")
00436         if RunIgProf:
00437             IgProfCandles = RunIgProf.split(",")
00438         if RunCallgrind:
00439             CallgrindCandles = RunCallgrind.split(",")
00440         if RunMemcheck:
00441             MemcheckCandles = RunMemcheck.split(",")
00442         if RunDigiPileUp:
00443             for candle in RunDigiPileUp.split(","):
00444                 if candle in TimeSizeCandles:
00445                     TimeSizePUCandles.append(candle)
00446                 if candle in IgProfCandles:
00447                     IgProfPUCandles.append(candle)
00448                 if candle in CallgrindCandles:
00449                     CallgrindPUCandles.append(candle)
00450                 if candle in MemcheckCandles:
00451                     MemcheckPUCandles.append(candle)
00452         if RunTimeSizePU:
00453             TimeSizePUCandles.extend(RunTimeSizePU.split(","))
00454             #Some smart removal of duplicates from the list!
00455             temp=set(TimeSizePUCandles)
00456             TimeSizePUCandles=list(temp) #Doing it in 2 steps to avoid potential issues with type of arguments
00457         if RunIgProfPU:
00458             IgProfPUCandles.extend(RunIgProfPU.split(","))
00459             #Some smart removal of duplicates from the list!
00460             temp=set(IgProfPUCandles)
00461             IgProfPUCandles=list(temp) #Doing it in 2 steps to avoid potential issues with type of arguments
00462         if RunCallgrindPU:
00463             CallgrindPUCandles.extend(RunCallgrindPU.split(","))
00464             #Some smart removal of duplicates from the list!
00465             temp=set(CallgrindPUCandles)
00466             CallgrindPUCandles=list(temp) #Doing it in 2 steps to avoid potential issues with type of arguments
00467         if RunMemcheckPU:
00468             MemcheckPUCandles.extend(RunMemcheckPU.split(","))
00469             #Some smart removal of duplicates from the list!
00470             temp=set(MemcheckPUCandles)
00471             MemcheckPUCandles=list(temp) #Doing it in 2 steps to avoid potential issues with type of arguments
00472         if userInputFile:
00473            userInputRootFiles=userInputFile.split(",")
00474 
00475            
00476 
00477         #############
00478         # Setup cmsdriver and eventual cmsdriverPUoption
00479         #
00480         cmsdriverPUOptions=""
00481         if cmsdriverOptions:
00482             #Set the eventual Pile Up cmsdriver options first:
00483             if TimeSizePUCandles or IgProfPUCandles or CallgrindPUCandles or MemcheckPUCandles:
00484                 #Bug fixed: no space between --pileup= and LowLumiPileUp (otherwise could omit the =)
00485                 cmsdriverPUOptions = '--cmsdriver="%s %s%s"'%(cmsdriverOptions," --pileup=",cmsDriverPileUpOption)
00486             #Set the regular ones too:
00487             cmsdriverOptions = '--cmsdriver="%s"'%cmsdriverOptions        
00488     
00489         return (castordir       ,
00490                 TimeSizeEvents  ,
00491                 IgProfEvents    ,
00492                 CallgrindEvents ,
00493                 MemcheckEvents  ,
00494                 cmsScimark      ,
00495                 cmsScimarkLarge ,
00496                 cmsdriverOptions,
00497                 cmsdriverPUOptions,
00498                 stepOptions     ,
00499                 quicktest       ,
00500                 profilers       ,
00501                 cpu             ,
00502                 cores           ,
00503                 prevrel         ,
00504                 bypasshlt       ,
00505                 runonspare      ,
00506                 outputdir       ,
00507                 logfile         ,
00508                 TimeSizeCandles ,
00509                 IgProfCandles   ,
00510                 CallgrindCandles,
00511                 MemcheckCandles ,
00512                 TimeSizePUCandles ,
00513                 IgProfPUCandles   ,
00514                 CallgrindPUCandles,
00515                 MemcheckPUCandles ,
00516                 PUInputFile     ,
00517                 userInputRootFiles,
00518                 MailLogRecipients,
00519                 tarball)
00520     
00521     #def usage(self):
00522     #    return __doc__
00523     
00524     ############
00525     # Run a list of commands using system
00526     # ! We should rewrite this not to use system (most cases it is unnecessary)
00527     def runCmdSet(self,cmd):
00528         exitstat = 0
00529         if len(cmd) <= 1:
00530             exitstat = self.runcmd(cmd)
00531             if self._verbose:
00532                 self.printFlush(cmd)
00533         else:
00534             for subcmd in cmd:
00535                 if self._verbose:
00536                     self.printFlush(subcmd)
00537             exitstat = self.runcmd(" && ".join(cmd))
00538         if self._verbose:
00539             self.printFlush(self.getDate())
00540         return exitstat
00541     
00542     #############
00543     # Print and flush a string (for output to a log file)
00544     #
00545     def printFlush(self,command):
00546         if self._verbose:
00547             self.logh.write(str(command) + "\n")
00548             self.logh.flush()
00549     
00550     #############
00551     # Run a command and return the exit status
00552     #
00553     def runcmd(self,command):
00554         #Substitute popen with subprocess.Popen!
00555         #Using try/except until Popen becomes thread safe (it seems that everytime it is called
00556         #all processes are checked to reap the ones that are done, this creates a race condition with the wait()... that
00557         #results into an error with "No child process".
00558         #os.popen(command)
00559         try:
00560             process  = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
00561             pid=process.pid
00562             exitstat= process.wait()
00563             cmdout   = process.stdout.read()
00564             exitstat = process.returncode
00565         except OSError, detail:
00566             self.logh.write("Race condition in subprocess.Popen has robbed us of the exit code of the %s process (PID %s).Assume it failed!\n %s\n"%(command,pid,detail))
00567             self.logh.flush()
00568             exitstat=999
00569             cmdout="Race condition in subprocess.Popen has robbed us of the exit code of the %s process (PID %s).Assume it failed!\n %s"%(command,pid,detail)
00570         if self._verbose:
00571             self.logh.write(cmdout)# + "\n") No need of extra \n!
00572             self.logh.flush()
00573         if exitstat == None:
00574             self.logh.write("Something strange is going on! Exit code was None for command %s: check if it really ran!"%command)
00575             self.logh.flush()
00576             exitstat=0
00577         return exitstat
00578     
00579     def getDate(self):
00580         return time.ctime()
00581     
00582     def printDate(self):
00583         self.logh.write(self.getDate() + "\n")
00584         self.logh.flush()
00585     #############
00586     # Make directory for a particular candle and profiler.
00587     # ! This is really unnecessary code and should be replaced with a os.mkdir() call
00588     def mkCandleDir(self,pfdir,candle,profiler):
00589         adir = os.path.join(pfdir,"%s_%s" % (candle,profiler))
00590         self.runcmd( "mkdir -p %s" % adir )
00591         if self._verbose:
00592             self.printDate()
00593         return adir
00594     
00595     #############
00596     # Copy root file from another candle's directory
00597     # ! Again this is messy. 
00598 
00599     def cprootfile(self,dir,candle,NumOfEvents,cmsdriverOptions=""):
00600         cmds = ("cd %s" % dir,
00601                 "cp -pR ../%s_IgProf/%s_GEN,SIM.root ."  % (candle,CandFname[candle]))
00602         
00603         if self.runCmdSet(cmds):
00604             self.logh.write("Since there was no ../%s_IgProf/%s_GEN,SIM.root file it will be generated first\n"%(candle,CandFname[candle]))
00605 
00606             cmd = "cd %s ; cmsDriver.py %s -s GEN,SIM -n %s --fileout %s_GEN,SIM.root %s>& %s_GEN_SIM_for_valgrind.log" % (dir,KeywordToCfi[candle],str(NumOfEvents),candle,cmsdriverOptions,candle)
00607 
00608             self.printFlush(cmd)
00609             #Obsolete popen4-> subprocess.Popen
00610             #cmdout=os.popen3(cmd)[2].read()
00611             cmdout=subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
00612             if cmdout:
00613                 self.printFlush(cmdout)
00614             return cmdout
00615             
00616     #############
00617     # Display G4 cerr errors and CMSExceptions in the logfile
00618     #
00619     def displayErrors(self,file):
00620         try:
00621             for line in open(file,"r"):
00622                 if "cerr" in line or "CMSException" in line:
00623                     self.logh.write("ERROR: %s\n" % line)
00624                     self.ERRORS += 1
00625         except OSError, detail:
00626             self.logh.write("WARNING: %s\n" % detail)
00627             self.ERRORS += 1        
00628         except IOError, detail:
00629             self.logh.write("WARNING: %s\n" % detail)
00630             self.ERRORS += 1
00631         
00632     ##############
00633     # Filter lines in the valgrind report that match GEN,SIM
00634     #
00635     def valFilterReport(self,dir):
00636         #cmds = ("cd %s" % dir,
00637         #        "grep -v \"step=GEN,SIM\" SimulationCandles_%s.txt > tmp" % (self.cmssw_version),
00638         #        "mv tmp SimulationCandles_%s.txt"                         % (self.cmssw_version))
00639         #FIXME:
00640         #Quick and dirty hack to have valgrind MemCheck run on 5 events on both GEN,SIM and DIGI in QCD_80_120, while removing the line for GEN,SIM for Callgrind
00641         InputFileName=os.path.join(dir,"SimulationCandles_%s.txt"%(self.cmssw_version))
00642         InputFile=open(InputFileName,"r")
00643         InputLines=InputFile.readlines()
00644         InputFile.close()
00645         Outputfile=open(InputFileName,"w")
00646         simRegxp=re.compile("step=GEN,SIM")
00647         digiRegxp=re.compile("step=DIGI")
00648         CallgrindRegxp=re.compile("ValgrindFCE")
00649         MemcheckRegxp=re.compile("Memcheck")
00650         NumEvtRegxp=re.compile("-n 1")#FIXME Either use the ValgrindEventNumber or do a more general match!
00651         for line in InputLines:
00652             if simRegxp.search(line) and CallgrindRegxp.search(line):
00653                 continue
00654             elif simRegxp.search(line) and MemcheckRegxp.search(line):
00655                 #Modify
00656                 if NumEvtRegxp.search(line):
00657                     line=NumEvtRegxp.sub(r"-n 5",line)
00658                 else:
00659                     self.logh.write("The number of Memcheck event was not changed since the original number of Callgrind event was not 1!\n")
00660                 Outputfile.write(line)
00661             elif digiRegxp.search(line) and MemcheckRegxp.search(line):
00662                 #Modify
00663                 if NumEvtRegxp.search(line):
00664                     line=NumEvtRegxp.sub(r"-n 5",line)
00665                 else:
00666                     self.logh.write("The number of Memcheck event was not changed since the original number of Callgrind event was not 1!\n")
00667                 Outputfile.write(line)
00668             else:
00669                 Outputfile.write(line)
00670         self.logh.flush()
00671         Outputfile.close()
00672             
00673         #self.runCmdSet(cmds)
00674     
00675     ##################
00676     # Run cmsScimark benchmarks a number of times
00677     #
00678     def benchmarks(self,cpu,pfdir,name,bencher,large=False):
00679         cmd = self.Commands[cpu][3]
00680         redirect = ""
00681         if large:
00682             redirect = " -large >>"    
00683         else:
00684             redirect = " >>"
00685     
00686         for i in range(bencher):
00687            #Check first for the existence of the file so that we can append:
00688            if not os.path.exists(os.path.join(pfdir,os.path.basename(name))):
00689               #Equivalent of touch to make sure the file exist to be able to append to it.
00690               open(os.path.join(pfdir,os.path.basename(name)))
00691               
00692            command= cmd + redirect + os.path.join(pfdir,os.path.basename(name))        
00693            self.printFlush(command + " [%s/%s]" % (i+1,bencher))
00694            self.runcmd(command)
00695            self.logh.flush()
00696     
00697     ##################
00698     # This function is a wrapper around cmsRelvalreport
00699     # 
00700     def runCmsReport(self,cpu,dir,candle):
00701         cmd  = self.Commands[cpu][1]
00702         cmds = ("cd %s"                 % (dir),
00703                 "%s -i SimulationCandles_%s.txt -t perfreport_tmp -R -P >& %s.log" % (cmd,self.cmssw_version,candle))
00704         exitstat = 0
00705         if not self._debug:
00706             exitstat = self.runCmdSet(cmds)
00707             
00708         if self._unittest and (not exitstat == 0):
00709             self.logh.write("ERROR: CMS Report returned a non-zero exit status \n")
00710             sys.exit(exitstat)
00711         else:
00712             return(exitstat) #To return the exit code of the cmsRelvalreport.py commands to the runPerfSuite function
00713     
00714     ##################
00715     # Test cmsDriver.py (parses the simcandles file, removing duplicate lines, and runs the cmsDriver part)
00716     #
00717     def testCmsDriver(self,cpu,dir,cmsver,candle):
00718         cmsdrvreg = re.compile("^cmsDriver.py")
00719         cmd  = self.Commands[cpu][0]
00720         noExit = True
00721         stepreg = re.compile("--step=([^ ]*)")
00722         previousCmdOnline = ""
00723         for line in open(os.path.join(dir,"SimulationCandles_%s.txt" % (cmsver))):
00724             if (not line.lstrip().startswith("#")) and not (line.isspace() or len(line) == 0): 
00725                 cmdonline  = line.split("@@@",1)[0]
00726                 if cmsdrvreg.search(cmdonline) and not previousCmdOnline == cmdonline:
00727                     stepbeingrun = "Unknown"
00728                     matches = stepreg.search(cmdonline)
00729                     if not matches == None:
00730                         stepbeingrun = matches.groups()[0]
00731                     if "PILEUP" in cmdonline:
00732                         stepbeingrun += "_PILEUP"
00733                     self.logh.write(cmdonline + "\n")
00734                     cmds = ("cd %s"      % (dir),
00735                             "%s  >& ../cmsdriver_unit_test_%s_%s.log"    % (cmdonline,candle,stepbeingrun))
00736                     if self._dryrun:
00737                         self.logh.write(cmds + "\n")
00738                     else:
00739                         out = self.runCmdSet(cmds)                    
00740                         if not out == None:
00741                             sig     = out >> 16    # Get the top 16 bits
00742                             xstatus = out & 0xffff # Mask out all bits except the first 16 
00743                             self.logh.write("FATAL ERROR: CMS Driver returned a non-zero exit status (which is %s) when running %s for candle %s. Signal interrupt was %s\n" % (xstatus,stepbeingrun,candle,sig))
00744                             sys.exit()
00745                 previousCmdOnline = cmdonline
00746         
00747     ##############
00748     # Wrapper for cmsRelvalreportInput 
00749     # 
00750     def runCmsInput(self,cpu,dir,numevents,candle,cmsdrvopts,stepopt,profiles,bypasshlt,userInputFile):
00751 
00752         #Crappy fix for optional options with special synthax (bypasshlt and userInputFile)
00753         bypass = ""
00754         if bypasshlt:
00755             bypass = "--bypass-hlt"
00756         userInputFileOption=""
00757         if userInputFile:
00758            userInputFileOption = "--filein %s"%userInputFile
00759         cmd = self.Commands[cpu][2]
00760         cmds=[]
00761         #print cmds
00762         cmds = ("cd %s"                    % (dir),
00763                 "%s %s \"%s\" %s %s %s %s %s" % (cmd,
00764                                               numevents,
00765                                               candle,
00766                                               profiles,
00767                                               cmsdrvopts,
00768                                               stepopt,
00769                                               bypass,userInputFileOption))
00770         exitstat=0
00771         exitstat = self.runCmdSet(cmds)
00772         if self._unittest and (not exitstat == 0):
00773             self.logh.write("ERROR: CMS Report Input returned a non-zero exit status \n" )
00774         return exitstat
00775     ##############
00776     # Prepares the profiling directory and runs all the selected profiles (if this is not a unit test)
00777     #
00778     #Making parameters named to facilitate the handling of arguments (especially with the threading use case)
00779     def simpleGenReport(self,cpus,perfdir=os.getcwd(),NumEvents=1,candles=['MinBias'],cmsdriverOptions='',stepOptions='',Name='',profilers='',bypasshlt='',userInputRootFiles=''):
00780         callgrind = Name == "Callgrind"
00781         memcheck  = Name == "Memcheck"
00782     
00783         profCodes = {"TimeSize" : "0123",
00784                      "IgProf"   : "4567",
00785                      "IgProf_Perf":"47", #Added the Analyse to IgProf_Perf #FIXME: At the moment Analyse is always run whether 7 is selected or not! Issue to solve in cmsRelvalreportInput.py... but not really important (it's always been there, not impacting our use-cases).
00786                      "IgProf_Mem":"567",
00787                      "Callgrind": "8",
00788                      "Memcheck" : "9",
00789                      None       : "-1"} 
00790     
00791         profiles = profCodes[Name]
00792         if not profilers == "":
00793             profiles = profilers        
00794     
00795         RelvalreportExitCode=0
00796         
00797         for cpu in cpus:
00798             pfdir = perfdir
00799             if len(cpus) > 1:
00800                 pfdir = os.path.join(perfdir,"cpu_%s" % cpu)
00801             for candle in candles:
00802                 #Create the directory for cmsRelvalreport.py running (e.g. MinBias_TimeSize, etc)
00803                 #Catch the case of PILE UP:
00804                 if "--pileup" in cmsdriverOptions:
00805                    candlename=candle+"_PU"
00806                 else:
00807                    candlename=candle
00808                 adir=self.mkCandleDir(pfdir,candlename,Name)
00809                 if self._unittest:
00810                     # Run cmsDriver.py
00811                     if userInputRootFiles:
00812                        self.logh.write(userInputRootFiles)
00813                        userInputFile=userInputRootFiles[0]
00814                     else:
00815                        userInputFile=""
00816                     self.logh.flush()
00817                     self.runCmsInput(cpu,adir,NumEvents,candle,cmsdriverOptions,stepOptions,profiles,bypasshlt,userInputFile) 
00818                     self.testCmsDriver(cpu,adir,candle)
00819                 else:
00820                     if userInputRootFiles:
00821                        self.logh.write("Variable userInputRootFiles is %s\n"%userInputRootFiles)
00822                        #Need to use regexp, cannot rely on the order... since for different tests there are different candles...
00823                        #userInputFile=userInputRootFiles[candles.index(candle)]
00824                        #FIXME:
00825                        #Note the issue that the input files HAVE to have in their name the candle as is used in cmsPerfSuite.py command line!
00826                        #This is currently caught by a printout in the log: should be either taken care of with some exception to throw?
00827                        #Will put this in the documentation
00828                        userInputFile=""
00829                        candleregexp=re.compile(candle)
00830                        for file in userInputRootFiles:
00831                           if candleregexp.search(file):
00832                              userInputFile=file
00833                              self.logh.write("For these %s %s tests will use user input file %s\n"%(candlename,Name,userInputFile))
00834                        if userInputFile == "":
00835                           self.logh.write("***WARNING: For these %s %s tests could not find a matching input file in %s: will try to do without it!!!!!\n"%(candlename,Name,userInputRootFiles))
00836                        self.logh.flush()
00837                     else:
00838                        userInputFile=""
00839                     DummyTestName=candlename+"_"+stepOptions.split("=")[1]
00840                     DummyTimer=PerfSuiteTimer(start=datetime.datetime.now()) #Start the timer (DummyTimer is just a reference, but we will use the dictionary to access this later...
00841                     TimerInfo[Name].update({DummyTestName:DummyTimer}) #Add the TimeSize timer to the dictionary
00842                     #The following command will create the appropriate SimulationCandlesX.txt file in the relevant directory, ready to run cmsRelvalreport.py on it.
00843                     self.runCmsInput(cpu,adir,NumEvents,candle,cmsdriverOptions,stepOptions,profiles,bypasshlt,userInputFile)            
00844                     #Here where the no_exec option kicks in (do everything but do not launch cmsRelvalreport.py, it also prevents cmsScimark spawning...):
00845                     if self._noexec:
00846                         self.logh.write("Running in debugging mode, without executing cmsRelvalreport.py\n")
00847                         self.logh.flush()
00848                         pass
00849                     else:
00850                         #The following command will launch cmsRelvalreport.py on the SimulationCandlesX.txt input file created above.
00851                         ExitCode=self.runCmsReport(cpu,adir,candle)
00852                         self.logh.write("Individual cmsRelvalreport.py ExitCode %s\n"%ExitCode)
00853                         RelvalreportExitCode=RelvalreportExitCode+ExitCode
00854                         self.logh.write("Summed cmsRelvalreport.py ExitCode %s\n"%RelvalreportExitCode)
00855                         self.logh.flush()
00856                     DummyTimer.set_end(datetime.datetime.now())
00857                     
00858                     #for proflog in proflogs:
00859                     #With the change from 2>1&|tee to >& to preserve exit codes, we need now to check all logs...
00860                     #less nice... we might want to do this externally so that in post-processing its a re-usable tool
00861                     globpath = os.path.join(adir,"*.log") #"%s.log"%candle)
00862                     self.logh.write("Looking for logs that match %s\n" % globpath)
00863                     logs     = glob.glob(globpath)
00864                     for log in logs:
00865                         self.logh.write("Found log %s\n" % log)
00866                         self.displayErrors(log)
00867         self.printFlush("Returned cumulative RelvalreportExitCode is %s"%RelvalreportExitCode)
00868         return RelvalreportExitCode
00869     
00870     ############
00871     # Runs benchmarking, cpu spinlocks on spare cores and profiles selected candles
00872     #
00873     #FIXME:
00874     #Could redesign interface of functions to use keyword arguments:
00875     #def runPerfSuite(**opts):
00876     #then instead of using castordir variable, would use opts['castordir'] etc    
00877     def runPerfSuite(self,
00878                      castordir        = "/castor/cern.ch/cms/store/relval/performance/",
00879                      TimeSizeEvents   = 100        ,
00880                      IgProfEvents     = 5          ,
00881                      CallgrindEvents  = 1          ,
00882                      MemcheckEvents   = 5          ,
00883                      cmsScimark       = 10         ,
00884                      cmsScimarkLarge  = 10         ,
00885                      cmsdriverOptions = ""         ,#Could use directly cmsRelValCmd.get_Options()
00886                      cmsdriverPUOptions= ""        ,
00887                      stepOptions      = ""         ,
00888                      quicktest        = False      ,
00889                      profilers        = ""         ,
00890                      cpus             = [1]        ,
00891                      cores            = 4          ,#Could use directly cmsCpuInfo.get_NumOfCores()
00892                      prevrel          = ""         ,
00893                      bypasshlt        = False      ,
00894                      runonspare       = True       ,
00895                      perfsuitedir     = os.getcwd(),
00896                      logfile          = None,
00897                      TimeSizeCandles      = ""         ,
00898                      IgProfCandles        = ""         ,
00899                      CallgrindCandles     = ""         ,
00900                      MemcheckCandles      = ""         ,
00901                      TimeSizePUCandles    = ""         ,
00902                      IgProfPUCandles      = ""         ,
00903                      CallgrindPUCandles   = ""         ,
00904                      MemcheckPUCandles    = ""         ,
00905                      PUInputFile          = ""         ,
00906                      userInputFile        = ""         ,
00907                      MailLogRecipients    = ""         ,
00908                      tarball              = ""         ):
00909         
00910         #Set up a variable for the FinalExitCode to be used as the sum of exit codes:
00911         FinalExitCode=0
00912 
00913         #Set up the logfile first!
00914         if not logfile == None:
00915            try:
00916               self.logh = open(logfile,"a")
00917            except (OSError, IOError), detail:
00918               self.logh.write(detail + "\n")
00919               self.logh.flush()  
00920 
00921         #Adding HEPSPEC06 score if available in /build/HEPSPEC06.score file
00922         self.HEPSPEC06 = 0 #Set it to 0 by default (so it is easy to catch in the DB too)
00923         try:
00924            HEPSPEC06_file=open("/build/HEPSPEC06.score","r")
00925            for line in HEPSPEC06_file.readlines():
00926               if not line.startswith("#") and "HEPSPEC06" in line:
00927                  self.HEPSPEC06= line.split()[2]
00928         except IOError:
00929            self.logh.write("***Warning***: Could not find file /build/HEPSPEC06.score file on this machine!\n")
00930            self.logh.flush()
00931 
00932         #Adding a copy of /proc/cpuinfo and /proc/meminfo in the working directory so it can be kept in the tarball on CASTOR:
00933         localcpuinfo=os.path.join(perfsuitedir,"cpuinfo")
00934         cpuinfo_exitcode=-1
00935         if os.path.exists(localcpuinfo):
00936            cpuinfo_exitcode=0
00937         else:
00938            self.logh.write("Copying /proc/cpuinfo in current working directory (%s)\n"%perfsuitedir)
00939            cpuinfo_exitcode=self.runcmd("cp /proc/cpuinfo %s"%perfsuitedir)
00940         localmeminfo=os.path.join(perfsuitedir,"meminfo")
00941         meminfo_exitcode=-1
00942         if os.path.exists(localmeminfo):
00943            meminfo_exitcode=0
00944         else:
00945            self.logh.write("Copying /proc/meminfo in current working directory (%s)\n"%perfsuitedir)
00946            meminfo_exitcode=self.runcmd("cp /proc/meminfo %s"%perfsuitedir)
00947         if cpuinfo_exitcode or meminfo_exitcode:
00948            self.logh.write("There was an issue copying the cpuinfo or meminfo files!\n")
00949         self.logh.flush()
00950         
00951         try:        
00952             if not prevrel == "":
00953                 self.logh.write("Production of regression information has been requested with release directory %s\n" % prevrel)
00954             if not cmsdriverOptions == "":
00955                 self.logh.write("Running cmsDriver.py with user defined options: %s\n" % cmsdriverOptions)
00956                 #Attach the full option synthax for cmsRelvalreportInput.py:
00957                 cmsdriverOptionsRelvalInput="--cmsdriver="+cmsdriverOptions
00958                 #FIXME: should import cmsRelvalreportInput.py and avoid these issues...
00959             if not stepOptions == "":
00960                 self.logh.write("Running user defined steps only: %s\n" % stepOptions)
00961                 #Attach the full option synthax for cmsRelvalreportInput.py:
00962                 setpOptionsRelvalInput="--usersteps="+stepOptions
00963                 #FIXME: should import cmsRelvalreportInput.py and avoid these issues...
00964             if bypasshlt:
00965                 #Attach the full option synthax for cmsRelvalreportInput.py:
00966                 bypasshltRelvalInput="--bypass-hlt"
00967                 #FIXME: should import cmsRelvalreportInput.py and avoid these issues...
00968             self.logh.write("Current Architecture is %s\n"%self.cmssw_arch)
00969             self.logh.write("Current CMSSW version is %s\n"%self.cmssw_version)
00970             self.logh.write("This machine ( %s ) is assumed to have %s cores, and the suite will be run on cpu %s\n" %(self.host,cores,cpus))
00971             self.logh.write("This machine's HEPSPEC06 score is: %s \n"%self.HEPSPEC06)
00972             path=os.path.abspath(".")
00973             self.logh.write("Performance Suite started running at %s on %s in directory %s, run by user %s\n" % (self.getDate(),self.host,path,self.user))
00974             #Start the timer for the total performance suite running time:
00975             TotalTime=PerfSuiteTimer(start=datetime.datetime.now())
00976             #Also initialize the dictionary that will contain all the timing information:
00977             global TimerInfo
00978             TimerInfo={'TotalTime':{'TotalTime':TotalTime}} #Structure will be {'key':[PerfSuiteTimerInstance,...],...}
00979             #Obsolete popen4-> subprocess.Popen
00980             #showtags=os.popen4("showtags -r")[1].read()
00981             showtags=subprocess.Popen("showtags -r",shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
00982             self.logh.write(showtags) # + "\n") No need for extra \n!
00983             self.logh.flush()
00984             #For the log:
00985             if self._verbose:
00986                 self.logh.write("The performance suite results tarball will be stored in CASTOR at %s\n" % self._CASTOR_DIR)
00987                 self.logh.write("%s TimeSize events\n" % TimeSizeEvents)
00988                 self.logh.write("%s IgProf events\n"   % IgProfEvents)
00989                 self.logh.write("%s Callgrind events\n" % CallgrindEvents)
00990                 self.logh.write("%s Memcheck events\n" % MemcheckEvents)
00991                 self.logh.write("%s cmsScimark benchmarks before starting the tests\n"      % cmsScimark)
00992                 self.logh.write("%s cmsScimarkLarge benchmarks before starting the tests\n" % cmsScimarkLarge)
00993                 self.logh.flush()
00994             #Actual script actions!
00995             #Will have to fix the issue with the matplotlib pie-charts:
00996             #Used to source /afs/cern.ch/user/d/dpiparo/w0/perfreport2.1installation/share/perfreport/init_matplotlib.sh
00997             #Need an alternative in the release
00998 
00999             #Code for the architecture benchmarking use-case
01000             if len(cpus) > 1:
01001                 for cpu in cpus:
01002                     cpupath = os.path.join(perfsuitedir,"cpu_%s" % cpu)
01003                     if not os.path.exists(cpupath):
01004                         os.mkdir(cpupath)
01005             
01006             self.Commands = {}
01007             AllScripts = self.Scripts + self.AuxiliaryScripts
01008     
01009             for cpu in range(cmsCpuInfo.get_NumOfCores()): #FIXME use the actual number of cores of the machine here!
01010                 self.Commands[cpu] = []
01011 
01012             #Information for the log:
01013             self.logh.write("Full path of all the scripts used in this run of the Performance Suite:\n")
01014             for script in AllScripts:
01015                 which="which " + script
01016     
01017                 #Logging the actual version of cmsDriver.py, cmsRelvalreport.py, cmsSimPyRelVal.pl
01018                 #Obsolete popen4-> subprocess.Popen
01019                 #whichstdout=os.popen4(which)[1].read()
01020                 whichstdout=subprocess.Popen(which,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read()
01021                 self.logh.write(whichstdout) # + "\n") No need of the extra \n!
01022                 if script in self.Scripts:
01023                     for cpu in range(cmsCpuInfo.get_NumOfCores()):#FIXME use the actual number of cores of the machine here!
01024                         command="taskset -c %s %s" % (cpu,script)
01025                         self.Commands[cpu].append(command)
01026                         
01027             #First submit the cmsScimark benchmarks on the unused cores:
01028             scimark = ""
01029             scimarklarge = ""
01030             if not (self._unittest or self._noexec):
01031                 for core in range(cores):
01032                     if (not core in cpus) and runonspare:
01033                         self.logh.write("Submitting cmsScimarkLaunch.csh to run on core cpu "+str(core) + "\n")
01034                         subcmd = "cd %s ; cmsScimarkLaunch.csh %s" % (perfsuitedir, str(core))            
01035                         command="taskset -c %s sh -c \"%s\" &" % (str(core), subcmd)
01036                         self.logh.write(command + "\n")
01037     
01038                         #cmsScimarkLaunch.csh is an infinite loop to spawn cmsScimark2 on the other
01039                         #cpus so it makes no sense to try reading its stdout/err
01040                         #Obsolete popen4-> subprocess.Popen
01041                         #os.popen4(command)
01042                         subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
01043 
01044             self.logh.flush()
01045     
01046             #Don't do benchmarking if in debug mode... saves time
01047             benching = not self._debug
01048             ##FIXME:
01049             #We may want to introduce a switch here or agree on a different default (currently 10 cmsScimark and 10 cmsScimarkLarge)
01050             if benching and not (self._unittest or self._noexec): 
01051                 #Submit the cmsScimark benchmarks on the cpu where the suite will be run:
01052                 for cpu in cpus:
01053                     scimark      = open(os.path.join(perfsuitedir,"cmsScimark2.log")      ,"w")        
01054                     scimarklarge = open(os.path.join(perfsuitedir,"cmsScimark2_large.log"),"w")
01055                     if cmsScimark > 0:
01056                         self.logh.write("Starting with %s cmsScimark on cpu%s\n"       % (cmsScimark,cpu))
01057                         cmsScimarkInitialTime=PerfSuiteTimer(start=datetime.datetime.now()) #Create the cmsScimark PerfSuiteTimer
01058                         TimerInfo.update({'cmsScimarkTime':{'cmsScimarkInitial':cmsScimarkInitialTime}}) #Add the cmsScimarkInitialTime information to the general TimerInfo dictionary
01059                         self.benchmarks(cpu,perfsuitedir,scimark.name,cmsScimark)
01060                         cmsScimarkInitialTime.set_end(datetime.datetime.now()) #Stop the cmsScimark initial timer
01061     
01062                     if cmsScimarkLarge > 0:
01063                         self.logh.write("Following with %s cmsScimarkLarge on cpu%s\n" % (cmsScimarkLarge,cpu))
01064                         cmsScimarkLargeInitialTime=PerfSuiteTimer(start=datetime.datetime.now()) #Create the cmsScimarkLarge PerfSuiteTimer
01065                         TimerInfo['cmsScimarkTime'].update({'cmsScimarkLargeInitial':cmsScimarkLargeInitialTime}) #Add the cmsScimarkLargeInitialTime information to the general TimerInfo dictionary
01066                         self.benchmarks(cpu,perfsuitedir,scimarklarge.name,cmsScimarkLarge, large=True)
01067                         cmsScimarkLargeInitialTime.set_end(datetime.datetime.now()) #Stop the cmsScimarkLarge Initial timer
01068                 self.logh.flush()
01069             #Handling the Pile up input file here:
01070             if (TimeSizePUCandles or IgProfPUCandles or CallgrindPUCandles or MemcheckPUCandles) and not ("FASTSIM" in stepOptions):
01071                 #Note the FASTSIM exclusion... since there is no need to copy the file for FASTSIM.
01072                 PUInputName=os.path.join(perfsuitedir,"INPUT_PILEUP_EVENTS.root")
01073                 if PUInputFile:
01074                     #Define the actual command to copy the file locally:
01075                     #Allow the file to be mounted locally (or accessible via AFS)
01076                     copycmd="cp"
01077                     #Allow the file to be on CASTOR (taking a full CASTOR path)
01078                     if '/store/relval/' in PUInputFile:
01079                         copycmd="rfcp"
01080                         #Accept plain LFNs from DBS for RelVal CASTOR files:
01081                         #Minor fix to allow the case of user using the full path /castor/cern.ch/cms...
01082                         if PUInputFile.startswith('/store/relval/'):
01083                            PUInputFile="/castor/cern.ch/cms"+PUInputFile
01084                     #Copy the file locally
01085                     self.logh.write("Copying the file %s locally to %s\n"%(PUInputFile,PUInputName))
01086                     self.logh.flush()
01087                     GetPUInput=subprocess.Popen("%s %s %s"%(copycmd,PUInputFile,PUInputName), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
01088                     GetPUInputExitCode=GetPUInput.wait()
01089                     #Allow even the potential copy of a local file (even one already named INPUT_PILEUP_EVENTS.root!)
01090                     if GetPUInputExitCode:
01091                         self.logh.write("The copying of the pile-up input file returned a non-zero exit code: %s \nThis is the stdout+stderr if the command:\n%s\n"%(GetPUInputExitCode,GetPUInput.stdout))
01092                 #Ultimately accept the case of the file being already there and not being specified in the --PUInputFile option
01093                 if not os.path.exists(PUInputName):
01094                     self.logh.write("The necessary INPUT_PILEUP_EVENTS.root file was not found in the working directory %s\nExiting now!"%perfsuitedir)
01095                     self.logh.flush()
01096                     sys.exit(1)
01097                 else:
01098                     #Set up here the DIGI PILE UP options
01099                     self.printFlush("Some PILE UP tests will be run!")
01100                     #Actually setting them earlier... when handling options... May not need this else after all... or just as a log entry.
01101                     self.printFlush("cmsdriverPUOptions is %s"%cmsdriverPUOptions)
01102                     pass
01103             
01104             #TimeSize tests:
01105             if TimeSizeEvents > 0:
01106                TimeSizeTime=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
01107                TimerInfo.update({'TimeSize':{'TotalTime':TimeSizeTime}}) #Add the TimeSize timer to the dictionary
01108                if TimeSizeCandles:
01109                   self.logh.write("Launching the TimeSize tests (TimingReport, TimeReport, SimpleMemoryCheck, EdmSize) with %s events each\n" % TimeSizeEvents)
01110                   NoPileUpTime=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
01111                   TimerInfo['TimeSize'].update({'NoPileUpTime':NoPileUpTime}) #Add the TimeSize No Pile Up tests timer to the list
01112                   self.printDate()
01113                   self.logh.flush()
01114                   ReportExit=self.simpleGenReport(cpus,perfsuitedir,TimeSizeEvents,TimeSizeCandles,cmsdriverOptions,stepOptions,"TimeSize",profilers,bypasshlt,userInputFile)
01115                   FinalExitCode=FinalExitCode+ReportExit
01116                   #Adding a time stamp here to parse for performance suite running time data
01117                   self.printFlush("Regular TimeSize tests were finished at %s"%(self.getDate()))
01118                   NoPileUpTime.set_end(datetime.datetime.now()) #Stop TimeSize timer
01119                
01120                #Launch eventual Digi Pile Up TimeSize too:
01121                if TimeSizePUCandles:
01122                   self.logh.write("Launching the PILE UP TimeSize tests (TimingReport, TimeReport, SimpleMemoryCheck, EdmSize) with %s events each\n" % TimeSizeEvents)
01123                   PileUpTime=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
01124                   TimerInfo['TimeSize'].update({'PileUpTime':PileUpTime}) #Add the TimeSize Pile Up tests timer to the list
01125                   self.printDate()
01126                   self.logh.flush()
01127                   ReportExit=self.simpleGenReport(cpus,perfsuitedir,TimeSizeEvents,TimeSizePUCandles,cmsdriverPUOptions,stepOptions,"TimeSize",profilers,bypasshlt,userInputFile)
01128                   FinalExitCode=FinalExitCode+ReportExit
01129                   #Adding a time stamp here to parse for performance suite running time data
01130                   self.printFlush("Pileup TimeSize tests were finished at %s"%(self.getDate()))
01131                   PileUpTime.set_end(datetime.datetime.now()) #Stop TimeSize timer
01132                   
01133                #Check for issue with 
01134                if not (TimeSizeCandles or TimeSizePUCandles):
01135                   self.printFlush("A number of events (%s) for TimeSize tests was selected, but no candle for regular or pileup tests was selected!"%(TimeSizeEvents))
01136                #Adding a time stamp here to parse for performance suite running time data
01137                self.printFlush("All TimeSize tests were finished at %s"%(self.getDate()))
01138                TimeSizeTime.set_end(datetime.datetime.now()) #Stop TimeSize timer
01139             
01140             #Stopping all cmsScimark jobs and analysing automatically the logfiles
01141             #No need to waste CPU while the load does not affect Valgrind measurements!
01142             if not (self._unittest or self._noexec):
01143                 self.logh.write("Stopping all cmsScimark jobs now\n")
01144                 subcmd = "cd %s ; %s" % (perfsuitedir,self.AuxiliaryScripts[2])
01145                 stopcmd = "sh -c \"%s\"" % subcmd
01146                 self.printFlush(stopcmd)
01147                 #os.popen(stopcmd)
01148                 #Obsolete popen4-> subprocess.Popen
01149                 #self.printFlush(os.popen4(stopcmd)[1].read())
01150                 self.printFlush(subprocess.Popen(stopcmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT).stdout.read())
01151 
01152             #From here on we can use all available cores to speed up the performance suite remaining tests:
01153             if cores==0: #When specifying the cpu to run the suite on, one has to set cores to 0 to avoid threading of PerfSuite itself...
01154                                           #So we need to catch this case for the IB tests case where we assign the test to a specific cpu.
01155                 AvailableCores=cpus
01156             else:
01157                 AvailableCores=range(cores)
01158             #Initialize a list that will contain all the simpleGenReport keyword arguments (1 dictionary per test):
01159             TestsToDo=[]
01160             #IgProf tests:
01161             if IgProfEvents > 0:
01162                if IgProfCandles:
01163                    self.printFlush("Preparing IgProf tests")
01164                    #Special case for IgProf: user could pick with the option --profilers to run only IgProf perf or Mem (or Mem_Total alone etc)
01165                    #So in general we want to be able to split the perf and mem tests...
01166                    #For the case of --profiler option we will run only 1 test (i.e. it will get one core slot until it is done with whatever profiling choosen)
01167                    if profilers:
01168                       self.printFlush("Special profiler option for IgProf was indicated by the user: %s"%profilers)
01169                       #Prepare the simpleGenReport arguments for this test:
01170                       IgProfProfilerArgs={
01171                          'perfdir':perfsuitedir,
01172                          'NumEvents':IgProfEvents,
01173                          'candles':IgProfCandles,
01174                          'cmsdriverOptions':cmsdriverOptions,
01175                          'stepOptions':stepOptions,
01176                          'Name':"IgProf",
01177                          'profilers':profilers,
01178                          'bypasshlt':bypasshlt,
01179                          'userInputRootFiles':userInputFile
01180                          }
01181                       #Append the test to the TestsToDo list:
01182                       TestsToDo.append(IgProfProfilerArgs)
01183                       self.printFlush("Appended IgProf test with profiler option %s to the TestsToDo list"%profilers)
01184                    #For the default case (4,5,6,7) we split the tests into 2 jobs since they naturally are 2 cmsRun jobs and for machines with many cores this will
01185                    #make the performance suite run faster.
01186                    else:
01187                       self.printFlush("Splitting the IgProf tests into Perf and Mem to parallelize the cmsRun execution as much as possible:")
01188                       ##PERF##
01189                       #Prepare the simpleGenReport arguments for this test:
01190                       IgProfPerfArgs={
01191                          'perfdir':perfsuitedir,
01192                          'NumEvents':IgProfEvents,
01193                          'candles':IgProfCandles,
01194                          'cmsdriverOptions':cmsdriverOptions,
01195                          'stepOptions':stepOptions,
01196                          'Name':"IgProf_Perf",
01197                          'profilers':profilers,
01198                          'bypasshlt':bypasshlt,
01199                          'userInputRootFiles':userInputFile
01200                          }
01201                       #Append the test to the TestsToDo list:
01202                       TestsToDo.append(IgProfPerfArgs)
01203                       self.printFlush("Appended IgProf PERF test to the TestsToDo list")
01204                       ##MEM##
01205                       #Prepare the simpleGenReport arguments for this test:
01206                       IgProfMemArgs={
01207                          'perfdir':perfsuitedir,
01208                          'NumEvents':IgProfEvents,
01209                          'candles':IgProfCandles,
01210                          'cmsdriverOptions':cmsdriverOptions,
01211                          'stepOptions':stepOptions,
01212                          'Name':"IgProf_Mem",
01213                          'profilers':profilers,
01214                          'bypasshlt':bypasshlt,
01215                          'userInputRootFiles':userInputFile
01216                          }
01217                       #Append the test to the TestsToDo list:
01218                       TestsToDo.append(IgProfMemArgs)
01219                       self.printFlush("Appended IgProf MEM test to the TestsToDo list")
01220                 #The following will be handled in the while loop that handles the starting of the threads:
01221                 #ReportExit=self.simpleGenReport(cpus,perfsuitedir,IgProfEvents,IgProfCandles,cmsdriverOptions,stepOptions,"IgProf",profilers,bypasshlt,userInputFile)
01222                 #FinalExitCode=FinalExitCode+ReportExit
01223                 #Launch eventual Digi Pile Up IgProf too:
01224                if IgProfPUCandles:
01225                    self.printFlush("Preparing IgProf PileUp tests")
01226                    #Special case for IgProf: user could pick with the option --profilers to run only IgProf perf or Mem (or Mem_Total alone etc)
01227                    #So in general we want to be able to split the perf and mem tests...
01228                    #For the case of --profiler option we will run only 1 test (i.e. it will get one core slot until it is done with whatever profiling choosen)
01229                    if profilers:
01230                       self.printFlush("Special profiler option for IgProf was indicated by the user: %s"%profilers)
01231                       #Prepare the simpleGenReport arguments for this test:
01232                       IgProfProfilerPUArgs={
01233                          'perfdir':perfsuitedir,
01234                          'NumEvents':IgProfEvents,
01235                          'candles':IgProfPUCandles,
01236                          'cmsdriverOptions':cmsdriverPUOptions,
01237                          'stepOptions':stepOptions,
01238                          'Name':"IgProf",
01239                          'profilers':profilers,
01240                          'bypasshlt':bypasshlt,
01241                          'userInputRootFiles':userInputFile
01242                          }
01243                       #Append the test to the TestsToDo list:
01244                       TestsToDo.append(IgProfProfilerPUArgs)
01245                       self.printFlush("Appended IgProf PileUp test with profiler option %s to the TestsToDo list"%profilers)
01246                    else:
01247                       self.printFlush("Splitting the IgProf tests into Perf and Mem to parallelize the cmsRun execution as much as possible:")
01248                       ##PERF##
01249                       #Prepare the simpleGenReport arguments for this test:
01250                       IgProfPerfPUArgs={
01251                          'perfdir':perfsuitedir,
01252                          'NumEvents':IgProfEvents,
01253                          'candles':IgProfPUCandles,
01254                          'cmsdriverOptions':cmsdriverPUOptions,
01255                          'stepOptions':stepOptions,
01256                          'Name':"IgProf_Perf",
01257                          'profilers':profilers,
01258                          'bypasshlt':bypasshlt,
01259                          'userInputRootFiles':userInputFile
01260                          }
01261                       #Append the test to the TestsToDo list:
01262                       TestsToDo.append(IgProfPerfPUArgs)
01263                       self.printFlush("Appended IgProf MEM PileUp test to the TestsToDo list")
01264                       ##MEM##
01265                       #Prepare the simpleGenReport arguments for this test:
01266                       IgProfMemPUArgs={
01267                          'perfdir':perfsuitedir,
01268                          'NumEvents':IgProfEvents,
01269                          'candles':IgProfPUCandles,
01270                          'cmsdriverOptions':cmsdriverPUOptions,
01271                          'stepOptions':stepOptions,
01272                          'Name':"IgProf_Mem",
01273                          'profilers':profilers,
01274                          'bypasshlt':bypasshlt,
01275                          'userInputRootFiles':userInputFile
01276                          }
01277                       #Append the test to the TestsToDo list:
01278                       TestsToDo.append(IgProfMemPUArgs)
01279                       self.printFlush("Appended IgProf MEM PileUp test to the TestsToDo list")
01280                if not (IgProfCandles or IgProfPUCandles):
01281                    self.printFlush("A number of events (%s) for IgProf tests was selected, but no candle for regular or pileup tests was selected!"%(IgProfEvents))
01282                
01283                     
01284             #Valgrind tests:
01285             if CallgrindEvents > 0:
01286                if CallgrindCandles:
01287                   self.printFlush("Preparing Callgrind tests")
01288                   CallgrindArgs={
01289                      'perfdir':perfsuitedir,
01290                      'NumEvents':CallgrindEvents,
01291                      'candles':CallgrindCandles,
01292                      'cmsdriverOptions':cmsdriverOptions,
01293                      'stepOptions':stepOptions,
01294                      'Name':"Callgrind",
01295                      'profilers':profilers,
01296                      'bypasshlt':bypasshlt,
01297                      'userInputRootFiles':userInputFile
01298                      }
01299                   #Append the test to the TestsToDo list:
01300                   TestsToDo.append(CallgrindArgs)
01301                   self.printFlush("Appended Callgrind test to the TestsToDo list")
01302                #Launch eventual Digi Pile Up Callgrind too:
01303                if CallgrindPUCandles:
01304                   self.printFlush("Preparing Callgrind PileUp tests")
01305                   CallgrindPUArgs={
01306                      'perfdir':perfsuitedir,
01307                      'NumEvents':CallgrindEvents,
01308                      'candles':CallgrindPUCandles,
01309                      'cmsdriverOptions':cmsdriverPUOptions,
01310                      'stepOptions':stepOptions,
01311                      'Name':"Callgrind",
01312                      'profilers':profilers,
01313                      'bypasshlt':bypasshlt,
01314                      'userInputRootFiles':userInputFile
01315                      }
01316                   #Append the test to the TestsToDo list:
01317                   TestsToDo.append(CallgrindPUArgs)
01318                   self.printFlush("Appended Callgrind PileUp test to the TestsToDo list")
01319                if not (CallgrindCandles or CallgrindPUCandles):
01320                   self.printFlush("A number of events (%s) for Callgrind tests was selected, but no candle for regular or pileup tests was selected!"%(CallgrindEvents))
01321                   
01322             if MemcheckEvents > 0:
01323                if MemcheckCandles:
01324                   self.printFlush("Preparing Memcheck tests")
01325                   MemcheckArgs={
01326                      'perfdir':perfsuitedir,
01327                      'NumEvents':MemcheckEvents,
01328                      'candles':MemcheckCandles,
01329                      'cmsdriverOptions':cmsdriverOptions,
01330                      'stepOptions':stepOptions,
01331                      'Name':"Memcheck",
01332                      'profilers':profilers,
01333                      'bypasshlt':bypasshlt,
01334                      'userInputRootFiles':userInputFile
01335                      }
01336                   #Append the test to the TestsToDo list:
01337                   TestsToDo.append(MemcheckArgs)
01338                   self.printFlush("Appended Memcheck test to the TestsToDo list")
01339                #Launch eventual Digi Pile Up Memcheck too:
01340                if MemcheckPUCandles:
01341                   self.printFlush("Preparing Memcheck PileUp tests")
01342                   MemcheckPUArgs={
01343                      'perfdir':perfsuitedir,
01344                      'NumEvents':MemcheckEvents,
01345                      'candles':MemcheckPUCandles,
01346                      'cmsdriverOptions':cmsdriverPUOptions,
01347                      'stepOptions':stepOptions,
01348                      'Name':"Memcheck",
01349                      'profilers':profilers,
01350                      'bypasshlt':bypasshlt,
01351                      'userInputRootFiles':userInputFile
01352                      }
01353                   #Append the test to the TestsToDo list:
01354                   TestsToDo.append(MemcheckPUArgs)  
01355                   self.printFlush("Appended Memcheck PileUp test to the TestsToDo list")
01356                if not (MemcheckCandles or MemcheckPUCandles):
01357                   self.printFlush("A number of events (%s) for Memcheck tests was selected, but no candle for regular or pileup tests was selected!"%(MemcheckEvents))
01358                   
01359             #Here if there are any IgProf, Callgrind or MemcheckEvents to be run,
01360             #run the infinite loop that submits the PerfTest() threads on the available cores:
01361             if IgProfEvents or CallgrindEvents or MemcheckEvents:
01362                #FIXME:We should consider what behavior makes most sense in case we use the --cores option at this time only the cores=0 care is considered...
01363                self.printFlush("Threading all remaining tests on all %s available cores!"%len(AvailableCores))
01364                self.printDate()
01365                self.logh.flush()
01366                #Save the original AvailableCores list to use it as a test to break the infinite loop:
01367                #While in the regular RelVal use-case it makes sense to use the actual number of cores of the machines, in
01368                #the IB case the AvailableCores will always consist of only 1 single core..
01369                OriginalAvailableCores=list(AvailableCores) #Tricky list copy bug! without the list() OriginalAvalaibleCores would point to AvailableCores!
01370                #Print this out in the log for debugging reasons
01371                self.printFlush("Original available cores list: %s"%AvailableCores)
01372 
01373                #Create a dictionary to keep track of running threads on the various cores:
01374                activePerfTestThreads={}
01375                #Flag for waiting messages:
01376                Waiting=False
01377                while 1:
01378                   #Check if there are tests to run:
01379                   if TestsToDo:
01380                      #Using the Waiting flag to avoid writing this message every 5 seconds in the case
01381                      #of having more tests to do than available cores...
01382                      if not Waiting:
01383                         self.printFlush("Currently %s tests are scheduled to be run:"%len(TestsToDo))
01384                         self.printFlush(TestsToDo)
01385                      #Check the available cores:
01386                      if AvailableCores:
01387                         #Set waiting flag to False since we'll be doing something
01388                         Waiting=False
01389                         self.printFlush("There is/are %s core(s) available"%len(AvailableCores))
01390                         cpu=AvailableCores.pop()
01391                         self.printFlush("Let's use cpu %s"%cpu)
01392                         simpleGenReportArgs=TestsToDo.pop()
01393                         self.printFlush("Let's submit %s test on core %s"%(simpleGenReportArgs['Name'],cpu))
01394                         #Adding a Total timer for each of the threaded tests:
01395                         if simpleGenReportArgs['Name'] not in TimerInfo.keys():
01396                            #if 'TotalTime' not in TimerInfo[simpleGenReportArgs['Name']].keys():
01397                            self.PerfTestTotalTimer=PerfSuiteTimer(start=datetime.datetime.now()) #Start the TimeSize timer
01398                            TimerInfo.update({simpleGenReportArgs['Name']:{'TotalTime':self.PerfTestTotalTimer}}) #Add the TimeSize timer to the dictionary 
01399                         threadToDo=self.simpleGenReportThread(cpu,self,**simpleGenReportArgs) #Need to send self too, so that the thread has access to the PerfSuite.simpleGenReport() function
01400                         self.printFlush("Starting thread %s"%threadToDo)
01401                         ReportExitCode=threadToDo.start()
01402                         self.printFlush("Adding thread %s to the list of active threads"%threadToDo)
01403                         activePerfTestThreads[cpu]=threadToDo
01404                      #If there is no available core, pass, there will be some checking of activeThreads, a little sleep and then another check.
01405                      else:
01406                         pass
01407                   #Test activePerfThreads:
01408                   activeTestNames=[]
01409                   activeTestNamesPU=[]
01410                   for cpu in activePerfTestThreads.keys():
01411                      if activePerfTestThreads[cpu].isAlive():
01412                         #print "%% cpu %s activerPerfTestThreads[cpu] %s activePerfTestThreads[cpu].simpleGenReportArgs['cmsdriverOptions'] %s"%(cpu,activePerfTestThreads[cpu],activePerfTestThreads[cpu].simpleGenReportArgs['cmsdriverOptions'])
01413                         if "--pileup" in activePerfTestThreads[cpu].simpleGenReportArgs['cmsdriverOptions']:
01414                            activeTestNamesPU.append(activePerfTestThreads[cpu].simpleGenReportArgs['Name'])
01415                         else:
01416                            activeTestNames.append(activePerfTestThreads[cpu].simpleGenReportArgs['Name'])
01417                         pass
01418                      elif cpu not in AvailableCores:
01419                         #Set waiting flag to False since we'll be doing something
01420                         Waiting=False
01421                         self.printFlush(time.ctime())
01422                         self.printFlush("%s test, in thread %s is done running on core %s"%(activePerfTestThreads[cpu].simpleGenReportArgs['Name'],activePerfTestThreads[cpu],cpu) )
01423                         self.printFlush("About to append cpu %s to AvailableCores list"%cpu)
01424                         AvailableCores.append(cpu)
01425                         #Eliminate from activeTestNames lists:
01426                         #print activeTestNames
01427                         #print activeTestNamesPU
01428                         #print activePerfTestThreads[cpu].simpleGenReportArgs['Name']
01429                         if "--pileup" in activePerfTestThreads[cpu].simpleGenReportArgs['cmsdriverOptions']:
01430                            try:
01431                               activeTestNamesPU.remove(activePerfTestThreads[cpu].simpleGenReportArgs['Name'])
01432                            except:
01433                               pass
01434                         else:
01435                            try:
01436                               activeTestNames.remove(activePerfTestThreads[cpu].simpleGenReportArgs['Name'])
01437                            except:
01438                               pass
01439                         #Eliminate also from activePErfTestThreads dictionary:
01440                         activePerfTestThreads.pop(cpu)
01441                         #FIXME:
01442                         #Delicate check to stop the timer on the individual threaded test!
01443                         #Need to thik about it still...
01444                   #FIXME:
01445                   #Delicate check to stop the timers on the threaded tests:
01446                   #Check activePerfTestThreads dictionary for "Name" if any name is missing, the total can be stopped for that name.
01447                   #self.PerfTestTotalTimer
01448                   for TestName in ["IgProf_Perf","IgProf_Mem","Memcheck","Valgrind"]:
01449                      if (TestName not in activeTestNames) and (TestName not in activeTestNamesPU) :
01450                         try:
01451                            TimerInfo[TestName]['TotalTime'].set_end(datetime.datetime.now())
01452                         except:
01453                            #print "No %s test was running"%TestName
01454                            pass
01455                   #Buggy if... it seems we don't wait for the running thread to be finished...
01456                   #We should request:
01457                   #-All OriginalAvailableCores should be actually available.
01458                   if not AvailableCores==[] and (set(AvailableCores)==set(range(cmsCpuInfo.get_NumOfCores())) or set(AvailableCores)==set(OriginalAvailableCores)) and not TestsToDo:
01459                      self.printFlush("PHEW! We're done... all TestsToDo are done... at %s "%(self.getDate()))
01460                      #Debug printouts:
01461                      #print "AvailableCores",AvailableCores
01462                      #print "set(AvailableCores)",set(AvailableCores)
01463                      #print "set(range(cmsCpuInfo.get_NumOfCores())",set(range(cmsCpuInfo.get_NumOfCores()))
01464                      #print "OriginalAvailableCores",OriginalAvailableCores
01465                      #print "set(OriginalAvailableCores)",set(OriginalAvailableCores)                                   
01466                      #print "TestsToDo",TestsToDo
01467                      break
01468                   else:
01469                      #Putting the sleep statement first to avoid writing Waiting... before the output of the started thread reaches the log... 
01470                      time.sleep(5)
01471                      #Use Waiting flag to writing 1 waiting message while waiting and avoid having 1 message every 5 seconds...
01472                      if not Waiting:
01473                         self.printFlush(time.ctime())
01474                         self.printFlush("Waiting for tests to be done...")
01475                         sys.stdout.flush()
01476                         Waiting=True
01477             #End of the if for IgProf, Callgrind, Memcheck tests      
01478                   
01479             if benching and not (self._unittest or self._noexec):
01480                 #Ending the performance suite with the cmsScimark benchmarks again:
01481                 for cpu in cpus:
01482                     if cmsScimark > 0:
01483                         self.logh.write("Ending with %s cmsScimark on cpu%s\n"         % (cmsScimark,cpu))
01484                         cmsScimarkFinalTime=PerfSuiteTimer(start=datetime.datetime.now()) #Create the cmsScimark PerfSuiteTimer
01485                         TimerInfo['cmsScimarkTime'].update({'cmsScimarkFinal':cmsScimarkFinalTime}) #Add the cmsScimarkFinalTime information to the general TimerInfo dictionary
01486 
01487                         self.benchmarks(cpu,perfsuitedir,scimark.name,cmsScimark)
01488                         cmsScimarkFinalTime.set_end(datetime.datetime.now()) #Stop the cmsScimarkLarge Initial timer
01489                     if cmsScimarkLarge > 0:
01490                         self.logh.write("Following with %s cmsScimarkLarge on cpu%s\n" % (cmsScimarkLarge,cpu))
01491                         cmsScimarkLargeFinalTime=PerfSuiteTimer(start=datetime.datetime.now()) #Create the cmsScimarkLargePerfSuiteTimer
01492                         TimerInfo['cmsScimarkTime'].update({'cmsScimarkLargeFinal':cmsScimarkLargeFinalTime}) #Add the cmsScimarkLargeFinalTime information to the general TimerInfo dictionary
01493                         self.benchmarks(cpu,perfsuitedir,scimarklarge.name,cmsScimarkLarge,large=True)
01494                         cmsScimarkLargeFinalTime.set_end(datetime.datetime.now()) #Stop the cmsScimarkLarge Initial timer
01495     
01496             if prevrel:
01497                 self.logh.write("Running the regression analysis with respect to %s\n"%getVerFromLog(prevrel))
01498                 self.logh.write(time.ctime(time.time()))
01499                 self.logh.flush()
01500                 
01501                 crr.regressReports(prevrel,os.path.abspath(perfsuitedir),oldRelName = getVerFromLog(prevrel),newRelName=self.cmssw_version)
01502     
01503             #Create a tarball of the work directory
01504             if tarball:
01505                tarballTime=PerfSuiteTimer(start=datetime.datetime.now()) #Create the tarball PerfSuiteTimer
01506                TimerInfo.update({'tarballTime':{'TotalTime':tarballTime}})
01507                # Adding the str(stepOptions to distinguish the tarballs for 1 release
01508                # (GEN->DIGI, L1->RECO will be run in parallel)
01509                
01510                # Cleaning the stepOptions from the --usersteps=:
01511                if "=" in str(stepOptions):
01512                   fileStepOption=str(stepOptions).split("=")[1]
01513                else:
01514                   fileStepOption=str(stepOptions)
01515                if fileStepOption=="":
01516                   fileStepOption="UnknownStep"
01517                # Add the working directory used to avoid overwriting castor files (also put a check...)
01518                fileWorkingDir=os.path.basename(perfsuitedir)
01519                
01520                # Also add the --conditions and --eventcontent options used in the --cmsdriver options since it
01521                # is possible that the same tests will be run with different conditions and/or event content:               
01522                # Parse it out of --cmsdriver option:
01523                fileEventContentOption="UnknownEventContent"
01524                fileConditionsOption="UnknownConditions"
01525                for token in cmsdriverOptions.split("--"):
01526                   if token!='' and 'cmsdriver' not in token:
01527                      if "=" in token:
01528                         fileOption=token.split("=")[0]
01529                         fileOptionValue=token.split("=")[1].strip("'").strip('"')
01530                      else:
01531                         fileOption=token.split()[0]
01532                         fileOptionValue=token.split()[1].strip("'").strip('"')
01533                      if "eventcontent" or "conditions" in fileOption:
01534                         if "eventcontent" in fileOption:
01535                            fileEventContentOption=fileOptionValue
01536                         elif "conditions" in fileOption:
01537                            # check if we are using the autoCond style of flexible conditions
01538                            # if so, expand the condition here so that the file names contain the real conditions
01539                            if "auto:" in fileOptionValue: 
01540                               from Configuration.PyReleaseValidation.autoCond import autoCond
01541                               fileConditionsOption = autoCond[ fileOptionValue.split(':')[1] ]
01542                            else:
01543                               # "old style" conditions, hardcoded values ...
01544                               # FIXME:
01545                               # Should put at least the convention in cmsPerfCommons to know how to parse it...
01546                               # Potential weak point if the conditions tag convention changes...
01547                               if "," in fileOptionValue: #Since 330, conditions don't have FrontierConditions_GlobalTag, in front of them anymore...
01548                                  fileConditionsOption=fileOptionValue.split("::")[0].split(",")[1] #"Backward" compatibility
01549                               else:
01550                                  fileConditionsOption=fileOptionValue.split("::")[0] 
01551                   else: # empty token
01552                      #print "Print this is the token: %s"%token
01553                      pass
01554                   
01555                #self.printFlush("Conditions label to add to the tarball name is %s"%fileConditionsOption)
01556                #self.printFlush("Eventcontent label to add to the tarball name is %s"%fileEventContentOption)
01557                      #FIXME:
01558                      #Could add the allowed event contents in the cmsPerfCommons.py file and use those to match in the command line options... This assumes maintenance of cmsPerfCommons.py
01559 
01560 
01561                #Create a tarball with just the logfiles
01562                subprocess.Popen("ls -R | grep .root > rootFiles",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read()
01563                LogFile = "%s_%s_%s_%s_%s_%s_%s_%s_log.tgz" % (self.cmssw_arch, self.cmssw_version, fileStepOption, fileConditionsOption, fileEventContentOption.split()[0], fileWorkingDir, self.host, self.user)
01564                AbsTarFileLOG = os.path.join(perfsuitedir,LogFile)
01565                tarcmd  = "tar zcfX %s %s %s" %(AbsTarFileLOG, "rootFiles", os.path.join(perfsuitedir,"*"))
01566                self.printFlush("Creating a tarball for the logfiles")
01567                self.printFlush(tarcmd)
01568                self.printFlush(subprocess.Popen(tarcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read())
01569                self.printFlush(subprocess.Popen("rm rootFiles",shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read())               
01570 
01571                fullcastorpathlog=os.path.join(castordir,LogFile)
01572 
01573 
01574                #Create the tarball with the contents of the directory + md5 checksum
01575                TarFile = "%s_%s_%s_%s_%s_%s_%s_%s.tgz" % (self.cmssw_arch, self.cmssw_version, fileStepOption, fileConditionsOption, fileEventContentOption.split()[0], fileWorkingDir, self.host, self.user)
01576                AbsTarFile = os.path.join(perfsuitedir,TarFile)
01577                tarcmd  = "tar -zcf %s %s" %(AbsTarFile, os.path.join(perfsuitedir,"*"))
01578                md5cmd = "md5sum %s" %(AbsTarFile)
01579                self.printFlush("Creating a tarball with the content of the directory")               
01580                self.printFlush(tarcmd)
01581                self.printFlush(md5cmd)               
01582                #FIXME:
01583                #Anything that will be logged after the tar command below will not enter the cmsPerfSuite.log in the tarball (by definition)...
01584                #To remain backward compatible the harvesting script needs to be based on the command above to identify the tarball location.
01585                #Obsolete popen4-> subprocess.Popen
01586                #self.printFlush(os.popen3(tarcmd)[2].read()) #Using popen3 to get only stderr we don't want the whole stdout of tar!
01587                self.printFlush(subprocess.Popen(tarcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read())
01588                md5sum = subprocess.Popen(md5cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.read().split()[0]
01589                self.printFlush("The md5 checksum of the tarball: %s" %(md5sum))
01590                AbsTarFileMD5 = AbsTarFile + ".md5"
01591                md5filecmd = "echo %s > %s" % (md5sum, AbsTarFileMD5)
01592                self.printFlush(subprocess.Popen(md5filecmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read())
01593                                                 
01594                #Archive it on CASTOR
01595                #Before archiving check if it already exist if it does print a message, but do not overwrite, so do not delete it from local dir:
01596                fullcastorpathfile=os.path.join(castordir,TarFile)
01597                fullcastorpathmd5=os.path.join(castordir,TarFile + ".md5")
01598                
01599                checkcastor="nsls  %s" % fullcastorpathfile
01600                #Obsolete os.popen-> subprocess.Popen                
01601                #checkcastorout=os.popen3(checkcastor)[1].read()
01602                checkcastorout=subprocess.Popen(checkcastor,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.read()                
01603                if checkcastorout.rstrip()==fullcastorpathfile:
01604                   castorcmdstderr="File %s is already on CASTOR! Will NOT OVERWRITE!!!"%fullcastorpathfile
01605                else:
01606                   castorcmd="rfcp %s %s" % (AbsTarFile,fullcastorpathfile)
01607                   castormd5cmd="rfcp %s %s" % (AbsTarFileMD5,fullcastorpathmd5)
01608                   castorlogcmd="rfcp %s %s" % (AbsTarFileLOG,fullcastorpathlog)
01609                   self.printFlush(castorcmd)
01610                   self.printFlush(castormd5cmd)
01611                   self.printFlush(castorlogcmd)
01612                   #Obsolete os.popen-> subprocess.Popen
01613                   #castorcmdstderr=os.popen3(castorcmd)[2].read()
01614                   castorcmdstderr=subprocess.Popen(castorcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read()
01615                   subprocess.Popen(castormd5cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read()
01616                   subprocess.Popen(castorlogcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).stderr.read()                  
01617                #Checking the stderr of the rfcp command to copy the tarball (.tgz) on CASTOR:
01618                if castorcmdstderr:
01619                    #If it failed print the stderr message to the log and tell the user the tarball (.tgz) is kept in the working directory
01620                    self.printFlush(castorcmdstderr)
01621                    self.printFlush("Since the CASTOR archiving for the tarball failed the file %s is kept in directory %s"%(TarFile, perfsuitedir))
01622                else:
01623                    #If it was successful then remove the tarball from the working directory:
01624                    self.printFlush("Successfully archived the tarball %s in CASTOR!"%(TarFile))
01625                    self.printFlush("The tarball can be found: %s"%(fullcastorpathfile))
01626                    self.printFlush("The logfile can be found: %s"%(fullcastorpathlog))                   
01627                    self.printFlush("Deleting the local copy of the tarballs")
01628                    rmtarballcmd="rm -Rf %s"%(AbsTarFile)
01629                    rmtarballmd5cmd="rm -Rf %s"%(AbsTarFileMD5)
01630                    rmtarballlogcmd="rm -Rf %s"%(AbsTarFileLOG)
01631                    self.printFlush(rmtarballcmd)
01632                    self.printFlush(rmtarballmd5cmd)
01633                    self.printFlush(rmtarballlogcmd)                   
01634                    #Obsolete os.popen-> subprocess.Popen
01635                    #self.printFlush(os.popen4(rmtarballcmd)[1].read())
01636                    self.printFlush(subprocess.Popen(rmtarballcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read() )
01637                    self.printFlush(subprocess.Popen(rmtarballmd5cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read() )
01638                    self.printFlush(subprocess.Popen(rmtarballlogcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read() )
01639                tarballTime.set_end(datetime.datetime.now())
01640             else:
01641                self.printFlush("Performance Suite directory will not be archived in a tarball since --no_tarball option was chosen")
01642 
01643             #End of script actions!
01644     
01645             #Print a time stamp at the end:
01646             date=time.ctime(time.time())
01647             self.logh.write("Performance Suite finished running at %s on %s in directory %s\n" % (date,self.host,path))
01648             if self.ERRORS == 0:
01649                 self.logh.write("There were no errors detected in any of the log files!\n")
01650             else:
01651                 self.logh.write("ERROR: There were %s errors detected in the log files, please revise!\n" % self.ERRORS)
01652                 #print "No exit code test"
01653                 #sys.exit(1)
01654         except exceptions.Exception, detail:
01655            self.logh.write(str(detail) + "\n")
01656            self.logh.flush()
01657            if not self.logh.isatty():
01658               self.logh.close()
01659            raise
01660         #Add the possibility to send as an email the execution logfile to the user and whoever else interested:
01661         if MailLogRecipients != "": #Basically leave the option to turn it off too.. --mail ""
01662            self.printFlush("Sending email notification for this execution of the performance suite with command:")
01663            sendLogByMailcmd='cat cmsPerfSuite.log |mail -s "Performance Suite finished running on %s" '%self.host + MailLogRecipients
01664            self.printFlush(sendLogByMailcmd)
01665            #Obsolete os.popen-> subprocess.Popen
01666            #self.printFlush(os.popen4(sendLogByMailcmd)[1].read())
01667            self.printFlush(subprocess.Popen(sendLogByMailcmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.read() )
01668         else:
01669            self.printFlush('No email notification will be sent for this execution of the performance suite since option --mail "" was used')
01670         
01671         TotalTime.set_end(datetime.datetime.now())        
01672         self.printFlush("Total Running Time\t%s hrs (%s mins)"%(TotalTime.get_duration()['hours'],TotalTime.get_duration()['minutes']))
01673 
01674         #Dump of the TimerInfo information
01675         #First dump it as a pickleq file...
01676         #Well in order to do so without the complication of serializing a custom class instance need to make the dictionary fully string-made:
01677         TimerInfoStr={}
01678         PerfSuiteTimerInfo=open("PerfSuiteTimerInfo.pkl","wb")
01679         #pickle.dump(TimerInfo,PerfSuiteTimerInfo)
01680         #PerfSuiteTimerInfo.close()
01681         #For now print it at the bottom of the log:
01682         self.logh.write("Test type\tActual Test\tDuration\tStart Time\tEnd Time\n")
01683         for key in TimerInfo.keys():
01684            #self.printFlush(key)
01685            TimerInfoStr.update({key:{}})
01686            for test in TimerInfo[key].keys():
01687               TimerInfoStr[key].update({test:[str(TimerInfo[key][test].get_duration()['hours'])+" hrs ("+str(TimerInfo[key][test].get_duration()['minutes'])+" mins)",TimerInfo[key][test].get_start(),TimerInfo[key][test].get_end()]})
01688               self.logh.write(key+"\t"+test+"\t")
01689               self.logh.write("%s hrs (%s mins)\t"%(TimerInfo[key][test].get_duration()['hours'],TimerInfo[key][test].get_duration()['minutes']))
01690               self.logh.write("%s\t"%TimerInfo[key][test].get_start())
01691               self.logh.write("%s\n"%TimerInfo[key][test].get_end())
01692         pickle.dump(TimerInfoStr,PerfSuiteTimerInfo) 
01693         PerfSuiteTimerInfo.close()
01694         
01695         self.logh.write("Final Performance Suite exit code was %s"%FinalExitCode)
01696         self.logh.flush()
01697         sys.exit(FinalExitCode)
01698     
01699 def main(argv=[__name__]): #argv is a list of arguments.
01700                      #Valid ways to call main with arguments:
01701                      #main(["--cmsScimark",10])
01702                      #main(["-t100"]) #With the caveat that the options.timeSize will be of type string... so should avoid using this!
01703                      #main(["-timeSize,100])
01704                      #Invalid ways:
01705                      #main(["One string with all options"])
01706     
01707     #Let's instatiate the class:
01708     suite=PerfSuite()
01709 
01710     #print suite
01711     #Uncomment this for tests with main() in inteactive python:
01712     #print suite.optionParse(argv)
01713     
01714     PerfSuiteArgs={}
01715     (PerfSuiteArgs['castordir'],
01716      PerfSuiteArgs['TimeSizeEvents'],
01717      PerfSuiteArgs['IgProfEvents'],    
01718      PerfSuiteArgs['CallgrindEvents'],
01719      PerfSuiteArgs['MemcheckEvents'],
01720      PerfSuiteArgs['cmsScimark'],      
01721      PerfSuiteArgs['cmsScimarkLarge'], 
01722      PerfSuiteArgs['cmsdriverOptions'],
01723      PerfSuiteArgs['cmsdriverPUOptions'],
01724      PerfSuiteArgs['stepOptions'],     
01725      PerfSuiteArgs['quicktest'],       
01726      PerfSuiteArgs['profilers'],       
01727      PerfSuiteArgs['cpus'],            
01728      PerfSuiteArgs['cores'],           
01729      PerfSuiteArgs['prevrel'],         
01730      PerfSuiteArgs['bypasshlt'],       
01731      PerfSuiteArgs['runonspare'],      
01732      PerfSuiteArgs['perfsuitedir'],    
01733      PerfSuiteArgs['logfile'],
01734      PerfSuiteArgs['TimeSizeCandles'],
01735      PerfSuiteArgs['IgProfCandles'],
01736      PerfSuiteArgs['CallgrindCandles'],
01737      PerfSuiteArgs['MemcheckCandles'],
01738      PerfSuiteArgs['TimeSizePUCandles'],
01739      PerfSuiteArgs['IgProfPUCandles'],
01740      PerfSuiteArgs['CallgrindPUCandles'],
01741      PerfSuiteArgs['MemcheckPUCandles'],
01742      PerfSuiteArgs['PUInputFile'],
01743      PerfSuiteArgs['userInputFile'],
01744      PerfSuiteArgs['MailLogRecipients'],
01745      PerfSuiteArgs['tarball']
01746      ) = suite.optionParse(argv)
01747 
01748     if not PerfSuiteArgs['logfile'] == None:
01749        if os.path.exists(PerfSuiteArgs['logfile']):
01750           oldlogfile=PerfSuiteArgs['logfile']+"_"+time.strftime("%d-%m-%Y_%H:%M:%S")
01751           #Move old logfile to a file with the same filename plus a timestamp appended
01752           mvOldLogfile=subprocess.Popen("mv %s %s"%(PerfSuiteArgs['logfile'],oldlogfile), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
01753           mvOldLogfileExitCode=mvOldLogfile.wait()
01754           #Finally open the logfile and put the information above in it:
01755           try:
01756              ActualLogfile = open(PerfSuiteArgs['logfile'],"w")
01757              if mvOldLogfileExitCode:
01758                 ActualLogfile.write("Please check what happened: A file named %s existed already and the attempt to move it to %s produced the following output: %s\n"%(PerfSuiteArgs['logfile'],oldlogfile,mvOldLogfile.stdout))
01759              else:
01760                 ActualLogfile.write("***WARNING! A file named %s existed already!\n***It has been moved to %s before starting the current logfile!\n"%(PerfSuiteArgs['logfile'],oldlogfile))
01761           except (OSError, IOError), detail:
01762              ActualLogfile.write("Failed to open the intended logfile %s, detail error:\n%s"%(PerfSuiteArgs['logfile'],detail))
01763               
01764        else:
01765           try:
01766              ActualLogfile = open(PerfSuiteArgs['logfile'],"w")
01767           except (OSError, IOError), detail:
01768              ActualLogfile.write("Failed to open the intended logfile %s, detail error:\n%s"%(PerfSuiteArgs['logfile'],detail))
01769        ActualLogfile.flush()
01770                  
01771     #Three lines to add the exact command line used to call the performance suite directly in the log.
01772     ActualLogfile.write("Performance suite invoked with command line:\n")
01773     cmdline=reduce(lambda x,y:x+" "+y,sys.argv)
01774     ActualLogfile.write(cmdline+"\n")
01775     ActualLogfile.flush()
01776     
01777     #Debug printout that we could silence...
01778     ActualLogfile.write("Initial PerfSuite Arguments:\n")
01779     for key in PerfSuiteArgs.keys():
01780         ActualLogfile.write("%s %s\n"%(key,PerfSuiteArgs[key]))
01781     ActualLogfile.flush()
01782     #print PerfSuiteArgs
01783 
01784     #Handle in here the case of multiple cores and the loading of cores with cmsScimark:
01785     if len(PerfSuiteArgs['cpus']) > 1:
01786         ActualLogfile.write("More than 1 cpu: threading the Performance Suite!\n")
01787         outputdir=PerfSuiteArgs['perfsuitedir']
01788         runonspare=PerfSuiteArgs['runonspare'] #Save the original value of runonspare for cmsScimark stuff
01789         cpus=PerfSuiteArgs['cpus']
01790         if runonspare:
01791             for core in range(PerfSuiteArgs['cores']):
01792                 cmsScimarkLaunch_pslist={}
01793                 if (core not in cpus):
01794                     #self.logh.write("Submitting cmsScimarkLaunch.csh to run on core cpu "+str(core) + "\n")
01795                     ActualLogfile.write("Submitting cmsScimarkLaunch.csh to run on core cpu "+str(core)+"\n")
01796                     subcmd = "cd %s ; cmsScimarkLaunch.csh %s" % (outputdir, str(core))            
01797                     command="taskset -c %s sh -c \"%s\" &" % (str(core), subcmd)
01798                     #self.logh.write(command + "\n")
01799                     ActualLogfile.write(command+"\n")
01800                     #cmsScimarkLaunch.csh is an infinite loop to spawn cmsScimark2 on the other
01801                     #cpus so it makes no sense to try reading its stdout/err
01802                     cmsScimarkLaunch_pslist[core]=subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
01803                     ActualLogfile.write("Spawned %s \n with PID %s"%(command,cmsScimarkLaunch_pslist[core].pid))
01804                     ActualLogfile.flush()
01805         PerfSuiteArgs['runonspare']=False #Set it to false to avoid cmsScimark being spawned by each thread
01806         logfile=PerfSuiteArgs['logfile']
01807         suitethread={}
01808         for cpu in cpus:
01809             #Make arguments "threaded" by setting for each instance of the suite:
01810             #1-A different output (sub)directory
01811             #2-Only 1 core on which to run
01812             #3-Automatically have a logfile... otherwise stdout is lost?
01813             #To be done:[3-A flag for Valgrind not to "thread" itself onto the other cores..]
01814             cpudir = os.path.join(outputdir,"cpu_%s" % cpu)
01815             if not os.path.exists(cpudir):
01816                 os.mkdir(cpudir)
01817             PerfSuiteArgs['perfsuitedir']=cpudir
01818             PerfSuiteArgs['cpus']=[cpu]  #Keeping the name cpus for now FIXME: change it to cpu in the whole code
01819             if PerfSuiteArgs['logfile']:
01820                 PerfSuiteArgs['logfile']=os.path.join(cpudir,os.path.basename(PerfSuiteArgs['logfile']))
01821             else:
01822                 PerfSuiteArgs['logfile']=os.path.join(cpudir,"cmsPerfSuiteThread.log")
01823             #Now spawn the thread with:
01824             suitethread[cpu]=PerfThread(**PerfSuiteArgs)
01825             ActualLogfile.write(suitethread[cpu])
01826             ActualLogfile.write("Launching PerfSuite thread on cpu%s"%cpu)
01827             ActualLogfile.flush()
01828             #print "With arguments:"
01829             #print PerfSuiteArgs
01830             suitethread[cpu].start()
01831             
01832         while reduce(lambda x,y: x or y, map(lambda x: x.isAlive(),suitethread.values())):
01833            try:            
01834               time.sleep(5.0)
01835               sys.stdout.flush()
01836            except (KeyboardInterrupt, SystemExit):
01837               raise
01838         ActualLogfile.write("All PerfSuite threads have completed!\n")
01839         ActualLogfile.flush()
01840 
01841     else: #No threading, just run the performance suite on the cpu core selected
01842         suite.runPerfSuite(**PerfSuiteArgs)
01843     
01844 if __name__ == "__main__":
01845     
01846     main(sys.argv)