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