blob: 86ead549579eb49de9d2b4ecc094769d34d8ca74 [file] [log] [blame]
bensong@google.com0d9e6de2013-10-15 00:08:14 +00001'''
2Created on May 16, 2011
3
4@author: bungeman
5'''
6import bench_util
7import getopt
8import httplib
9import itertools
10import json
11import os
12import re
13import sys
14import urllib
15import urllib2
16import xml.sax.saxutils
17
18# Maximum expected number of characters we expect in an svn revision.
19MAX_SVN_REV_LENGTH = 5
20
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +000021# Indices for getting elements from bench expectation files.
22# See bench_expectations_<builder>.txt for details.
23EXPECTED_IDX = -3
24LB_IDX = -2
25UB_IDX = -1
26
27# Indices of the tuple of dictionaries containing slower and faster alerts.
28SLOWER = 0
29FASTER = 1
30
bensong@google.com0d9e6de2013-10-15 00:08:14 +000031def usage():
32 """Prints simple usage information."""
33
34 print '-a <representation_alg> bench representation algorithm to use. '
35 print ' Defaults to "25th". See bench_util.py for details.'
36 print '-b <builder> name of the builder whose bench data we are checking.'
37 print '-d <dir> a directory containing bench_<revision>_<scalar> files.'
38 print '-e <file> file containing expected bench builder values/ranges.'
39 print ' Will raise exception if actual bench values are out of range.'
40 print ' See bench_expectations_<builder>.txt for data format / examples.'
41 print '-r <revision> the git commit hash or svn revision for checking '
42 print ' bench values.'
43
44
45class Label:
46 """The information in a label.
47
48 (str, str, str, str, {str:str})"""
49 def __init__(self, bench, config, time_type, settings):
50 self.bench = bench
51 self.config = config
52 self.time_type = time_type
53 self.settings = settings
54
55 def __repr__(self):
56 return "Label(%s, %s, %s, %s)" % (
57 str(self.bench),
58 str(self.config),
59 str(self.time_type),
60 str(self.settings),
61 )
62
63 def __str__(self):
64 return "%s_%s_%s_%s" % (
65 str(self.bench),
66 str(self.config),
67 str(self.time_type),
68 str(self.settings),
69 )
70
71 def __eq__(self, other):
72 return (self.bench == other.bench and
73 self.config == other.config and
74 self.time_type == other.time_type and
75 self.settings == other.settings)
76
77 def __hash__(self):
78 return (hash(self.bench) ^
79 hash(self.config) ^
80 hash(self.time_type) ^
81 hash(frozenset(self.settings.iteritems())))
82
bensong@google.com0d9e6de2013-10-15 00:08:14 +000083def create_bench_dict(revision_data_points):
84 """Convert current revision data into a dictionary of line data.
85
86 Args:
87 revision_data_points: a list of bench data points
88
89 Returns:
90 a dictionary of this form:
91 keys = Label objects
92 values = the corresponding bench value
93 """
94 bench_dict = {}
95 for point in revision_data_points:
96 point_name = Label(point.bench,point.config,point.time_type,
97 point.settings)
98 if point_name not in bench_dict:
99 bench_dict[point_name] = point.time
100 else:
101 raise Exception('Duplicate expectation entry: ' + str(point_name))
102
103 return bench_dict
104
105def read_expectations(expectations, filename):
106 """Reads expectations data from file and put in expectations dict."""
107 for expectation in open(filename).readlines():
108 elements = expectation.strip().split(',')
109 if not elements[0] or elements[0].startswith('#'):
110 continue
111 if len(elements) != 5:
112 raise Exception("Invalid expectation line format: %s" %
113 expectation)
114 bench_entry = elements[0] + ',' + elements[1]
115 if bench_entry in expectations:
116 raise Exception("Dup entries for bench expectation %s" %
117 bench_entry)
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +0000118 # [<Bench_BmpConfig_TimeType>,<Platform-Alg>] -> (LB, UB, EXPECTED)
119 expectations[bench_entry] = (float(elements[LB_IDX]),
120 float(elements[UB_IDX]),
121 float(elements[EXPECTED_IDX]))
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000122
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +0000123def check_expectations(lines, expectations, key_suffix):
124 """Check if any bench results are outside of expected range.
125
126 For each input line in lines, checks the expectations dictionary to see if
127 the bench is out of the given range.
128
129 Args:
130 lines: dictionary mapping Label objects to the bench values.
131 expectations: dictionary returned by read_expectations().
132 key_suffix: string of <Platform>-<Alg> containing the bot platform and the
133 bench representation algorithm.
134
135 Returns:
136 No return value.
137
138 Raises:
139 Exception containing bench data that are out of range, if any.
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000140 """
141 # The platform for this bot, to pass to the dashboard plot.
142 platform = key_suffix[ : key_suffix.rfind('-')]
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +0000143 # Tuple of dictionaries recording exceptions that are slower and faster,
144 # respectively. Each dictionary maps off_ratio (ratio of actual to expected)
145 # to a list of corresponding exception messages.
146 exceptions = ({}, {})
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000147 for line in lines:
148 line_str = str(line)
149 line_str = line_str[ : line_str.find('_{')]
150 bench_platform_key = line_str + ',' + key_suffix
151 if bench_platform_key not in expectations:
152 continue
153 this_bench_value = lines[line]
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +0000154 this_min, this_max, this_expected = expectations[bench_platform_key]
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000155 if this_bench_value < this_min or this_bench_value > this_max:
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +0000156 off_ratio = this_bench_value / this_expected
157 exception = 'Bench %s out of range [%s, %s] (%s vs %s, %s%%).' % (
158 bench_platform_key, this_min, this_max, this_bench_value,
159 this_expected, (off_ratio - 1) * 100)
160 if off_ratio > 1: # Bench is slower.
161 exceptions[SLOWER].setdefault(off_ratio, []).append(exception)
162 else:
163 exceptions[FASTER].setdefault(off_ratio, []).append(exception)
164 outputs = []
165 for i in [SLOWER, FASTER]:
166 if exceptions[i]:
167 ratios = exceptions[i].keys()
168 ratios.sort(reverse=True)
169 li = []
170 for ratio in ratios:
171 li.extend(exceptions[i][ratio])
172 header = '%s benches got slower (sorted by %% difference):' % len(li)
173 if i == FASTER:
174 header = header.replace('slower', 'faster')
175 outputs.extend(['', header] + li)
176
177 if outputs:
commit-bot@chromium.orgcb3f0792014-03-28 17:55:51 +0000178 # Directly raising Exception will have stderr outputs tied to the line
179 # number of the script, so use sys.stderr.write() instead.
180 # Add a trailing newline to supress new line checking errors.
181 sys.stderr.write('\n'.join(['Exception:'] + outputs + ['\n']))
182 exit(1)
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000183
commit-bot@chromium.orgb1bcb212014-03-17 21:16:29 +0000184
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000185def main():
186 """Parses command line and checks bench expectations."""
187 try:
188 opts, _ = getopt.getopt(sys.argv[1:],
189 "a:b:d:e:r:",
190 "default-setting=")
191 except getopt.GetoptError, err:
192 print str(err)
193 usage()
194 sys.exit(2)
195
196 directory = None
197 bench_expectations = {}
198 rep = '25th' # bench representation algorithm, default to 25th
199 rev = None # git commit hash or svn revision number
200 bot = None
201
202 try:
203 for option, value in opts:
204 if option == "-a":
205 rep = value
206 elif option == "-b":
207 bot = value
208 elif option == "-d":
209 directory = value
210 elif option == "-e":
211 read_expectations(bench_expectations, value)
212 elif option == "-r":
213 rev = value
214 else:
215 usage()
216 assert False, "unhandled option"
217 except ValueError:
218 usage()
219 sys.exit(2)
220
221 if directory is None or bot is None or rev is None:
222 usage()
223 sys.exit(2)
224
225 platform_and_alg = bot + '-' + rep
226
commit-bot@chromium.orgb1bcb212014-03-17 21:16:29 +0000227 data_points = bench_util.parse_skp_bench_data(directory, rev, rep)
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000228
229 bench_dict = create_bench_dict(data_points)
230
231 if bench_expectations:
commit-bot@chromium.org557e3cd2014-02-20 15:58:32 +0000232 check_expectations(bench_dict, bench_expectations, platform_and_alg)
bensong@google.com0d9e6de2013-10-15 00:08:14 +0000233
234
235if __name__ == "__main__":
236 main()