blob: d9a2fac09c0dc748ed7017e9c6b0e3bf88dc90a8 [file] [log] [blame]
# 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 logging
import os
import subprocess
import sys
import tempfile
from telemetry.core import exceptions
from telemetry.internal.platform import profiler
from telemetry.internal.platform.profiler import android_profiling_helper
class _SingleProcessVTuneProfiler(object):
"""An internal class for using vtune for a given process."""
def __init__(self, pid, output_file, browser_backend, platform_backend):
self._pid = pid
self._browser_backend = browser_backend
self._platform_backend = platform_backend
self._output_file = output_file
self._tmp_output_file = tempfile.NamedTemporaryFile('w', 0)
cmd = ['amplxe-cl', '-collect', 'hotspots',
'-target-pid', str(pid), '-r', self._output_file]
self._is_android = platform_backend.GetOSName() == 'android'
if self._is_android:
cmd += ['-target-system', 'android']
self._proc = subprocess.Popen(
cmd, stdout=self._tmp_output_file, stderr=subprocess.STDOUT)
def CollectProfile(self):
if 'renderer' in self._output_file:
try:
self._platform_backend.GetCommandLine(self._pid)
except exceptions.ProcessGoneException:
logging.warning('Renderer was swapped out during profiling. '
'To collect a full profile rerun with '
'"--extra-browser-args=--single-process"')
subprocess.call(['amplxe-cl', '-command', 'stop', '-r', self._output_file])
exit_code = self._proc.wait()
try:
# 1: amplxe: Error: Cannot find a running process with the specified ID.
# Provide a valid PID.
if exit_code not in (0, 1):
raise Exception(
'amplxe-cl failed with exit code %d. Output:\n%s' % (exit_code,
self._GetStdOut()))
finally:
self._tmp_output_file.close()
if exit_code:
# The renderer process was swapped out. Now that we made sure VTune has
# stopped, return without further processing the invalid profile.
return self._output_file
if self._is_android:
required_libs = \
android_profiling_helper.GetRequiredLibrariesForVTuneProfile(
self._output_file)
device = self._browser_backend.device
symfs_root = os.path.dirname(self._output_file)
android_profiling_helper.CreateSymFs(device,
symfs_root,
required_libs,
use_symlinks=True)
logging.info('Resolving symbols in profile.')
subprocess.call(['amplxe-cl', '-finalize', '-r', self._output_file,
'-search-dir', symfs_root])
print 'To view the profile, run:'
print ' amplxe-gui %s' % self._output_file
return self._output_file
def _GetStdOut(self):
self._tmp_output_file.flush()
try:
with open(self._tmp_output_file.name) as f:
return f.read()
except IOError:
return ''
class VTuneProfiler(profiler.Profiler):
def __init__(self, browser_backend, platform_backend, output_path, state):
super(VTuneProfiler, self).__init__(
browser_backend, platform_backend, output_path, state)
process_output_file_map = self._GetProcessOutputFileMap()
self._process_profilers = []
has_renderer = False
for pid, output_file in process_output_file_map.iteritems():
if 'renderer' in output_file:
has_renderer = True
break
for pid, output_file in process_output_file_map.iteritems():
if has_renderer:
if not 'renderer' in output_file:
continue
elif not 'browser0' in output_file:
continue
self._process_profilers.append(
_SingleProcessVTuneProfiler(pid, output_file, browser_backend,
platform_backend))
@classmethod
def name(cls):
return 'vtune'
@classmethod
def is_supported(cls, browser_type):
if sys.platform != 'linux2':
return False
if browser_type.startswith('cros'):
return False
try:
proc = subprocess.Popen(['amplxe-cl', '-version'],
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE)
proc.communicate()
if proc.returncode != 0:
return False
if browser_type.startswith('android'):
# VTune checks if 'su' is available on the device.
proc = subprocess.Popen(['adb', 'shell', 'su', '-c', 'id'],
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE)
return 'not found' not in proc.communicate()[0]
return True
except OSError:
return False
@classmethod
def CustomizeBrowserOptions(cls, browser_type, options):
options.AppendExtraBrowserArgs([
'--no-sandbox',
'--allow-sandbox-debugging',
])
def CollectProfile(self):
print 'Processing profile, this will take a few minutes...'
output_files = []
for single_process in self._process_profilers:
output_files.append(single_process.CollectProfile())
return output_files