| Index: tools/bisect-perf-regression.py
|
| diff --git a/tools/bisect-perf-regression.py b/tools/bisect-perf-regression.py
|
| index 3e7dfe2246e7a0fb3d71e64ebe1b7363577bd708..7ec156fa270efa487e04ec3e7f151b7413a80aa0 100755
|
| --- a/tools/bisect-perf-regression.py
|
| +++ b/tools/bisect-perf-regression.py
|
| @@ -278,7 +278,7 @@ def RunProcess(command):
|
| return subprocess.call(command, shell=shell)
|
|
|
|
|
| -def RunProcessAndRetrieveOutput(command):
|
| +def RunProcessAndRetrieveOutput(command, cwd=None):
|
| """Run an arbitrary command, returning its output and return code. Since
|
| output is collected via communicate(), there will be no output until the
|
| call terminates. If you need output while the program runs (ie. so
|
| @@ -286,24 +286,20 @@ def RunProcessAndRetrieveOutput(command):
|
|
|
| Args:
|
| command: A list containing the command and args to execute.
|
| - print_output: Optional parameter to write output to stdout as it's
|
| - being collected.
|
|
|
| Returns:
|
| A tuple of the output and return code.
|
| """
|
| # On Windows, use shell=True to get PATH interpretation.
|
| shell = IsWindows()
|
| - proc = subprocess.Popen(command,
|
| - shell=shell,
|
| - stdout=subprocess.PIPE)
|
| + proc = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE, cwd=cwd)
|
|
|
| (output, _) = proc.communicate()
|
|
|
| return (output, proc.returncode)
|
|
|
|
|
| -def RunGit(command):
|
| +def RunGit(command, cwd=None):
|
| """Run a git subcommand, returning its output and return code.
|
|
|
| Args:
|
| @@ -314,10 +310,10 @@ def RunGit(command):
|
| """
|
| command = ['git'] + command
|
|
|
| - return RunProcessAndRetrieveOutput(command)
|
| + return RunProcessAndRetrieveOutput(command, cwd=cwd)
|
|
|
|
|
| -def CheckRunGit(command):
|
| +def CheckRunGit(command, cwd=None):
|
| """Run a git subcommand, returning its output and return code. Asserts if
|
| the return code of the call is non-zero.
|
|
|
| @@ -327,7 +323,7 @@ def CheckRunGit(command):
|
| Returns:
|
| A tuple of the output and return code.
|
| """
|
| - (output, return_code) = RunGit(command)
|
| + (output, return_code) = RunGit(command, cwd=cwd)
|
|
|
| assert not return_code, 'An error occurred while running'\
|
| ' "git %s"' % ' '.join(command)
|
| @@ -717,7 +713,7 @@ class GitSourceControl(SourceControl):
|
|
|
| return not results
|
|
|
| - def ResolveToRevision(self, revision_to_check, depot, search):
|
| + def ResolveToRevision(self, revision_to_check, depot, search, cwd=None):
|
| """If an SVN revision is supplied, try to resolve it to a git SHA1.
|
|
|
| Args:
|
| @@ -753,7 +749,7 @@ class GitSourceControl(SourceControl):
|
| cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern,
|
| 'origin/master']
|
|
|
| - (log_output, return_code) = RunGit(cmd)
|
| + (log_output, return_code) = RunGit(cmd, cwd=cwd)
|
|
|
| assert not return_code, 'An error occurred while running'\
|
| ' "git %s"' % ' '.join(cmd)
|
| @@ -779,7 +775,7 @@ class GitSourceControl(SourceControl):
|
|
|
| git_revision = None
|
|
|
| - log_output = CheckRunGit(cmd)
|
| + log_output = CheckRunGit(cmd, cwd=cwd)
|
| if log_output:
|
| git_revision = log_output
|
| git_revision = int(log_output.strip())
|
| @@ -820,7 +816,7 @@ class GitSourceControl(SourceControl):
|
|
|
| return None
|
|
|
| - def QueryRevisionInfo(self, revision):
|
| + def QueryRevisionInfo(self, revision, cwd=None):
|
| """Gathers information on a particular revision, such as author's name,
|
| email, subject, and date.
|
|
|
| @@ -843,7 +839,7 @@ class GitSourceControl(SourceControl):
|
|
|
| for i in xrange(len(formats)):
|
| cmd = ['log', '--format=%s' % formats[i], '-1', revision]
|
| - output = CheckRunGit(cmd)
|
| + output = CheckRunGit(cmd, cwd=cwd)
|
| commit_info[targets[i]] = output.rstrip()
|
|
|
| return commit_info
|
| @@ -956,6 +952,71 @@ class BisectPerformanceMetrics(object):
|
|
|
| return revision_work_list
|
|
|
| + def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision):
|
| + svn_revision = self.source_control.SVNFindRev(revision)
|
| +
|
| + if IsStringInt(svn_revision):
|
| + # V8 is tricky to bisect, in that there are only a few instances when
|
| + # we can dive into bleeding_edge and get back a meaningful result.
|
| + # Try to detect a V8 "business as usual" case, which is when:
|
| + # 1. trunk revision N has description "Version X.Y.Z"
|
| + # 2. bleeding_edge revision (N-1) has description "Prepare push to
|
| + # trunk. Now working on X.Y.(Z+1)."
|
| + v8_dir = self._GetDepotDirectory('v8')
|
| + v8_bleeding_edge_dir = self._GetDepotDirectory('v8_bleeding_edge')
|
| +
|
| + revision_info = self.source_control.QueryRevisionInfo(revision,
|
| + cwd=v8_dir)
|
| +
|
| + version_re = re.compile("Version (?P<values>[0-9,.]+)")
|
| +
|
| + regex_results = version_re.search(revision_info['subject'])
|
| +
|
| + if regex_results:
|
| + version = regex_results.group('values')
|
| +
|
| + git_revision = self.source_control.ResolveToRevision(
|
| + int(svn_revision) - 1, 'v8_bleeding_edge', -1,
|
| + cwd=v8_bleeding_edge_dir)
|
| +
|
| + if git_revision:
|
| + revision_info = self.source_control.QueryRevisionInfo(git_revision,
|
| + cwd=v8_bleeding_edge_dir)
|
| +
|
| + if 'Prepare push to trunk' in revision_info['subject']:
|
| + return git_revision
|
| + return None
|
| +
|
| + def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True):
|
| + cwd = self._GetDepotDirectory('v8')
|
| + cmd = ['log', '--format=%ct', '-1', revision]
|
| + output = CheckRunGit(cmd, cwd=cwd)
|
| + commit_time = int(output)
|
| + commits = []
|
| +
|
| + if search_forward:
|
| + cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time,
|
| + 'origin/master']
|
| + output = CheckRunGit(cmd, cwd=cwd)
|
| + output = output.split()
|
| + commits = output
|
| + commits = reversed(commits)
|
| + else:
|
| + cmd = ['log', '--format=%H', '-10', '--before=%d' % commit_time,
|
| + 'origin/master']
|
| + output = CheckRunGit(cmd, cwd=cwd)
|
| + output = output.split()
|
| + commits = output
|
| +
|
| + bleeding_edge_revision = None
|
| +
|
| + for c in commits:
|
| + bleeding_edge_revision = self._GetV8BleedingEdgeFromV8TrunkIfMappable(c)
|
| + if bleeding_edge_revision:
|
| + break
|
| +
|
| + return bleeding_edge_revision
|
| +
|
| def Get3rdPartyRevisionsFromCurrentRevision(self, depot, revision):
|
| """Parses the DEPS file to determine WebKit/v8/etc... versions.
|
|
|
| @@ -1041,39 +1102,11 @@ class BisectPerformanceMetrics(object):
|
|
|
| results['chromium'] = output.strip()
|
| elif depot == 'v8':
|
| + # We can't try to map the trunk revision to bleeding edge yet, because
|
| + # we don't know which direction to try to search in. Have to wait until
|
| + # the bisect has narrowed the results down to 2 v8 rolls.
|
| results['v8_bleeding_edge'] = None
|
|
|
| - svn_revision = self.source_control.SVNFindRev(revision)
|
| -
|
| - if IsStringInt(svn_revision):
|
| - # V8 is tricky to bisect, in that there are only a few instances when
|
| - # we can dive into bleeding_edge and get back a meaningful result.
|
| - # Try to detect a V8 "business as usual" case, which is when:
|
| - # 1. trunk revision N has description "Version X.Y.Z"
|
| - # 2. bleeding_edge revision (N-1) has description "Prepare push to
|
| - # trunk. Now working on X.Y.(Z+1)."
|
| - self.ChangeToDepotWorkingDirectory(depot)
|
| -
|
| - revision_info = self.source_control.QueryRevisionInfo(revision)
|
| -
|
| - version_re = re.compile("Version (?P<values>[0-9,.]+)")
|
| -
|
| - regex_results = version_re.search(revision_info['subject'])
|
| -
|
| - if regex_results:
|
| - version = regex_results.group('values')
|
| -
|
| - self.ChangeToDepotWorkingDirectory('v8_bleeding_edge')
|
| -
|
| - git_revision = self.source_control.ResolveToRevision(
|
| - int(svn_revision) - 1, 'v8_bleeding_edge', -1)
|
| -
|
| - if git_revision:
|
| - revision_info = self.source_control.QueryRevisionInfo(git_revision)
|
| -
|
| - if 'Prepare push to trunk' in revision_info['subject']:
|
| - results['v8_bleeding_edge'] = git_revision
|
| -
|
| return results
|
|
|
| def BuildCurrentRevision(self, depot):
|
| @@ -1607,29 +1640,50 @@ class BisectPerformanceMetrics(object):
|
|
|
| return dist_to_good_value < dist_to_bad_value
|
|
|
| - def ChangeToDepotWorkingDirectory(self, depot_name):
|
| - """Given a depot, changes to the appropriate working directory.
|
| -
|
| - Args:
|
| - depot_name: The name of the depot (see DEPOT_NAMES).
|
| - """
|
| + def _GetDepotDirectory(self, depot_name):
|
| if depot_name == 'chromium':
|
| - os.chdir(self.src_cwd)
|
| + return self.src_cwd
|
| elif depot_name == 'cros':
|
| - os.chdir(self.cros_cwd)
|
| + return self.cros_cwd
|
| elif depot_name in DEPOT_NAMES:
|
| - os.chdir(self.depot_cwd[depot_name])
|
| + return self.depot_cwd[depot_name]
|
| else:
|
| assert False, 'Unknown depot [ %s ] encountered. Possibly a new one'\
|
| ' was added without proper support?' %\
|
| (depot_name,)
|
|
|
| - def FindNextDepotToBisect(self, current_revision, min_revision_data,
|
| - max_revision_data):
|
| + def ChangeToDepotWorkingDirectory(self, depot_name):
|
| + """Given a depot, changes to the appropriate working directory.
|
| +
|
| + Args:
|
| + depot_name: The name of the depot (see DEPOT_NAMES).
|
| + """
|
| + os.chdir(self._GetDepotDirectory(depot_name))
|
| +
|
| + def _FillInV8BleedingEdgeInfo(self, min_revision_data, max_revision_data):
|
| + r1 = self._GetNearestV8BleedingEdgeFromTrunk(min_revision_data['revision'],
|
| + search_forward=True)
|
| + r2 = self._GetNearestV8BleedingEdgeFromTrunk(max_revision_data['revision'],
|
| + search_forward=False)
|
| + min_revision_data['external']['v8_bleeding_edge'] = r1
|
| + max_revision_data['external']['v8_bleeding_edge'] = r2
|
| +
|
| + if (not self._GetV8BleedingEdgeFromV8TrunkIfMappable(
|
| + min_revision_data['revision']) or
|
| + not self._GetV8BleedingEdgeFromV8TrunkIfMappable(
|
| + max_revision_data['revision'])):
|
| + self.warnings.append('Trunk revisions in V8 did not map directly to '
|
| + 'bleeding_edge. Attempted to expand the range to find V8 rolls which '
|
| + 'did map directly to bleeding_edge revisions, but results might not '
|
| + 'be valid.')
|
| +
|
| + def _FindNextDepotToBisect(self, current_depot, current_revision,
|
| + min_revision_data, max_revision_data):
|
| """Given the state of the bisect, decides which depot the script should
|
| dive into next (if any).
|
|
|
| Args:
|
| + current_depot: Current depot being bisected.
|
| current_revision: Current revision synced to.
|
| min_revision_data: Data about the earliest revision in the bisect range.
|
| max_revision_data: Data about the latest revision in the bisect range.
|
| @@ -1638,23 +1692,29 @@ class BisectPerformanceMetrics(object):
|
| The depot to bisect next, or None.
|
| """
|
| external_depot = None
|
| - for current_depot in DEPOT_NAMES:
|
| - if DEPOT_DEPS_NAME[current_depot].has_key('platform'):
|
| - if DEPOT_DEPS_NAME[current_depot]['platform'] != os.name:
|
| + for next_depot in DEPOT_NAMES:
|
| + if DEPOT_DEPS_NAME[next_depot].has_key('platform'):
|
| + if DEPOT_DEPS_NAME[next_depot]['platform'] != os.name:
|
| continue
|
|
|
| - if not (DEPOT_DEPS_NAME[current_depot]["recurse"] and
|
| - DEPOT_DEPS_NAME[current_depot]['from'] ==
|
| + if not (DEPOT_DEPS_NAME[next_depot]["recurse"] and
|
| + DEPOT_DEPS_NAME[next_depot]['from'] ==
|
| min_revision_data['depot']):
|
| continue
|
|
|
| - if (min_revision_data['external'][current_depot] ==
|
| - max_revision_data['external'][current_depot]):
|
| + if current_depot == 'v8':
|
| + # We grab the bleeding_edge info here rather than earlier because we
|
| + # finally have the revision range. From that we can search forwards and
|
| + # backwards to try to match trunk revisions to bleeding_edge.
|
| + self._FillInV8BleedingEdgeInfo(min_revision_data, max_revision_data)
|
| +
|
| + if (min_revision_data['external'][next_depot] ==
|
| + max_revision_data['external'][next_depot]):
|
| continue
|
|
|
| - if (min_revision_data['external'][current_depot] and
|
| - max_revision_data['external'][current_depot]):
|
| - external_depot = current_depot
|
| + if (min_revision_data['external'][next_depot] and
|
| + max_revision_data['external'][next_depot]):
|
| + external_depot = next_depot
|
| break
|
|
|
| return external_depot
|
| @@ -2054,7 +2114,7 @@ class BisectPerformanceMetrics(object):
|
| previous_revision = revision_list[min_revision]
|
| # If there were changes to any of the external libraries we track,
|
| # should bisect the changes there as well.
|
| - external_depot = self.FindNextDepotToBisect(
|
| + external_depot = self._FindNextDepotToBisect(current_depot,
|
| previous_revision, min_revision_data, max_revision_data)
|
|
|
| # If there was no change in any of the external depots, the search
|
|
|