aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.')