1926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# Copyright (C) 2012 Google Inc. All rights reserved. 2926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# 3926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# Redistribution and use in source and binary forms, with or without 4926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# modification, are permitted provided that the following conditions are 5926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# met: 6926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# 7926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# * Redistributions of source code must retain the above copyright 8926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# notice, this list of conditions and the following disclaimer. 9926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# * Redistributions in binary form must reproduce the above 10926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# copyright notice, this list of conditions and the following disclaimer 11926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# in the documentation and/or other materials provided with the 12926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# distribution. 13926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# * Neither the Google name nor the names of its 14926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# contributors may be used to endorse or promote products derived from 15926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# this software without specific prior written permission. 16926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# 17926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 29926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)import logging 30926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)import re 31926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)import itertools 32926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 33926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)_log = logging.getLogger(__name__) 34926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 35926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 36926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class ProfilerFactory(object): 37926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) @classmethod 38926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def create_profiler(cls, host, executable_path, output_dir, profiler_name=None, identifier=None): 39926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profilers = cls.profilers_for_platform(host.platform) 40926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if not profilers: 41926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return None 42926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profiler_name = profiler_name or cls.default_profiler_name(host.platform) 43926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profiler_class = next(itertools.ifilter(lambda profiler: profiler.name == profiler_name, profilers), None) 44926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if not profiler_class: 45926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return None 46926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return profilers[0](host, executable_path, output_dir, identifier) 47926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 48926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) @classmethod 49926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def default_profiler_name(cls, platform): 50926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profilers = cls.profilers_for_platform(platform) 51926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return profilers[0].name if profilers else None 52926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 53926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) @classmethod 54926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def profilers_for_platform(cls, platform): 55926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # GooglePProf requires TCMalloc/google-perftools, but is available everywhere. 56926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profilers_by_os_name = { 57926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 'mac': [IProfiler, Sample, GooglePProf], 58926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 'linux': [Perf, GooglePProf], 59926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # Note: freebsd, win32 have no profilers defined yet, thus --profile will be ignored 60926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # by default, but a profiler can be selected with --profiler=PROFILER explicitly. 61926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) } 62926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return profilers_by_os_name.get(platform.os_name, []) 63926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 64926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 65926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class Profiler(object): 66926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # Used by ProfilerFactory to lookup a profiler from the --profiler=NAME option. 67926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) name = None 68926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 69926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def __init__(self, host, executable_path, output_dir, identifier=None): 70926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._host = host 71926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._executable_path = executable_path 72926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._output_dir = output_dir 73926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._identifier = "test" 74926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._host.filesystem.maybe_make_directory(self._output_dir) 75926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 76926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def adjusted_environment(self, env): 77926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return env 78926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 79926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def attach_to_pid(self, pid): 80926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) pass 81926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 82926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def profile_after_exit(self): 83926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) pass 84926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 85926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 86926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class SingleFileOutputProfiler(Profiler): 87926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def __init__(self, host, executable_path, output_dir, output_suffix, identifier=None): 88926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) super(SingleFileOutputProfiler, self).__init__(host, executable_path, output_dir, identifier) 89926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # FIXME: Currently all reports are kept as test.*, until we fix that, search up to 1000 names before giving up. 90926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._output_path = self._host.workspace.find_unused_filename(self._output_dir, self._identifier, output_suffix, search_limit=1000) 91926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) assert(self._output_path) 92926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 93926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 94926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class GooglePProf(SingleFileOutputProfiler): 95926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) name = 'pprof' 96926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 97926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def __init__(self, host, executable_path, output_dir, identifier=None): 98926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) super(GooglePProf, self).__init__(host, executable_path, output_dir, "pprof", identifier) 99926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 100926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def adjusted_environment(self, env): 101926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) env['CPUPROFILE'] = self._output_path 102926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return env 103926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 104926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def _first_ten_lines_of_profile(self, pprof_output): 105926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) match = re.search("^Total:[^\n]*\n((?:[^\n]*\n){0,10})", pprof_output, re.MULTILINE) 106926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return match.group(1) if match else None 107926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 108926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def _pprof_path(self): 109926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # FIXME: We should have code to find the right google-pprof executable, some Googlers have 110926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # google-pprof installed as "pprof" on their machines for them. 111926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return '/usr/bin/google-pprof' 112926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 113926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def profile_after_exit(self): 114926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # google-pprof doesn't check its arguments, so we have to. 115926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if not (self._host.filesystem.exists(self._output_path)): 116926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "Failed to gather profile, %s does not exist." % self._output_path 117926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return 118926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 119926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) pprof_args = [self._pprof_path(), '--text', self._executable_path, self._output_path] 120926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) profile_text = self._host.executive.run_command(pprof_args) 121926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "First 10 lines of pprof --text:" 122926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print self._first_ten_lines_of_profile(profile_text) 123926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "http://google-perftools.googlecode.com/svn/trunk/doc/cpuprofile.html documents output." 124926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print 125926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "To interact with the the full profile, including produce graphs:" 126926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print ' '.join([self._pprof_path(), self._executable_path, self._output_path]) 127926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 128926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 129926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class Perf(SingleFileOutputProfiler): 130926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) name = 'perf' 131926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 132926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def __init__(self, host, executable_path, output_dir, identifier=None): 133926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) super(Perf, self).__init__(host, executable_path, output_dir, "data", identifier) 134926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._perf_process = None 135926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._pid_being_profiled = None 136926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 137926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def _perf_path(self): 138926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # FIXME: We may need to support finding the perf binary in other locations. 139926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return 'perf' 140926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 141926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def attach_to_pid(self, pid): 142926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) assert(not self._perf_process and not self._pid_being_profiled) 143926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._pid_being_profiled = pid 144926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) cmd = [self._perf_path(), "record", "--call-graph", "--pid", pid, "--output", self._output_path] 145926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._perf_process = self._host.executive.popen(cmd) 146926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 147926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def _first_ten_lines_of_profile(self, perf_output): 148926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) match = re.search("^#[^\n]*\n((?: [^\n]*\n){1,10})", perf_output, re.MULTILINE) 149926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return match.group(1) if match else None 150926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 151926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def profile_after_exit(self): 152926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # Perf doesn't automatically watch the attached pid for death notifications, 153926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # so we have to do it for it, and then tell it its time to stop sampling. :( 154926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._host.executive.wait_limited(self._pid_being_profiled, limit_in_seconds=10) 155926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) perf_exitcode = self._perf_process.poll() 156926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if perf_exitcode is None: # This should always be the case, unless perf error'd out early. 157926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._host.executive.interrupt(self._perf_process.pid) 158926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 159926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) perf_exitcode = self._perf_process.wait() 160926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if perf_exitcode not in (0, -2): # The exit code should always be -2, as we're always interrupting perf. 161926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "'perf record' failed (exit code: %i), can't process results:" % perf_exitcode 162926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) return 163926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 164926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) perf_args = [self._perf_path(), 'report', '--call-graph', 'none', '--input', self._output_path] 165926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "First 10 lines of 'perf report --call-graph=none':" 166926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 167926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print " ".join(perf_args) 168926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) perf_output = self._host.executive.run_command(perf_args) 169926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print self._first_ten_lines_of_profile(perf_output) 170926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 171926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print "To view the full profile, run:" 172926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print ' '.join([self._perf_path(), 'report', '-i', self._output_path]) 173926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) print # An extra line between tests looks nicer. 174926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 175926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 176926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class Sample(SingleFileOutputProfiler): 177926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) name = 'sample' 178926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 179926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def __init__(self, host, executable_path, output_dir, identifier=None): 180926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) super(Sample, self).__init__(host, executable_path, output_dir, "txt", identifier) 181926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._profiler_process = None 182926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 183926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def attach_to_pid(self, pid): 184926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) cmd = ["sample", pid, "-mayDie", "-file", self._output_path] 185926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._profiler_process = self._host.executive.popen(cmd) 186926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 187926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def profile_after_exit(self): 188926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._profiler_process.wait() 189926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 190926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 191926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)class IProfiler(SingleFileOutputProfiler): 192926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) name = 'iprofiler' 193926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 194926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def __init__(self, host, executable_path, output_dir, identifier=None): 195926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) super(IProfiler, self).__init__(host, executable_path, output_dir, "dtps", identifier) 196926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._profiler_process = None 197926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 198926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def attach_to_pid(self, pid): 199926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # FIXME: iprofiler requires us to pass the directory separately 200926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # from the basename of the file, with no control over the extension. 201926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) fs = self._host.filesystem 202926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) cmd = ["iprofiler", "-timeprofiler", "-a", pid, 203926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) "-d", fs.dirname(self._output_path), "-o", fs.splitext(fs.basename(self._output_path))[0]] 204926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # FIXME: Consider capturing instead of letting instruments spam to stderr directly. 205926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._profiler_process = self._host.executive.popen(cmd) 206926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 207926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def profile_after_exit(self): 208926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # It seems like a nicer user experiance to wait on the profiler to exit to prevent 209926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) # it from spewing to stderr at odd times. 210926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._profiler_process.wait() 211