Modified: trunk/Tools/Scripts/webkitpy/common/system/executive.py (137705 => 137706)
--- trunk/Tools/Scripts/webkitpy/common/system/executive.py 2012-12-14 04:19:09 UTC (rev 137705)
+++ trunk/Tools/Scripts/webkitpy/common/system/executive.py 2012-12-14 04:30:20 UTC (rev 137706)
@@ -304,6 +304,13 @@
while self.check_running_pid(pid):
time.sleep(0.25)
+ def wait_limited(self, pid, limit_in_seconds=None, check_frequency_in_seconds=None):
+ seconds_left = limit_in_seconds or 10
+ sleep_length = check_frequency_in_seconds or 1
+ while seconds_left > 0 and self.check_running_pid(pid):
+ seconds_left -= sleep_length
+ time.sleep(sleep_length)
+
def _windows_image_name(self, process_name):
name, extension = os.path.splitext(process_name)
if not extension:
@@ -312,6 +319,17 @@
process_name = "%s.exe" % name
return process_name
+ def interrupt(self, pid):
+ interrupt_signal = signal.SIGINT
+ # FIXME: The python docs seem to imply that platform == 'win32' may need to use signal.CTRL_C_EVENT
+ # http://docs.python.org/2/library/signal.html
+ try:
+ os.kill(pid, interrupt_signal)
+ except OSError:
+ # Silently ignore when the pid doesn't exist.
+ # It's impossible for callers to avoid race conditions with process shutdown.
+ pass
+
def kill_all(self, process_name):
"""Attempts to kill processes matching process_name.
Will fail silently if no process are found."""
Modified: trunk/Tools/Scripts/webkitpy/common/system/profiler.py (137705 => 137706)
--- trunk/Tools/Scripts/webkitpy/common/system/profiler.py 2012-12-14 04:19:09 UTC (rev 137705)
+++ trunk/Tools/Scripts/webkitpy/common/system/profiler.py 2012-12-14 04:30:20 UTC (rev 137706)
@@ -37,7 +37,7 @@
def create_profiler(cls, host, executable_path, output_dir, identifier=None):
if host.platform.is_mac():
return IProfiler(host, executable_path, output_dir, identifier)
- return GooglePProf(host, executable_path, output_dir, identifier)
+ return Perf(host, executable_path, output_dir, identifier)
class Profiler(object):
@@ -97,6 +97,49 @@
print ' '.join([self._pprof_path(), self._executable_path, self._output_path])
+class Perf(SingleFileOutputProfiler):
+ def __init__(self, host, executable_path, output_dir, identifier=None):
+ super(Perf, self).__init__(host, executable_path, output_dir, "data", identifier)
+ self._perf_process = None
+ self._pid_being_profiled = None
+
+ def _perf_path(self):
+ # FIXME: We may need to support finding the perf binary in other locations.
+ return 'perf'
+
+ def attach_to_pid(self, pid):
+ assert(not self._perf_process and not self._pid_being_profiled)
+ self._pid_being_profiled = pid
+ cmd = [self._perf_path(), "record", "-g", "-p", pid, "-o", self._output_path]
+ cmd = map(unicode, cmd)
+ self._perf_process = self._host.executive.popen(cmd)
+
+ def _first_ten_lines_of_profile(self, perf_output):
+ match = re.search("^#[^\n]*\n((?: [^\n]*\n){1,10})", perf_output, re.MULTILINE)
+ return match.group(1) if match else None
+
+ def profile_after_exit(self):
+ # Perf doesn't automatically watch the attached pid for death notifications,
+ # so we have to do it for it, and then tell it its time to stop sampling. :(
+ self._host.executive.wait_limited(self._pid_being_profiled, limit_in_seconds=10)
+ perf_exitcode = self._perf_process.poll()
+ if perf_exitcode is None: # This should always be the case, unless perf error'd out early.
+ self._host.executive.interrupt(self._perf_process.pid)
+
+ perf_exitcode = self._perf_process.wait()
+ if perf_exitcode not in (0, -2): # The exit code should always be -2, as we're always interrupting perf.
+ print "'perf record' failed (exit code: %i), can't process results:" % perf_exitcode
+ return
+
+ perf_args = [self._perf_path(), 'report', '-g', 'none', '-i', self._output_path]
+ print " ".join(perf_args)
+ perf_output = self._host.executive.run_command(perf_args)
+ print self._first_ten_lines_of_profile(perf_output)
+
+ print "To view the full profile, run:"
+ print ' '.join([self._perf_path(), 'report', '-i', self._output_path])
+
+
class IProfiler(SingleFileOutputProfiler):
def __init__(self, host, executable_path, output_dir, identifier=None):
super(IProfiler, self).__init__(host, executable_path, output_dir, "dtps", identifier)