CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_5_3_13_patch3/src/Validation/Performance/scripts/cmsPerfStripChart.py

Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 
00003 import os, sys
00004 try: import simplejson as json
00005 except ImportError: import json
00006 
00007 
00008 def get_min_error(list):
00009     min = 1e20 
00010     for node in list:
00011         value = node['average'] - node['error']
00012         if value < min:
00013              min = value
00014     return min
00015 
00016 def get_max_error(list):
00017     max = -1
00018     for node in list:
00019         value = node['average'] + node['error']
00020         if value > max:
00021              max = value
00022     return max
00023 
00024 def operate(timelog, memlog, json_f, num):
00025 
00026     import re
00027     import commands
00028     import ROOT
00029     from datetime import datetime
00030 
00031     script_name=os.path.basename(__file__)
00032 
00033     # Open files and store the lines.
00034     timefile=open(timelog, 'r')
00035     timelog_lines=timefile.readlines()
00036     timefile.close()
00037 
00038     memfile=open(memlog, 'r')
00039     memlog_lines=memfile.readlines()
00040     memfile.close()
00041  
00042     # Get average, uncertainty of average and maximum rss.
00043     max_rss=average=error=' '
00044     i=0
00045     while i<len(timelog_lines):
00046         line=timelog_lines[i]
00047         if 'Uncertainty of Average Time' in line:
00048             line=line[:-1]
00049             line_list=line.split(' ')
00050             average=line_list[5]
00051             error=line_list[7]
00052         i+=1
00053     i=0
00054     while i<len(memlog_lines):
00055         line=memlog_lines[i]
00056         if 'Maximum rss' in line:
00057             line=line[:-1]
00058             line_list=line.split(' ')
00059             max_rss=line_list[3]
00060             break
00061         i+=1
00062 
00063     # Get Integration Build's identifier
00064     IB=os.path.basename(commands.getoutput("echo $CMSSW_BASE"))
00065 
00066     # Check if log files were parsed properly...
00067     # and if the IB is valid using regular expressions.
00068     try: 
00069         # regex for a float
00070         regex="^\d+\.?\d*$"
00071         if average == ' ' or re.match(regex, average) is None:
00072             raise RuntimeError('Could not parse \"' + timelog + '\" properly. ' +\
00073                                'Check if Average Time is defined correctly.')
00074         if error == ' ' or re.match(regex, error) is None:
00075             raise RuntimeError('Could not parse \"' + timelog + '\" properly. ' +\
00076                                'Check if Uncertainty of Average Time is defined correctly.')
00077         if max_rss == ' ' or re.match(regex, max_rss) is None:
00078             raise RuntimeError('Could not parse \"' + memlog + '\" properly. ' +\
00079                                ' Check if Maximum rss is defined correct.')
00080  
00081        # regex for dates 'YYYY-MM-DD-HHMM'
00082         regex = '(19|20|21)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]'+\
00083                 '[0-9]|3[01])-([01][0-9]|2[0-4])([0-5][0-9])$'
00084         if re.search(regex, IB) is None:
00085             raise RuntimeError('Not a valid IB. Valid IB: ' +\
00086                                '[CMSSW_X_X_X_YYYY-MM-DD-HHMM]')
00087     except Exception, err:
00088         sys.stderr.write(script_name + ': Error: ' + str(err) + '\n')
00089         return 2
00090     
00091     # Open for getting the data.
00092     json_db=open(json_f, "r")
00093     dict=json.load(json_db)
00094     json_db.close()
00095 
00096     # Get the data to be stored and check if already exists.
00097     ib_list=IB.split('_')
00098     cmsrelease=ib_list[0] + '_' + ib_list[1] +\
00099                '_' + ib_list[2] + '_' + ib_list[3]
00100     data={"IB" : ib_list[4], "average" : float(average), "error" : float(error), "max_rss" : float(max_rss)}
00101 
00102     if data in dict["strips"]:
00103         sys.stderr.write(script_name + ": Warning: Entry already exists " +\
00104                                        "in json file and will not be stored! " +\
00105                                        "Only the strip charts will be created.\n")
00106     else:
00107         dict["strips"].append(data)
00108         print 'Storing entry to \"' + json_f +\
00109               '\" file with attribute values:\n' +\
00110               'IB=' + IB + '\naverage=' + average +\
00111               '\nUncertainty of average=' + error +'\nmax_rss=' + max_rss
00112         # Store the data in json file.
00113         json_db = open(json_f, "w+")
00114         json.dump(dict, json_db, indent=2)
00115         json_db.close()
00116         print 'File \"%s\" was updated successfully!' % json_f
00117 
00118     # Change to datetime type (helpful for sorting).
00119     for record in dict["strips"]:
00120         time_list = record['IB'].split('-')
00121         d = datetime(int(time_list[0]), int(time_list[1]),  
00122                      int(time_list[2]), int(time_list[3][0:2]), 
00123                      int(time_list[3][2:]))
00124         record['IB'] = d
00125 
00126     # Sort the list.
00127     list = sorted(dict["strips"], key=lambda k : k['IB'], reverse=True)
00128  
00129     # Check if there are NUM entries.
00130     if num > len(list):
00131         new_num = len(list)
00132         sys.stderr.write(script_name + ': Warning: There are less than ' +\
00133                          str(num) + ' entries in json file. Changed number to ' +\
00134                          str(new_num) + '.\n')
00135         num = new_num
00136 
00137     # The histograms.
00138     ROOT.gROOT.SetStyle("Plain")
00139     outdir='.'
00140 
00141     # Save in file
00142     rootfilename=outdir + '/histograms.root'
00143     myfile=ROOT.TFile(rootfilename, 'RECREATE')  
00144 
00145     # Average time histogram.
00146     histo1=ROOT.TH1F("AveCPU per IB", "Ave CPU per IB", num, 0., num)
00147     histo1.SetTitle(cmsrelease + ": Showing last " + str(num) + " IB's")
00148     histo1.SetName('avecpu_histo')
00149 
00150     # Maximum rss histogram.
00151     histo2=ROOT.TH1F("Max rrs per IB", "Max rss per IB", num, 0., num)
00152     histo2.SetTitle(cmsrelease + ": Showing last " + str(num) + " IB's")
00153     histo2.SetName('maxrss_histo')
00154 
00155     # fill in the histograms
00156 
00157     for i in range(num): 
00158         datime = list[i]['IB'].__format__('%Y-%b-%d %H:%M')
00159         average = list[i]['average']
00160         max_rss = list[i]['max_rss']
00161         error = list[i]['error']
00162       
00163         histo1.GetXaxis().SetBinLabel(num-i, datime)
00164         histo1.SetBinContent(num-i, average)   
00165         histo1.SetBinError(num-i, error)
00166         histo2.GetXaxis().SetBinLabel(num-i, datime)
00167         histo2.SetBinContent(num-i, max_rss)
00168 
00169     histo1.SetStats(0)   
00170     histo1.GetYaxis().SetTitle("Average CPU time")
00171     histo1.GetYaxis().SetTitleOffset(1.8)
00172     histo1.GetXaxis().SetTitle("Integration Build")  
00173     histo1.GetXaxis().SetTitleOffset(4.) 
00174     histo1.GetXaxis().CenterTitle()
00175     histo1.GetXaxis().LabelsOption('v')    
00176     # Histo1 - Set limits on the Y-axis
00177     min = get_min_error(list)
00178     max = get_max_error(list)
00179     interval = max - min
00180     min = min-interval*0.1
00181     max = max+interval*0.1
00182     histo1.GetYaxis().SetRangeUser(min, max)
00183 
00184     histo2.SetStats(0)
00185     histo2.GetYaxis().SetTitle("Maximum rss")
00186     histo2.GetYaxis().SetTitleOffset(1.8)
00187     histo2.GetXaxis().SetTitle("Integration Build")
00188     histo2.GetXaxis().SetTitleOffset(4.)
00189     histo2.GetXaxis().CenterTitle()
00190     histo2.GetXaxis().LabelsOption('v')
00191 
00192     # Draw and save!
00193 
00194     ave_canvas = ROOT.TCanvas(cmsrelease + '_average_canvas')
00195     ave_canvas.SetGridy()
00196     ave_canvas.SetBottomMargin(0.28)
00197     ave_canvas.SetLeftMargin(0.18)
00198     ave_canvas.cd()
00199     # Histo1 - draw line
00200     histo1.SetLineColor(2)
00201     histo1.SetLineWidth(2)
00202     histo1.DrawCopy("HISTO L")
00203     # Histo1 - draw errors and markers
00204     histo1.SetLineColor(1)
00205     histo1.SetLineStyle(2)
00206     histo1.SetLineWidth(1)
00207     histo1.SetMarkerStyle(8)
00208     histo1.SetMarkerSize(.6)
00209     histo1.SetMarkerColor(1)
00210     histo1.Draw("E1P SAME")
00211     ave_canvas.Print(outdir + "/average_cpu_histo.png","png")
00212 
00213     rss_canvas = ROOT.TCanvas(cmsrelease + '_maxrss_canvas')
00214     rss_canvas.SetGridy()
00215     rss_canvas.SetBottomMargin(0.28)
00216     rss_canvas.SetLeftMargin(0.18)
00217     rss_canvas.cd()
00218     # Histo2 - draw line
00219     histo2.SetLineColor(2)
00220     histo2.SetLineWidth(2)
00221     histo2.DrawCopy("L")
00222     # Histo2 - draw markers    
00223     histo2.SetMarkerStyle(8)
00224     histo2.SetMarkerSize(.6)
00225     histo2.SetMarkerColor(1)
00226     histo2.Draw("P SAME")
00227     rss_canvas.Print(outdir + "/maximum_rss_histo.png","png")
00228 
00229     # write them on file
00230     histo1.Write()
00231     ave_canvas.Write()
00232     histo2.Write()
00233     rss_canvas.Write()
00234 
00235   
00236 ########################################################################################### 
00237 
00238 if __name__ == '__main__':
00239 
00240     import optparse, stat
00241  
00242     ################################
00243     # Definition of command usage. #
00244     ################################
00245     script_name= os.path.basename(__file__)
00246     usage = script_name + ' <options> -t TIMELOG -m MEMLOG'
00247     parser = optparse.OptionParser(usage)
00248     parser.add_option('-t', '--timelog',
00249                       action='store',
00250                       dest='timelog',
00251                       default='',
00252                       metavar='TIMELOG',
00253                       help='input file TIMELOG, the output of cmsTiming_parser.py')
00254     parser.add_option('-m', '--memlog',
00255                       action='store',
00256                       dest='memlog',
00257                       default='',
00258                       metavar='MEMLOG', 
00259                       help='input file MEMLOG, the output of cmsSimplememchecker_parser.py')
00260     parser.add_option('-j', '--jsonfile',
00261                      action='store',
00262                      dest='json_f',
00263                      default='strips.json',
00264                      metavar='FILE.JSON',
00265                      help='the .json file database')
00266     parser.add_option('-n', type='int',
00267                       action='store',
00268                       dest='num',
00269                       default='30',
00270                       metavar='NUM',
00271                       help='last NUM entries to be printed in the strip charts. Default is 30.')
00272     (options, args) = parser.parse_args()
00273 
00274     ######################################
00275     # Some error handling for the usage. #
00276     ######################################
00277     if options.timelog == '' or\
00278        options.memlog == '':
00279         sys.exit('%s: Missing file operands!\n' % script_name+\
00280                  'Type %s --help for more information!' % script_name)
00281     if not os.path.exists(options.timelog) or\
00282        not os.path.exists(options.memlog):
00283         sys.exit('%s: Error: Not present file(s)!' % script_name)
00284 
00285     #############################################
00286     # Validity of .json file-database.          #
00287     #############################################
00288  
00289     # The format that the json file must have:  
00290     format = "\n  {  \"strips\" :\n"   +\
00291              "    [\n      {\"IB\" : \"XXX_XXX\", \"average\" : M, \"error\" : E \"max_rss\" : N},\n" +\
00292              "        .........................................\n" +\
00293              "    ]\n"+\
00294              "  }\n"
00295 
00296     # Acquire locking the database file.
00297     
00298     json_db = open(options.json_f, "r+")
00299     try:
00300         # -check if the json file is empty; if yes, create a new database upon it
00301         if os.stat(options.json_f)[stat.ST_SIZE] == 0:
00302             sys.stderr.write(script_name + ': Warning: File \"' + options.json_f +\
00303                              '\" is empty. A new database will be created upon it.\n')
00304             json_db.write("{\n  \"strips\" : [\n  ]\n}\n")
00305             json_db.seek(0, 0)
00306 
00307         # -check if file loads as a valid json
00308         dict = json.load(json_db)
00309 
00310         # -check if strips key is there.(Look format above!)
00311         dict["strips"]
00312 
00313         # -check if value of strips is type of list
00314         if not isinstance(dict["strips"], list):
00315             raise Exception
00316 
00317         # -check if the list has valid elements
00318         if dict["strips"]:
00319             for item in dict["strips"]:
00320                 if not set(['IB', 'average', 'error', 'max_rss']).issubset(item):
00321                     raise KeyError
00322     except ValueError: 
00323         sys.exit(script_name + ': Error: Not a valid json file! Please, check the format:\n' + format)
00324     except KeyError:
00325         sys.exit(script_name + ': Error: Invalid format in the json file! Check it here:\n' + format)
00326     finally:
00327         json_db.close()
00328 
00329     ####################
00330     # Start operation. #
00331     ####################
00332 
00333     # sys.exit() used in order to return an exit code to shell, in case of error
00334     sys.exit(operate(options.timelog, options.memlog, options.json_f, options.num))
00335