Home | History | Annotate | Download | only in tracing_agents
      1 # Copyright 2017 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 # Tracing agent that captures friendly process and thread data - names, pids and
      6 # tids and names, etc to enrich display in the trace viewer. Captures snapshots
      7 # of the output of 'ps' on the device at intervals.
      8 
      9 import logging
     10 import py_utils
     11 
     12 from devil.android import device_utils
     13 from devil.android.device_errors import AdbShellCommandFailedError
     14 from systrace import tracing_agents
     15 from systrace import trace_result
     16 
     17 # Leftmost output columns match those used on legacy devices.
     18 # Get thread names separately as there may be spaces that breaks col
     19 # splitting.
     20 # TODO(benm): Refactor device_utils.GetPids to get threads and use that here.
     21 PS_COMMAND_PROC = "ps -A -o USER,PID,PPID,VSIZE,RSS,WCHAN,ADDR=PC,S,NAME,COMM" \
     22     "&& ps -AT -o USER,PID,TID,CMD"
     23 
     24 # Fallback for old devices.
     25 PS_COMMAND_PROC_LEGACY = "ps && ps -t"
     26 
     27 # identify this as trace of thread / process state
     28 TRACE_HEADER = 'PROCESS DUMP\n'
     29 
     30 def try_create_agent(config):
     31   if config.target != 'android':
     32     return None
     33   if config.from_file is not None:
     34     return None
     35   return AndroidProcessDataAgent()
     36 
     37 def get_config(options):
     38   return options
     39 
     40 class AndroidProcessDataAgent(tracing_agents.TracingAgent):
     41   def __init__(self):
     42     super(AndroidProcessDataAgent, self).__init__()
     43     self._trace_data = ""
     44     self._device = None
     45 
     46   def __repr__(self):
     47     return 'android_process_data'
     48 
     49   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
     50   def StartAgentTracing(self, config, timeout=None):
     51     self._device = device_utils.DeviceUtils(config.device_serial_number)
     52     self._trace_data += self._get_process_snapshot()
     53     return True
     54 
     55   @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
     56   def StopAgentTracing(self, timeout=None):
     57     self._trace_data += self._get_process_snapshot()
     58     return True
     59 
     60   @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
     61   def GetResults(self, timeout=None):
     62     result = TRACE_HEADER + self._trace_data
     63     return trace_result.TraceResult('androidProcessDump', result)
     64 
     65   def SupportsExplicitClockSync(self):
     66     return False
     67 
     68   def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
     69     pass
     70 
     71   def _get_process_snapshot(self):
     72     use_legacy = False
     73     try:
     74       dump = self._device.RunShellCommand( \
     75           PS_COMMAND_PROC, check_return=True, as_root=True, shell=True)
     76     except AdbShellCommandFailedError:
     77       use_legacy = True
     78 
     79     # Check length of 2 as we execute two commands, which in case of failure
     80     # on old devices output 1 line each.
     81     if use_legacy or len(dump) == 2:
     82       logging.debug('Couldn\'t parse ps dump, trying legacy method ...')
     83       dump = self._device.RunShellCommand( \
     84           PS_COMMAND_PROC_LEGACY, check_return=True, as_root=True, shell=True)
     85       if len(dump) == 2:
     86         logging.error('Unable to extract process data!')
     87         return ""
     88 
     89     return '\n'.join(dump) + '\n'
     90