| Index: tools/telemetry/telemetry/value/__init__.py
|
| diff --git a/tools/telemetry/telemetry/value/__init__.py b/tools/telemetry/telemetry/value/__init__.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..08d623c85d9030f4a49234d34523e64d979c046b
|
| --- /dev/null
|
| +++ b/tools/telemetry/telemetry/value/__init__.py
|
| @@ -0,0 +1,182 @@
|
| +# Copyright (c) 2013 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.
|
| +"""
|
| +The Value hierarchy provides a way of representing the values measurements
|
| +produce such that they can be merged across runs, grouped by page, and output
|
| +to different targets.
|
| +
|
| +The core Value concept provides the basic functionality:
|
| +- association with a page, may be none
|
| +- naming and units
|
| +- importance tracking [whether a value will show up on a waterfall or output
|
| + file by default]
|
| +- default conversion to scalar and string
|
| +- merging properties
|
| +
|
| +A page may actually run a few times during a single telemetry session.
|
| +Downstream consumers of test results typically want to group these runs
|
| +together, then compute summary statistics across runs. Value provides the
|
| +Merge* family of methods for this kind of aggregation.
|
| +"""
|
| +
|
| +# When combining a pair of Values togehter, it is sometimes ambiguous whether
|
| +# the values should be concatenated, or one should be picked as representative.
|
| +# The possible merging policies are listed here.
|
| +CONCATENATE = 'concatenate'
|
| +PICK_FIRST = 'pick-first'
|
| +
|
| +# When converting a Value to its buildbot equivalent, the context in which the
|
| +# value is being interpreted actually affects the conversion. This is insane,
|
| +# but there you have it. There are three contexts in which Values are converted
|
| +# for use by buildbot, represented by these output-intent values.
|
| +PER_PAGE_RESULT_OUTPUT_CONTEXT = 'per-page-result-output-context'
|
| +MERGED_PAGES_RESULT_OUTPUT_CONTEXT = 'merged-pages-result-output-context'
|
| +SUMMARY_RESULT_OUTPUT_CONTEXT = 'summary-result-output-context'
|
| +
|
| +class Value(object):
|
| + """An abstract value produced by a telemetry page test.
|
| + """
|
| + def __init__(self, page, name, units, important):
|
| + """A generic Value object.
|
| +
|
| + Note: page may be given as None to indicate that the value represents
|
| + results multiple pages.
|
| + """
|
| + self.page = page
|
| + self.name = name
|
| + self.units = units
|
| + self.important = important
|
| +
|
| + def IsMergableWith(self, that):
|
| + return (self.units == that.units and
|
| + type(self) == type(that) and
|
| + self.important == that.important)
|
| +
|
| + @classmethod
|
| + def MergeLikeValuesFromSamePage(cls, values):
|
| + """Combines the provided list of values into a single compound value.
|
| +
|
| + When a page runs multiple times, it may produce multiple values. This
|
| + function is given the same-named values across the multiple runs, and has
|
| + the responsibility of producing a single result.
|
| +
|
| + It must return a single Value. If merging does not make sense, the
|
| + implementation must pick a representative value from one of the runs.
|
| +
|
| + For instance, it may be given
|
| + [ScalarValue(page, 'a', 1), ScalarValue(page, 'a', 2)]
|
| + and it might produce
|
| + ListOfScalarValues(page, 'a', [1, 2])
|
| + """
|
| + raise NotImplementedError()
|
| +
|
| + @classmethod
|
| + def MergeLikeValuesFromDifferentPages(cls, values,
|
| + group_by_name_suffix=False):
|
| + """Combines the provided values into a single compound value.
|
| +
|
| + When a full pageset runs, a single value_name will usually end up getting
|
| + collected for multiple pages. For instance, we may end up with
|
| + [ScalarValue(page1, 'a', 1),
|
| + ScalarValue(page2, 'a', 2)]
|
| +
|
| + This function takes in the values of the same name, but across multiple
|
| + pages, and produces a single summary result value. In this instance, it
|
| + could produce a ScalarValue(None, 'a', 1.5) to indicate averaging, or even
|
| + ListOfScalarValues(None, 'a', [1, 2]) if concatenated output was desired.
|
| +
|
| + Some results are so specific to a page that they make no sense when
|
| + aggregated across pages. If merging values of this type across pages is
|
| + non-sensical, this method may return None.
|
| +
|
| + If group_by_name_suffix is True, then x.z and y.z are considered to be the
|
| + same value and are grouped together. If false, then x.z and y.z are
|
| + considered different.
|
| + """
|
| + raise NotImplementedError()
|
| +
|
| + def _IsImportantGivenOutputIntent(self, output_context):
|
| + if output_context == PER_PAGE_RESULT_OUTPUT_CONTEXT:
|
| + return False
|
| + elif output_context == MERGED_PAGES_RESULT_OUTPUT_CONTEXT:
|
| + return self.important
|
| + elif output_context == SUMMARY_RESULT_OUTPUT_CONTEXT:
|
| + return self.important
|
| +
|
| + def GetBuildbotDataType(self, output_context):
|
| + """Returns the buildbot's equivalent data_type.
|
| +
|
| + This should be one of the values accepted by perf_tests_results_helper.py.
|
| + """
|
| + raise NotImplementedError()
|
| +
|
| + def GetBuildbotValue(self):
|
| + """Returns the buildbot's equivalent value."""
|
| + raise NotImplementedError()
|
| +
|
| + def GetBuildbotMeasurementAndTraceNameForPerPageResult(self):
|
| + measurement, _ = (
|
| + _ConvertValueNameToBuildbotChartAndTraceName(self.name))
|
| + return measurement + '_by_url', self.page.display_name
|
| +
|
| + @property
|
| + def name_suffix(self):
|
| + """Returns the string after a . in the name, or the full name otherwise."""
|
| + if '.' in self.name:
|
| + return self.name.split('.', 1)[1]
|
| + else:
|
| + return self.name
|
| +
|
| + def GetBuildbotMeasurementAndTraceNameForMergedPagesResult(self, trace_tag):
|
| + measurement, bb_trace_name = (
|
| + _ConvertValueNameToBuildbotChartAndTraceName(self.name))
|
| + if trace_tag:
|
| + return measurement, bb_trace_name + trace_tag
|
| + else:
|
| + return measurement, bb_trace_name
|
| +
|
| + def GetRepresentativeNumber(self):
|
| + """Gets a single scalar value that best-represents this value.
|
| +
|
| + Returns None if not possible.
|
| + """
|
| + raise NotImplementedError()
|
| +
|
| + def GetRepresentativeString(self):
|
| + """Gets a string value that best-represents this value.
|
| +
|
| + Returns None if not possible.
|
| + """
|
| + raise NotImplementedError()
|
| +
|
| +def ValueNameFromTraceAndChartName(trace_name, chart_name=None):
|
| + """Mangles a trace name plus optional chart name into a standard string.
|
| +
|
| + A value might just be a bareword name, e.g. numPixels. In that case, its
|
| + chart may be None.
|
| +
|
| + But, a value might also be intended for display with other values, in which
|
| + case the chart name indicates that grouping. So, you might have
|
| + screen.numPixels, screen.resolution, where chartName='screen'.
|
| + """
|
| + assert trace_name != 'url', 'The name url cannot be used'
|
| + if chart_name:
|
| + return '%s.%s' % (chart_name, trace_name)
|
| + else:
|
| + return trace_name
|
| +
|
| +def _ConvertValueNameToBuildbotChartAndTraceName(value_name):
|
| + """Converts a value_name into the buildbot equivalent name pair.
|
| +
|
| + Buildbot represents values by the measurement name and an optional trace name,
|
| + whereas telemetry represents values with a chart_name.trace_name convention,
|
| + where chart_name is optional.
|
| +
|
| + This converts from the telemetry convention to the buildbot convention,
|
| + returning a 2-tuple (measurement_name, trace_name).
|
| + """
|
| + if '.' in value_name:
|
| + return value_name.split('.', 1)
|
| + else:
|
| + return value_name, value_name
|
|
|