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