# 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 periodic per-process memory dumps and other
|
# useful information from ProcFS like utime, stime, OOM stats, etc.
|
|
import json
|
import logging
|
import optparse
|
import py_utils
|
|
from devil.android import device_utils
|
from devil.android.device_errors import AdbShellCommandFailedError
|
from py_trace_event import trace_time as trace_time_module
|
from systrace import tracing_agents
|
from systrace import trace_result
|
|
TRACE_HEADER = 'ATRACE_PROCESS_DUMP'
|
TRACE_RESULT_NAME = 'atraceProcessDump'
|
|
HELPER_COMMAND = '/data/local/tmp/atrace_helper'
|
HELPER_STOP_COMMAND = 'kill -TERM `pidof atrace_helper`'
|
HELPER_DUMP_JSON = '/data/local/tmp/procdump.json'
|
|
|
class AtraceProcessDumpAgent(tracing_agents.TracingAgent):
|
def __init__(self):
|
super(AtraceProcessDumpAgent, self).__init__()
|
self._device = None
|
self._dump = None
|
self._clock_sync_markers = {}
|
|
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
def StartAgentTracing(self, config, timeout=None):
|
self._device = device_utils.DeviceUtils(config.device_serial_number)
|
cmd = [HELPER_COMMAND, '-b', '-g',
|
'-t', str(config.dump_interval_ms),
|
'-o', HELPER_DUMP_JSON]
|
if config.full_dump_config:
|
cmd += ['-m', config.full_dump_config]
|
if config.enable_mmaps:
|
cmd += ['-s']
|
self._device.RunShellCommand(cmd, check_return=True, as_root=True)
|
return True
|
|
@py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
|
def StopAgentTracing(self, timeout=None):
|
self._device.RunShellCommand(
|
HELPER_STOP_COMMAND,
|
shell=True, check_return=True, as_root=True)
|
try:
|
self._device.RunShellCommand(['test', '-f', HELPER_DUMP_JSON],
|
check_return=True, as_root=True)
|
self._dump = self._device.ReadFile(HELPER_DUMP_JSON, force_pull=True)
|
self._device.RunShellCommand(['rm', HELPER_DUMP_JSON],
|
check_return=True, as_root=True)
|
except AdbShellCommandFailedError:
|
logging.error('AtraceProcessDumpAgent failed to pull data. Check device storage.')
|
return False
|
return True
|
|
@py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
|
def GetResults(self, timeout=None):
|
result = TRACE_HEADER + '\n' + self._dump
|
cs = json.dumps(self._clock_sync_markers)
|
result = TRACE_HEADER + \
|
'\n{\"clock_sync_markers\":' + cs + ',\n\"dump\":' + self._dump + '}'
|
return trace_result.TraceResult(TRACE_RESULT_NAME, result)
|
|
def SupportsExplicitClockSync(self):
|
return True
|
|
def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback):
|
with self._device.adb.PersistentShell(self._device.serial) as shell:
|
ts_in_controller_domain = trace_time_module.Now()
|
output = shell.RunCommand(HELPER_COMMAND + ' --echo-ts', close=True)
|
ts_in_agent_domain = int(output[0][0])
|
self._clock_sync_markers[sync_id] = ts_in_agent_domain
|
did_record_sync_marker_callback(ts_in_controller_domain, sync_id)
|
|
|
class AtraceProcessDumpConfig(tracing_agents.TracingConfig):
|
def __init__(self, enabled, device_serial_number,
|
dump_interval_ms, full_dump_config, enable_mmaps):
|
tracing_agents.TracingConfig.__init__(self)
|
self.enabled = enabled
|
self.device_serial_number = device_serial_number
|
self.dump_interval_ms = dump_interval_ms
|
self.full_dump_config = full_dump_config
|
self.enable_mmaps = enable_mmaps
|
|
|
def add_options(parser):
|
options = optparse.OptionGroup(parser, 'Atrace process dump options')
|
options.add_option('--process-dump', dest='process_dump_enable',
|
default=False, action='store_true',
|
help='Capture periodic per-process memory dumps.')
|
options.add_option('--process-dump-interval', dest='process_dump_interval_ms',
|
default=5000,
|
help='Interval between memory dumps in milliseconds.')
|
options.add_option('--process-dump-full', dest='process_dump_full_config',
|
default=None,
|
help='Capture full memory dumps for some processes.\n' \
|
'Value: all, apps or comma-separated process names.')
|
options.add_option('--process-dump-mmaps', dest='process_dump_mmaps',
|
default=False, action='store_true',
|
help='Capture VM regions and memory-mapped files.\n' \
|
'It increases dump size dramatically, hence only ' \
|
'has effect if --process-dump-full is a whitelist.')
|
return options
|
|
|
def get_config(options):
|
can_enable = (options.target == 'android') and (not options.from_file)
|
return AtraceProcessDumpConfig(
|
enabled=(options.process_dump_enable and can_enable),
|
device_serial_number=options.device_serial_number,
|
dump_interval_ms=options.process_dump_interval_ms,
|
full_dump_config=options.process_dump_full_config,
|
enable_mmaps=options.process_dump_mmaps
|
)
|
|
|
def try_create_agent(config):
|
if config.enabled:
|
return AtraceProcessDumpAgent()
|
return None
|