Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(634)

Unified Diff: systrace/systrace/tracing_agents/monsoon_agent.py

Issue 3018533002: Implementing a Monsoon power monitor trace agent, utilizing the UI infrastructure that the BattOr a…
Patch Set: Fixing test failures. Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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.")

Powered by Google App Engine
This is Rietveld 408576698