# Copyright 2017 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. # Tracing agent that captures friendly process and thread data - names, pids and # tids and names, etc to enrich display in the trace viewer. Captures snapshots # of the output of 'ps' on the device at intervals. import logging import py_utils from devil.android import device_utils from devil.android.device_errors import AdbShellCommandFailedError from systrace import tracing_agents from systrace import trace_result # Leftmost output columns match those used on legacy devices. # Get thread names separately as there may be spaces that breaks col # splitting. # TODO(benm): Refactor device_utils.GetPids to get threads and use that here. PS_COMMAND_PROC = "ps -A -o USER,PID,PPID,VSIZE,RSS,WCHAN,ADDR=PC,S,NAME,COMM" \ "&& ps -AT -o USER,PID,TID,CMD" # Fallback for old devices. PS_COMMAND_PROC_LEGACY = "ps && ps -t" # identify this as trace of thread / process state TRACE_HEADER = 'PROCESS DUMP\n' def try_create_agent(config): if config.target != 'android': return None if config.from_file is not None: return None if config.process_dump_enable: # Since AtraceProcessDumpAgent was enabled it's unnecessary to collect ps # data because each process memory dump updates information about processes # and their threads. It's more complete data than two ps snapshots for an # entire trace. However, that agent isn't enabled by default. return None return AndroidProcessDataAgent() def get_config(options): return options class AndroidProcessDataAgent(tracing_agents.TracingAgent): def __init__(self): super(AndroidProcessDataAgent, self).__init__() self._trace_data = "" self._device = None def __repr__(self): return 'android_process_data' @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) def StartAgentTracing(self, config, timeout=None): self._device = device_utils.DeviceUtils(config.device_serial_number) self._trace_data += self._get_process_snapshot() return True @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) def StopAgentTracing(self, timeout=None): self._trace_data += self._get_process_snapshot() return True @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT) def GetResults(self, timeout=None): result = TRACE_HEADER + self._trace_data return trace_result.TraceResult('androidProcessDump', result) def SupportsExplicitClockSync(self): return False def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): pass def _get_process_snapshot(self): use_legacy = False try: dump = self._device.RunShellCommand( \ PS_COMMAND_PROC, check_return=True, as_root=True, shell=True) except AdbShellCommandFailedError: use_legacy = True # Check length of 2 as we execute two commands, which in case of failure # on old devices output 1 line each. if use_legacy or len(dump) == 2: logging.debug('Couldn\'t parse ps dump, trying legacy method ...') dump = self._device.RunShellCommand( \ PS_COMMAND_PROC_LEGACY, check_return=True, as_root=True, shell=True) if len(dump) == 2: logging.error('Unable to extract process data!') return "" return '\n'.join(dump) + '\n'