CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
List of all members | Public Member Functions | Public Attributes | Private Member Functions
cmsRelvalreport.Profile Class Reference

Public Member Functions

def __init__
 
def make_profile
 
def make_report
 

Public Attributes

 command
 
 profile_name
 
 profiler
 

Private Member Functions

def _profile_edmsize
 
def _profile_igprof
 
def _profile_Memcheck_Valgrind
 
def _profile_None
 
def _profile_SimpleMem_Parser
 
def _profile_Timereport_Parser
 
def _profile_Timing_Parser
 
def _profile_valgrindfce
 
def _save_output
 

Detailed Description

Class that represents the procedure of performance report creation

Definition at line 268 of file cmsRelvalreport.py.

Constructor & Destructor Documentation

def cmsRelvalreport.Profile.__init__ (   self,
  command,
  profiler,
  profile_name 
)

Definition at line 272 of file cmsRelvalreport.py.

273  def __init__(self,command,profiler,profile_name):
274  self.command=command
275  self.profile_name=profile_name
276  self.profiler=profiler

Member Function Documentation

def cmsRelvalreport.Profile._profile_edmsize (   self)
private
Launch edm size profiler

Definition at line 355 of file cmsRelvalreport.py.

References runall.testit.command, cmsRelvalreport.Profile.command, cmsRelvalreport.execute(), cmsRelvalreport.Profile.profile_name, and cmsRelvalreport.Profile.profiler.

Referenced by cmsRelvalreport.Profile.make_profile().

356  def _profile_edmsize(self):
357  '''
358  Launch edm size profiler
359  '''
360  # In this case we replace the name to be clear
361  input_rootfile=self.command
362 
363  # Skim the content if requested!
364  if '.' in self.profiler:
365 
366  clean_profiler_name,options=self.profiler.split('.')
367  content,nevts=options.split(',')
368  outfilename='%s_%s.root'%(os.path.basename(self.command)[:-6],content)
369  oldpypath=os.environ['PYTHONPATH']
370  os.environ['PYTHONPATH']+=':%s' %MAKESKIMDRIVERDIR
371  execute('%s -i %s -o %s --outputcommands %s -n %s' %(MAKESKIMDRIVER,
372  self.command,
373  outfilename,
374  content,
375  nevts))
376  os.environ['PYTHONPATH']=oldpypath
377  #execute('rm %s' %outfilename)
378  self.command=outfilename
379  self.profiler=clean_profiler_name
380 
381 
382  profiler_line='edmEventSize -o %s -d %s'\
383  %(self.profile_name,self.command)
384 
385  return execute(profiler_line)
def cmsRelvalreport.Profile._profile_igprof (   self)
private
IgProf profiler launcher.

Definition at line 327 of file cmsRelvalreport.py.

References runall.testit.command, cmsRelvalreport.Profile.command, cmsRelvalreport.execute(), cmsRelvalreport.Profile.profile_name, and cmsRelvalreport.Profile.profiler.

Referenced by cmsRelvalreport.Profile.make_profile().

328  def _profile_igprof(self):
329  '''
330  IgProf profiler launcher.
331  '''
332  profiler_line=''
333 
334  igprof_options='igprof -d -t %s ' \
335  %EXECUTABLE # IgProf profile not general only for CMSRUN!
336 
337  # To handle Igprof memory and performance profiler in one function
338  if self.profiler=='IgProf_perf':
339  igprof_options+='-pp '
340  elif self.profiler=='IgProf_mem':
341  igprof_options+='-mp '
342  else:
343  raise ('Unknown IgProf flavour: %s !'%self.profiler)
344  igprof_options+='-z -o %s' %(self.profile_name)
345 
346  # If we are using cmsDriver we should use the prefix switch
347  if EXECUTABLE=='cmsRun' and self.command.find('cmsDriver.py')!=-1:
348  profiler_line='%s --prefix "%s"' %(self.command,igprof_options)
349  else:
350  profiler_line='%s %s' %(igprof_options, self.command)
351 
352  return execute(profiler_line)
def cmsRelvalreport.Profile._profile_Memcheck_Valgrind (   self)
private
Valgrind Memcheck profile launcher

Definition at line 388 of file cmsRelvalreport.py.

