Home | History | Annotate | Download | only in profiler
      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 
      5 import signal
      6 import subprocess
      7 import sys
      8 import tempfile
      9 
     10 from telemetry.core import exceptions
     11 from telemetry.core import util
     12 from telemetry.core.platform import profiler
     13 
     14 
     15 class _SingleProcessSampleProfiler(object):
     16   """An internal class for using iprofiler for a given process."""
     17   def __init__(self, pid, output_path):
     18     self._output_path = output_path
     19     self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
     20     self._proc = subprocess.Popen(
     21         ['sample', str(pid), '-mayDie', '-file', self._output_path],
     22         stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
     23     def IsStarted():
     24       stdout = self._GetStdOut()
     25       if 'sample cannot examine process' in stdout:
     26         raise exceptions.ProfilingException(
     27             'Failed to start sample for process %s\n' %
     28             self._output_path.split('.')[1])
     29       return 'Sampling process' in stdout
     30     util.WaitFor(IsStarted, 120)
     31 
     32   def CollectProfile(self):
     33     self._proc.send_signal(signal.SIGINT)
     34     exit_code = self._proc.wait()
     35     try:
     36       if exit_code:
     37         raise Exception(
     38             'sample failed with exit code %d. Output:\n%s' % (
     39             exit_code, self._GetStdOut()))
     40     finally:
     41       self._proc = None
     42       self._tmp_output_file.close()
     43 
     44     print 'To view the profile, run:'
     45     print '  open -a TextEdit %s' % self._output_path
     46 
     47     return self._output_path
     48 
     49   def _GetStdOut(self):
     50     self._tmp_output_file.flush()
     51     try:
     52       with open(self._tmp_output_file.name) as f:
     53         return f.read()
     54     except IOError:
     55       return ''
     56 
     57 
     58 class SampleProfiler(profiler.Profiler):
     59 
     60   def __init__(self, browser_backend, platform_backend, output_path, state):
     61     super(SampleProfiler, self).__init__(
     62         browser_backend, platform_backend, output_path, state)
     63     process_output_file_map = self._GetProcessOutputFileMap()
     64     self._process_profilers = []
     65     for pid, output_file in process_output_file_map.iteritems():
     66       if '.utility' in output_file:
     67         # The utility process may not have been started by Telemetry.
     68         # So we won't have permissing to profile it
     69         continue
     70       self._process_profilers.append(
     71           _SingleProcessSampleProfiler(pid, output_file))
     72 
     73   @classmethod
     74   def name(cls):
     75     return 'sample'
     76 
     77   @classmethod
     78   def is_supported(cls, browser_type):
     79     if sys.platform != 'darwin':
     80       return False
     81     if browser_type == 'any':
     82       return True
     83     return (not browser_type.startswith('android') and
     84             not browser_type.startswith('cros'))
     85 
     86   def CollectProfile(self):
     87     output_paths = []
     88     for single_process in self._process_profilers:
     89       output_paths.append(single_process.CollectProfile())
     90     return output_paths
     91