| # Copyright 2013 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import os |
| import signal |
| import sys |
| |
| from telemetry.core import exceptions |
| from telemetry.core import util |
| from telemetry.internal.platform import profiler |
| |
| try: |
| import pexpect # pylint: disable=import-error |
| except ImportError: |
| pass |
| |
| |
| class _SingleProcessIprofilerProfiler(object): |
| """An internal class for using iprofiler for a given process.""" |
| def __init__(self, pid, output_path): |
| self._output_path = output_path |
| output_dir = os.path.dirname(self._output_path) |
| output_file = os.path.basename(self._output_path) |
| self._proc = pexpect.spawn( |
| 'iprofiler', ['-timeprofiler', '-T', '300', '-a', str(pid), |
| '-d', output_dir, '-o', output_file], |
| timeout=300) |
| while True: |
| if self._proc.getecho(): |
| output = self._proc.readline().strip() |
| if not output: |
| continue |
| if 'iprofiler: Profiling process' in output: |
| break |
| print output |
| self._proc.interact(escape_character='\x0d') |
| if 'Failed to authorize rights' in output: |
| raise exceptions.ProfilingException( |
| 'Failed to authorize rights for iprofiler\n') |
| if 'iprofiler error' in output: |
| raise exceptions.ProfilingException( |
| 'Failed to start iprofiler for process %s\n' % |
| self._output_path.split('.')[1]) |
| self._proc.write('\x0d') |
| print |
| def Echo(): |
| return self._proc.getecho() |
| util.WaitFor(Echo, timeout=5) |
| |
| def CollectProfile(self): |
| self._proc.kill(signal.SIGINT) |
| try: |
| self._proc.wait() |
| except pexpect.ExceptionPexpect: |
| pass |
| finally: |
| self._proc = None |
| |
| print 'To view the profile, run:' |
| print ' open -a Instruments %s.dtps' % self._output_path |
| return self._output_path |
| |
| |
| class IprofilerProfiler(profiler.Profiler): |
| |
| def __init__(self, browser_backend, platform_backend, output_path, state): |
| super(IprofilerProfiler, self).__init__( |
| browser_backend, platform_backend, output_path, state) |
| process_output_file_map = self._GetProcessOutputFileMap() |
| self._process_profilers = [] |
| for pid, output_file in process_output_file_map.iteritems(): |
| if '.utility' in output_file: |
| # The utility process may not have been started by Telemetry. |
| # So we won't have permissing to profile it |
| continue |
| self._process_profilers.append( |
| _SingleProcessIprofilerProfiler(pid, output_file)) |
| |
| @classmethod |
| def name(cls): |
| return 'iprofiler' |
| |
| @classmethod |
| def is_supported(cls, browser_type): |
| if sys.platform != 'darwin': |
| return False |
| if browser_type == 'any': |
| return True |
| return (not browser_type.startswith('android') and |
| not browser_type.startswith('cros')) |
| |
| def CollectProfile(self): |
| output_files = [] |
| for single_process in self._process_profilers: |
| output_files.append(single_process.CollectProfile()) |
| return output_files |