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