1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4import json 5 6from telemetry import value as value_module 7from telemetry import perf_tests_helper 8from telemetry.value import histogram_util 9 10class HistogramValueBucket(object): 11 def __init__(self, low, high, count=0): 12 self.low = low 13 self.high = high 14 self.count = count 15 16 def AsDict(self): 17 return { 18 'low': self.low, 19 'high': self.high, 20 'count': self.count 21 } 22 23 def ToJSONString(self): 24 return '{%s}' % ', '.join([ 25 '"low": %i' % self.low, 26 '"high": %i' % self.high, 27 '"count": %i' % self.count]) 28 29class HistogramValue(value_module.Value): 30 def __init__(self, page, name, units, 31 raw_value=None, raw_value_json=None, important=True, 32 description=None): 33 super(HistogramValue, self).__init__(page, name, units, important, 34 description) 35 if raw_value_json: 36 assert raw_value == None, \ 37 'Don\'t specify both raw_value and raw_value_json' 38 raw_value = json.loads(raw_value_json) 39 if raw_value: 40 assert 'buckets' in raw_value 41 assert isinstance(raw_value['buckets'], list) 42 self.buckets = [] 43 for bucket in raw_value['buckets']: 44 self.buckets.append(HistogramValueBucket( 45 low=bucket['low'], 46 high=bucket['high'], 47 count=bucket['count'])) 48 else: 49 self.buckets = [] 50 51 def __repr__(self): 52 if self.page: 53 page_name = self.page.url 54 else: 55 page_name = None 56 return ('HistogramValue(%s, %s, %s, raw_json_string="%s", ' 57 'important=%s, description=%s') % ( 58 page_name, 59 self.name, self.units, 60 self.ToJSONString(), 61 self.important, 62 self.description) 63 64 def GetBuildbotDataType(self, output_context): 65 if self._IsImportantGivenOutputIntent(output_context): 66 return 'histogram' 67 return 'unimportant-histogram' 68 69 def GetBuildbotValue(self): 70 # More buildbot insanity: perf_tests_results_helper requires the histogram 71 # to be an array of size one. 72 return [self.ToJSONString()] 73 74 def ToJSONString(self): 75 # This has to hand-JSONify the histogram to ensure the order of keys 76 # produced is stable across different systems. 77 # 78 # This is done because the buildbot unittests are string equality 79 # assertions. Thus, tests that contain histograms require stable 80 # stringification of the histogram. 81 # 82 # Sigh, buildbot, Y U gotta be that way. 83 return '{"buckets": [%s]}' % ( 84 ', '.join([b.ToJSONString() for b in self.buckets])) 85 86 def GetRepresentativeNumber(self): 87 (mean, _) = perf_tests_helper.GeomMeanAndStdDevFromHistogram( 88 self.ToJSONString()) 89 return mean 90 91 def GetRepresentativeString(self): 92 return self.GetBuildbotValue() 93 94 @staticmethod 95 def GetJSONTypeName(): 96 return 'histogram' 97 98 def AsDict(self): 99 d = super(HistogramValue, self).AsDict() 100 d['buckets'] = [b.AsDict() for b in self.buckets] 101 return d 102 103 @staticmethod 104 def FromDict(value_dict, page_dict): 105 kwargs = value_module.Value.GetConstructorKwArgs(value_dict, page_dict) 106 kwargs['raw_value'] = value_dict 107 108 return HistogramValue(**kwargs) 109 110 @classmethod 111 def MergeLikeValuesFromSamePage(cls, values): 112 assert len(values) > 0 113 v0 = values[0] 114 return HistogramValue( 115 v0.page, v0.name, v0.units, 116 raw_value_json=histogram_util.AddHistograms( 117 [v.ToJSONString() for v in values]), 118 important=v0.important) 119 120 @classmethod 121 def MergeLikeValuesFromDifferentPages(cls, values, 122 group_by_name_suffix=False): 123 # Histograms cannot be merged across pages, at least for now. It should be 124 # theoretically possible, just requires more work. Instead, return None. 125 # This signals to the merging code that the data is unmergable and it will 126 # cope accordingly. 127 return None 128