1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import glob 6import os 7import subprocess 8import sys 9 10from telemetry.internal.platform import profiler 11 12_PGOSWEEP_EXECUTABLE = 'pgosweep.exe' 13 14 15class WinPGOProfiler(profiler.Profiler): 16 """A profiler that run the Visual Studio PGO utility 'pgosweep.exe' before 17 terminating a browser or a renderer process. 18 """ 19 20 def __init__(self, browser_backend, platform_backend, output_path, state): 21 super(WinPGOProfiler, self).__init__( 22 browser_backend, platform_backend, output_path, state) 23 24 pgosweep_is_in_path = False 25 for entry in os.environ['PATH'].split(os.pathsep): 26 if os.path.exists(os.path.join(entry, _PGOSWEEP_EXECUTABLE)): 27 pgosweep_is_in_path = True 28 break 29 if not pgosweep_is_in_path: 30 raise IOError(2, '%s isn\'t in the current path, run vcvarsall.bat to fix' 31 ' this.' % _PGOSWEEP_EXECUTABLE) 32 33 self._browser_dir = browser_backend.browser_directory 34 self._chrome_child_pgc_counter = self._GetNextProfileIndex('chrome_child') 35 36 def _GetNextProfileIndex(self, dll_name): 37 """Scan the directory containing the DLL |dll_name| to find the next index 38 to use for the profile data files. 39 40 Args: 41 dll_name: The name of the DLL for which we want to get the next index to 42 to use. 43 """ 44 max_index = 0 45 pgc_files = glob.glob(os.path.join(self._browser_dir, 46 '%s!*.pgc' % dll_name)) 47 for pgc_file in pgc_files: 48 max_index = max(max_index, 49 int(os.path.splitext(os.path.split(pgc_file)[1])[0].split('!')[1])) 50 return max_index + 1 51 52 def _RunPGOSweep(self, pid, dll_name, index): 53 """Run the pgosweep utility to gather the profile data of a given process. 54 55 Args: 56 pid: The PID of the process we're interested in. 57 dll_name: The name of the DLL for which we want the profile data. 58 index: The index to use for the profile data file. 59 60 Returns the name of the profile data file. 61 """ 62 pgc_filename = '%s\\%s!%d.pgc' % (self._browser_dir, dll_name, index) 63 subprocess.Popen([_PGOSWEEP_EXECUTABLE, 64 '/pid:%d' % pid, 65 '%s.dll' % dll_name, 66 pgc_filename] 67 ).wait() 68 return pgc_filename 69 70 @classmethod 71 def name(cls): 72 return 'win_pgo_profiler' 73 74 @classmethod 75 def is_supported(cls, browser_type): 76 # This profiler only make sense when doing a Windows build with Visual 77 # Studio (minimal supported version is 2013 Update 2). 78 return sys.platform.startswith('win') 79 80 @classmethod 81 def CustomizeBrowserOptions(cls, browser_type, options): 82 # The sandbox need to be disabled if we want to be able to gather the 83 # profile data. 84 options.AppendExtraBrowserArgs('--no-sandbox') 85 86 def CollectProfile(self): 87 """Collect the profile data for the current processes.""" 88 output_files = [] 89 for pid, output_file in self._GetProcessOutputFileMap().iteritems(): 90 if 'renderer' in output_file: 91 output_files.append(self._RunPGOSweep(pid, 92 'chrome_child', 93 self._chrome_child_pgc_counter)) 94 self._chrome_child_pgc_counter += 1 95 return output_files 96