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