blob: b809a0c2cbb39c72f3a75e0a2f7c90ea2b399984 [file] [log] [blame] [edit]
# 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'