Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Side by Side Diff: scripts/slave/recipe_modules/auto_bisect/perf_revision_state.py

Issue 940123005: Adding ability to bisect recipe to bisect into dependency repos. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@hax
Patch Set: Addressing feedback, adding TODOs Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import json 5 import json
6 import tempfile
7 import os
6 import uuid 8 import uuid
7 9
8 from . import revision_state 10 from . import revision_state
9 11
12 if 'CACHE_TEST_RESULTS' in os.environ: # pragma: no cover
13 from . import test_results_cache
14
10 15
11 class PerfRevisionState(revision_state.RevisionState): 16 class PerfRevisionState(revision_state.RevisionState):
12 17
13 """Contains the state and results for one revision in a perf bisect job.""" 18 """Contains the state and results for one revision in a perf bisect job."""
14 def __init__(self, *args, **kwargs): 19 def __init__(self, *args, **kwargs):
15 super(PerfRevisionState, self).__init__(*args, **kwargs) 20 super(PerfRevisionState, self).__init__(*args, **kwargs)
16 self.values = [] 21 self.values = []
17 self.mean_value = None 22 self.mean_value = None
18 self.std_err = None 23 self.std_err = None
24 self._test_config = None
19 25
20 def test_info(self): 26 def test_info(self):
21 """Returns a dictionary with information that describes this test. 27 """Returns a dictionary with information that describes this test.
22 28
23 It is meant to be used by the bisector to describe the test being run for 29 It is meant to be used by the bisector to describe the test being run for
24 each revision evaluated. 30 each revision evaluated.
25 """ 31 """
26 return { 32 return {
27 'command': self._test_config['command'], 33 'command': self._test_config['command'],
28 'metric': self._test_config['metric'], 34 'metric': self._test_config['metric'],
29 } 35 }
30 36
31 def _read_test_results(self): 37 def _read_test_results(self):
32 """Gets the test results from GS and checks if the rev is good or bad.""" 38 """Gets the test results from GS and checks if the rev is good or bad."""
33 results = self._get_test_results() 39 results = self._get_test_results()
34 # Results will contain the keys 'results' and 'output' where output is the 40 # Results will contain the keys 'results' and 'output' where output is the
35 # stdout of the command, and 'results' is itself a dict with the keys: 41 # stdout of the command, and 'results' is itself a dict with the keys:
36 # 'mean', 'values', 'std_err' 42 # 'mean', 'values', 'std_err'
37 results = results['results'] 43 results = results['results']
38 self.mean_value = results['mean'] 44 self.mean_value = results['mean']
39 self.values = results['values'] 45 self.values = results['values']
40 self.std_err = results['std_err'] 46 self.std_err = results['std_err']
41 # We cannot test the goodness of the initial rev range. 47 # We cannot test the goodness of the initial rev range.
42 if self.bisector.good_rev != self and self.bisector.bad_rev != self: 48 if self.bisector.good_rev != self and self.bisector.bad_rev != self:
43 if self._check_revision_good(): 49 if self._check_revision_good():
44 self.good = True 50 self.good = True
45 else: 51 else:
46 self.bad = True 52 self.bad = True
47 53
54 def _write_deps_patch_file(self, build_name):
55 api = self.bisector.api
56 file_name = os.path.join(tempfile.gettempdir(), build_name + '.diff')
57 api.m.file.write('Saving diff patch for ' + str(self.revision_string),
58 file_name, self.deps_patch + self.deps_sha_patch)
59 return file_name
60
48 def _request_build(self): 61 def _request_build(self):
49 """Posts a request to buildbot to build this revision and archive it.""" 62 """Posts a request to buildbot to build this revision and archive it."""
50 # TODO: Rewrite using the trigger module. 63 # TODO: Rewrite using the trigger module.
51 # TODO: Send a diff patch when appropriate
52 api = self.bisector.api 64 api = self.bisector.api
53 bot_name = self.bisector.get_builder_bot_for_this_platform() 65 bot_name = self.bisector.get_builder_bot_for_this_platform()
54 if self.bisector.dummy_builds: 66 if self.bisector.dummy_builds:
55 self.build_job_name = self.commit_hash + '-build' 67 self.build_job_name = self.commit_hash + '-build'
56 else: # pragma: no cover 68 else: # pragma: no cover
57 self.build_job_name = uuid.uuid4().hex 69 self.build_job_name = uuid.uuid4().hex
70 if self.needs_patch:
71 self.patch_file = self._write_deps_patch_file(
72 self.build_job_name)
73 else:
74 self.patch_file = '/dev/null'
58 try_cmd = [ 75 try_cmd = [
59 'try', 76 'try',
60 '--bot=%s' % bot_name, 77 '--bot', bot_name,
61 '--revision=%s' % self.commit_hash, 78 '--revision', self.commit_hash,
62 '--name=%s' % self.build_job_name, 79 '--name', self.build_job_name,
63 '--svn_repo=%s' % api.SVN_REPO_URL, 80 '--svn_repo', api.SVN_REPO_URL,
64 '--diff', 81 '--diff', self.patch_file,
65 '/dev/null',
66 ] 82 ]
67 api.m.git(*try_cmd, name='Requesting build for %s via git try.' 83 try:
68 % str(self.commit_hash)) 84 if not self.bisector.bisect_config.get('skip_gclient_ops'):
85 api.m.bot_update.ensure_checkout()
86 api.m.git(*try_cmd, name='Requesting build for %s via git try.'
87 % str(self.commit_hash))
88 finally:
89 if (self.patch_file != '/dev/null' and not 'TESTING_SLAVENAME' in
90 os.environ):
91 try:
92 api.m.step('cleaning up patch', ['rm', self.patch_file])
93 except api.m.step.StepFailure: # pragma: no cover
94 print 'Could not clean up ' + self.patch_file
69 95
70 def _get_bisect_config_for_tester(self): 96 def _get_bisect_config_for_tester(self):
71 """Copies the key-value pairs required by a tester bot to a new dict.""" 97 """Copies the key-value pairs required by a tester bot to a new dict."""
72 result = {} 98 result = {}
73 required_test_properties = { 99 required_test_properties = {
74 'truncate_percent', 100 'truncate_percent',
75 'metric', 101 'metric',
76 'max_time_minutes', 102 'max_time_minutes',
77 'command', 103 'command',
78 'repeat_count', 104 'repeat_count',
79 'test_type' 105 'test_type'
80 } 106 }
81 for k, v in self.bisector.bisect_config.iteritems(): 107 for k, v in self.bisector.bisect_config.iteritems():
82 if k in required_test_properties: 108 if k in required_test_properties:
83 result[k] = v 109 result[k] = v
84 self._test_config = result 110 self._test_config = result
85 return result 111 return result
86 112
87 def _do_test(self): 113 def _do_test(self):
88 """Posts a request to buildbot to download and perf-test this build.""" 114 """Posts a request to buildbot to download and perf-test this build."""
89 if self.bisector.dummy_builds: 115 if self.bisector.dummy_builds:
90 self.test_job_name = self.commit_hash + '-test' 116 self.test_job_name = self.commit_hash + '-test'
117 elif 'CACHE_TEST_RESULTS' in os.environ: # pragma: no cover
118 self.test_job_name = test_results_cache.make_id(
119 self.revision_string, self._get_bisect_config_for_tester())
91 else: # pragma: no cover 120 else: # pragma: no cover
92 self.test_job_name = uuid.uuid4().hex 121 self.test_job_name = uuid.uuid4().hex
93 api = self.bisector.api 122 api = self.bisector.api
94 perf_test_properties = { 123 perf_test_properties = {
95 'buildername': self.bisector.get_perf_tester_name(), 124 'buildername': self.bisector.get_perf_tester_name(),
96 'revision': self.revision_string, 125 'revision': self.revision_string,
97 'parent_build_archive_url': self.build_url, 126 'parent_build_archive_url': self.build_url,
98 'bisect_config': self._get_bisect_config_for_tester(), 127 'bisect_config': self._get_bisect_config_for_tester(),
99 'job_name': self.test_job_name, 128 'job_name': self.test_job_name,
100 } 129 }
130 if 'CACHE_TEST_RESULTS' in os.environ and test_results_cache.has_results(
131 self.test_job_name): # pragma: no cover
132 return
101 step_name = 'Triggering test job for ' + str(self.revision_string) 133 step_name = 'Triggering test job for ' + str(self.revision_string)
102 api.m.trigger(perf_test_properties, name=step_name) 134 api.m.trigger(perf_test_properties, name=step_name)
103 135
104 def _get_build_status(self): 136 def _get_build_status(self):
105 """Queries buildbot through the json API to check if the job is done.""" 137 """Queries buildbot through the json API to check if the job is done."""
106 api = self.bisector.api 138 api = self.bisector.api
107 try: 139 try:
108 stdout = api.m.raw_io.output() 140 stdout = api.m.raw_io.output()
109 name = 'Get test status for build ' + self.commit_hash 141 name = 'Get test status for build ' + self.commit_hash
110 step_result = api.m.python(name, api.resource('check_job_status.py'), 142 step_result = api.m.python(name, api.resource('check_job_status.py'),
(...skipping 17 matching lines...) Expand all
128 """ 160 """
129 api = self.bisector.api 161 api = self.bisector.api
130 url_file_url = api.GS_RESULTS_URL + self.test_job_name 162 url_file_url = api.GS_RESULTS_URL + self.test_job_name
131 try: 163 try:
132 stdout = api.m.raw_io.output() 164 stdout = api.m.raw_io.output()
133 name = 'Get test status url for build ' + self.commit_hash 165 name = 'Get test status url for build ' + self.commit_hash
134 step_result = api.m.gsutil.cat(url_file_url, stdout=stdout, name=name) 166 step_result = api.m.gsutil.cat(url_file_url, stdout=stdout, name=name)
135 except api.m.step.StepFailure: # pragma: no cover 167 except api.m.step.StepFailure: # pragma: no cover
136 return None 168 return None
137 else: 169 else:
138 return step_result.stdout 170 url = step_result.stdout
171 if 'CACHE_TEST_RESULTS' in os.environ: # pragma: no cover
172 test_results_cache.save_results(self.test_job_name, url)
173 return url
139 174
140 def get_next_url(self): 175 def get_next_url(self):
141 if not self.in_progress: 176 if not self.in_progress:
142 return None 177 return None
143 if not self.built: 178 if not self.built:
144 return self.build_url 179 return self.build_url
145 if not self.build_status_url: 180 if not self.build_status_url:
146 # The file that will eventually contain the buildbot job url 181 # The file that will eventually contain the buildbot job url
147 return self.bisector.api.GS_RESULTS_URL + self.test_job_name 182 return self.bisector.api.GS_RESULTS_URL + self.test_job_name
148 return self.build_status_url # pragma: no cover 183 return self.build_status_url # pragma: no cover
(...skipping 23 matching lines...) Expand all
172 True if this revision is closer to the initial good revision's value than 207 True if this revision is closer to the initial good revision's value than
173 to the initial bad revision's value. False otherwise. 208 to the initial bad revision's value. False otherwise.
174 """ 209 """
175 # TODO: Reevaluate this approach 210 # TODO: Reevaluate this approach
176 bisector = self.bisector 211 bisector = self.bisector
177 distance_to_good = abs(self.mean_value - bisector.good_rev.mean_value) 212 distance_to_good = abs(self.mean_value - bisector.good_rev.mean_value)
178 distance_to_bad = abs(self.mean_value - bisector.bad_rev.mean_value) 213 distance_to_bad = abs(self.mean_value - bisector.bad_rev.mean_value)
179 if distance_to_good < distance_to_bad: 214 if distance_to_good < distance_to_bad:
180 return True 215 return True
181 return False 216 return False
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698