aboutsummaryrefslogtreecommitdiff
path: root/catapult/systrace/systrace/tracing_agents/atrace_process_dump.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/systrace/systrace/tracing_agents/atrace_process_dump.py')
-rw-r--r--catapult/systrace/systrace/tracing_agents/atrace_process_dump.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/catapult/systrace/systrace/tracing_agents/atrace_process_dump.py b/catapult/systrace/systrace/tracing_agents/atrace_process_dump.py
new file mode 100644
index 00000000..107da58e
--- /dev/null
+++ b/catapult/systrace/systrace/tracing_agents/atrace_process_dump.py
@@ -0,0 +1,128 @@
+# 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