References runall.testit.command, cmsRelvalreport.Profile.command, cmsRelvalreport.execute(), and cmsRelvalreport.Profile.profile_name.

Referenced by cmsRelvalreport.Profile.make_profile().

389  def _profile_Memcheck_Valgrind(self):
390  '''
391  Valgrind Memcheck profile launcher
392  '''
393  profiler_line=''
394  #Adding cms suppression of useless messages (cmsvgsupp)
395  #Removing leak-checking (done with igprof)
396  #'--leak-check=no '+\ (no is the default)
397  #'--show-reachable=yes '+\
398  #'--track-fds=yes '
399  #Adding xml logging
400  valgrind_options='time valgrind --tool=memcheck `cmsvgsupp` '+\
401  '--num-callers=20 '+\
402  '--xml=yes '+\
403  '--xml-file=%s.xml '%self.profile_name.replace(",","-")[:-4]
404 
405  # If we are using cmsDriver we should use the prefix switch
406  if EXECUTABLE=='cmsRun' and self.command.find('cmsDriver.py')!=-1:
407  #Replacing 2>&1 |tee with >& in the shell command to preserve the return code significance:
408  # using tee return would be 0 even if the command failed before the pipe:
409  profiler_line='%s --prefix "%s" >& %s' %(self.command,valgrind_options,self.profile_name)
410 
411  else:
412  profiler_line='%s %s >& %s' %(valgrind_options,self.command,self.profile_name)
413  #'--trace-children=yes '+\
return execute(profiler_line)
def cmsRelvalreport.Profile._profile_None (   self)
private
Just Run the command!

Definition at line 445 of file cmsRelvalreport.py.

References runall.testit.command, cmsRelvalreport.Profile.command, cmsRelvalreport.execute(), and cmsRelvalreport.Profile.make_report().

Referenced by cmsRelvalreport.Profile.make_profile().

446  def _profile_None(self):
447  '''
448  Just Run the command!
449  '''
450  return execute(self.command)
def cmsRelvalreport.Profile._profile_SimpleMem_Parser (   self)
private

Definition at line 421 of file cmsRelvalreport.py.

References cmsRelvalreport.Profile._save_output().

Referenced by cmsRelvalreport.Profile.make_profile().

422  def _profile_SimpleMem_Parser(self):
423  return self._save_output()
def cmsRelvalreport.Profile._profile_Timereport_Parser (   self)
private

Definition at line 416 of file cmsRelvalreport.py.

References cmsRelvalreport.Profile._save_output().

Referenced by cmsRelvalreport.Profile.make_profile().

417  def _profile_Timereport_Parser(self):
418  return self._save_output()
def cmsRelvalreport.Profile._profile_Timing_Parser (   self)
private

Definition at line 426 of file cmsRelvalreport.py.

References cmsRelvalreport.Profile._save_output().

Referenced by cmsRelvalreport.Profile.make_profile().

427  def _profile_Timing_Parser(self):
428  return self._save_output()
def cmsRelvalreport.Profile._profile_valgrindfce (   self)
private
Valgrind profile launcher.

Definition at line 304 of file cmsRelvalreport.py.

References runall.testit.command, cmsRelvalreport.Profile.command, cmsRelvalreport.execute(), and cmsRelvalreport.Profile.profile_name.

Referenced by cmsRelvalreport.Profile.make_profile().

305  def _profile_valgrindfce(self):
306  '''
307  Valgrind profile launcher.
308  '''
309  # ValgrindFCE needs a special library to run
310  os.environ["VALGRIND_LIB"]=VFCE_LIB
311 
312  profiler_line=''
313  valgrind_options= 'time valgrind '+\
314  '--tool=callgrind '+\
315  '--fce=%s ' %(self.profile_name)
316 
317  # If we are using cmsDriver we should use the prefix switch
318  if EXECUTABLE=='cmsRun' and self.command.find('cmsDriver.py')!=-1:
319  profiler_line='%s --prefix "%s"' %(self.command,valgrind_options)
320 
321  else:
322  profiler_line='%s %s' %(valgrind_options,self.command)
323  #'--trace-children=yes '+\
324 
325  return execute(profiler_line)
def cmsRelvalreport.Profile._save_output (   self)
private
Save the output of cmsRun on a file!

Definition at line 431 of file cmsRelvalreport.py.

