diff options
-rwxr-xr-x | binary_search_tool/android/generate_cmd.sh | 41 | ||||
-rwxr-xr-x | binary_search_tool/binary_search_state.py | 69 | ||||
-rw-r--r-- | binary_search_tool/common.py | 61 |
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.') |