power_status: add CheckpointLogger class
BUG=chromium:878233
TEST=none
Change-Id: Ie506fad65e81af192580dfc724ac5cad35da96cc
Signed-off-by: Sean Kao <seankao@google.com>
Reviewed-on: https://chromium-review.googlesource.com/1193202
Reviewed-by: Ruben Rodriguez Buchillon <coconutruben@chromium.org>
diff --git a/client/cros/power/power_status.py b/client/cros/power/power_status.py
index 92748e4..6895bf4 100644
--- a/client/cros/power/power_status.py
+++ b/client/cros/power/power_status.py
@@ -1405,25 +1405,127 @@
return float(keyvals['Battery']['energy rate'])
+class CheckpointLogger(object):
+ """Class to log checkpoint data.
+
+ Public attributes:
+ checkpoint_data: dictionary of (tname, tlist).
+ tname: String of testname associated with these time intervals
+ tlist: list of tuples. Tuple contains:
+ tstart: Float of time when subtest started
+ tend: Float of time when subtest ended
+
+ Public methods:
+ start: records a start timestamp
+ checkpoint
+ checkblock
+ save_checkpoint_data
+
+ Static methods:
+ load_checkpoint_data
+
+ Private attributes:
+ _start_time: start timestamp for checkpoint logger
+ """
+ CHECKPOINT_LOG_DEFAULT_FNAME = 'checkpoint_log.json'
+
+ def __init__(self):
+ self.checkpoint_data = collections.defaultdict(list)
+
+ # If multiple MeasurementLoggers call start() on the same CheckpointLogger,
+ # the latest one will register start time.
+ def start(self):
+ self._start_time = time.time()
+
+ @contextlib.contextmanager
+ def checkblock(self, tname=''):
+ """Check point for the following block with test tname.
+
+ Args:
+ tname: String of testname associated with this time interval
+ """
+ start_time = time.time()
+ yield
+ self.checkpoint(tname, start_time)
+
+ def checkpoint(self, tname='', tstart=None, tend=None):
+ """Check point the times in seconds associated with test tname.
+
+ Args:
+ tname: String of testname associated with this time interval
+ tstart: Float in seconds of when tname test started. Should be based
+ off time.time(). If None, use start timestamp for the checkpoint
+ logger.
+ tend: Float in seconds of when tname test ended. Should be based
+ off time.time(). If None, then value computed in the method.
+ """
+ if not tstart and self._start_time:
+ tstart = self._start_time
+ if not tend:
+ tend = time.time()
+ self.checkpoint_data[tname].append((tstart, tend))
+ logging.info('Finished test "%s" between timestamps [%s, %s]',
+ tname, tstart, tend)
+
+ def save_checkpoint_data(self, resultsdir, fname=CHECKPOINT_LOG_DEFAULT_FNAME):
+ """Save checkpoint data.
+
+ Args:
+ resultsdir: String, directory to write results to
+ fname: String, name of file to write results to
+ """
+ fname = os.path.join(resultsdir, fname)
+ with file(fname, 'wt') as f:
+ json.dump(self.checkpoint_data, f, indent=4, separators=(',', ': '))
+
+ @staticmethod
+ def load_checkpoint_data(resultsdir, fname=CHECKPOINT_LOG_DEFAULT_FNAME):
+ """Load checkpoint data.
+
+ Args:
+ resultsdir: String, directory to load results from
+ fname: String, name of file to load results from
+ """
+ fname = os.path.join(resultsdir, fname)
+ with file(fname, 'r') as f:
+ checkpoint_data = json.load(f)
+ return checkpoint_data
+
+
class MeasurementLogger(threading.Thread):
"""A thread that logs measurement readings.
Example code snippet:
- mylogger = MeasurementLogger([Measurent1, Measurent2])
- mylogger.run()
- for testname in tests:
- with my_logger.checkblock(testname):
- #run the test method for testname
- keyvals = mylogger.calc()
+ my_logger = MeasurementLogger([Measurent1, Measurent2])
+ my_logger.start()
+ for testname in tests:
+ # Option 1: use checkblock
+ with my_logger.checkblock(testname):
+ # run the test method for testname
- or
- mylogger = MeasurementLogger([Measurent1, Measurent2])
- mylogger.run()
- for testname in tests:
- start_time = time.time()
- #run the test method for testname
- mlogger.checkpoint(testname, start_time)
- keyvals = mylogger.calc()
+ # Option 2: use checkpoint
+ start_time = time.time()
+ # run the test method for testname
+ my_logger.checkpoint(testname, start_time)
+
+ keyvals = my_logger.calc()
+
+ or using CheckpointLogger:
+ checkpoint_logger = CheckpointLogger()
+ my_logger = MeasurementLogger([Measurent1, Measurent2],
+ checkpoint_logger)
+ my_logger.start()
+ for testname in tests:
+ # Option 1: use checkblock
+ with checkpoint_logger.checkblock(testname):
+ # run the test method for testname
+
+ # Option 2: use checkpoint
+ start_time = time.time()
+ # run the test method for testname
+ checkpoint_logger.checkpoint(testname, start_time)
+
+ keyvals = my_logger.calc()
Public attributes:
seconds_period: float, probing interval in seconds.
@@ -1440,22 +1542,20 @@
save_results:
Private attributes:
- _measurements: list of Measurement objects to be sampled.
- _checkpoint_data: dictionary of (tname, tlist).
- tname: String of testname associated with these time intervals
- tlist: list of tuples. Tuple contains:
- tstart: Float of time when subtest started
- tend: Float of time when subtest ended
- _results: list of results tuples. Tuple contains:
- prefix: String of subtest
- mean: Float of mean in watts
- std: Float of standard deviation of measurements
- tstart: Float of time when subtest started
- tend: Float of time when subtest ended
+ _measurements: list of Measurement objects to be sampled.
+ _checkpoint_data: dictionary of (tname, tlist).
+ tname: String of testname associated with these time intervals
+ tlist: list of tuples. Tuple contains:
+ tstart: Float of time when subtest started
+ tend: Float of time when subtest ended
+ _results: list of results tuples. Tuple contains:
+ prefix: String of subtest
+ mean: Float of mean in watts
+ std: Float of standard deviation of measurements
+ tstart: Float of time when subtest started
+ tend: Float of time when subtest ended
"""
- CHECKPOINT_LOG_DEFAULT_FNAME = 'checkpoint_log.json'
-
- def __init__(self, measurements, seconds_period=1.0):
+ def __init__(self, measurements, seconds_period=1.0, checkpoint_logger=None):
"""Initialize a logger.
Args:
@@ -1468,15 +1568,21 @@
self.readings = []
self.times = []
- self._checkpoint_data = collections.defaultdict(list)
self.domains = []
self._measurements = measurements
for meas in self._measurements:
self.domains.append(meas.domain)
+ self._checkpoint_logger = \
+ checkpoint_logger if checkpoint_logger else CheckpointLogger()
+
self.done = False
+ def start(self):
+ self._checkpoint_logger.start()
+ super(MeasurementLogger, self).start()
+
def refresh(self):
"""Perform data samplings for every measurements.
@@ -1515,7 +1621,7 @@
self.checkpoint(tname, start_time)
def checkpoint(self, tname='', tstart=None, tend=None):
- """Check point the times in seconds associated with test tname.
+ """Just a thin method calling the CheckpointLogger checkpoint method.
Args:
tname: String of testname associated with this time interval
@@ -1524,15 +1630,10 @@
tend: Float in seconds of when tname test ended. Should be based
off time.time(). If None, then value computed in the method.
"""
- if not tstart and self.times:
- tstart = self.times[0]
- if not tend:
- tend = time.time()
- self._checkpoint_data[tname].append((tstart, tend))
- logging.info('Finished test "%s" between timestamps [%s, %s]',
- tname, tstart, tend)
+ self._checkpoint_logger.checkpoint(tname, tstart, tend)
-
+ # TODO(seankao): It might be useful to pull this method to CheckpointLogger,
+ # to allow checkpoint usage without an explicit MeasurementLogger.
def calc(self, mtype=None):
"""Calculate average measurement during each of the sub-tests.
@@ -1562,14 +1663,14 @@
# times 2 the sleep time in order to allow for readings as well.
self.join(timeout=self.seconds_period * 2)
- if not self._checkpoint_data:
- self.checkpoint()
+ if not self._checkpoint_logger.checkpoint_data:
+ self._checkpoint_logger.checkpoint()
for i, domain_readings in enumerate(zip(*self.readings)):
meas = numpy.array(domain_readings)
domain = self.domains[i]
- for tname, tlist in self._checkpoint_data.iteritems():
+ for tname, tlist in self._checkpoint_logger.checkpoint_data.iteritems():
if tname:
prefix = '%s_%s' % (tname, domain)
else:
@@ -1635,32 +1736,6 @@
f.write(line + '\n')
- def save_checkpoint_data(self, resultsdir, fname=CHECKPOINT_LOG_DEFAULT_FNAME):
- """Save checkpoint data.
-
- Args:
- resultsdir: String, directory to write results to
- fname: String, name of file to write results to
- """
- fname = os.path.join(resultsdir, fname)
- with file(fname, 'wt') as f:
- json.dump(self._checkpoint_data, f, indent=4, separators=(',', ': '))
-
-
- @staticmethod
- def load_checkpoint_data(resultsdir, fname=CHECKPOINT_LOG_DEFAULT_FNAME):
- """Load checkpoint data.
-
- Args:
- resultsdir: String, directory to load results from
- fname: String, name of file to load results from
- """
- fname = os.path.join(resultsdir, fname)
- with file(fname, 'r') as f:
- checkpoint_data = json.load(f)
- return checkpoint_data
-
-
class CPUStatsLogger(MeasurementLogger):
"""Class to measure CPU Frequency and CPU Idle Stats.
@@ -1677,14 +1752,14 @@
_refresh_count: number of times refresh() has been called.
_last_wavg: dict of wavg when refresh() was last called.
"""
- def __init__(self, seconds_period=1.0):
+ def __init__(self, seconds_period=1.0, checkpoint_logger=None):
"""Initialize a CPUStatsLogger.
Args:
seconds_period: float, probing interval in seconds. Default 1.0
"""
# We don't use measurements since CPU stats can't measure separately.
- super(CPUStatsLogger, self).__init__([], seconds_period)
+ super(CPUStatsLogger, self).__init__([], seconds_period, checkpoint_logger)
self._stats = get_avaliable_cpu_stats()
self._stats.append(GPUFreqStats())
@@ -1802,7 +1877,7 @@
class TempLogger(MeasurementLogger):
"""A thread that logs temperature readings in millidegrees Celsius."""
- def __init__(self, measurements, seconds_period=30.0):
+ def __init__(self, measurements, seconds_period=30.0, checkpoint_logger=None):
if not measurements:
measurements = []
tstats = ThermalStatHwmon()
@@ -1817,7 +1892,7 @@
if has_battery_temp():
measurements.append(BatteryTempMeasurement())
- super(TempLogger, self).__init__(measurements, seconds_period)
+ super(TempLogger, self).__init__(measurements, seconds_period, checkpoint_logger)
def save_results(self, resultsdir, fname=None):