References runall.testit.command, cmsRelvalreport.Profile.command, cmsRelvalreport.execute(), and cmsRelvalreport.Profile.profile_name.

Referenced by cmsRelvalreport.Profile._profile_SimpleMem_Parser(), cmsRelvalreport.Profile._profile_Timereport_Parser(), and cmsRelvalreport.Profile._profile_Timing_Parser().

432  def _save_output(self):
433  '''
434  Save the output of cmsRun on a file!
435  '''
436 # # a first maquillage about the profilename:
437 # if self.profile_name[-4:]!='.log':
438 # self.profile_name+='.log'
439  #Replacing 2>&1 |tee with >& in the shell command to preserve the return code significance:
440  # using tee return would be 0 even if the command failed before the pipe:
441  profiler_line='%s >& %s' %(self.command,self.profile_name)
442  return execute(profiler_line)
def cmsRelvalreport.Profile.make_profile (   self)
Launch the right function according to the profiler name.

Definition at line 279 of file cmsRelvalreport.py.

References cmsRelvalreport.Profile._profile_edmsize(), cmsRelvalreport.Profile._profile_igprof(), cmsRelvalreport.Profile._profile_Memcheck_Valgrind(), cmsRelvalreport.Profile._profile_None(), cmsRelvalreport.Profile._profile_SimpleMem_Parser(), cmsRelvalreport.Profile._profile_Timereport_Parser(), cmsRelvalreport.Profile._profile_Timing_Parser(), cmsRelvalreport.Profile._profile_valgrindfce(), and cmsRelvalreport.Profile.profiler.

280  def make_profile(self):
281  '''
282  Launch the right function according to the profiler name.
283  '''
284  if self.profiler=='ValgrindFCE':
285  return self._profile_valgrindfce()
286  elif self.profiler.find('IgProf')!=-1:
287  return self._profile_igprof()
288  elif self.profiler.find('Edm_Size')!=-1:
289  return self._profile_edmsize()
290  elif self.profiler=='Memcheck_Valgrind':
291  return self._profile_Memcheck_Valgrind()
292  elif self.profiler=='Timereport_Parser':
293  return self._profile_Timereport_Parser()
294  elif self.profiler=='Timing_Parser':
295  return self._profile_Timing_Parser()
296  elif self.profiler=='SimpleMem_Parser':
297  return self._profile_SimpleMem_Parser()
298  elif self.profiler=='':
299  return self._profile_None()
300  elif self.profiler=='None': #adding this for the case of candle ASCII file non-profiling commands
301  return self._profile_None()
302  else:
raise('No %s profiler found!' %self.profiler)
def cmsRelvalreport.Profile.make_report (   self,
  fill_db = False,
  db_name = None,
  tmp_dir = None,
  outdir = None,
  IgProf_option = None,
  metastring = None 
)
Make a performance report with CMSSW scripts for CMSSW internal profiling (Timing/SimpleMemoryCheck) and Memcheck, PR2 for edmEventSize and Callgrind (NOTE PR2 is not supported anymore and is not currently in the CMSSW external, running froma privat AFS!), igprof-analyse for all IgProf profiling.

Definition at line 459 of file cmsRelvalreport.py.

References cmsRelvalreport.execute(), align_tpl.filter, cmsRelvalreport.logger(), cmsRelvalreport.Profile.profile_name, cmsRelvalreport.Profile.profiler, linker.replace(), and split.

Referenced by cmsRelvalreport.Profile._profile_None().

