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. 4 5"""This is a helper module to get and manipulate histogram data. 6 7The histogram data is the same data as is visible from "chrome://histograms". 8More information can be found at: chromium/src/base/metrics/histogram.h 9""" 10 11import collections 12import json 13import logging 14 15from telemetry.core import exceptions 16 17BROWSER_HISTOGRAM = 'browser_histogram' 18RENDERER_HISTOGRAM = 'renderer_histogram' 19 20 21def GetHistogramBucketsFromJson(histogram_json): 22 return GetHistogramBucketsFromRawValue(json.loads(histogram_json)) 23 24 25def GetHistogramBucketsFromRawValue(raw_value): 26 buckets = raw_value.get('buckets', []) 27 if buckets: 28 # If there are values greater than the maximum allowable for the histogram, 29 # the highest bucket will have a 'low': maxvalue entry in the dict but no 30 # 'high' entry. Code often assumes the 'high' value will always be present, 31 # and uses it to get bucket mean. So default it to the same value as low. 32 buckets[-1].setdefault('high', buckets[-1]['low']) 33 return buckets 34 35 36def CustomizeBrowserOptions(options): 37 """Allows histogram collection.""" 38 options.AppendExtraBrowserArgs(['--enable-stats-collection-bindings']) 39 40 41def SubtractHistogram(histogram_json, start_histogram_json): 42 """Subtracts a previous histogram from a histogram. 43 44 Both parameters and the returned result are json serializations. 45 """ 46 start_histogram = json.loads(start_histogram_json) 47 start_histogram_buckets = GetHistogramBucketsFromRawValue(start_histogram) 48 # It's ok if the start histogram is empty (we had no data, maybe even no 49 # histogram at all, at the start of the test). 50 if not start_histogram_buckets: 51 return histogram_json 52 53 histogram = json.loads(histogram_json) 54 if ('pid' in start_histogram and 'pid' in histogram 55 and start_histogram['pid'] != histogram['pid']): 56 raise Exception( 57 'Trying to compare histograms from different processes (%d and %d)' 58 % (start_histogram['pid'], histogram['pid'])) 59 60 start_histogram_bucket_counts = dict() 61 for b in start_histogram_buckets: 62 start_histogram_bucket_counts[b['low']] = b['count'] 63 64 new_buckets = [] 65 for b in GetHistogramBucketsFromRawValue(histogram): 66 new_bucket = b 67 low = b['low'] 68 if low in start_histogram_bucket_counts: 69 new_bucket['count'] = b['count'] - start_histogram_bucket_counts[low] 70 if new_bucket['count'] < 0: 71 logging.error('Histogram subtraction error, starting histogram most ' 72 'probably invalid.') 73 if new_bucket['count']: 74 new_buckets.append(new_bucket) 75 histogram['buckets'] = new_buckets 76 histogram['count'] -= start_histogram['count'] 77 78 return json.dumps(histogram) 79 80 81def AddHistograms(histogram_jsons): 82 """Adds histograms together. Used for aggregating data. 83 84 The parameter is a list of json serializations and the returned result is a 85 json serialization too. 86 87 Note that the histograms to be added together are typically from different 88 processes. 89 """ 90 91 buckets = collections.defaultdict(int) 92 for histogram_json in histogram_jsons: 93 for b in GetHistogramBucketsFromJson(histogram_json): 94 key = (b['low'], b['high']) 95 buckets[key] += b['count'] 96 97 buckets = [{'low': key[0], 'high': key[1], 'count': value} 98 for key, value in buckets.iteritems()] 99 buckets.sort(key=lambda h: h['low']) 100 101 result_histogram = {} 102 result_histogram['buckets'] = buckets 103 return json.dumps(result_histogram) 104 105 106def GetHistogram(histogram_type, histogram_name, tab): 107 """Get a json serialization of a histogram.""" 108 assert histogram_type in [BROWSER_HISTOGRAM, RENDERER_HISTOGRAM] 109 function = 'getHistogram' 110 if histogram_type == BROWSER_HISTOGRAM: 111 function = 'getBrowserHistogram' 112 try: 113 histogram_json = tab.EvaluateJavaScript( 114 'statsCollectionController.%s("%s")' % 115 (function, histogram_name)) 116 except exceptions.EvaluateException: 117 # Sometimes JavaScript flakily fails to execute: http://crbug.com/508431 118 histogram_json = None 119 if histogram_json: 120 return histogram_json 121 return None 122 123 124def GetHistogramCount(histogram_type, histogram_name, tab): 125 """Get the count of events for the given histograms.""" 126 histogram_json = GetHistogram(histogram_type, histogram_name, tab) 127 histogram = json.loads(histogram_json) 128 if 'count' in histogram: 129 return histogram['count'] 130 else: 131 return 0 132 133def GetHistogramSum(histogram_type, histogram_name, tab): 134 """Get the sum of events for the given histograms.""" 135 histogram_json = GetHistogram(histogram_type, histogram_name, tab) 136 histogram = json.loads(histogram_json) 137 if 'sum' in histogram: 138 return histogram['sum'] 139 else: 140 return 0 141