Chromium Code Reviews| Index: systrace/systrace/tracing_agents/monsoon_agent.py |
| diff --git a/systrace/systrace/tracing_agents/monsoon_agent.py b/systrace/systrace/tracing_agents/monsoon_agent.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ef5291796bd0e8374db3e6dd78092579b893d86f |
| --- /dev/null |
| +++ b/systrace/systrace/tracing_agents/monsoon_agent.py |
| @@ -0,0 +1,179 @@ |
| +# 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. |
| + |
| +import optparse |
| +import py_utils |
| +import StringIO |
| +import tempfile |
| +import time |
| + |
| +from devil.android.device_utils import DeviceUtils |
| +from py_trace_event import trace_time |
| +from systrace import tracing_agents |
| +from systrace import trace_result |
| +from monsoon.monsoon_wrapper import MonsoonWrapper # pylint: disable=import-error |
| +from monsoon.monsoon_utils import ReadSamplesFromFile # pylint: disable=import-error |
| +from monsoon.monsoon_utils import TransformSamplesWithFrequency # pylint: disable=import-error |
| + |
| + |
| +class MonsoonConfig(tracing_agents.TracingConfig): |
| + def __init__(self, monsoon, frequency, latency_ms, device_serial_number): |
| + tracing_agents.TracingConfig.__init__(self) |
| + self.monsoon = monsoon |
| + self.frequency = frequency |
| + self.latency_ms = latency_ms |
| + self.device_serial_number = device_serial_number |
| + |
| +def add_options(parser): |
| + options = optparse.OptionGroup(parser, 'Monsoon trace options') |
| + options.add_option('--monsoon', dest='monsoon', default=None, |
| + action='store', help='Path to monsoon controller script.') |
| + options.add_option('--monsoon_freq', dest='freq', default=10, |
| + action='store', help='Frequency of power monitoring in '+ |
| + 'hertz. Default is 10.') |
| + # The default latency value here has been arrived at by empirical measurement. |
| + # There is latency, it just may vary from machine to machine, depend on USB |
| + # hubs between device and host, USB bus contention, etc. If higher accuracy |
| + # is required it should be up to the user to determine their Monsoon power |
| + # latency for their local setup and use this parameter to override the |
| + # default. |
| + options.add_option('--monsoon_latency', dest='latency_ms', default=3.5, |
| + action='store', help='Latency of monsoon power measurement'+ |
| + ' in milliseconds. Value varies depending upon host ' + |
| + 'configuration and Monsoon device. Default is 3.5ms.') |
| + return options |
| + |
| +def get_config(options): |
| + return MonsoonConfig( |
| + options.monsoon, int(options.freq), options.latency_ms, |
| + options.device_serial_number) |
| + |
| +def try_create_agent(config): |
| + """Create a Monsoon power monitor agent. |
| + Retrieves voltage from the Monsoon device with USB disabled. BattOr logs |
| + include voltage which changes constantly. In comparison, Monsoon is a constant |
| + voltage source whose voltage only needs to be queried once. |
| + |
| + Args: |
| + config: Command line config. |
| + """ |
| + if type(config.monsoon) is not str: |
| + return None |
| + |
| + device = DeviceUtils(config.device_serial_number) |
| + monsoon_wrapper = MonsoonWrapper(config.monsoon) |
| + |
| + # Read the status values while USB is disconnected, reconnecting afterwards |
| + # for other agents to initialize. |
| + monsoon_wrapper.DisableUSB() |
| + status = monsoon_wrapper.ReadStatusProperties() |
| + monsoon_wrapper.EnableUSB() |
| + device.adb.WaitForDevice() |
| + |
| + if "voltage1" not in status: |
| + raise Exception("Could not read voltage") |
| + |
| + voltage = float(status["voltage1"]) |
| + |
| + return MonsoonTracingAgent(monsoon_wrapper, device, voltage, |
| + config.latency_ms, config.frequency) |
| + |
| +class MonsoonTracingAgent(tracing_agents.TracingAgent): |
| + def __init__(self, monsoon_wrapper, device, voltage, latency_ms, frequency): |
| + super(MonsoonTracingAgent, self).__init__() |
| + self._monsoon_wrapper = monsoon_wrapper |
| + self._device = device |
| + self._voltage = voltage |
| + self._latency_seconds = latency_ms / 1e3 |
| + self._frequency = frequency |
| + self._sync_id = None |
| + self._sync_timestamp = None |
| + self._result_file = None |
| + self._trace_start = None |
| + |
| + def SupportsExplicitClockSync(self): |
| + """Returns whether this supports explicit clock sync. |
| + |
| + Although Monsoon agent implement the clock sync marker, this is performed |
| + explicitly from the trace controller as the Monsoon agent has to immediately |
| + reconnect USB afterwards for the other agents to issue clock sync markers |
| + and to retrieve data. |
| + """ |
| + return False |
| + |
| + def RecordClockSyncMarker(self, sync_id, did_record_sync_marker_callback): |
| + # pylint: disable=unused-argument |
| + assert self.SupportsExplicitClockSync(), ('Clock sync marker cannot be ' |
| + 'recorded since explicit clock sync is not supported.') |
| + |
| + @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) |
| + def StopCollectionWithClockSync(self, sync_id, |
| + did_record_sync_marker_callback): |
| + """ Stops the collection of monsoon power data and re-enables USB. |
| + Records the time at which collection was stopped so the record at this time |
| + can be annotated in the resultant log. |
| + """ |
| + self._sync_id = sync_id |
| + sync_trace = trace_time.Now() |
| + self._sync_timestamp = time.time() |
| + did_record_sync_marker_callback(sync_trace, sync_id) |
| + |
| + # Wait for at least two synchronization periods to allow Monsoon to gather |
| + # and flush data. Min wait time is half a second. |
| + flush_period = max(self._latency_seconds * 2.0, 0.5) |
| + time.sleep(flush_period) |
| + |
| + self._monsoon_wrapper.EndExecution() |
| + self._monsoon_wrapper.EnableUSB() |
| + self._device.adb.WaitForDevice() |
| + return True |
| + |
| + @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) |
| + def StopAgentTracing(self, timeout=None): |
| + return True |
|
Chris Craik
2017/09/19 18:47:02
can you do the actual stop here? we'd like to avoi
Zhen Wang
2017/09/19 23:53:50
Seem that the actual stopping is done in StopColle
ddeptford
2017/09/20 23:45:58
Unfortunately this is a limitation of the monsoon
|
| + |
| + @py_utils.Timeout(tracing_agents.START_STOP_TIMEOUT) |
| + def StartAgentTracing(self, config, timeout=None): |
| + # Disable USB pass-through while collecting power samples to prevent adding |
| + # USB power to sample data. |
| + self._monsoon_wrapper.DisableUSB() |
| + |
| + # Begin a power reading operation. |
| + self._result_file = tempfile.TemporaryFile() |
| + self._monsoon_wrapper.BeginReadPower(self._frequency, out=self._result_file) |
| + self._trace_start = time.time() |
| + return True |
| + |
| + @py_utils.Timeout(tracing_agents.GET_RESULTS_TIMEOUT) |
| + def GetResults(self, timeout=None): |
| + self._result_file.flush() |
| + self._result_file.seek(0) |
| + |
| + millivolts = self._voltage * 1000 |
| + |
| + # Retrieve the samples that have second-only precision. |
| + coarse_samples = ReadSamplesFromFile( |
| + self._result_file) |
| + |
| + # Use fixed period to transform samples with whole second precision to |
| + # fractional precision. |
| + fine_samples = TransformSamplesWithFrequency( |
| + coarse_samples, self._frequency) |
| + |
| + # Build a string result for BattOr log (whose format we use). |
| + result_builder = StringIO.StringIO() |
| + result_builder.write("# BattOr") |
| + |
| + for sample in fine_samples: |
| + # For timestamp comparisons, adjust using the monsoon latency. |
| + adj_timestamp = sample.Timestamp - self._latency_seconds |
| + if adj_timestamp >= self._trace_start: |
| + result_builder.write("\n{0:f} {1:f} {2:f}".format(sample.Timestamp * |
| + 1000.0, sample.Amps * 1000.0, millivolts)) |
| + if self._sync_timestamp < adj_timestamp: |
| + result_builder.write(" <{}>".format(self._sync_id)) |
| + return trace_result.TraceResult('powerTraceAsString', |
| + result_builder.getvalue()) |
| + |
| + raise Exception("Failed to find end timestamp in monsoon log.") |