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
5import py_utils
6import optparse
7import threading
8
9from devil.android import device_utils
10from systrace import trace_result
11from systrace import tracing_agents
12from py_trace_event import trace_time as trace_time_module
13
14TRACE_FILE_PATH = \
15    '/sdcard/Android/data/org.chromium.latency.walt/files/trace.txt'
16
17CLOCK_DOMAIN_MARKER = '# clock_type=LINUX_CLOCK_MONOTONIC\n'
18
19
20def try_create_agent(options):
21  if options.is_walt_enabled:
22    return WaltAgent()
23  return None
24
25
26class WaltConfig(tracing_agents.TracingConfig):
27  def __init__(self, device_serial_number, is_walt_enabled):
28    tracing_agents.TracingConfig.__init__(self)
29    self.device_serial_number = device_serial_number
30    self.is_walt_enabled = is_walt_enabled
31
32
33def add_options(parser):
34  options = optparse.OptionGroup(parser, 'WALT trace options')
35  options.add_option('--walt', dest='is_walt_enabled', default=False,
36                    action='store_true', help='Use the WALT tracing agent. '
37                    'WALT is a device for measuring latency of physical '
38                    'sensors on phones and computers. '
39                    'See https://github.com/google/walt')
40  return options
41
42
43def get_config(options):
44  return WaltConfig(options.device_serial_number, options.is_walt_enabled)
45
46
47class WaltAgent(tracing_agents.TracingAgent):
48  """
49  This tracing agent requires the WALT app to be installed on the Android phone,
50  and requires the WALT device to be attached to the phone. WALT is a device
51  for measuring latency of physical sensors and outputs on phones and
52  computers. For more information, visit https://github.com/google/walt
53  """
54  def __init__(self):
55    super(WaltAgent, self).__init__()
56    self._trace_contents = None
57    self._config = None
58    self._device_utils = None
59    self._clock_sync_marker = None
60    self._collection_thread = None
61
62  def __repr__(self):
63    return 'WaltAgent'
64
65  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
66  def StartAgentTracing(self, config, timeout=None):
67    del timeout  # unused
68    self._config = config
69    self._device_utils = device_utils.DeviceUtils(
70        self._config.device_serial_number)
71    if self._device_utils.PathExists(TRACE_FILE_PATH):
72      # clear old trace events so they are not included in the current trace
73      self._device_utils.WriteFile(TRACE_FILE_PATH, '')
74    return True
75
76  @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT)
77  def StopAgentTracing(self, timeout=None):
78    """Stops tracing and starts collecting results.
79
80    To synchronously retrieve the results after calling this function,
81    call GetResults().
82    """
83    del timeout  # unused
84    self._collection_thread = threading.Thread(
85        target=self._collect_trace_data)
86    self._collection_thread.start()
87    return True
88
89  def _collect_trace_data(self):
90    self._trace_contents = self._device_utils.ReadFile(TRACE_FILE_PATH)
91
92  def SupportsExplicitClockSync(self):
93    return True
94
95  def RecordClockSyncMarker(self, sync_id, did_record_clock_sync_callback):
96    cmd = 'cat /proc/timer_list | grep now'
97    t1 = trace_time_module.Now()
98    command_result = self._device_utils.RunShellCommand(cmd, shell=True)
99    nsec = command_result[0].split()[2]
100    self._clock_sync_marker = format_clock_sync_marker(sync_id, nsec)
101    did_record_clock_sync_callback(t1, sync_id)
102
103  @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT)
104  def GetResults(self, timeout=None):
105    del timeout  # unused
106    self._collection_thread.join()
107    self._collection_thread = None
108    return trace_result.TraceResult('waltTrace', self._get_trace_result())
109
110  def _get_trace_result(self):
111    result = '# tracer: \n' + CLOCK_DOMAIN_MARKER + self._trace_contents
112    if self._clock_sync_marker is not None:
113      result += self._clock_sync_marker
114    return result
115
116
117def format_clock_sync_marker(sync_id, nanosec_time):
118  return ('<0>-0  (-----) [001] ...1  ' + str(float(nanosec_time) / 1e9)
119          + ': tracing_mark_write: trace_event_clock_sync: name='
120          + sync_id + '\n')
121