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