460  metastring=None):
461  '''
462  Make a performance report with CMSSW scripts for CMSSW internal profiling (Timing/SimpleMemoryCheck) and Memcheck, PR2 for edmEventSize and Callgrind (NOTE PR2 is not supported anymore and is not currently in the CMSSW external, running froma privat AFS!), igprof-analyse for all IgProf profiling.
463  '''
464 
465  if outdir==None or outdir==self.profile_name:
466  outdir=self.profile_name+'_outdir'
467 
468  #Create the directory where the report will be stored:
469  if not os.path.exists(outdir) and not fill_db and not IgProf_option:
470  #Added an IgProf condition to avoid the creation of a directory that we will not need anymore, since we will put all info in the filenames
471  execute('mkdir %s' %outdir)
472 
473  if fill_db:
474  db_option='-a'
475  if not os.path.exists(db_name):
476  db_option='-A'
477 
478  # temp in the local dir for PR
479  tmp_switch=''
480  if tmp_dir!='':
481  execute('mkdir %s' %tmp_dir)
482  tmp_switch=' -t %s' %tmp_dir
483 
484  #Handle the various profilers:
485 
486  #####################################################################
487 
488  # Profiler is ValgrindFCE:
489  if self.profiler=='ValgrindFCE':
490  perfreport_command=''
491  # Switch for filling the db
492  if not fill_db:
493  os.environ["PERFREPORT_PATH"]='%s/' %PERFREPORT2_PATH
494  perfreport_command='%s %s -ff -i %s -o %s' %(PR2,
495  tmp_switch,
496  self.profile_name,
497  outdir)
498  else:
499  os.environ["PERFREPORT_PATH"]='%s/' %PERFREPORT3_PATH
500  perfreport_command='%s %s -n5000 -u%s -ff -m \'scram_cmssw_version_string,%s\' -i %s %s -o %s' \
501  %(PR3,
502  tmp_switch,
503  PR3_PRODUCER_PLUGIN,
504  metastring,
505  self.profile_name,
506  db_option,
507  db_name)
508  return execute(perfreport_command)
509 
510  #####################################################################
511 
512  # Profiler is IgProf:
513  if self.profiler.find('IgProf')!=-1:
514  #First the case of IgProf PERF and MEM reporting:
515  if not 'ANALYSE' in IgProf_option:
516  #Switch to the use of igprof-analyse instead of PerfReport!
517  #Will use the ANALYSE case for regressions between early event dumps and late event dumps of the profiles
518  #Following Andreas suggestion, add the number of events for the EndOfJob report
519  NumberOfEvents=self.command.split()[3] #FIXME: this is quite hardcoded... but should be stable...
520  sqlite_outputfile=self.profile_name.split(".")[0].replace(IgProfProfiles[IgProf_option[0]],IgProf_option[0])+'___'+NumberOfEvents+'_EndOfJob.sql3'
521  logger("Executing the report of the IgProf end of job profile")
522  exit=execute('igprof-analyse --sqlite -d -v -g -r %s %s | sqlite3 %s'%(IgProf_option[0],self.profile_name,sqlite_outputfile))
523  return exit
524  #Then the "ANALYSE" case that we want to use to add to the same directories (Perf, MemTotal, MemLive)
525  #also some other analyses and in particular:
526  #1-the first 7 lines of the ASCII analysis of the IgProf profile dumps (total of the counters)
527  #2-the dump at different event numbers,
528  #3-the diff between the first and last dump,
529  #4-the counters grouped by library using regexp at the last dump:
530  else: #We use IgProf Analysis
531  #Set the IgProfCounter from the ANALYSE.MEM_TOT style IgProf_option
532  #print IgProf_option
533  IgProfCounter=IgProf_option[1]
534  #Add here the handling of the new IgProf.N.gz files so that they will get preserved and not overwritten:
535  logger("Looking for IgProf intermediate event profile dumps")
536  #Check if there are IgProf.N.gz dump files:
537  IgProfDumps=glob.glob("IgProf.*.gz")
538  #in case there are none check if they have already been mv'd to another name to avoid overwriting
539  #(MEM_LIVE usually re-uses MEM_TOTAL, so the IgProf.N.gz files will already have a MemTotal name...)
540  if not IgProfDumps:
541  localFiles=os.listdir('.')
542  IgProfDumpProfilesPrevious=re.compile(r"\w+.\d+.gz")
543  IgProfDumps=filter(lambda x: IgProfDumpProfilesPrevious.search(x),localFiles)
544  #Now if there are dumps execute the following analyses:
545  if IgProfDumps:
546  IgProfDumps.sort()
547  logger("Found the following IgProf intermediate event profile dumps:")
548  logger(IgProfDumps)
549  FirstDumpEvent=9999999
550  LastDumpEvent=0
551  exit=0
552  for dump in IgProfDumps:
553  if "___" in dump:
554  DumpEvent=dump.split(".")[0].split("___")[-1]
555  else:
556  DumpEvent=dump.split(".")[1]
557  #New naming convention using ___ as separator
558  DumpedProfileName=self.profile_name[:-3]+"___"+DumpEvent+".gz"
559  if dump.startswith("IgProf"):
560  execute('mv %s %s'%(dump,DumpedProfileName))
561  #Keep a tab of the first and last dump event for the diff analysis
562  if int(DumpEvent) < FirstDumpEvent:
563  FirstDumpEvent = int(DumpEvent)
564  if int(DumpEvent) > LastDumpEvent:
565  LastDumpEvent = int(DumpEvent)
566  #Eliminating the ASCII analysis to get the totals, Giulio will provide this information in igprof-navigator with a special query
567  #First type of analysis: dump first 7 lines of ASCII analysis:
568  #logger("Executing the igprof-analyse analysis to dump the ASCII 7 lines output with the totals for the IgProf counter")
569  #exit=execute('%s -o%s -i%s -t%s' %(IGPROFANALYS,outdir,DumpedProfileName,"ASCII"))
570  #Second type of analysis: dump the report in sqlite format to be ready to be browsed with igprof-navigator
571  logger("Executing the igprof-analyse analysis saving into igprof-navigator browseable SQLite3 format")
572  #exit=exit+execute('%s -o%s -i%s -t%s' %(IGPROFANALYS,outdir,DumpedProfileName,"SQLite3"))
573  #Execute all types of analyses available with the current profile (using the dictionary IgProfProfile):
574  #To avoid this we should use a further input in SimulationCandles.txt IgProfMem.ANALYSE.MEM_TOTAL maybe the cleanest solution.
575  #for IgProfile in IgProfCounters.keys():
576  # if DumpedProfileName.find(IgProfile)>0:
577  # for counter in IgProfCounters[IgProfile]:
578  #Check that the file does not exist:
579  #if not os.path.exists(DumpedProfileName.split(".")[0].replace(IgProfProfiles[counter],counter)+".sql3"):
580  exit=exit+execute('%s -c%s -i%s -t%s' %(IGPROFANALYS,IgProfCounter,DumpedProfileName,"SQLite3"))
581  #else:
582  # print "File %s already exists will not process profile"%DumpedProfileName.split(".")[0].replace(IgProfProfiles[counter],counter)+".sql3"
583  #FIXME:
584  #Issue with multiple profiles in the same dir: assuming Perf and Mem will always be in separate dirs
585  #Potential ssue with multiple steps?
586  #Adapting to new igprof naming scheme:
587  FirstDumpProfile=self.profile_name[:-3]+"___"+str(FirstDumpEvent)+".gz"
588  LastDumpProfile=self.profile_name[:-3]+"___"+str(LastDumpEvent)+".gz"
589  #Third type of analysis: execute the diff analysis:
590  #Check there are at least 2 IgProf intermediate event dump profiles to do a regression!
591  if len(IgProfDumps)>1:
592  logger("Executing the igprof-analyse regression between the first IgProf profile dump and the last one")
593  #for IgProfile in IgProfCounters.keys():
594  # if DumpedProfileName.find(IgProfile)>0:
595  # IgProfCounter=IgProfCounters[IgProfile]
596  exit=exit+execute('%s -c%s -i%s -r%s' %(IGPROFANALYS,IgProfCounter,LastDumpProfile,FirstDumpProfile))
597  else:
598  logger("CANNOT execute any regressions: not enough IgProf intermediate event profile dumps!")
599  #Fourth type of analysis: execute the grouped by library igprof-analyse:
600  logger("Executing the igprof-analyse analysis merging the results by library via regexp and saving the result in igprof-navigator browseable SQLite3 format")
601  #for IgProfile in IgProfCounters.keys():
602  # if DumpedProfileName.find(IgProfile)>0:
603  # IgProfCounter=IgProfCounters[IgProfile]
604  exit=exit+execute('%s -c%s -i%s --library' %(IGPROFANALYS,IgProfCounter,LastDumpProfile))
605  #If they are not there at all (no dumps)
606  else:
607  logger("No IgProf intermediate event profile dumps found!")
608  exit=0
609 
610  return exit
611 
612 
613  #####################################################################
614 
615  # Profiler is EdmSize:
616  if 'Edm_Size' in self.profiler:
617  perfreport_command=''
618  if not fill_db:
619  os.environ["PERFREPORT_PATH"]='%s/' \
620  %PERFREPORT2_PATH
621  perfreport_command='%s %s -fe -i %s -o %s' \
622  %(PR2,
623  tmp_switch,
624  self.profile_name,
625  outdir)
626  else:
627  os.environ["PERFREPORT_PATH"]='%s/' \
628  %PERFREPORT3_PATH
629  perfreport_command='%s %s -n5000 -u%s -fe -i %s -a -o %s' \
630  %(PR3,
631  tmp_switch,
632  PR3_PRODUCER_PLUGIN,
633  self.profile_name,
634  db_name)
635 
636  return execute(perfreport_command)
637 
638  #FIXME: probably need to move this somewhere else now that we use return statements
639  if tmp_dir!='':
640  execute('rm -r %s' %tmp_dir)
641 
642  #####################################################################
643 
644  # Profiler is Valgrind Memcheck
645  if self.profiler=='Memcheck_Valgrind':
646  # Three pages will be produced:
647  os.environ['PERL5LIB']=PERL5_LIB
648  report_coordinates=(VMPARSER,self.profile_name,outdir)
649  # Copy the Valgrind Memcheck parser style file in the outdir
650  copyStyleFile='cp -pR %s %s'%(VMPARSERSTYLE,outdir)
651  execute(copyStyleFile)
652  report_commands=('%s --preset +prod,-prod1 %s > %s/edproduce.html'\
653  %report_coordinates,
654  '%s --preset +prod1 %s > %s/esproduce.html'\
655  %report_coordinates,
656  '%s -t beginJob %s > %s/beginjob.html'\
657  %report_coordinates)
658  exit=0
659  for command in report_commands:
660  exit= exit + execute(command)
661  return exit
662  #####################################################################
663 
664  # Profiler is TimeReport parser
665 
666  if self.profiler=='Timereport_Parser':
667  return execute('%s %s %s' %(TIMEREPORTPARSER,self.profile_name,outdir))
668 
669  #####################################################################
670 
671  # Profiler is Timing Parser
672 
673  if self.profiler=='Timing_Parser':
674  return execute('%s -i %s -o %s' %(TIMINGPARSER,self.profile_name,outdir))
675 
676 
677  #####################################################################
678 
679  # Profiler is Simple memory parser
680 
681  if self.profiler=='SimpleMem_Parser':
682  return execute('%s -i %s -o %s' %(SIMPLEMEMPARSER,self.profile_name,outdir))
683 
684  #####################################################################
685 
686  # no profiler
687 
688  if self.profiler=='' or self.profiler=='None': #Need to catch the None case, since otherwise we get no return code (crash for pre-requisite step running).
689  return 0 #Used to be pass, but we need a return 0 to handle exit code properly!
def replace
Definition: linker.py:10
tuple filter
USE THIS FOR SKIMMED TRACKS process.p = cms.Path(process.hltLevel1GTSeed*process.skimming*process.offlineBeamSpot*process.TrackRefitter2) OTHERWISE USE THIS.
Definition: align_tpl.py:86
double split
Definition: MVATrainer.cc:139

Member Data Documentation

cmsRelvalreport.Profile.command

Definition at line 273 of file cmsRelvalreport.py.

Referenced by cmsRelvalreport.Profile._profile_edmsize(), cmsRelvalreport.Profile._profile_igprof(), cmsRelvalreport.Profile._profile_Memcheck_Valgrind(), cmsRelvalreport.Profile._profile_None(), cmsRelvalreport.Profile._profile_valgrindfce(), and cmsRelvalreport.Profile._save_output().

cmsRelvalreport.Profile.profile_name

Definition at line 274 of file cmsRelvalreport.py.

Referenced by cmsRelvalreport.Profile._profile_edmsize(), cmsRelvalreport.Profile._profile_igprof(), cmsRelvalreport.Profile._profile_Memcheck_Valgrind(), cmsRelvalreport.Profile._profile_valgrindfce(), cmsRelvalreport.Profile._save_output(), and cmsRelvalreport.Profile.make_report().

cmsRelvalreport.Profile.profiler

Definition at line 275 of file cmsRelvalreport.py.

Referenced by cmsRelvalreport.Profile._profile_edmsize(), cmsRelvalreport.Profile._profile_igprof(), cmsRelvalreport.Profile.make_profile(), and cmsRelvalreport.Profile.make_report().