Add support in showbootdata for the new firmware time keyvals.
This adds a new -f/--firmware option for reporting the
power_on_to_* keyvals. These report timings of various firmware
phases during boot.
BUG=chromium-os:14925
TEST=use the new option on a recently generated results directory
Change-Id: I9a962db0696b4f767184d8bd8eede1af0a9a1274
Reviewed-on: http://gerrit.chromium.org/gerrit/3461
Reviewed-by: Mike Truty <truty@chromium.org>
Tested-by: Rajesh Chenna <rchenna@google.com>
Reviewed-by: Rajesh Chenna <rchenna@google.com>
diff --git a/bootperf-bin/perfprinter.py b/bootperf-bin/perfprinter.py
index 95ec4ac..d610714 100644
--- a/bootperf-bin/perfprinter.py
+++ b/bootperf-bin/perfprinter.py
@@ -9,15 +9,14 @@
import os.path
import re
+import resultset
-def PrintRawData(reader, dirlist, use_timestats, keylist):
+
+def PrintRawData(reader, dirlist, keytype, keylist):
"""Print 'bootperf' results in "raw data" format."""
for dir_ in dirlist:
results = reader(dir_)
- if use_timestats:
- keyset = results.TimeKeySet()
- else:
- keyset = results.DiskKeySet()
+ keyset = results.KeySet(keytype)
for i in range(0, keyset.num_iterations):
if len(dirlist) > 1:
line = "%s %3d" % (results.name, i)
@@ -33,23 +32,21 @@
print line
-def PrintStatisticsSummary(reader, dirlist, use_timestats, keylist):
+def PrintStatisticsSummary(reader, dirlist, keytype, keylist):
"""Print 'bootperf' results in "summary of averages" format."""
- if use_timestats:
+ if (keytype == resultset.TestResultSet.BOOTTIME_KEYSET or
+ keytype == resultset.TestResultSet.FIRMWARE_KEYSET):
header = "%5s %3s %5s %3s %s" % (
"time", "s%", "dt", "s%", "event")
format = "%5s %2d%% %5s %2d%% %s"
else:
- header = "%6s %3s %6s %3s %s" % (
+ header = "%7s %3s %7s %3s %s" % (
"diskrd", "s%", "delta", "s%", "event")
- format = "%6s %2d%% %6s %2d%% %s"
+ format = "%7s %2d%% %7s %2d%% %s"
havedata = False
for dir_ in dirlist:
results = reader(dir_)
- if use_timestats:
- keyset = results.TimeKeySet()
- else:
- keyset = results.DiskKeySet()
+ keyset = results.KeySet(keytype)
if keylist is not None:
markers = keylist
else:
diff --git a/bootperf-bin/resultset.py b/bootperf-bin/resultset.py
index 92f29f9..a17fc76 100644
--- a/bootperf-bin/resultset.py
+++ b/bootperf-bin/resultset.py
@@ -5,15 +5,16 @@
"""Classes and functions for managing platform_BootPerf results.
Results from the platform_BootPerf test in the ChromiumOS autotest
-package are stored in as performance 'keyvals', that is, a mapping
+package are stored as performance 'keyvals', that is, a mapping
of names to numeric values. For each iteration of the test, one
set of keyvals is recorded.
-This module currently tracks two kinds of keyval results, the boot
-time results, and the disk read results. These results are stored
-with keyval names such as 'seconds_kernel_to_login' and
-'rdbytes_kernel_to_login'. These keyvals record an accumulated
-total measured from a fixed time in the past (kernel startup), e.g.
+This module currently tracks three kinds of keyval results: the boot
+time results, the disk read results, and firmware time results.
+These results are stored with keyval names such as
+'seconds_kernel_to_login', 'rdbytes_kernel_to_login', and
+'seconds_power_on_to_kernel'. These keyvals record an accumulated
+total measured from a fixed time in the past, e.g.
'seconds_kernel_to_login' records the total seconds from kernel
startup to login screen ready.
@@ -25,6 +26,10 @@
'rdbytes_kernel_to_', and record bytes read from the boot device
since kernel startup.
+The firmware keyval names all start with the prefix
+'seconds_power_on_to_', and record time in seconds since CPU
+power on.
+
"""
import math
@@ -70,36 +75,47 @@
class TestResultSet(object):
"""A set of boot time and disk usage result statistics.
- Objects of this class consist of two sets of result statistics:
- the boot time statistics and the disk statistics.
+ Objects of this class consist of three sets of result statistics:
+ the boot time statistics, the disk statistics, and the firmware
+ time statistics.
Class TestResultsSet does not interpret or store keyval mappings
directly; iteration results are processed by attached _KeySet
- objects, one for boot time (`_timekeys`), one for disk read
- (`_diskkeys`). These attached _KeySet objects can be obtained
- with appropriate methods; various methods on these objects will
- calculate statistics on the results, and provide the raw data.
+ objects, one for each of the three types of result keyval. The
+ _KeySet objects are kept in a dictionary; they can be obtained
+ by calling the KeySet with the name of the keyset desired.
+ Various methods on the KeySet objects will calculate statistics on
+ the results, and provide the raw data.
"""
+ # The names of the available KeySets, to be used as arguments to
+ # KeySet().
+ BOOTTIME_KEYSET = "time"
+ DISK_KEYSET = "disk"
+ FIRMWARE_KEYSET = "firmware"
+
def __init__(self, name):
self.name = name
- self._timekeys = _TimeKeySet()
- self._diskkeys = _DiskKeySet()
+ self._keysets = {
+ self.BOOTTIME_KEYSET : _TimeKeySet(),
+ self.DISK_KEYSET : _DiskKeySet(),
+ self.FIRMWARE_KEYSET : _FirmwareKeySet(),
+ }
def AddIterationResults(self, runkeys):
"""Add keyval results from a single iteration.
A TestResultSet is constructed by repeatedly calling
- AddRunResults(), iteration by iteration. Iteration results are
- passed in as a dictionary mapping keyval attributes to values.
- When all iteration results have been added, FinalizeResults()
- makes the results available for analysis.
+ AddIterationResults(), iteration by iteration. Iteration
+ results are passed in as a dictionary mapping keyval attributes
+ to values. When all iteration results have been added,
+ FinalizeResults() makes the results available for analysis.
"""
- self._timekeys.AddRunResults(runkeys)
- self._diskkeys.AddRunResults(runkeys)
+ for keyset in self._keysets.itervalues():
+ keyset.AddIterationResults(runkeys)
def FinalizeResults(self):
"""Make results available for analysis.
@@ -112,46 +128,40 @@
"""
- self._timekeys.FinalizeResults()
- self._diskkeys.FinalizeResults()
+ for keyset in self._keysets.itervalues():
+ keyset.FinalizeResults()
- def TimeKeySet(self):
+ def KeySet(self, keytype):
"""Return the boot time statistics result set."""
- return self._timekeys
-
- def DiskKeySet(self):
- """Return the disk read statistics result set."""
- return self._diskkeys
+ return self._keysets[keytype]
class _KeySet(object):
"""Container for a set of related statistics.
_KeySet is an abstract superclass for containing collections of
- either boot time or disk read statistics. Statistics are stored
+ a related set of performance statistics. Statistics are stored
as a dictionary (`_keyvals`) mapping keyval names to lists of
- values.
+ values. The lists are indexed by the iteration number.
The mapped keyval names are shortened by stripping the prefix
- that identifies the type of prefix (keyvals that don't start with
+ that identifies the type of keyval (keyvals that don't start with
the proper prefix are ignored). So, for example, with boot time
keyvals, 'seconds_kernel_to_login' becomes 'login' (and
'rdbytes_kernel_to_login' is ignored).
A list of all valid keyval names is stored in the `markers`
instance variable. The list is sorted by the natural ordering of
- the underlying values (see the module comments for more details).
-
- The list of values associated with a given keyval name are indexed
- in the order in which they were added. So, all values for a given
- iteration are stored at the same index.
+ the underlying values. Each iteration is required to contain
+ the same set of keyvals. This is enforced in FinalizeResults()
+ (see below).
"""
def __init__(self):
self._keyvals = {}
- def AddRunResults(self, runkeys):
+ def AddIterationResults(self, runkeys):
"""Add results for one iteration."""
for key, value in runkeys.iteritems():
@@ -200,14 +210,12 @@
between two keys.
"""
-
return _ListStats(self.DeltaData(key0, key1))
class _TimeKeySet(_KeySet):
"""Concrete subclass of _KeySet for boot time statistics."""
- # TIME_KEY_PREFIX = 'seconds_kernel_to_'
PREFIX = 'seconds_kernel_to_'
# Time-based keyvals are reported in seconds and get converted to
@@ -215,15 +223,26 @@
TIME_SCALE = 1000
def _ConvertVal(self, value):
- # We use a "round to nearest int" formula here to make sure we
- # don't lose anything in the conversion from decimal.
- return int(self.TIME_SCALE * float(value) + 0.5)
+ # We want to return the nearest exact integer here. round()
+ # returns a float, and int() truncates its results, so we have
+ # to combine them.
+ return int(round(self.TIME_SCALE * float(value)))
def PrintableStatistic(self, value):
- v = int(value + 0.5)
+ v = int(round(value))
return ("%d" % v, v)
+class _FirmwareKeySet(_TimeKeySet):
+ """Concrete subclass of _KeySet for firmware time statistics."""
+
+ PREFIX = 'seconds_power_on_to_'
+
+ # Time-based keyvals are reported in seconds and get converted to
+ # milliseconds
+ TIME_SCALE = 1000
+
+
class _DiskKeySet(_KeySet):
"""Concrete subclass of _KeySet for disk read statistics."""
diff --git a/bootperf-bin/showbootdata b/bootperf-bin/showbootdata
index e474281..009698a 100755
--- a/bootperf-bin/showbootdata
+++ b/bootperf-bin/showbootdata
@@ -6,26 +6,28 @@
"""A command to display summary statistics from runs of 'bootperf'.
-Command line options allow selecting from one of two sets of
-performance statistics: The boot time statistics (selected by
---timestats) measure time spent from kernel startup in milliseconds.
-The disk statistics (selected by --diskstats) measure total bytes
-read from the boot device since kernel startup.
+Command line options allow selecting from one of three sets of
+performance statistics: boot time statistics (selected by
+--timestats) measure time spent since kernel startup; disk statistics
+(selected by --diskstats) measure total bytes read from the boot
+device since kernel startup; firmware time statistics (selected by
+--firmware) measure time spent since CPU power on.
-Boot time statistics are recorded as cumulative time (or disk read)
-since kernel startup, measured when specific events occur during
-boot. Events include such things as 'startup', the moment when the
-upstart 'startup' job begins running, and 'login', when the
-Chrome OS login screen is displayed. By default, all recorded events
-are included in the output; command line options allow restricting
-the view to a selected subset of events.
+The various statistics are recorded as cumulative time (or disk read)
+since kernel startup (or CPU power on), sampled when specific events
+occur during boot. Events include such things as 'startup', the
+moment when the upstart 'startup' job begins running, and 'login',
+when the Chrome OS login screen is displayed. By default, all
+recorded events are included in the output; command line options
+allow restricting the view to a selected subset of events.
Separate command line options allow selecting from one of two
different display modes. When --averages is selected, the display
shows the average value and sample standard deviation (as a percent
-of the average) for all selected events. The --averages display also
-calculates the time (or bytes) between adjacent events, and shows
-the avearage and sample standard deviation of the differences.
+of the average) for all selected events. The --averages display
+also calculates the difference (in time or bytes) between adjacent
+events, and shows the average and sample standard deviation of the
+differences.
The --rawdata display shows the raw data value associated with each
event for each boot: Each line of output represents the event values
@@ -36,8 +38,9 @@
import sys
import optparse
-import resultsdir
import perfprinter
+import resultsdir
+import resultset
_USAGE = "%prog [options] [results-directory ...]"
@@ -61,9 +64,14 @@
"-d", "--diskstats", action="store_true",
dest="use_diskstats",
help="use statistics for bytes read since kernel startup")
+ optgroup.add_option(
+ "-f", "--firmwarestats", action="store_true",
+ dest="use_firmware",
+ help="use statistics for firmware startup times")
optparser.add_option_group(optgroup)
optparser.set_defaults(use_diskstats=False)
optparser.set_defaults(use_timestats=False)
+ optparser.set_defaults(use_firmware=False)
optgroup = optparse.OptionGroup(optparser, "Event selection")
optgroup.add_option(
@@ -104,11 +112,22 @@
return printfunc
-def _ProcessStatsOptions(options):
- if options.use_timestats and options.use_diskstats:
- print >>sys.stderr, "Can't use -t and -d together.\n"
- return None
- return not options.use_diskstats
+def _ProcessKeysetOptions(options):
+ keyset_count = 0
+ keyset_type = resultset.TestResultSet.BOOTTIME_KEYSET
+ if options.use_timestats:
+ keyset_count += 1
+ keyset_type = resultset.TestResultSet.BOOTTIME_KEYSET
+ if options.use_diskstats:
+ keyset_count += 1
+ keyset_type = resultset.TestResultSet.DISK_KEYSET
+ if options.use_firmware:
+ keyset_count += 1
+ keyset_type = resultset.TestResultSet.FIRMWARE_KEYSET
+ if keyset_count > 1:
+ print >>sys.stderr, "Can use only one of -t, -d, or -f.\n"
+ keyset_type = None
+ return keyset_type
def _ProcessKeylistOptions(options):
@@ -124,15 +143,15 @@
optparser = _SetupOptions()
(options, args) = optparser.parse_args(argv)
printfunc = _ProcessDisplayOptions(options)
- use_timestats = _ProcessStatsOptions(options)
+ keyset_type = _ProcessKeysetOptions(options)
keylist = _ProcessKeylistOptions(options)
- if printfunc is None or use_timestats is None:
+ if printfunc is None or keyset_type is None:
optparser.print_help()
sys.exit(1)
if not args:
args = ["."]
printfunc(resultsdir.ReadResultsDirectory,
- args, use_timestats, keylist)
+ args, keyset_type, keylist)
if __name__ == "__main__":