aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZhizhou Yang <zhizhouy@google.com>2018-08-29 16:30:00 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-09-04 21:04:00 -0700
commitd6c617782ab73c5552f4bfea9e0cede8431d1a65 (patch)
tree6a8f34b632a2ac6f0f15caced23bd52e2338d7a9
parentb1afe3f2c2d4219ce490ffa111f530983b171141 (diff)
downloadtoolchain-utils-d6c617782ab73c5552f4bfea9e0cede8431d1a65.tar.gz
Bisect tool: Add pass level bisection command option helper
This patch introduces a new option for bisection tool which is `--pass_bisect`. When it is set to android/generate_cmd.sh, bisection tool will create a new script which extracts command line options to build the bad item (We now only support pass level bisecting for single bad item, so prune need to be turned off with this option on). Another python wrapper script will come soon and it will take the generated script as one argument, indicating how the compiler will build the bad item at certain limit. BUG=chromium:878954 TEST=Ran test successfully with Android compiler wrapper. Will add unittests once verified ChromeOS. Change-Id: Iaa972e9e1a364143b59a89e6518bbb2bba6e3522 Reviewed-on: https://chromium-review.googlesource.com/1195595 Commit-Ready: Zhizhou Yang <zhizhouy@google.com> Tested-by: Zhizhou Yang <zhizhouy@google.com> Reviewed-by: Caroline Tice <cmtice@chromium.org>
-rwxr-xr-xbinary_search_tool/android/generate_cmd.sh41
-rwxr-xr-xbinary_search_tool/binary_search_state.py69
-rw-r--r--binary_search_tool/common.py61
3 files changed, 131 insertions, 40 deletions
diff --git a/binary_search_tool/android/generate_cmd.sh b/binary_search_tool/android/generate_cmd.sh
new file mode 100755
index 00000000..eaae738b
--- /dev/null
+++ b/binary_search_tool/android/generate_cmd.sh
@@ -0,0 +1,41 @@
+#!/bin/bash -u
+
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script extracts command line options to build bad item.
+# The generated script will be used by pass level bisection.
+#
+
+source android/common.sh
+
+abs_path=$1
+
+# The item will be `-o relative-path-to-object `, which will be used
+# for seeking command in populate log.
+# We care about the `-o` at the beginning and ` ` at the end are necessary,
+# so that we can get build command for exact this object file.
+# Example: prebuilt/../clang++ -O3 -MF obj1.o.d -o obj.o obj.cpp
+# We should count this command as one to build obj.o, not obj1.o.d.
+real_path=$(realpath --relative-to="${BISECT_WORK_BUILD}" "${abs_path}")
+item="-o $real_path "
+
+populate_log=${BISECT_BAD_BUILD}/_POPULATE_LOG
+
+output='#!/bin/bash -u\n'
+output+='source android/common.sh\n'
+
+result=$(egrep -m 1 -- "${item}" ${populate_log})
+
+# Remove `:` after cd command
+result=$(sed 's/cd\:/cd/g' <<< ${result})
+
+# Add environment variable which helps pass level bisection
+result=$(sed 's/ \-o / $LIMIT_FLAGS \-o /g' <<< ${result})
+
+output+=${result}
+
+echo -e "${output}" > android/cmd_script.sh
+
+echo 'Script created as android/cmd_script.sh'
diff --git a/binary_search_tool/binary_search_state.py b/binary_search_tool/binary_search_state.py
index 19065252..645e613c 100755
--- a/binary_search_tool/binary_search_state.py
+++ b/binary_search_tool/binary_search_state.py
@@ -1,4 +1,9 @@
#!/usr/bin/env python2
+
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
"""The binary search wrapper."""
from __future__ import print_function
@@ -63,8 +68,8 @@ class BinarySearchState(object):
"""The binary search state class."""
def __init__(self, get_initial_items, switch_to_good, switch_to_bad,
- test_setup_script, test_script, incremental, prune, iterations,
- prune_iterations, verify, file_args, verbose):
+ test_setup_script, test_script, incremental, prune, pass_bisect,
+ iterations, prune_iterations, verify, file_args, verbose):
"""BinarySearchState constructor, see Run for full args documentation."""
self.get_initial_items = get_initial_items
self.switch_to_good = switch_to_good
@@ -73,6 +78,7 @@ class BinarySearchState(object):
self.test_script = test_script
self.incremental = incremental
self.prune = prune
+ self.pass_bisect = pass_bisect
self.iterations = iterations
self.prune_iterations = prune_iterations
self.verify = verify
@@ -270,6 +276,26 @@ class BinarySearchState(object):
# FIXME: Do we need to Convert the currently good items to bad
self.PopulateItemsUsingList(new_all_items)
+ # If pass level bisecting is set, generate a script which contains command
+ # line options to rebuild bad item.
+ if self.pass_bisect:
+ assert not self.prune, 'Prune must be false if pass_bisect is set.'
+ status = self.GenerateBadCommandScript(self.found_items)
+ if status == 0:
+ self.l.LogOutput('Command script generated.')
+ else:
+ raise RuntimeError('Error while generating command script.')
+
+ def GenerateBadCommandScript(self, bad_items):
+ assert len(bad_items) == 1, 'Pruning is off, but number of bad items' \
+ 'found was not 1.'
+ item = list(bad_items)[0]
+
+ command = '%s %s' % (self.pass_bisect, item)
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(command,
+ print_to_console=self.verbose)
+ return ret
+
def DoBinarySearch(self):
"""Perform single iteration of binary search."""
# If in resume mode don't reset search_cycles
@@ -467,6 +493,7 @@ class MockBinarySearchState(BinarySearchState):
'test_script': None,
'incremental': True,
'prune': False,
+ 'pass_bisect': None,
'iterations': 50,
'prune_iterations': 100,
'verify': True,
@@ -498,6 +525,7 @@ def Run(get_initial_items,
test_setup_script=None,
iterations=50,
prune=False,
+ pass_bisect=None,
noincremental=False,
file_args=False,
verify=True,
@@ -520,6 +548,9 @@ def Run(get_initial_items,
prune: If False the binary search tool will stop when the first bad item is
found. Otherwise then binary search tool will continue searching
until all bad items are found (or prune_iterations is reached).
+ pass_bisect: Script that takes single bad item from POPULATE_BAD and
+ returns the compiler command used to generate the bad item.
+ Requires that 'prune' be set to False.
noincremental: Whether to send "diffs" of good/bad items to switch scripts.
file_args: If True then arguments to switch scripts will be a file name
containing a newline separated list of the items to switch.
@@ -532,18 +563,36 @@ def Run(get_initial_items,
Returns:
0 for success, error otherwise
"""
+ # Notice that all the argument checks are in the Run() function rather than
+ # in the Main() function. It is not common to do so but some wrappers are
+ # going to call Run() directly and bypass checks in Main() function.
if resume:
+ logger.GetLogger().LogOutput('Resuming from %s' % STATE_FILE)
bss = BinarySearchState.LoadState()
if not bss:
logger.GetLogger().LogOutput(
'%s is not a valid binary_search_tool state file, cannot resume!' %
STATE_FILE)
return 1
+ logger.GetLogger().LogOutput('Note: resuming from previous state, '
+ 'ignoring given options and loading saved '
+ 'options instead.')
else:
+ if not (get_initial_items and switch_to_good and
+ switch_to_bad and test_script):
+ logger.GetLogger().LogOutput('The following options are required: '
+ '[-i, -g, -b, -t] | [-r]')
+ return 1
+ if pass_bisect and prune:
+ logger.GetLogger().LogOutput('"--pass_bisect" only works when '
+ '"--prune" is set to be False.')
+ return 1
switch_to_good = _CanonicalizeScript(switch_to_good)
switch_to_bad = _CanonicalizeScript(switch_to_bad)
if test_setup_script:
test_setup_script = _CanonicalizeScript(test_setup_script)
+ if pass_bisect:
+ pass_bisect = _CanonicalizeScript(pass_bisect)
test_script = _CanonicalizeScript(test_script)
get_initial_items = _CanonicalizeScript(get_initial_items)
incremental = not noincremental
@@ -552,8 +601,8 @@ def Run(get_initial_items,
bss = BinarySearchState(get_initial_items, switch_to_good, switch_to_bad,
test_setup_script, test_script, incremental, prune,
- iterations, prune_iterations, verify, file_args,
- verbose)
+ pass_bisect, iterations, prune_iterations, verify,
+ file_args, verbose)
bss.DoVerify()
try:
@@ -577,18 +626,6 @@ def Main(argv):
logger.GetLogger().LogOutput(' '.join(argv))
options = parser.parse_args(argv)
- if not (options.get_initial_items and options.switch_to_good and
- options.switch_to_bad and options.test_script) and not options.resume:
- parser.print_help()
- return 1
-
- if options.resume:
- logger.GetLogger().LogOutput('Resuming from %s' % STATE_FILE)
- if len(argv) > 1:
- logger.GetLogger().LogOutput(('Note: resuming from previous state, '
- 'ignoring given options and loading saved '
- 'options instead.'))
-
# Get dictionary of all options
args = vars(options)
return Run(**args)
diff --git a/binary_search_tool/common.py b/binary_search_tool/common.py
index 945270be..1d950ced 100644
--- a/binary_search_tool/common.py
+++ b/binary_search_tool/common.py
@@ -1,3 +1,7 @@
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
"""Common config and logic for binary search tool
This module serves two main purposes:
@@ -142,35 +146,35 @@ def _BuildArgsDict(args):
'-i',
'--get_initial_items',
dest='get_initial_items',
- help=('Script to run to get the initial objects. '
- 'If your script requires user input '
- 'the --verbose option must be used'))
+ help='Script to run to get the initial objects. '
+ 'If your script requires user input '
+ 'the --verbose option must be used')
args.AddArgument(
'-g',
'--switch_to_good',
dest='switch_to_good',
- help=('Script to run to switch to good. '
- 'If your switch script requires user input '
- 'the --verbose option must be used'))
+ help='Script to run to switch to good. '
+ 'If your switch script requires user input '
+ 'the --verbose option must be used')
args.AddArgument(
'-b',
'--switch_to_bad',
dest='switch_to_bad',
- help=('Script to run to switch to bad. '
- 'If your switch script requires user input '
- 'the --verbose option must be used'))
+ help='Script to run to switch to bad. '
+ 'If your switch script requires user input '
+ 'the --verbose option must be used')
args.AddArgument(
'-I',
'--test_setup_script',
dest='test_setup_script',
- help=('Optional script to perform building, flashing, '
- 'and other setup before the test script runs.'))
+ help='Optional script to perform building, flashing, '
+ 'and other setup before the test script runs.')
args.AddArgument(
'-t',
'--test_script',
dest='test_script',
- help=('Script to run to test the '
- 'output after packages are built.'))
+ help='Script to run to test the '
+ 'output after packages are built.')
# No input (evals to False),
# --prune (evals to True),
# --prune=False,
@@ -184,8 +188,17 @@ def _BuildArgsDict(args):
default=False,
type=StrToBool,
metavar='bool',
- help=('If True, continue until all bad items are found. '
- 'Defaults to False.'))
+ help='If True, continue until all bad items are found. '
+ 'Defaults to False.')
+ args.AddArgument(
+ '-P',
+ '--pass_bisect',
+ dest='pass_bisect',
+ default=None,
+ help='Script to generate another script for pass level bisect, '
+ 'which contains command line options to build bad item.'
+ 'For now it only supports one single bad item, so to use it, '
+ 'prune must be set to false.')
# No input (evals to False),
# --noincremental (evals to True),
# --noincremental=False,
@@ -199,8 +212,8 @@ def _BuildArgsDict(args):
default=False,
type=StrToBool,
metavar='bool',
- help=('If True, don\'t propagate good/bad changes '
- 'incrementally. Defaults to False.'))
+ help='If True, don\'t propagate good/bad changes '
+ 'incrementally. Defaults to False.')
# No input (evals to False),
# --file_args (evals to True),
# --file_args=False,
@@ -214,8 +227,8 @@ def _BuildArgsDict(args):
default=False,
type=StrToBool,
metavar='bool',
- help=('Whether to use a file to pass arguments to scripts. '
- 'Defaults to False.'))
+ help='Whether to use a file to pass arguments to scripts. '
+ 'Defaults to False.')
# No input (evals to True),
# --verify (evals to True),
# --verify=False,
@@ -228,8 +241,8 @@ def _BuildArgsDict(args):
default=True,
type=StrToBool,
metavar='bool',
- help=('Whether to run verify iterations before searching. '
- 'Defaults to True.'))
+ help='Whether to run verify iterations before searching. '
+ 'Defaults to True.')
args.AddArgument(
'-N',
'--prune_iterations',
@@ -256,6 +269,6 @@ def _BuildArgsDict(args):
'--resume',
dest='resume',
action='store_true',
- help=('Resume bisection tool execution from state file.'
- 'Useful if the last bisection was terminated '
- 'before it could properly finish.'))
+ help='Resume bisection tool execution from state file.'
+ 'Useful if the last bisection was terminated '
+ 'before it could properly finish.')