Home | History | Annotate | Download | only in profile_chrome
      1 # Copyright 2014 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 json
      6 import os
      7 import re
      8 import time
      9 
     10 from profile_chrome import controllers
     11 
     12 from devil.android import device_errors
     13 from devil.android.sdk import intent
     14 
     15 
     16 _HEAP_PROFILE_MMAP_PROPERTY = 'heapprof.mmap'
     17 
     18 class ChromeTracingController(controllers.BaseController):
     19   def __init__(self, device, package_info,
     20                categories, ring_buffer, trace_memory=False):
     21     controllers.BaseController.__init__(self)
     22     self._device = device
     23     self._package_info = package_info
     24     self._categories = categories
     25     self._ring_buffer = ring_buffer
     26     self._logcat_monitor = self._device.GetLogcatMonitor()
     27     self._trace_file = None
     28     self._trace_interval = None
     29     self._trace_memory = trace_memory
     30     self._is_tracing = False
     31     self._trace_start_re = \
     32        re.compile(r'Logging performance trace to file')
     33     self._trace_finish_re = \
     34        re.compile(r'Profiler finished[.] Results are in (.*)[.]')
     35 
     36   def __repr__(self):
     37     return 'chrome trace'
     38 
     39   @staticmethod
     40   def GetCategories(device, package_info):
     41     with device.GetLogcatMonitor() as logmon:
     42       device.BroadcastIntent(intent.Intent(
     43           action='%s.GPU_PROFILER_LIST_CATEGORIES' % package_info.package))
     44       try:
     45         json_category_list = logmon.WaitFor(
     46             re.compile(r'{"traceCategoriesList(.*)'), timeout=5).group(0)
     47       except device_errors.CommandTimeoutError:
     48         raise RuntimeError('Performance trace category list marker not found. '
     49                            'Is the correct version of the browser running?')
     50 
     51     record_categories = set()
     52     disabled_by_default_categories = set()
     53     json_data = json.loads(json_category_list)['traceCategoriesList']
     54     for item in json_data:
     55       for category in item.split(','):
     56         if category.startswith('disabled-by-default'):
     57           disabled_by_default_categories.add(category)
     58         else:
     59           record_categories.add(category)
     60 
     61     return list(record_categories), list(disabled_by_default_categories)
     62 
     63   def StartTracing(self, interval):
     64     self._trace_interval = interval
     65     self._logcat_monitor.Start()
     66     start_extras = {'categories': ','.join(self._categories)}
     67     if self._ring_buffer:
     68       start_extras['continuous'] = None
     69     self._device.BroadcastIntent(intent.Intent(
     70         action='%s.GPU_PROFILER_START' % self._package_info.package,
     71         extras=start_extras))
     72 
     73     if self._trace_memory:
     74       self._device.EnableRoot()
     75       self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 1)
     76 
     77     # Chrome logs two different messages related to tracing:
     78     #
     79     # 1. "Logging performance trace to file"
     80     # 2. "Profiler finished. Results are in [...]"
     81     #
     82     # The first one is printed when tracing starts and the second one indicates
     83     # that the trace file is ready to be pulled.
     84     try:
     85       self._logcat_monitor.WaitFor(self._trace_start_re, timeout=5)
     86       self._is_tracing = True
     87     except device_errors.CommandTimeoutError:
     88       raise RuntimeError(
     89           'Trace start marker not found. Possible causes: 1) Is the correct '
     90           'version of the browser running? 2) Is the browser already launched?')
     91 
     92   def StopTracing(self):
     93     if self._is_tracing:
     94       self._device.BroadcastIntent(intent.Intent(
     95           action='%s.GPU_PROFILER_STOP' % self._package_info.package))
     96       self._trace_file = self._logcat_monitor.WaitFor(
     97           self._trace_finish_re, timeout=120).group(1)
     98       self._is_tracing = False
     99     if self._trace_memory:
    100       self._device.SetProp(_HEAP_PROFILE_MMAP_PROPERTY, 0)
    101 
    102   def PullTrace(self):
    103     # Wait a bit for the browser to finish writing the trace file.
    104     time.sleep(self._trace_interval / 4 + 1)
    105 
    106     trace_file = self._trace_file.replace('/storage/emulated/0/', '/sdcard/')
    107     host_file = os.path.join(os.path.curdir, os.path.basename(trace_file))
    108     try:
    109       self._device.PullFile(trace_file, host_file)
    110     except device_errors.AdbCommandFailedError:
    111       raise RuntimeError(
    112           'Cannot pull the trace file. Have you granted Storage permission to '
    113           'the browser? (Android Settings -> Apps -> [the browser app] -> '
    114           'Permissions -> Storage)')
    115     return host_file
    116