Chromium Code Reviews| Index: tools/bisect-perf-regression.py |
| diff --git a/tools/bisect-perf-regression.py b/tools/bisect-perf-regression.py |
| index 14cf10c8ff61df3e8c7992101519e2e44096da99..da07b5d7bdce627eb9a7648972681eb0fc2cdd7d 100755 |
| --- a/tools/bisect-perf-regression.py |
| +++ b/tools/bisect-perf-regression.py |
| @@ -251,6 +251,23 @@ def RunGit(command): |
| return RunProcess(command) |
| +def CheckRunGit(command): |
| + """Run a git subcommand, returning its output and return code. Asserts if |
| + the return code of the call is non-zero. |
| + |
| + Args: |
| + command: A list containing the args to git. |
| + |
| + Returns: |
| + A tuple of the output and return code. |
| + """ |
| + (output, return_code) = RunGit(command) |
| + |
| + assert not return_code, 'An error occurred while running'\ |
| + ' "git %s"' % ' '.join(command) |
| + return (output, return_code) |
|
tonyg
2013/05/28 23:45:06
Since we know return_code is 0 at this point, I'd
shatch
2013/05/29 16:25:28
Done.
|
| + |
| + |
| def BuildWithMake(threads, targets, print_output): |
| cmd = ['make', 'BUILDTYPE=Release', '-j%d' % threads] + targets |
| @@ -326,10 +343,7 @@ class GitSourceControl(SourceControl): |
| """ |
| revision_range = '%s..%s' % (revision_range_start, revision_range_end) |
| cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range] |
| - (log_output, return_code) = RunGit(cmd) |
| - |
| - assert not return_code, 'An error occurred while running'\ |
| - ' "git %s"' % ' '.join(cmd) |
| + (log_output, return_code) = CheckRunGit(cmd) |
| revision_hash_list = log_output.split() |
| revision_hash_list.append(revision_range_start) |
| @@ -389,10 +403,7 @@ class GitSourceControl(SourceControl): |
| svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i) |
| cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern, 'origin/master'] |
| - (log_output, return_code) = RunGit(cmd) |
| - |
| - assert not return_code, 'An error occurred while running'\ |
| - ' "git %s"' % ' '.join(cmd) |
| + (log_output, return_code) = CheckRunGit(cmd) |
| if not return_code: |
| log_output = log_output.strip() |
| @@ -412,10 +423,7 @@ class GitSourceControl(SourceControl): |
| True if the current branch on src is 'master' |
| """ |
| cmd = ['rev-parse', '--abbrev-ref', 'HEAD'] |
| - (log_output, return_code) = RunGit(cmd) |
| - |
| - assert not return_code, 'An error occurred while running'\ |
| - ' "git %s"' % ' '.join(cmd) |
| + (log_output, return_code) = CheckRunGit(cmd) |
| log_output = log_output.strip() |
| @@ -433,10 +441,7 @@ class GitSourceControl(SourceControl): |
| cmd = ['svn', 'find-rev', revision] |
| - (output, return_code) = RunGit(cmd) |
| - |
| - assert not return_code, 'An error occurred while running'\ |
| - ' "git %s"' % ' '.join(cmd) |
| + (output, return_code) = CheckRunGit(cmd) |
| svn_revision = output.strip() |
| @@ -467,12 +472,9 @@ class GitSourceControl(SourceControl): |
| for i in xrange(len(formats)): |
| cmd = ['log', '--format=%s' % formats[i], '-1', revision] |
| - (output, return_code) = RunGit(cmd) |
| + (output, return_code) = CheckRunGit(cmd) |
| commit_info[targets[i]] = output.rstrip() |
| - assert not return_code, 'An error occurred while running'\ |
| - ' "git %s"' % ' '.join(cmd) |
| - |
| return commit_info |
| def CheckoutFileAtRevision(self, file_name, revision): |
| @@ -494,6 +496,23 @@ class GitSourceControl(SourceControl): |
| return not RunGit(['checkout', bisect_utils.FILE_DEPS_GIT])[1] |
| + def QueryFileRevisionHistory(self, filename, revision_start, revision_end): |
| + """Returns a list of commits that modified this file. |
| + |
| + Args: |
| + filename: Name of file. |
| + revision_start: Start of revision range. |
| + revision_end: End of revision range. |
| + |
| + Returns: |
| + Returns a list of commits that touched this file. |
| + """ |
| + cmd = ['log', '--format=%H', '%s^1..%s' % (revision_start, revision_end), |
| + filename] |
| + (output, return_code) = CheckRunGit(cmd) |
| + |
| + return [o for o in output.split('\n') if o] |
| + |
| class BisectPerformanceMetrics(object): |
| """BisectPerformanceMetrics performs a bisection against a list of range |
| of revisions to narrow down where performance regressions may have |
| @@ -507,6 +526,7 @@ class BisectPerformanceMetrics(object): |
| self.src_cwd = os.getcwd() |
| self.depot_cwd = {} |
| self.cleanup_commands = [] |
| + self.warnings = [] |
| # This always starts true since the script grabs latest first. |
| self.was_blink = True |
| @@ -1044,6 +1064,53 @@ class BisectPerformanceMetrics(object): |
| if self.opts.output_buildbot_annotations: |
| bisect_utils.OutputAnnotationStepClosed() |
| + def NudgeRevisionsIfDEPSChange(self, bad_revision, good_revision): |
| + """Checks to see if changes to DEPS file occurred, and that the revision |
| + range also includes the change to .DEPS.git. If it doesn't, attempts to |
| + expand the revision range to include it. |
| + |
| + Args: |
| + bad_rev: First known bad revision. |
| + good_revision: Last known good revision. |
| + |
| + Returns: |
| + A tuple with the new bad and good revisions. |
| + """ |
| + if self.source_control.IsGit(): |
| + changes_to_deps = self.source_control.QueryFileRevisionHistory( |
| + 'DEPS', good_revision, bad_revision) |
| + |
| + if changes_to_deps: |
| + # DEPS file was changed, search from the oldest change to DEPS file to |
| + # bad_revision to see if there are matching .DEPS.git changes. |
| + oldest_deps_change = changes_to_deps[-1] |
| + changes_to_gitdeps = self.source_control.QueryFileRevisionHistory( |
| + bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) |
| + |
| + if len(changes_to_deps) != len(changes_to_gitdeps): |
| + # Grab the timestamp of the last DEPS change |
| + cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] |
| + (output, return_code) = CheckRunGit(cmd) |
| + |
| + commit_time = int(output) |
| + |
| + # Try looking for a commit that touches the .DEPS.git file in the |
| + # next 15 minutes after the DEPS file change. |
| + cmd = ['log', '--format=%H', '-1', |
| + '--before=%d' % (commit_time + 900), '--after=%d' % commit_time, |
| + 'origin/master', bisect_utils.FILE_DEPS_GIT] |
| + (output, return_code) = CheckRunGit(cmd) |
| + |
| + output = output.strip() |
| + if output: |
| + self.warnings.append('Detected change to DEPS and modified ' |
| + 'revision range to include change to .DEPS.git') |
| + return (output, good_revision) |
| + else: |
| + self.warnings.append('Detected change to DEPS but couldn\'t find ' |
| + 'matching change to .DEPS.git') |
| + return (bad_revision, good_revision) |
| + |
| def Run(self, command_to_run, bad_revision_in, good_revision_in, metric): |
| """Given known good and bad revisions, run a binary search on all |
| intermediate revisions to determine the CL where the performance regression |
| @@ -1105,6 +1172,9 @@ class BisectPerformanceMetrics(object): |
| results['error'] = 'Could\'t resolve [%s] to SHA1.' % (good_revision_in,) |
| return results |
| + (bad_revision, good_revision) = self.NudgeRevisionsIfDEPSChange( |
| + bad_revision, good_revision) |
| + |
| if self.opts.output_buildbot_annotations: |
| bisect_utils.OutputAnnotationStepStart('Gathering Revisions') |
| @@ -1380,13 +1450,11 @@ class BisectPerformanceMetrics(object): |
| deviations = math.fabs(bad_mean - good_mean) / good_std_dev |
| if deviations < 1.5: |
| - print 'Warning: Regression was less than 1.5 standard deviations '\ |
| - 'from "good" value. Results may not be accurate.' |
| + self.warnings.append('Regression was less than 1.5 standard ' |
| + 'deviations from "good" value. Results may not be accurate.') |
| elif self.opts.repeat_test_count == 1: |
| - print 'Warning: Tests were only set to run once. This may be '\ |
| - 'insufficient to get meaningful results.' |
| + self.warnings.append('Tests were only set to run once. This ' |
| + 'may be insufficient to get meaningful results.') |
| # Check for any other possible regression ranges |
| prev_revision_data = revision_data_sorted[0][1] |
| @@ -1447,6 +1515,14 @@ class BisectPerformanceMetrics(object): |
| current_data['depot'], current_id) |
| + if self.warnings: |
| + print 'The following warnings were generated:' |
| + for w in self.warnings: |
| + print ' - %s' % w |
| + |
| if self.opts.output_buildbot_annotations: |
| bisect_utils.OutputAnnotationStepClosed() |