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 import re 6 import sys 7 8 import json 9 import logging 10 import math 11 12 import perf_result_data_type 13 14 15 # Mapping from result type to test output 16 RESULT_TYPES = {perf_result_data_type.UNIMPORTANT: 'RESULT ', 17 perf_result_data_type.DEFAULT: '*RESULT ', 18 perf_result_data_type.INFORMATIONAL: '', 19 perf_result_data_type.UNIMPORTANT_HISTOGRAM: 'HISTOGRAM ', 20 perf_result_data_type.HISTOGRAM: '*HISTOGRAM '} 21 22 23 def _EscapePerfResult(s): 24 """Escapes |s| for use in a perf result.""" 25 return re.sub('[\:|=/#&,]', '_', s) 26 27 28 def FlattenList(values): 29 """Returns a simple list without sub-lists.""" 30 ret = [] 31 for entry in values: 32 if isinstance(entry, list): 33 ret.extend(FlattenList(entry)) 34 else: 35 ret.append(entry) 36 return ret 37 38 39 def GeomMeanAndStdDevFromHistogram(histogram_json): 40 histogram = json.loads(histogram_json) 41 # Handle empty histograms gracefully. 42 if not 'buckets' in histogram: 43 return 0.0, 0.0 44 count = 0 45 sum_of_logs = 0 46 for bucket in histogram['buckets']: 47 if 'high' in bucket: 48 bucket['mean'] = (bucket['low'] + bucket['high']) / 2.0 49 else: 50 bucket['mean'] = bucket['low'] 51 if bucket['mean'] > 0: 52 sum_of_logs += math.log(bucket['mean']) * bucket['count'] 53 count += bucket['count'] 54 55 if count == 0: 56 return 0.0, 0.0 57 58 sum_of_squares = 0 59 geom_mean = math.exp(sum_of_logs / count) 60 for bucket in histogram['buckets']: 61 if bucket['mean'] > 0: 62 sum_of_squares += (bucket['mean'] - geom_mean) ** 2 * bucket['count'] 63 return geom_mean, math.sqrt(sum_of_squares / count) 64 65 66 def _ValueToString(v): 67 # Special case for floats so we don't print using scientific notation. 68 if isinstance(v, float): 69 return '%f' % v 70 else: 71 return str(v) 72 73 74 def _MeanAndStdDevFromList(values): 75 avg = None 76 sd = None 77 if len(values) > 1: 78 try: 79 value = '[%s]' % ','.join([_ValueToString(v) for v in values]) 80 avg = sum([float(v) for v in values]) / len(values) 81 sqdiffs = [(float(v) - avg) ** 2 for v in values] 82 variance = sum(sqdiffs) / (len(values) - 1) 83 sd = math.sqrt(variance) 84 except ValueError: 85 value = ', '.join(values) 86 else: 87 value = values[0] 88 return value, avg, sd 89 90 91 def PrintPages(page_list): 92 """Prints list of pages to stdout in the format required by perf tests.""" 93 print 'Pages: [%s]' % ','.join([_EscapePerfResult(p) for p in page_list]) 94 95 96 def PrintPerfResult(measurement, trace, values, units, 97 result_type=perf_result_data_type.DEFAULT, 98 print_to_stdout=True): 99 """Prints numerical data to stdout in the format required by perf tests. 100 101 The string args may be empty but they must not contain any colons (:) or 102 equals signs (=). 103 This is parsed by the buildbot using: 104 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/process_log_utils.py 105 106 Args: 107 measurement: A description of the quantity being measured, e.g. "vm_peak". 108 On the dashboard, this maps to a particular graph. Mandatory. 109 trace: A description of the particular data point, e.g. "reference". 110 On the dashboard, this maps to a particular "line" in the graph. 111 Mandatory. 112 values: A list of numeric measured values. An N-dimensional list will be 113 flattened and treated as a simple list. 114 units: A description of the units of measure, e.g. "bytes". 115 result_type: Accepts values of perf_result_data_type.ALL_TYPES. 116 print_to_stdout: If True, prints the output in stdout instead of returning 117 the output to caller. 118 119 Returns: 120 String of the formated perf result. 121 """ 122 assert perf_result_data_type.IsValidType(result_type), \ 123 'result type: %s is invalid' % result_type 124 125 trace_name = _EscapePerfResult(trace) 126 127 if (result_type == perf_result_data_type.UNIMPORTANT or 128 result_type == perf_result_data_type.DEFAULT or 129 result_type == perf_result_data_type.INFORMATIONAL): 130 assert isinstance(values, list) 131 assert '/' not in measurement 132 flattened_values = FlattenList(values) 133 assert len(flattened_values) 134 value, avg, sd = _MeanAndStdDevFromList(flattened_values) 135 output = '%s%s: %s%s%s %s' % ( 136 RESULT_TYPES[result_type], 137 _EscapePerfResult(measurement), 138 trace_name, 139 # Do not show equal sign if the trace is empty. Usually it happens when 140 # measurement is enough clear to describe the result. 141 '= ' if trace_name else '', 142 value, 143 units) 144 else: 145 assert perf_result_data_type.IsHistogram(result_type) 146 assert isinstance(values, list) 147 # The histograms can only be printed individually, there's no computation 148 # across different histograms. 149 assert len(values) == 1 150 value = values[0] 151 output = '%s%s: %s= %s %s' % ( 152 RESULT_TYPES[result_type], 153 _EscapePerfResult(measurement), 154 trace_name, 155 value, 156 units) 157 avg, sd = GeomMeanAndStdDevFromHistogram(value) 158 159 if avg: 160 output += '\nAvg %s: %f%s' % (measurement, avg, units) 161 if sd: 162 output += '\nSd %s: %f%s' % (measurement, sd, units) 163 if print_to_stdout: 164 print output 165 sys.stdout.flush() 166 return output 167