1b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org#!/usr/bin/env python
2b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org# Copyright (c) 2014 The Chromium Authors. All rights reserved.
3b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org# Use of this source code is governed by a BSD-style license that can be
4b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org# found in the LICENSE file.
5b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
6b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org""" Generate bench_expectations file from a given set of bench data files. """
7b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
8b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgimport argparse
9b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgimport bench_util
10fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlyimport json
11b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgimport os
12b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgimport re
13b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgimport sys
14fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlyimport urllib2
15b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
16b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org# Parameters for calculating bench ranges.
17c9c5c42bbbe7574f7758f7cb0a4f5666f0253061commit-bot@chromium.orgRANGE_RATIO_UPPER = 1.5  # Ratio of range for upper bounds.
18379475f709dc8d62a4f661d464995b687ec06cafcommit-bot@chromium.orgRANGE_RATIO_LOWER = 2.0  # Ratio of range for lower bounds.
1902a5a09334eddd8003933948a8ea13e249a4a9ddcommit-bot@chromium.orgERR_RATIO = 0.08  # Further widens the range by the ratio of average value.
20c9c5c42bbbe7574f7758f7cb0a4f5666f0253061commit-bot@chromium.orgERR_UB = 1.0  # Adds an absolute upper error to cope with small benches.
2159d318b9f768fefd5ce9633d3dbc66954939d7c9commit-bot@chromium.orgERR_LB = 1.5
22b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
23b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org# List of bench configs to monitor. Ignore all other configs.
24b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgCONFIGS_TO_INCLUDE = ['simple_viewport_1000x1000',
255f640a30b92b7cf926ff1168b386882182bcb3bbcommit-bot@chromium.org                      'simple_viewport_1000x1000_angle',
26b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org                      'simple_viewport_1000x1000_gpu',
27b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org                      'simple_viewport_1000x1000_scalar_1.100000',
28b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org                      'simple_viewport_1000x1000_scalar_1.100000_gpu',
29b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org                     ]
30b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
311961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# List of flaky entries that should be excluded. Each entry is defined by a list
321961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# of 3 strings, corresponding to the substrings of [bench, config, builder] to
331961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# search for. A bench expectations line is excluded when each of the 3 strings
341961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# in the list is a substring of the corresponding element of the given line. For
351961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# instance, ['desk_yahooanswers', 'gpu', 'Ubuntu'] will skip expectation entries
361961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# of SKP benchs whose name contains 'desk_yahooanswers' on all gpu-related
371961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org# configs of all Ubuntu builders.
381961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.orgENTRIES_TO_EXCLUDE = [
391961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org                     ]
408cb46b963a20e65a64086a1c3e105dc858f22fb3commit-bot@chromium.org
41fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly_GS_CLOUD_FORMAT = 'http://storage.googleapis.com/chromium-skia-gm/perfdata/%s/%s'
42b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
43fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlydef compute_ranges(benches, more_benches=None):
44b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  """Given a list of bench numbers, calculate the alert range.
45b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
46b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  Args:
47b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    benches: a list of float bench values.
48fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    more_benches: a tuple of lists of additional bench values.
49fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      The first value of each tuple is the number of commits before the current
50fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      one that set of values is at, and the second value is a list of
51fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      bench results.
52b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
53b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  Returns:
54b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    a list of float [lower_bound, upper_bound].
55b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  """
56607953200dba71ba378c429de957f9739f02d428kelvinly  avg = sum(benches)/len(benches)
57607953200dba71ba378c429de957f9739f02d428kelvinly  minimum = min(benches)
58607953200dba71ba378c429de957f9739f02d428kelvinly  maximum = max(benches)
59e8433c36e06aa6657bdc1794360630dadd98f8bfbensong  diff = maximum - minimum
60b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
61607953200dba71ba378c429de957f9739f02d428kelvinly  return [minimum - diff*RANGE_RATIO_LOWER - avg*ERR_RATIO - ERR_LB,
62607953200dba71ba378c429de957f9739f02d428kelvinly          maximum + diff*RANGE_RATIO_UPPER + avg*ERR_RATIO + ERR_UB]
63b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
64b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
65fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlydef create_expectations_dict(revision_data_points, builder, extra_data=None):
66b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  """Convert list of bench data points into a dictionary of expectations data.
67b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
68b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  Args:
69b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    revision_data_points: a list of BenchDataPoint objects.
701961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org    builder: string of the corresponding buildbot builder name.
71b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
72b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  Returns:
73b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    a dictionary of this form:
74b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        keys = tuple of (config, bench) strings.
75b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        values = list of float [expected, lower_bound, upper_bound] for the key.
76b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  """
77b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  bench_dict = {}
78b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  for point in revision_data_points:
79b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    if (point.time_type or  # Not walltime which has time_type ''
801961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org        not point.config in CONFIGS_TO_INCLUDE):
811961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org      continue
821961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org    to_skip = False
831961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org    for bench_substr, config_substr, builder_substr in ENTRIES_TO_EXCLUDE:
841961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org      if (bench_substr in point.bench and config_substr in point.config and
851961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org          builder_substr in builder):
861961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org        to_skip = True
871961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org        break
881961c08a38573627972ebb6d0082162cf6bb449ccommit-bot@chromium.org    if to_skip:
89b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org      continue
90b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    key = (point.config, point.bench)
91fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
92fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    extras = []
93fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    for idx, dataset in extra_data:
94fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      for data in dataset:
95fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        if (data.bench == point.bench and data.config == point.config and
96fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly              data.time_type == point.time_type and data.per_iter_time):
97fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly          extras.append((idx, data.per_iter_time))
98fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
99b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    if key in bench_dict:
100b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org      raise Exception('Duplicate bench entry: ' + str(key))
101fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    bench_dict[key] = [point.time] + compute_ranges(point.per_iter_time, extras)
102b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
103b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org  return bench_dict
104b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
105b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
106fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlydef get_parent_commits(start_hash, num_back):
107fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  """Returns a list of commits that are the parent of the commit passed in."""
108fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  list_commits = urllib2.urlopen(
109fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      'https://skia.googlesource.com/skia/+log/%s?format=json&n=%d' %
110fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      (start_hash, num_back))
111fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  # NOTE: Very brittle. Removes the four extraneous characters
112fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  # so json can be read successfully
113fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  trunc_list = list_commits.read()[4:]
114fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  json_data = json.loads(trunc_list)
115fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  return [revision['commit'] for revision in json_data['log']]
116fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
117fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
118fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlydef get_file_suffixes(commit_hash, directory):
119fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  """Gets all the suffixes available in the directory"""
120fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  possible_files = os.listdir(directory)
121fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  prefix = 'bench_' + commit_hash + '_data_'
122fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  return [name[len(prefix):] for name in possible_files
123fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      if name.startswith(prefix)]
124fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
125fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
126fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinlydef download_bench_data(builder, commit_hash, suffixes, directory):
127fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  """Downloads data, returns the number successfully downloaded"""
128fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  cur_files = os.listdir(directory)
129fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  count = 0
130fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  for suffix in suffixes:
131fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    file_name = 'bench_'+commit_hash+'_data_'+suffix
132fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    if file_name in cur_files:
133fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      continue
134fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    try:
135fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      src = urllib2.urlopen(_GS_CLOUD_FORMAT % (builder, file_name))
136fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      with open(os.path.join(directory, file_name), 'w') as dest:
137fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        dest.writelines(src)
138fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        count += 1
139fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    except urllib2.HTTPError:
140fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      pass
141fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly  return count
142fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
143fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
144b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgdef main():
145b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    """Reads bench data points, then calculate and export expectations.
146b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    """
147b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    parser = argparse.ArgumentParser()
148b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    parser.add_argument(
149b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        '-a', '--representation_alg', default='25th',
150b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        help='bench representation algorithm to use, see bench_util.py.')
151b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    parser.add_argument(
152b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        '-b', '--builder', required=True,
153b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        help='name of the builder whose bench ranges we are computing.')
154b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    parser.add_argument(
155b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        '-d', '--input_dir', required=True,
156b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        help='a directory containing bench data files.')
157b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    parser.add_argument(
158b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        '-o', '--output_file', required=True,
159b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        help='file path and name for storing the output bench expectations.')
160b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    parser.add_argument(
161b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        '-r', '--git_revision', required=True,
162b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        help='the git hash to indicate the revision of input data to use.')
163fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    parser.add_argument(
164fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        '-t', '--back_track', required=False, default=10,
165fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        help='the number of commit hashes backwards to look to include' +
166fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly             'in the calculations.')
167fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    parser.add_argument(
168fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        '-m', '--max_commits', required=False, default=1,
169fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        help='the number of commit hashes to include in the calculations.')
170b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    args = parser.parse_args()
171b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
172b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    builder = args.builder
173b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
174b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    data_points = bench_util.parse_skp_bench_data(
175b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org        args.input_dir, args.git_revision, args.representation_alg)
176b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
177fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    parent_commits = get_parent_commits(args.git_revision, args.back_track)
178fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    print "Using commits: {}".format(parent_commits)
179fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    suffixes = get_file_suffixes(args.git_revision, args.input_dir)
180fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    print "Using suffixes: {}".format(suffixes)
181fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
182fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    # TODO(kelvinly): Find a better approach to than directly copying from
183fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    # the GS server?
184fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    downloaded_commits = []
185fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    for idx, commit in enumerate(parent_commits):
186fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      num_downloaded = download_bench_data(
187fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly          builder, commit, suffixes, args.input_dir)
188fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      if num_downloaded > 0:
189fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly        downloaded_commits.append((num_downloaded, idx, commit))
190fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
191fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    if len(downloaded_commits) < args.max_commits:
192fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      print ('Less than desired number of commits found. Please increase'
193fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly            '--back_track in later runs')
194fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    trunc_commits = sorted(downloaded_commits, reverse=True)[:args.max_commits]
195fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    extra_data = []
196fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    for _, idx, commit in trunc_commits:
197fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly      extra_data.append((idx, bench_util.parse_skp_bench_data(
198fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly          args.input_dir, commit, args.representation_alg)))
199fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly
200fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly    expectations_dict = create_expectations_dict(data_points, builder,
201fa1eaaa843367b5b732ed923dc5522e258e6b27akelvinly                                                 extra_data)
202b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
203b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    out_lines = []
204b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    keys = expectations_dict.keys()
205b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    keys.sort()
206b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    for (config, bench) in keys:
207b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org      (expected, lower_bound, upper_bound) = expectations_dict[(config, bench)]
208b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org      out_lines.append('%(bench)s_%(config)s_,%(builder)s-%(representation)s,'
209b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org          '%(expected)s,%(lower_bound)s,%(upper_bound)s' % {
210b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'bench': bench,
211b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'config': config,
212b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'builder': builder,
213b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'representation': args.representation_alg,
214b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'expected': expected,
215b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'lower_bound': lower_bound,
216b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org              'upper_bound': upper_bound})
217b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
218b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    with open(args.output_file, 'w') as file_handle:
219b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org      file_handle.write('\n'.join(out_lines))
220b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
221b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org
222b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.orgif __name__ == "__main__":
223b1bcb21631275fd5d0f5313b1ea37d0b975c066acommit-bot@chromium.org    main()
224