1# Copyright 2013 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 os 6import signal 7import sys 8 9from telemetry.core import exceptions 10from telemetry.core import util 11from telemetry.internal.platform import profiler 12 13try: 14 import pexpect # pylint: disable=import-error 15except ImportError: 16 pass 17 18 19class _SingleProcessIprofilerProfiler(object): 20 """An internal class for using iprofiler for a given process.""" 21 def __init__(self, pid, output_path): 22 self._output_path = output_path 23 output_dir = os.path.dirname(self._output_path) 24 output_file = os.path.basename(self._output_path) 25 self._proc = pexpect.spawn( 26 'iprofiler', ['-timeprofiler', '-T', '300', '-a', str(pid), 27 '-d', output_dir, '-o', output_file], 28 timeout=300) 29 while True: 30 if self._proc.getecho(): 31 output = self._proc.readline().strip() 32 if not output: 33 continue 34 if 'iprofiler: Profiling process' in output: 35 break 36 print output 37 self._proc.interact(escape_character='\x0d') 38 if 'Failed to authorize rights' in output: 39 raise exceptions.ProfilingException( 40 'Failed to authorize rights for iprofiler\n') 41 if 'iprofiler error' in output: 42 raise exceptions.ProfilingException( 43 'Failed to start iprofiler for process %s\n' % 44 self._output_path.split('.')[1]) 45 self._proc.write('\x0d') 46 print 47 def Echo(): 48 return self._proc.getecho() 49 util.WaitFor(Echo, timeout=5) 50 51 def CollectProfile(self): 52 self._proc.kill(signal.SIGINT) 53 try: 54 self._proc.wait() 55 except pexpect.ExceptionPexpect: 56 pass 57 finally: 58 self._proc = None 59 60 print 'To view the profile, run:' 61 print ' open -a Instruments %s.dtps' % self._output_path 62 return self._output_path 63 64 65class IprofilerProfiler(profiler.Profiler): 66 67 def __init__(self, browser_backend, platform_backend, output_path, state): 68 super(IprofilerProfiler, self).__init__( 69 browser_backend, platform_backend, output_path, state) 70 process_output_file_map = self._GetProcessOutputFileMap() 71 self._process_profilers = [] 72 for pid, output_file in process_output_file_map.iteritems(): 73 if '.utility' in output_file: 74 # The utility process may not have been started by Telemetry. 75 # So we won't have permissing to profile it 76 continue 77 self._process_profilers.append( 78 _SingleProcessIprofilerProfiler(pid, output_file)) 79 80 @classmethod 81 def name(cls): 82 return 'iprofiler' 83 84 @classmethod 85 def is_supported(cls, browser_type): 86 if sys.platform != 'darwin': 87 return False 88 if browser_type == 'any': 89 return True 90 return (not browser_type.startswith('android') and 91 not browser_type.startswith('cros')) 92 93 def CollectProfile(self): 94 output_files = [] 95 for single_process in self._process_profilers: 96 output_files.append(single_process.CollectProfile()) 97 return output_files 98