aboutsummaryrefslogtreecommitdiff
path: root/binary_search_tool
diff options
context:
space:
mode:
Diffstat (limited to 'binary_search_tool')
-rw-r--r--binary_search_tool/MAINTENANCE2
-rw-r--r--binary_search_tool/__init__.py2
-rwxr-xr-xbinary_search_tool/android/boot_test.sh2
-rwxr-xr-xbinary_search_tool/android/cleanup.sh2
-rwxr-xr-xbinary_search_tool/android/generate_cmd.sh2
-rwxr-xr-xbinary_search_tool/android/get_initial_items.sh3
-rwxr-xr-xbinary_search_tool/android/interactive_test.sh2
-rwxr-xr-xbinary_search_tool/android/setup.sh2
-rwxr-xr-xbinary_search_tool/android/switch_to_bad.sh2
-rwxr-xr-xbinary_search_tool/android/switch_to_good.sh2
-rwxr-xr-xbinary_search_tool/android/test_setup.sh2
-rwxr-xr-xbinary_search_tool/binary_search_perforce.py978
-rwxr-xr-xbinary_search_tool/binary_search_state.py1780
-rw-r--r--binary_search_tool/bisect_driver.py653
-rw-r--r--binary_search_tool/common.py505
-rwxr-xr-xbinary_search_tool/common/boot_test.sh2
-rwxr-xr-xbinary_search_tool/common/hash_test.sh2
-rwxr-xr-xbinary_search_tool/common/interactive_test.sh2
-rwxr-xr-xbinary_search_tool/common/interactive_test_noping.sh2
-rwxr-xr-xbinary_search_tool/common/test_setup.sh2
-rwxr-xr-xbinary_search_tool/compiler_wrapper.py54
-rwxr-xr-xbinary_search_tool/cros_pkg/create_cleanup_script.py174
-rwxr-xr-xbinary_search_tool/cros_pkg/get_initial_items.sh3
-rwxr-xr-xbinary_search_tool/cros_pkg/setup.sh2
-rwxr-xr-xbinary_search_tool/cros_pkg/switch_to_bad.sh2
-rwxr-xr-xbinary_search_tool/cros_pkg/switch_to_good.sh2
-rwxr-xr-xbinary_search_tool/cros_pkg/test_setup_usb.sh2
-rwxr-xr-xbinary_search_tool/ndk/DO_BISECTION.sh2
-rwxr-xr-xbinary_search_tool/ndk/boot_test.sh2
-rwxr-xr-xbinary_search_tool/ndk/get_initial_items.sh3
-rwxr-xr-xbinary_search_tool/ndk/switch_to_good.sh3
-rwxr-xr-xbinary_search_tool/ndk/test_setup.sh2
-rw-r--r--binary_search_tool/pass_mapping.py26
-rwxr-xr-xbinary_search_tool/run_bisect.py777
-rwxr-xr-xbinary_search_tool/run_bisect_tests.py279
-rwxr-xr-xbinary_search_tool/sysroot_wrapper/cleanup.sh2
-rwxr-xr-xbinary_search_tool/sysroot_wrapper/interactive_test_host.sh2
-rwxr-xr-xbinary_search_tool/sysroot_wrapper/setup.sh2
-rwxr-xr-xbinary_search_tool/sysroot_wrapper/test_setup_host.sh2
-rwxr-xr-xbinary_search_tool/sysroot_wrapper/testing_test.py41
-rw-r--r--binary_search_tool/test/__init__.py2
-rwxr-xr-xbinary_search_tool/test/binary_search_tool_test.py1106
-rwxr-xr-xbinary_search_tool/test/cmd_script.py116
-rw-r--r--binary_search_tool/test/cmd_script_no_support.py26
-rwxr-xr-xbinary_search_tool/test/common.py42
-rwxr-xr-xbinary_search_tool/test/gen_init_list.py20
-rwxr-xr-xbinary_search_tool/test/gen_obj.py169
-rwxr-xr-xbinary_search_tool/test/generate_cmd.py21
-rwxr-xr-xbinary_search_tool/test/is_good.py27
-rwxr-xr-xbinary_search_tool/test/is_good_noinc_prune.py45
-rwxr-xr-xbinary_search_tool/test/switch_tmp.py27
-rwxr-xr-xbinary_search_tool/test/switch_to_bad.py25
-rwxr-xr-xbinary_search_tool/test/switch_to_bad_noinc_prune.py27
-rwxr-xr-xbinary_search_tool/test/switch_to_bad_set_file.py31
-rwxr-xr-xbinary_search_tool/test/switch_to_good.py21
-rwxr-xr-xbinary_search_tool/test/switch_to_good_noinc_prune.py23
-rwxr-xr-xbinary_search_tool/test/switch_to_good_set_file.py27
-rwxr-xr-xbinary_search_tool/test/test_setup.py17
-rwxr-xr-xbinary_search_tool/test/test_setup_bad.py11
59 files changed, 3737 insertions, 3377 deletions
diff --git a/binary_search_tool/MAINTENANCE b/binary_search_tool/MAINTENANCE
index 8f96ff10..90ac582d 100644
--- a/binary_search_tool/MAINTENANCE
+++ b/binary_search_tool/MAINTENANCE
@@ -1,4 +1,4 @@
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/binary_search_tool/__init__.py b/binary_search_tool/__init__.py
index 76500def..6e3ade4a 100644
--- a/binary_search_tool/__init__.py
+++ b/binary_search_tool/__init__.py
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/binary_search_tool/android/boot_test.sh b/binary_search_tool/android/boot_test.sh
index dc871601..4c0c77e2 100755
--- a/binary_search_tool/android/boot_test.sh
+++ b/binary_search_tool/android/boot_test.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script pings the android device to determine if it successfully booted.
#
diff --git a/binary_search_tool/android/cleanup.sh b/binary_search_tool/android/cleanup.sh
index 759b3ed4..480b830b 100755
--- a/binary_search_tool/android/cleanup.sh
+++ b/binary_search_tool/android/cleanup.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is part of the Android binary search triage process.
# It should be the last script called by the user, after the user has
diff --git a/binary_search_tool/android/generate_cmd.sh b/binary_search_tool/android/generate_cmd.sh
index 78a39b12..6d0e5692 100755
--- a/binary_search_tool/android/generate_cmd.sh
+++ b/binary_search_tool/android/generate_cmd.sh
@@ -1,6 +1,6 @@
#!/bin/bash -eu
-# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Copyright 2018 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/binary_search_tool/android/get_initial_items.sh b/binary_search_tool/android/get_initial_items.sh
index 2a1eda3a..1ed30425 100755
--- a/binary_search_tool/android/get_initial_items.sh
+++ b/binary_search_tool/android/get_initial_items.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on the Android source tree. This script
@@ -11,4 +11,3 @@
source android/common.sh
cat ${BISECT_GOOD_BUILD}/_LIST
-
diff --git a/binary_search_tool/android/interactive_test.sh b/binary_search_tool/android/interactive_test.sh
index e506b236..0a8a4b8c 100755
--- a/binary_search_tool/android/interactive_test.sh
+++ b/binary_search_tool/android/interactive_test.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script pings the android device to determine if it successfully booted.
# It then asks the user if the image is good or not, allowing the user to
diff --git a/binary_search_tool/android/setup.sh b/binary_search_tool/android/setup.sh
index 7f8ba0e9..06918226 100755
--- a/binary_search_tool/android/setup.sh
+++ b/binary_search_tool/android/setup.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is part of the Android binary search triage process.
# It should be the first script called by the user, after the user has set up
diff --git a/binary_search_tool/android/switch_to_bad.sh b/binary_search_tool/android/switch_to_bad.sh
index d44f9f13..2100ed43 100755
--- a/binary_search_tool/android/switch_to_bad.sh
+++ b/binary_search_tool/android/switch_to_bad.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on the Android source tree. This script
diff --git a/binary_search_tool/android/switch_to_good.sh b/binary_search_tool/android/switch_to_good.sh
index 557553c3..a5be3c3e 100755
--- a/binary_search_tool/android/switch_to_good.sh
+++ b/binary_search_tool/android/switch_to_good.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on the Android source tree. This script
diff --git a/binary_search_tool/android/test_setup.sh b/binary_search_tool/android/test_setup.sh
index 26f8ec22..be4a0b76 100755
--- a/binary_search_tool/android/test_setup.sh
+++ b/binary_search_tool/android/test_setup.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This is the test setup script for generating an Android image based off the
# current working build tree. make is called to relink the object files and
diff --git a/binary_search_tool/binary_search_perforce.py b/binary_search_tool/binary_search_perforce.py
index f2a3c8d5..01756b8e 100755
--- a/binary_search_tool/binary_search_perforce.py
+++ b/binary_search_tool/binary_search_perforce.py
@@ -1,15 +1,13 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module of binary serch for perforce."""
-from __future__ import division
-from __future__ import print_function
-import math
import argparse
+import math
import os
import re
import sys
@@ -18,496 +16,562 @@ import tempfile
from cros_utils import command_executer
from cros_utils import logger
+
verbose = True
def _GetP4ClientSpec(client_name, p4_paths):
- p4_string = ''
- for p4_path in p4_paths:
- if ' ' not in p4_path:
- p4_string += ' -a %s' % p4_path
- else:
- p4_string += ' -a "' + (' //' + client_name + '/').join(p4_path) + '"'
-
- return p4_string
-
-
-def GetP4Command(client_name, p4_port, p4_paths, checkoutdir, p4_snapshot=''):
- command = ''
-
- if p4_snapshot:
- command += 'mkdir -p ' + checkoutdir
+ p4_string = ""
for p4_path in p4_paths:
- real_path = p4_path[1]
- if real_path.endswith('...'):
- real_path = real_path.replace('/...', '')
- command += (
- '; mkdir -p ' + checkoutdir + '/' + os.path.dirname(real_path))
- command += ('&& rsync -lr ' + p4_snapshot + '/' + real_path + ' ' +
- checkoutdir + '/' + os.path.dirname(real_path))
+ if " " not in p4_path:
+ p4_string += " -a %s" % p4_path
+ else:
+ p4_string += (
+ ' -a "' + (" //" + client_name + "/").join(p4_path) + '"'
+ )
+
+ return p4_string
+
+
+def GetP4Command(client_name, p4_port, p4_paths, checkoutdir, p4_snapshot=""):
+ command = ""
+
+ if p4_snapshot:
+ command += "mkdir -p " + checkoutdir
+ for p4_path in p4_paths:
+ real_path = p4_path[1]
+ if real_path.endswith("..."):
+ real_path = real_path.replace("/...", "")
+ command += (
+ "; mkdir -p "
+ + checkoutdir
+ + "/"
+ + os.path.dirname(real_path)
+ )
+ command += (
+ "&& rsync -lr "
+ + p4_snapshot
+ + "/"
+ + real_path
+ + " "
+ + checkoutdir
+ + "/"
+ + os.path.dirname(real_path)
+ )
+ return command
+
+ command += " export P4CONFIG=.p4config"
+ command += " && mkdir -p " + checkoutdir
+ command += " && cd " + checkoutdir
+ command += " && cp ${HOME}/.p4config ."
+ command += " && chmod u+w .p4config"
+ command += ' && echo "P4PORT=' + p4_port + '" >> .p4config'
+ command += ' && echo "P4CLIENT=' + client_name + '" >> .p4config'
+ command += " && g4 client " + _GetP4ClientSpec(client_name, p4_paths)
+ command += " && g4 sync "
+ command += " && cd -"
return command
- command += ' export P4CONFIG=.p4config'
- command += ' && mkdir -p ' + checkoutdir
- command += ' && cd ' + checkoutdir
- command += ' && cp ${HOME}/.p4config .'
- command += ' && chmod u+w .p4config'
- command += ' && echo "P4PORT=' + p4_port + '" >> .p4config'
- command += ' && echo "P4CLIENT=' + client_name + '" >> .p4config'
- command += (' && g4 client ' + _GetP4ClientSpec(client_name, p4_paths))
- command += ' && g4 sync '
- command += ' && cd -'
- return command
-
class BinarySearchPoint(object):
- """Class of binary search point."""
+ """Class of binary search point."""
- def __init__(self, revision, status, tag=None):
- self.revision = revision
- self.status = status
- self.tag = tag
+ def __init__(self, revision, status, tag=None):
+ self.revision = revision
+ self.status = status
+ self.tag = tag
class BinarySearcherForPass(object):
- """Class of pass level binary searcher."""
-
- def __init__(self, logger_to_set=None):
- self.current = 0
- self.lo = 0
- self.hi = 0
- self.total = 0
- if logger_to_set is not None:
- self.logger = logger_to_set
- else:
- self.logger = logger.GetLogger()
-
- def GetNext(self):
- # For the first run, update self.hi with total pass/transformation count
- if self.hi == 0:
- self.hi = self.total
- self.current = (self.hi + self.lo) // 2
- message = ('Bisecting between: (%d, %d)' % (self.lo, self.hi))
- self.logger.LogOutput(message, print_to_console=verbose)
- message = ('Current limit number: %d' % self.current)
- self.logger.LogOutput(message, print_to_console=verbose)
- return self.current
-
- def SetStatus(self, status):
- """Set lo/hi status based on test script result
-
- If status == 0, it means that runtime error is not introduced until current
- pass/transformation, so we need to increase lower bound for binary search.
-
- If status == 1, it means that runtime error still happens with current pass/
- transformation, so we need to decrease upper bound for binary search.
-
- Returns:
- True if we find the bad pass/transformation, or cannot find bad one after
- decreasing to the first pass/transformation. Otherwise False.
- """
- assert status in (0, 1, 125), status
-
- if self.current == 0:
- message = ('Runtime error occurs before first pass/transformation. '
- 'Stop binary searching.')
- self.logger.LogOutput(message, print_to_console=verbose)
- return True
-
- if status == 0:
- message = ('Runtime error is not reproduced, increasing lower bound.')
- self.logger.LogOutput(message, print_to_console=verbose)
- self.lo = self.current + 1
- elif status == 1:
- message = ('Runtime error is reproduced, decreasing upper bound..')
- self.logger.LogOutput(message, print_to_console=verbose)
- self.hi = self.current
-
- if self.lo >= self.hi:
- return True
-
- return False
+ """Class of pass level binary searcher."""
+
+ def __init__(self, logger_to_set=None):
+ self.current = 0
+ self.lo = 0
+ self.hi = 0
+ self.total = 0
+ if logger_to_set is not None:
+ self.logger = logger_to_set
+ else:
+ self.logger = logger.GetLogger()
+
+ def GetNext(self):
+ # For the first run, update self.hi with total pass/transformation count
+ if self.hi == 0:
+ self.hi = self.total
+ self.current = (self.hi + self.lo) // 2
+ message = "Bisecting between: (%d, %d)" % (self.lo, self.hi)
+ self.logger.LogOutput(message, print_to_console=verbose)
+ message = "Current limit number: %d" % self.current
+ self.logger.LogOutput(message, print_to_console=verbose)
+ return self.current
+
+ def SetStatus(self, status):
+ """Set lo/hi status based on test script result
+
+ If status == 0, it means that runtime error is not introduced until current
+ pass/transformation, so we need to increase lower bound for binary search.
+
+ If status == 1, it means that runtime error still happens with current pass/
+ transformation, so we need to decrease upper bound for binary search.
+
+ Returns:
+ True if we find the bad pass/transformation, or cannot find bad one after
+ decreasing to the first pass/transformation. Otherwise False.
+ """
+ assert status in (0, 1, 125), status
+
+ if self.current == 0:
+ message = (
+ "Runtime error occurs before first pass/transformation. "
+ "Stop binary searching."
+ )
+ self.logger.LogOutput(message, print_to_console=verbose)
+ return True
+
+ if status == 0:
+ message = "Runtime error is not reproduced, increasing lower bound."
+ self.logger.LogOutput(message, print_to_console=verbose)
+ self.lo = self.current + 1
+ elif status == 1:
+ message = "Runtime error is reproduced, decreasing upper bound.."
+ self.logger.LogOutput(message, print_to_console=verbose)
+ self.hi = self.current
+
+ if self.lo >= self.hi:
+ return True
+
+ return False
class BinarySearcher(object):
- """Class of binary searcher."""
-
- def __init__(self, logger_to_set=None):
- self.sorted_list = []
- self.index_log = []
- self.status_log = []
- self.skipped_indices = []
- self.current = 0
- self.points = {}
- self.lo = 0
- self.hi = 0
- if logger_to_set is not None:
- self.logger = logger_to_set
- else:
- self.logger = logger.GetLogger()
-
- def SetSortedList(self, sorted_list):
- assert sorted_list
- self.sorted_list = sorted_list
- self.index_log = []
- self.hi = len(sorted_list) - 1
- self.lo = 0
- self.points = {}
- for i in range(len(self.sorted_list)):
- bsp = BinarySearchPoint(self.sorted_list[i], -1, 'Not yet done.')
- self.points[i] = bsp
-
- def SetStatus(self, status, tag=None):
- message = ('Revision: %s index: %d returned: %d' %
- (self.sorted_list[self.current], self.current, status))
- self.logger.LogOutput(message, print_to_console=verbose)
- assert status in (0, 1, 125), status
- self.index_log.append(self.current)
- self.status_log.append(status)
- bsp = BinarySearchPoint(self.sorted_list[self.current], status, tag)
- self.points[self.current] = bsp
-
- if status == 125:
- self.skipped_indices.append(self.current)
-
- if status in (0, 1):
- if status == 0:
- self.lo = self.current + 1
- elif status == 1:
- self.hi = self.current
- self.logger.LogOutput('lo: %d hi: %d\n' % (self.lo, self.hi))
- self.current = (self.lo + self.hi) // 2
-
- if self.lo == self.hi:
- message = ('Search complete. First bad version: %s'
- ' at index: %d' % (self.sorted_list[self.current], self.lo))
- self.logger.LogOutput(message)
- return True
-
- for index in range(self.lo, self.hi):
- if index not in self.skipped_indices:
- return False
- self.logger.LogOutput(
- 'All skipped indices between: %d and %d\n' % (self.lo, self.hi),
- print_to_console=verbose)
- return True
-
- # Does a better job with chromeos flakiness.
- def GetNextFlakyBinary(self):
- t = (self.lo, self.current, self.hi)
- q = [t]
- while q:
- element = q.pop(0)
- if element[1] in self.skipped_indices:
- # Go top
- to_add = (element[0], (element[0] + element[1]) // 2, element[1])
- q.append(to_add)
- # Go bottom
- to_add = (element[1], (element[1] + element[2]) // 2, element[2])
- q.append(to_add)
- else:
- self.current = element[1]
- return
- assert q, 'Queue should never be 0-size!'
-
- def GetNextFlakyLinear(self):
- current_hi = self.current
- current_lo = self.current
- while True:
- if current_hi < self.hi and current_hi not in self.skipped_indices:
- self.current = current_hi
- break
- if current_lo >= self.lo and current_lo not in self.skipped_indices:
- self.current = current_lo
- break
- if current_lo < self.lo and current_hi >= self.hi:
- break
-
- current_hi += 1
- current_lo -= 1
-
- def GetNext(self):
- self.current = (self.hi + self.lo) // 2
- # Try going forward if current is skipped.
- if self.current in self.skipped_indices:
- self.GetNextFlakyBinary()
-
- # TODO: Add an estimated time remaining as well.
- message = ('Estimated tries: min: %d max: %d\n' % (1 + math.log(
- self.hi - self.lo, 2), self.hi - self.lo - len(self.skipped_indices)))
- self.logger.LogOutput(message, print_to_console=verbose)
- message = ('lo: %d hi: %d current: %d version: %s\n' %
- (self.lo, self.hi, self.current, self.sorted_list[self.current]))
- self.logger.LogOutput(message, print_to_console=verbose)
- self.logger.LogOutput(str(self), print_to_console=verbose)
- return self.sorted_list[self.current]
-
- def SetLoRevision(self, lo_revision):
- self.lo = self.sorted_list.index(lo_revision)
-
- def SetHiRevision(self, hi_revision):
- self.hi = self.sorted_list.index(hi_revision)
-
- def GetAllPoints(self):
- to_return = ''
- for i in range(len(self.sorted_list)):
- to_return += (
- '%d %d %s\n' % (self.points[i].status, i, self.points[i].revision))
-
- return to_return
-
- def __str__(self):
- to_return = ''
- to_return += 'Current: %d\n' % self.current
- to_return += str(self.index_log) + '\n'
- revision_log = []
- for index in self.index_log:
- revision_log.append(self.sorted_list[index])
- to_return += str(revision_log) + '\n'
- to_return += str(self.status_log) + '\n'
- to_return += 'Skipped indices:\n'
- to_return += str(self.skipped_indices) + '\n'
- to_return += self.GetAllPoints()
- return to_return
+ """Class of binary searcher."""
+
+ def __init__(self, logger_to_set=None):
+ self.sorted_list = []
+ self.index_log = []
+ self.status_log = []
+ self.skipped_indices = []
+ self.current = 0
+ self.points = {}
+ self.lo = 0
+ self.hi = 0
+ if logger_to_set is not None:
+ self.logger = logger_to_set
+ else:
+ self.logger = logger.GetLogger()
+
+ def SetSortedList(self, sorted_list):
+ assert sorted_list
+ self.sorted_list = sorted_list
+ self.index_log = []
+ self.hi = len(sorted_list) - 1
+ self.lo = 0
+ self.points = {}
+ for i in range(len(self.sorted_list)):
+ bsp = BinarySearchPoint(self.sorted_list[i], -1, "Not yet done.")
+ self.points[i] = bsp
+
+ def SetStatus(self, status, tag=None):
+ message = "Revision: %s index: %d returned: %d" % (
+ self.sorted_list[self.current],
+ self.current,
+ status,
+ )
+ self.logger.LogOutput(message, print_to_console=verbose)
+ assert status in (0, 1, 125), status
+ self.index_log.append(self.current)
+ self.status_log.append(status)
+ bsp = BinarySearchPoint(self.sorted_list[self.current], status, tag)
+ self.points[self.current] = bsp
+
+ if status == 125:
+ self.skipped_indices.append(self.current)
+
+ if status in (0, 1):
+ if status == 0:
+ self.lo = self.current + 1
+ elif status == 1:
+ self.hi = self.current
+ self.logger.LogOutput("lo: %d hi: %d\n" % (self.lo, self.hi))
+ self.current = (self.lo + self.hi) // 2
+
+ if self.lo == self.hi:
+ message = (
+ "Search complete. First bad version: %s"
+ " at index: %d"
+ % (
+ self.sorted_list[self.current],
+ self.lo,
+ )
+ )
+ self.logger.LogOutput(message)
+ return True
+
+ for index in range(self.lo, self.hi):
+ if index not in self.skipped_indices:
+ return False
+ self.logger.LogOutput(
+ "All skipped indices between: %d and %d\n" % (self.lo, self.hi),
+ print_to_console=verbose,
+ )
+ return True
+
+ # Does a better job with chromeos flakiness.
+ def GetNextFlakyBinary(self):
+ t = (self.lo, self.current, self.hi)
+ q = [t]
+ while q:
+ element = q.pop(0)
+ if element[1] in self.skipped_indices:
+ # Go top
+ to_add = (
+ element[0],
+ (element[0] + element[1]) // 2,
+ element[1],
+ )
+ q.append(to_add)
+ # Go bottom
+ to_add = (
+ element[1],
+ (element[1] + element[2]) // 2,
+ element[2],
+ )
+ q.append(to_add)
+ else:
+ self.current = element[1]
+ return
+ assert q, "Queue should never be 0-size!"
+
+ def GetNextFlakyLinear(self):
+ current_hi = self.current
+ current_lo = self.current
+ while True:
+ if current_hi < self.hi and current_hi not in self.skipped_indices:
+ self.current = current_hi
+ break
+ if current_lo >= self.lo and current_lo not in self.skipped_indices:
+ self.current = current_lo
+ break
+ if current_lo < self.lo and current_hi >= self.hi:
+ break
+
+ current_hi += 1
+ current_lo -= 1
+
+ def GetNext(self):
+ self.current = (self.hi + self.lo) // 2
+ # Try going forward if current is skipped.
+ if self.current in self.skipped_indices:
+ self.GetNextFlakyBinary()
+
+ # TODO: Add an estimated time remaining as well.
+ message = "Estimated tries: min: %d max: %d\n" % (
+ 1 + math.log(self.hi - self.lo, 2),
+ self.hi - self.lo - len(self.skipped_indices),
+ )
+ self.logger.LogOutput(message, print_to_console=verbose)
+ message = "lo: %d hi: %d current: %d version: %s\n" % (
+ self.lo,
+ self.hi,
+ self.current,
+ self.sorted_list[self.current],
+ )
+ self.logger.LogOutput(message, print_to_console=verbose)
+ self.logger.LogOutput(str(self), print_to_console=verbose)
+ return self.sorted_list[self.current]
+
+ def SetLoRevision(self, lo_revision):
+ self.lo = self.sorted_list.index(lo_revision)
+
+ def SetHiRevision(self, hi_revision):
+ self.hi = self.sorted_list.index(hi_revision)
+
+ def GetAllPoints(self):
+ to_return = ""
+ for i in range(len(self.sorted_list)):
+ to_return += "%d %d %s\n" % (
+ self.points[i].status,
+ i,
+ self.points[i].revision,
+ )
+
+ return to_return
+
+ def __str__(self):
+ to_return = ""
+ to_return += "Current: %d\n" % self.current
+ to_return += str(self.index_log) + "\n"
+ revision_log = []
+ for index in self.index_log:
+ revision_log.append(self.sorted_list[index])
+ to_return += str(revision_log) + "\n"
+ to_return += str(self.status_log) + "\n"
+ to_return += "Skipped indices:\n"
+ to_return += str(self.skipped_indices) + "\n"
+ to_return += self.GetAllPoints()
+ return to_return
class RevisionInfo(object):
- """Class of reversion info."""
+ """Class of reversion info."""
- def __init__(self, date, client, description):
- self.date = date
- self.client = client
- self.description = description
- self.status = -1
+ def __init__(self, date, client, description):
+ self.date = date
+ self.client = client
+ self.description = description
+ self.status = -1
class VCSBinarySearcher(object):
- """Class of VCS binary searcher."""
+ """Class of VCS binary searcher."""
- def __init__(self):
- self.bs = BinarySearcher()
- self.rim = {}
- self.current_ce = None
- self.checkout_dir = None
- self.current_revision = None
+ def __init__(self):
+ self.bs = BinarySearcher()
+ self.rim = {}
+ self.current_ce = None
+ self.checkout_dir = None
+ self.current_revision = None
- def Initialize(self):
- pass
+ def Initialize(self):
+ pass
- def GetNextRevision(self):
- pass
+ def GetNextRevision(self):
+ pass
- def CheckoutRevision(self, current_revision):
- pass
+ def CheckoutRevision(self, current_revision):
+ pass
- def SetStatus(self, status):
- pass
+ def SetStatus(self, status):
+ pass
- def Cleanup(self):
- pass
+ def Cleanup(self):
+ pass
- def SetGoodRevision(self, revision):
- if revision is None:
- return
- assert revision in self.bs.sorted_list
- self.bs.SetLoRevision(revision)
+ def SetGoodRevision(self, revision):
+ if revision is None:
+ return
+ assert revision in self.bs.sorted_list
+ self.bs.SetLoRevision(revision)
- def SetBadRevision(self, revision):
- if revision is None:
- return
- assert revision in self.bs.sorted_list
- self.bs.SetHiRevision(revision)
+ def SetBadRevision(self, revision):
+ if revision is None:
+ return
+ assert revision in self.bs.sorted_list
+ self.bs.SetHiRevision(revision)
class P4BinarySearcher(VCSBinarySearcher):
- """Class of P4 binary searcher."""
-
- def __init__(self, p4_port, p4_paths, test_command):
- VCSBinarySearcher.__init__(self)
- self.p4_port = p4_port
- self.p4_paths = p4_paths
- self.test_command = test_command
- self.checkout_dir = tempfile.mkdtemp()
- self.ce = command_executer.GetCommandExecuter()
- self.client_name = 'binary-searcher-$HOSTNAME-$USER'
- self.job_log_root = '/home/asharif/www/coreboot_triage/'
- self.changes = None
-
- def Initialize(self):
- self.Cleanup()
- command = GetP4Command(self.client_name, self.p4_port, self.p4_paths, 1,
- self.checkout_dir)
- self.ce.RunCommand(command)
- command = 'cd %s && g4 changes ...' % self.checkout_dir
- _, out, _ = self.ce.RunCommandWOutput(command)
- self.changes = re.findall(r'Change (\d+)', out)
- change_infos = re.findall(
- r'Change (\d+) on ([\d/]+) by '
- r"([^\s]+) ('[^']*')", out)
- for change_info in change_infos:
- ri = RevisionInfo(change_info[1], change_info[2], change_info[3])
- self.rim[change_info[0]] = ri
- # g4 gives changes in reverse chronological order.
- self.changes.reverse()
- self.bs.SetSortedList(self.changes)
-
- def SetStatus(self, status):
- self.rim[self.current_revision].status = status
- return self.bs.SetStatus(status)
-
- def GetNextRevision(self):
- next_revision = self.bs.GetNext()
- self.current_revision = next_revision
- return next_revision
-
- def CleanupCLs(self):
- if not os.path.isfile(self.checkout_dir + '/.p4config'):
- command = 'cd %s' % self.checkout_dir
- command += ' && cp ${HOME}/.p4config .'
- command += ' && echo "P4PORT=' + self.p4_port + '" >> .p4config'
- command += ' && echo "P4CLIENT=' + self.client_name + '" >> .p4config'
- self.ce.RunCommand(command)
- command = 'cd %s' % self.checkout_dir
- command += '; g4 changes -c %s' % self.client_name
- _, out, _ = self.ce.RunCommandWOutput(command)
- changes = re.findall(r'Change (\d+)', out)
- if changes:
- command = 'cd %s' % self.checkout_dir
- for change in changes:
- command += '; g4 revert -c %s' % change
- self.ce.RunCommand(command)
-
- def CleanupClient(self):
- command = 'cd %s' % self.checkout_dir
- command += '; g4 revert ...'
- command += '; g4 client -d %s' % self.client_name
- self.ce.RunCommand(command)
-
- def Cleanup(self):
- self.CleanupCLs()
- self.CleanupClient()
-
- def __str__(self):
- to_return = ''
- for change in self.changes:
- ri = self.rim[change]
- if ri.status == -1:
- to_return = '%s\t%d\n' % (change, ri.status)
- else:
- to_return += ('%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\n' %
- (change, ri.status, ri.date, ri.client, ri.description,
- self.job_log_root + change + '.cmd', self.job_log_root +
- change + '.out', self.job_log_root + change + '.err'))
- return to_return
+ """Class of P4 binary searcher."""
+
+ def __init__(self, p4_port, p4_paths, test_command):
+ VCSBinarySearcher.__init__(self)
+ self.p4_port = p4_port
+ self.p4_paths = p4_paths
+ self.test_command = test_command
+ self.checkout_dir = tempfile.mkdtemp()
+ self.ce = command_executer.GetCommandExecuter()
+ self.client_name = "binary-searcher-$HOSTNAME-$USER"
+ self.job_log_root = "/home/asharif/www/coreboot_triage/"
+ self.changes = None
+
+ def Initialize(self):
+ self.Cleanup()
+ command = GetP4Command(
+ self.client_name, self.p4_port, self.p4_paths, 1, self.checkout_dir
+ )
+ self.ce.RunCommand(command)
+ command = "cd %s && g4 changes ..." % self.checkout_dir
+ _, out, _ = self.ce.RunCommandWOutput(command)
+ self.changes = re.findall(r"Change (\d+)", out)
+ change_infos = re.findall(
+ r"Change (\d+) on ([\d/]+) by " r"([^\s]+) ('[^']*')", out
+ )
+ for change_info in change_infos:
+ ri = RevisionInfo(change_info[1], change_info[2], change_info[3])
+ self.rim[change_info[0]] = ri
+ # g4 gives changes in reverse chronological order.
+ self.changes.reverse()
+ self.bs.SetSortedList(self.changes)
+
+ def SetStatus(self, status):
+ self.rim[self.current_revision].status = status
+ return self.bs.SetStatus(status)
+
+ def GetNextRevision(self):
+ next_revision = self.bs.GetNext()
+ self.current_revision = next_revision
+ return next_revision
+
+ def CleanupCLs(self):
+ if not os.path.isfile(self.checkout_dir + "/.p4config"):
+ command = "cd %s" % self.checkout_dir
+ command += " && cp ${HOME}/.p4config ."
+ command += ' && echo "P4PORT=' + self.p4_port + '" >> .p4config'
+ command += (
+ ' && echo "P4CLIENT=' + self.client_name + '" >> .p4config'
+ )
+ self.ce.RunCommand(command)
+ command = "cd %s" % self.checkout_dir
+ command += "; g4 changes -c %s" % self.client_name
+ _, out, _ = self.ce.RunCommandWOutput(command)
+ changes = re.findall(r"Change (\d+)", out)
+ if changes:
+ command = "cd %s" % self.checkout_dir
+ for change in changes:
+ command += "; g4 revert -c %s" % change
+ self.ce.RunCommand(command)
+
+ def CleanupClient(self):
+ command = "cd %s" % self.checkout_dir
+ command += "; g4 revert ..."
+ command += "; g4 client -d %s" % self.client_name
+ self.ce.RunCommand(command)
+
+ def Cleanup(self):
+ self.CleanupCLs()
+ self.CleanupClient()
+
+ def __str__(self):
+ to_return = ""
+ for change in self.changes:
+ ri = self.rim[change]
+ if ri.status == -1:
+ to_return = "%s\t%d\n" % (change, ri.status)
+ else:
+ to_return += "%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\n" % (
+ change,
+ ri.status,
+ ri.date,
+ ri.client,
+ ri.description,
+ self.job_log_root + change + ".cmd",
+ self.job_log_root + change + ".out",
+ self.job_log_root + change + ".err",
+ )
+ return to_return
class P4GCCBinarySearcher(P4BinarySearcher):
- """Class of P4 gcc binary searcher."""
-
- # TODO: eventually get these patches from g4 instead of creating them manually
- def HandleBrokenCLs(self, current_revision):
- cr = int(current_revision)
- problematic_ranges = []
- problematic_ranges.append([44528, 44539])
- problematic_ranges.append([44528, 44760])
- problematic_ranges.append([44335, 44882])
- command = 'pwd'
- for pr in problematic_ranges:
- if cr in range(pr[0], pr[1]):
- patch_file = '/home/asharif/triage_tool/%d-%d.patch' % (pr[0], pr[1])
- with open(patch_file, encoding='utf-8') as f:
- patch = f.read()
- files = re.findall('--- (//.*)', patch)
- command += '; cd %s' % self.checkout_dir
- for f in files:
- command += '; g4 open %s' % f
- command += '; patch -p2 < %s' % patch_file
- self.current_ce.RunCommand(command)
-
- def CheckoutRevision(self, current_revision):
- job_logger = logger.Logger(
- self.job_log_root, current_revision, True, subdir='')
- self.current_ce = command_executer.GetCommandExecuter(job_logger)
-
- self.CleanupCLs()
- # Change the revision of only the gcc part of the toolchain.
- command = (
- 'cd %s/gcctools/google_vendor_src_branch/gcc '
- '&& g4 revert ...; g4 sync @%s' % (self.checkout_dir, current_revision))
- self.current_ce.RunCommand(command)
-
- self.HandleBrokenCLs(current_revision)
+ """Class of P4 gcc binary searcher."""
+
+ # TODO: eventually get these patches from g4 instead of creating them manually
+ def HandleBrokenCLs(self, current_revision):
+ cr = int(current_revision)
+ problematic_ranges = []
+ problematic_ranges.append([44528, 44539])
+ problematic_ranges.append([44528, 44760])
+ problematic_ranges.append([44335, 44882])
+ command = "pwd"
+ for pr in problematic_ranges:
+ if cr in range(pr[0], pr[1]):
+ patch_file = "/home/asharif/triage_tool/%d-%d.patch" % (
+ pr[0],
+ pr[1],
+ )
+ with open(patch_file, encoding="utf-8") as f:
+ patch = f.read()
+ files = re.findall("--- (//.*)", patch)
+ command += "; cd %s" % self.checkout_dir
+ for f in files:
+ command += "; g4 open %s" % f
+ command += "; patch -p2 < %s" % patch_file
+ self.current_ce.RunCommand(command)
+
+ def CheckoutRevision(self, current_revision):
+ job_logger = logger.Logger(
+ self.job_log_root, current_revision, True, subdir=""
+ )
+ self.current_ce = command_executer.GetCommandExecuter(job_logger)
+
+ self.CleanupCLs()
+ # Change the revision of only the gcc part of the toolchain.
+ command = (
+ "cd %s/gcctools/google_vendor_src_branch/gcc "
+ "&& g4 revert ...; g4 sync @%s"
+ % (self.checkout_dir, current_revision)
+ )
+ self.current_ce.RunCommand(command)
+
+ self.HandleBrokenCLs(current_revision)
def Main(argv):
- """The main function."""
- # Common initializations
- ### command_executer.InitCommandExecuter(True)
- ce = command_executer.GetCommandExecuter()
-
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '-n',
- '--num_tries',
- dest='num_tries',
- default='100',
- help='Number of tries.')
- parser.add_argument(
- '-g',
- '--good_revision',
- dest='good_revision',
- help='Last known good revision.')
- parser.add_argument(
- '-b',
- '--bad_revision',
- dest='bad_revision',
- help='Last known bad revision.')
- parser.add_argument(
- '-s', '--script', dest='script', help='Script to run for every version.')
- options = parser.parse_args(argv)
- # First get all revisions
- p4_paths = [
- '//depot2/gcctools/google_vendor_src_branch/gcc/gcc-4.4.3/...',
- '//depot2/gcctools/google_vendor_src_branch/binutils/'
- 'binutils-2.20.1-mobile/...',
- '//depot2/gcctools/google_vendor_src_branch/'
- 'binutils/binutils-20100303/...'
- ]
- p4gccbs = P4GCCBinarySearcher('perforce2:2666', p4_paths, '')
-
- # Main loop:
- terminated = False
- num_tries = int(options.num_tries)
- script = os.path.expanduser(options.script)
-
- try:
- p4gccbs.Initialize()
- p4gccbs.SetGoodRevision(options.good_revision)
- p4gccbs.SetBadRevision(options.bad_revision)
- while not terminated and num_tries > 0:
- current_revision = p4gccbs.GetNextRevision()
-
- # Now run command to get the status
- ce = command_executer.GetCommandExecuter()
- command = '%s %s' % (script, p4gccbs.checkout_dir)
- status = ce.RunCommand(command)
- message = (
- 'Revision: %s produced: %d status\n' % (current_revision, status))
- logger.GetLogger().LogOutput(message, print_to_console=verbose)
- terminated = p4gccbs.SetStatus(status)
- num_tries -= 1
- logger.GetLogger().LogOutput(str(p4gccbs), print_to_console=verbose)
-
- if not terminated:
- logger.GetLogger().LogOutput(
- 'Tries: %d expired.' % num_tries, print_to_console=verbose)
- logger.GetLogger().LogOutput(str(p4gccbs.bs), print_to_console=verbose)
- except (KeyboardInterrupt, SystemExit):
- logger.GetLogger().LogOutput('Cleaning up...')
- finally:
- logger.GetLogger().LogOutput(str(p4gccbs.bs), print_to_console=verbose)
- p4gccbs.Cleanup()
-
-
-if __name__ == '__main__':
- Main(sys.argv[1:])
+ """The main function."""
+ # Common initializations
+ ### command_executer.InitCommandExecuter(True)
+ ce = command_executer.GetCommandExecuter()
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-n",
+ "--num_tries",
+ dest="num_tries",
+ default="100",
+ help="Number of tries.",
+ )
+ parser.add_argument(
+ "-g",
+ "--good_revision",
+ dest="good_revision",
+ help="Last known good revision.",
+ )
+ parser.add_argument(
+ "-b",
+ "--bad_revision",
+ dest="bad_revision",
+ help="Last known bad revision.",
+ )
+ parser.add_argument(
+ "-s", "--script", dest="script", help="Script to run for every version."
+ )
+ options = parser.parse_args(argv)
+ # First get all revisions
+ p4_paths = [
+ "//depot2/gcctools/google_vendor_src_branch/gcc/gcc-4.4.3/...",
+ "//depot2/gcctools/google_vendor_src_branch/binutils/"
+ "binutils-2.20.1-mobile/...",
+ "//depot2/gcctools/google_vendor_src_branch/"
+ "binutils/binutils-20100303/...",
+ ]
+ p4gccbs = P4GCCBinarySearcher("perforce2:2666", p4_paths, "")
+
+ # Main loop:
+ terminated = False
+ num_tries = int(options.num_tries)
+ script = os.path.expanduser(options.script)
+
+ try:
+ p4gccbs.Initialize()
+ p4gccbs.SetGoodRevision(options.good_revision)
+ p4gccbs.SetBadRevision(options.bad_revision)
+ while not terminated and num_tries > 0:
+ current_revision = p4gccbs.GetNextRevision()
+
+ # Now run command to get the status
+ ce = command_executer.GetCommandExecuter()
+ command = "%s %s" % (script, p4gccbs.checkout_dir)
+ status = ce.RunCommand(command)
+ message = "Revision: %s produced: %d status\n" % (
+ current_revision,
+ status,
+ )
+ logger.GetLogger().LogOutput(message, print_to_console=verbose)
+ terminated = p4gccbs.SetStatus(status)
+ num_tries -= 1
+ logger.GetLogger().LogOutput(str(p4gccbs), print_to_console=verbose)
+
+ if not terminated:
+ logger.GetLogger().LogOutput(
+ "Tries: %d expired." % num_tries, print_to_console=verbose
+ )
+ logger.GetLogger().LogOutput(str(p4gccbs.bs), print_to_console=verbose)
+ except (KeyboardInterrupt, SystemExit):
+ logger.GetLogger().LogOutput("Cleaning up...")
+ finally:
+ logger.GetLogger().LogOutput(str(p4gccbs.bs), print_to_console=verbose)
+ p4gccbs.Cleanup()
+
+
+if __name__ == "__main__":
+ Main(sys.argv[1:])
diff --git a/binary_search_tool/binary_search_state.py b/binary_search_tool/binary_search_state.py
index 1ddd65ce..1b423b5c 100755
--- a/binary_search_tool/binary_search_state.py
+++ b/binary_search_tool/binary_search_state.py
@@ -1,13 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# 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 division
-from __future__ import print_function
import argparse
import contextlib
@@ -30,871 +28,993 @@ from binary_search_tool import pass_mapping
from cros_utils import command_executer
from cros_utils import logger
-GOOD_SET_VAR = 'BISECT_GOOD_SET'
-BAD_SET_VAR = 'BISECT_BAD_SET'
-STATE_FILE = '%s.state' % sys.argv[0]
+GOOD_SET_VAR = "BISECT_GOOD_SET"
+BAD_SET_VAR = "BISECT_BAD_SET"
+
+STATE_FILE = "%s.state" % sys.argv[0]
HIDDEN_STATE_FILE = os.path.join(
- os.path.dirname(STATE_FILE), '.%s' % os.path.basename(STATE_FILE))
+ os.path.dirname(STATE_FILE), ".%s" % os.path.basename(STATE_FILE)
+)
@contextlib.contextmanager
def SetFile(env_var, items):
- """Generate set files that can be used by switch/test scripts.
-
- Generate temporary set file (good/bad) holding contents of good/bad items for
- the current binary search iteration. Store the name of each file as an
- environment variable so all child processes can access it.
-
- This function is a contextmanager, meaning it's meant to be used with the
- "with" statement in Python. This is so cleanup and setup happens automatically
- and cleanly. Execution of the outer "with" statement happens at the "yield"
- statement.
+ """Generate set files that can be used by switch/test scripts.
- Args:
- env_var: What environment variable to store the file name in.
- items: What items are in this set.
- """
- with tempfile.NamedTemporaryFile('w', encoding='utf-8') as f:
- os.environ[env_var] = f.name
- f.write('\n'.join(items))
- f.flush()
- yield
+ Generate temporary set file (good/bad) holding contents of good/bad items for
+ the current binary search iteration. Store the name of each file as an
+ environment variable so all child processes can access it.
-
-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, pass_bisect,
- ir_diff, 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
- self.switch_to_bad = switch_to_bad
- self.test_setup_script = test_setup_script
- self.test_script = test_script
- self.incremental = incremental
- self.prune = prune
- self.pass_bisect = pass_bisect
- self.ir_diff = ir_diff
- self.iterations = iterations
- self.prune_iterations = prune_iterations
- self.verify = verify
- self.file_args = file_args
- self.verbose = verbose
-
- self.l = logger.GetLogger()
- self.ce = command_executer.GetCommandExecuter()
-
- self.resumed = False
- self.prune_cycles = 0
- self.search_cycles = 0
- self.binary_search = None
- self.all_items = None
- self.cmd_script = None
- self.mode = None
- self.PopulateItemsUsingCommand(self.get_initial_items)
- self.currently_good_items = set()
- self.currently_bad_items = set()
- self.found_items = set()
- self.known_good = set()
-
- self.start_time = time.time()
-
- def SwitchToGood(self, item_list):
- """Switch given items to "good" set."""
- if self.incremental:
- self.l.LogOutput(
- 'Incremental set. Wanted to switch %s to good' % str(item_list),
- print_to_console=self.verbose)
- incremental_items = [
- item for item in item_list if item not in self.currently_good_items
- ]
- item_list = incremental_items
- self.l.LogOutput(
- 'Incremental set. Actually switching %s to good' % str(item_list),
- print_to_console=self.verbose)
-
- if not item_list:
- return
-
- self.l.LogOutput(
- 'Switching %s to good' % str(item_list), print_to_console=self.verbose)
- self.RunSwitchScript(self.switch_to_good, item_list)
- self.currently_good_items = self.currently_good_items.union(set(item_list))
- self.currently_bad_items.difference_update(set(item_list))
-
- def SwitchToBad(self, item_list):
- """Switch given items to "bad" set."""
- if self.incremental:
- self.l.LogOutput(
- 'Incremental set. Wanted to switch %s to bad' % str(item_list),
- print_to_console=self.verbose)
- incremental_items = [
- item for item in item_list if item not in self.currently_bad_items
- ]
- item_list = incremental_items
- self.l.LogOutput(
- 'Incremental set. Actually switching %s to bad' % str(item_list),
- print_to_console=self.verbose)
-
- if not item_list:
- return
-
- self.l.LogOutput(
- 'Switching %s to bad' % str(item_list), print_to_console=self.verbose)
- self.RunSwitchScript(self.switch_to_bad, item_list)
- self.currently_bad_items = self.currently_bad_items.union(set(item_list))
- self.currently_good_items.difference_update(set(item_list))
-
- def RunSwitchScript(self, switch_script, item_list):
- """Pass given items to switch script.
+ This function is a contextmanager, meaning it's meant to be used with the
+ "with" statement in Python. This is so cleanup and setup happens automatically
+ and cleanly. Execution of the outer "with" statement happens at the "yield"
+ statement.
Args:
- switch_script: path to switch script
- item_list: list of all items to be switched
+ env_var: What environment variable to store the file name in.
+ items: What items are in this set.
"""
- if self.file_args:
- with tempfile.NamedTemporaryFile('w', encoding='utf-8') as f:
- f.write('\n'.join(item_list))
+ with tempfile.NamedTemporaryFile("w", encoding="utf-8") as f:
+ os.environ[env_var] = f.name
+ f.write("\n".join(items))
f.flush()
- command = '%s %s' % (switch_script, f.name)
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- command, print_to_console=self.verbose)
- else:
- command = '%s %s' % (switch_script, ' '.join(item_list))
- try:
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- command, print_to_console=self.verbose)
- except OSError as e:
- if e.errno == errno.E2BIG:
- raise RuntimeError('Too many arguments for switch script! Use '
- '--file_args')
- assert ret == 0, 'Switch script %s returned %d' % (switch_script, ret)
-
- def TestScript(self):
- """Run test script and return exit code from script."""
- command = self.test_script
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(command)
- return ret
-
- def TestSetupScript(self):
- """Run test setup script and return exit code from script."""
- if not self.test_setup_script:
- return 0
-
- command = self.test_setup_script
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(command)
- return ret
-
- def GenerateBadCommandScript(self, bad_items):
- """Generate command line script for building bad item."""
- assert not self.prune, 'Prune must be false if pass_bisect is set.'
- 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 DoVerify(self):
- """Verify correctness of test environment.
-
- Verify that a "good" set of items produces a "good" result and that a "bad"
- set of items produces a "bad" result. To be run directly before running
- DoSearch. If verify is False this step is skipped.
- """
- if not self.verify:
- return
-
- self.l.LogOutput('VERIFICATION')
- self.l.LogOutput('Beginning tests to verify good/bad sets\n')
-
- self._OutputProgress('Verifying items from GOOD set\n')
- with SetFile(GOOD_SET_VAR, self.all_items), SetFile(BAD_SET_VAR, []):
- self.l.LogOutput('Resetting all items to good to verify.')
- self.SwitchToGood(self.all_items)
- status = self.TestSetupScript()
- assert status == 0, 'When reset_to_good, test setup should succeed.'
- status = self.TestScript()
- assert status == 0, 'When reset_to_good, status should be 0.'
-
- self._OutputProgress('Verifying items from BAD set\n')
- with SetFile(GOOD_SET_VAR, []), SetFile(BAD_SET_VAR, self.all_items):
- self.l.LogOutput('Resetting all items to bad to verify.')
- self.SwitchToBad(self.all_items)
- status = self.TestSetupScript()
- # The following assumption is not true; a bad image might not
- # successfully push onto a device.
- # assert status == 0, 'When reset_to_bad, test setup should succeed.'
- if status == 0:
- status = self.TestScript()
- assert status == 1, 'When reset_to_bad, status should be 1.'
-
- def DoSearchBadItems(self):
- """Perform full search for bad items.
-
- Perform full search until prune_iterations number of bad items are found.
- """
- while (True and len(self.all_items) > 1 and
- self.prune_cycles < self.prune_iterations):
- terminated = self.DoBinarySearchBadItems()
- self.prune_cycles += 1
- if not terminated:
- break
- # Prune is set.
- prune_index = self.binary_search.current
-
- # If found item is last item, no new items can be found
- if prune_index == len(self.all_items) - 1:
- self.l.LogOutput('First bad item is the last item. Breaking.')
- self.l.LogOutput('Bad items are: %s' % self.all_items[-1])
- self.found_items.add(self.all_items[-1])
- break
-
- # If already seen item we have no new bad items to find, finish up
- if self.all_items[prune_index] in self.found_items:
- self.l.LogOutput(
- 'Found item already found before: %s.' %
- self.all_items[prune_index],
- print_to_console=self.verbose)
- self.l.LogOutput('No more bad items remaining. Done searching.')
- self.l.LogOutput('Bad items are: %s' % ' '.join(self.found_items))
- break
-
- new_all_items = list(self.all_items)
- # Move prune item to the end of the list.
- new_all_items.append(new_all_items.pop(prune_index))
- self.found_items.add(new_all_items[-1])
-
- # Everything below newly found bad item is now known to be a good item.
- # Take these good items out of the equation to save time on the next
- # search. We save these known good items so they are still sent to the
- # switch_to_good script.
- if prune_index:
- self.known_good.update(new_all_items[:prune_index])
- new_all_items = new_all_items[prune_index:]
-
- self.l.LogOutput(
- 'Old list: %s. New list: %s' % (str(self.all_items),
- str(new_all_items)),
- print_to_console=self.verbose)
-
- if not self.prune:
- self.l.LogOutput('Not continuning further, --prune is not set')
- break
- # 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:
- status = self.GenerateBadCommandScript(self.found_items)
- if status == 0:
- self.cmd_script = os.path.join(
- os.path.dirname(self.pass_bisect), 'cmd_script.sh')
- self.l.LogOutput('Command script generated at %s.' % self.cmd_script)
- else:
- raise RuntimeError('Error while generating command script.')
-
- def DoBinarySearchBadItems(self):
- """Perform single iteration of binary search."""
- # If in resume mode don't reset search_cycles
- if not self.resumed:
- self.search_cycles = 0
- else:
- self.resumed = False
-
- terminated = False
- while self.search_cycles < self.iterations and not terminated:
- self.SaveState()
- self.OutputIterationProgressBadItem()
+ yield
- self.search_cycles += 1
- [bad_items, good_items] = self.GetNextItems()
- with SetFile(GOOD_SET_VAR, good_items), SetFile(BAD_SET_VAR, bad_items):
- # TODO: bad_items should come first.
- self.SwitchToGood(good_items)
- self.SwitchToBad(bad_items)
- status = self.TestSetupScript()
- if status == 0:
- status = self.TestScript()
- terminated = self.binary_search.SetStatus(status)
+class BinarySearchState(object):
+ """The binary search state class."""
- if terminated:
- self.l.LogOutput('Terminated!', print_to_console=self.verbose)
- if not terminated:
- self.l.LogOutput('Ran out of iterations searching...')
- self.l.LogOutput(str(self), print_to_console=self.verbose)
- return terminated
+ def __init__(
+ self,
+ get_initial_items,
+ switch_to_good,
+ switch_to_bad,
+ test_setup_script,
+ test_script,
+ incremental,
+ prune,
+ pass_bisect,
+ ir_diff,
+ 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
+ self.switch_to_bad = switch_to_bad
+ self.test_setup_script = test_setup_script
+ self.test_script = test_script
+ self.incremental = incremental
+ self.prune = prune
+ self.pass_bisect = pass_bisect
+ self.ir_diff = ir_diff
+ self.iterations = iterations
+ self.prune_iterations = prune_iterations
+ self.verify = verify
+ self.file_args = file_args
+ self.verbose = verbose
+
+ self.l = logger.GetLogger()
+ self.ce = command_executer.GetCommandExecuter()
+
+ self.resumed = False
+ self.prune_cycles = 0
+ self.search_cycles = 0
+ self.binary_search = None
+ self.all_items = None
+ self.cmd_script = None
+ self.mode = None
+ self.PopulateItemsUsingCommand(self.get_initial_items)
+ self.currently_good_items = set()
+ self.currently_bad_items = set()
+ self.found_items = set()
+ self.known_good = set()
+
+ self.start_time = time.time()
+
+ def SwitchToGood(self, item_list):
+ """Switch given items to "good" set."""
+ if self.incremental:
+ self.l.LogOutput(
+ "Incremental set. Wanted to switch %s to good" % str(item_list),
+ print_to_console=self.verbose,
+ )
+ incremental_items = [
+ item
+ for item in item_list
+ if item not in self.currently_good_items
+ ]
+ item_list = incremental_items
+ self.l.LogOutput(
+ "Incremental set. Actually switching %s to good"
+ % str(item_list),
+ print_to_console=self.verbose,
+ )
+
+ if not item_list:
+ return
- def CollectPassName(self, pass_info):
- """Mapping opt-bisect output of pass info to debugcounter name."""
- self.l.LogOutput('Pass info: %s' % pass_info, print_to_console=self.verbose)
+ self.l.LogOutput(
+ "Switching %s to good" % str(item_list),
+ print_to_console=self.verbose,
+ )
+ self.RunSwitchScript(self.switch_to_good, item_list)
+ self.currently_good_items = self.currently_good_items.union(
+ set(item_list)
+ )
+ self.currently_bad_items.difference_update(set(item_list))
+
+ def SwitchToBad(self, item_list):
+ """Switch given items to "bad" set."""
+ if self.incremental:
+ self.l.LogOutput(
+ "Incremental set. Wanted to switch %s to bad" % str(item_list),
+ print_to_console=self.verbose,
+ )
+ incremental_items = [
+ item
+ for item in item_list
+ if item not in self.currently_bad_items
+ ]
+ item_list = incremental_items
+ self.l.LogOutput(
+ "Incremental set. Actually switching %s to bad"
+ % str(item_list),
+ print_to_console=self.verbose,
+ )
+
+ if not item_list:
+ return
- for desc in pass_mapping.pass_name:
- if desc in pass_info:
- return pass_mapping.pass_name[desc]
+ self.l.LogOutput(
+ "Switching %s to bad" % str(item_list),
+ print_to_console=self.verbose,
+ )
+ self.RunSwitchScript(self.switch_to_bad, item_list)
+ self.currently_bad_items = self.currently_bad_items.union(
+ set(item_list)
+ )
+ self.currently_good_items.difference_update(set(item_list))
+
+ def RunSwitchScript(self, switch_script, item_list):
+ """Pass given items to switch script.
+
+ Args:
+ switch_script: path to switch script
+ item_list: list of all items to be switched
+ """
+ if self.file_args:
+ with tempfile.NamedTemporaryFile("w", encoding="utf-8") as f:
+ f.write("\n".join(item_list))
+ f.flush()
+ command = "%s %s" % (switch_script, f.name)
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ command, print_to_console=self.verbose
+ )
+ else:
+ command = "%s %s" % (switch_script, " ".join(item_list))
+ try:
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ command, print_to_console=self.verbose
+ )
+ except OSError as e:
+ if e.errno == errno.E2BIG:
+ raise RuntimeError(
+ "Too many arguments for switch script! Use "
+ "--file_args"
+ )
+ assert ret == 0, "Switch script %s returned %d" % (switch_script, ret)
+
+ def TestScript(self):
+ """Run test script and return exit code from script."""
+ command = self.test_script
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(command)
+ return ret
+
+ def TestSetupScript(self):
+ """Run test setup script and return exit code from script."""
+ if not self.test_setup_script:
+ return 0
+
+ command = self.test_setup_script
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(command)
+ return ret
+
+ def GenerateBadCommandScript(self, bad_items):
+ """Generate command line script for building bad item."""
+ assert not self.prune, "Prune must be false if pass_bisect is set."
+ 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 DoVerify(self):
+ """Verify correctness of test environment.
+
+ Verify that a "good" set of items produces a "good" result and that a "bad"
+ set of items produces a "bad" result. To be run directly before running
+ DoSearch. If verify is False this step is skipped.
+ """
+ if not self.verify:
+ return
+
+ self.l.LogOutput("VERIFICATION")
+ self.l.LogOutput("Beginning tests to verify good/bad sets\n")
+
+ self._OutputProgress("Verifying items from GOOD set\n")
+ with SetFile(GOOD_SET_VAR, self.all_items), SetFile(BAD_SET_VAR, []):
+ self.l.LogOutput("Resetting all items to good to verify.")
+ self.SwitchToGood(self.all_items)
+ status = self.TestSetupScript()
+ assert status == 0, "When reset_to_good, test setup should succeed."
+ status = self.TestScript()
+ assert status == 0, "When reset_to_good, status should be 0."
+
+ self._OutputProgress("Verifying items from BAD set\n")
+ with SetFile(GOOD_SET_VAR, []), SetFile(BAD_SET_VAR, self.all_items):
+ self.l.LogOutput("Resetting all items to bad to verify.")
+ self.SwitchToBad(self.all_items)
+ status = self.TestSetupScript()
+ # The following assumption is not true; a bad image might not
+ # successfully push onto a device.
+ # assert status == 0, 'When reset_to_bad, test setup should succeed.'
+ if status == 0:
+ status = self.TestScript()
+ assert status == 1, "When reset_to_bad, status should be 1."
+
+ def DoSearchBadItems(self):
+ """Perform full search for bad items.
+
+ Perform full search until prune_iterations number of bad items are found.
+ """
+ while (
+ True
+ and len(self.all_items) > 1
+ and self.prune_cycles < self.prune_iterations
+ ):
+ terminated = self.DoBinarySearchBadItems()
+ self.prune_cycles += 1
+ if not terminated:
+ break
+ # Prune is set.
+ prune_index = self.binary_search.current
+
+ # If found item is last item, no new items can be found
+ if prune_index == len(self.all_items) - 1:
+ self.l.LogOutput("First bad item is the last item. Breaking.")
+ self.l.LogOutput("Bad items are: %s" % self.all_items[-1])
+ self.found_items.add(self.all_items[-1])
+ break
+
+ # If already seen item we have no new bad items to find, finish up
+ if self.all_items[prune_index] in self.found_items:
+ self.l.LogOutput(
+ "Found item already found before: %s."
+ % self.all_items[prune_index],
+ print_to_console=self.verbose,
+ )
+ self.l.LogOutput("No more bad items remaining. Done searching.")
+ self.l.LogOutput(
+ "Bad items are: %s" % " ".join(self.found_items)
+ )
+ break
+
+ new_all_items = list(self.all_items)
+ # Move prune item to the end of the list.
+ new_all_items.append(new_all_items.pop(prune_index))
+ self.found_items.add(new_all_items[-1])
+
+ # Everything below newly found bad item is now known to be a good item.
+ # Take these good items out of the equation to save time on the next
+ # search. We save these known good items so they are still sent to the
+ # switch_to_good script.
+ if prune_index:
+ self.known_good.update(new_all_items[:prune_index])
+ new_all_items = new_all_items[prune_index:]
+
+ self.l.LogOutput(
+ "Old list: %s. New list: %s"
+ % (str(self.all_items), str(new_all_items)),
+ print_to_console=self.verbose,
+ )
+
+ if not self.prune:
+ self.l.LogOutput("Not continuning further, --prune is not set")
+ break
+ # 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:
+ status = self.GenerateBadCommandScript(self.found_items)
+ if status == 0:
+ self.cmd_script = os.path.join(
+ os.path.dirname(self.pass_bisect), "cmd_script.sh"
+ )
+ self.l.LogOutput(
+ "Command script generated at %s." % self.cmd_script
+ )
+ else:
+ raise RuntimeError("Error while generating command script.")
+
+ def DoBinarySearchBadItems(self):
+ """Perform single iteration of binary search."""
+ # If in resume mode don't reset search_cycles
+ if not self.resumed:
+ self.search_cycles = 0
+ else:
+ self.resumed = False
+
+ terminated = False
+ while self.search_cycles < self.iterations and not terminated:
+ self.SaveState()
+ self.OutputIterationProgressBadItem()
+
+ self.search_cycles += 1
+ [bad_items, good_items] = self.GetNextItems()
+
+ with SetFile(GOOD_SET_VAR, good_items), SetFile(
+ BAD_SET_VAR, bad_items
+ ):
+ # TODO: bad_items should come first.
+ self.SwitchToGood(good_items)
+ self.SwitchToBad(bad_items)
+ status = self.TestSetupScript()
+ if status == 0:
+ status = self.TestScript()
+ terminated = self.binary_search.SetStatus(status)
+
+ if terminated:
+ self.l.LogOutput("Terminated!", print_to_console=self.verbose)
+ if not terminated:
+ self.l.LogOutput("Ran out of iterations searching...")
+ self.l.LogOutput(str(self), print_to_console=self.verbose)
+ return terminated
+
+ def CollectPassName(self, pass_info):
+ """Mapping opt-bisect output of pass info to debugcounter name."""
+ self.l.LogOutput(
+ "Pass info: %s" % pass_info, print_to_console=self.verbose
+ )
+
+ for desc in pass_mapping.pass_name:
+ if desc in pass_info:
+ return pass_mapping.pass_name[desc]
+
+ # If pass not found, return None
+ return None
+
+ def BuildWithPassLimit(self, limit, generate_ir=False):
+ """Rebuild bad item with pass level bisect limit
+
+ Run command line script generated by GenerateBadCommandScript(), with
+ pass level limit flags.
+
+ Returns:
+ pass_num: current number of the pass, or total number of passes if
+ limit set to -1.
+ pass_name: The debugcounter name of current limit pass.
+ """
+ os.environ["LIMIT_FLAGS"] = "-mllvm -opt-bisect-limit=" + str(limit)
+ if generate_ir:
+ os.environ["LIMIT_FLAGS"] += " -S -emit-llvm"
+ self.l.LogOutput(
+ "Limit flags: %s" % os.environ["LIMIT_FLAGS"],
+ print_to_console=self.verbose,
+ )
+ command = self.cmd_script
+ _, _, msg = self.ce.RunCommandWOutput(command, print_to_console=False)
+
+ # Massages we get will be like this:
+ # BISECT: running pass (9) <Pass Description> on <function> (<file>)
+ # BISECT: running pass (10) <Pass Description> on <module> (<file>)
+ # BISECT: NOT running pass (11) <Pass Description> on <SCG> (<file>)
+ # BISECT: NOT running pass (12) <Pass Description> on <SCG> (<file>)
+ # We want to get the pass description of last running pass, to have
+ # transformation level bisect on it.
+ if "BISECT: " not in msg:
+ raise RuntimeError(
+ "No bisect info printed, OptBisect may not be "
+ "supported by the compiler."
+ )
+
+ lines = msg.split("\n")
+ pass_num = 0
+ last_pass = ""
+ for l in lines:
+ if "running pass" in l:
+ # For situation of limit==-1, we want the total number of passes
+ if limit != -1 and "BISECT: NOT " in l:
+ break
+ pass_num += 1
+ last_pass = l
+ if limit not in (-1, pass_num):
+ raise ValueError(
+ "[Error] While building, limit number does not match."
+ )
+ return pass_num, self.CollectPassName(last_pass)
+
+ def BuildWithTransformLimit(
+ self, limit, pass_name=None, pass_limit=-1, generate_ir=False
+ ):
+ """Rebuild bad item with transformation level bisect limit
+
+ Run command line script generated by GenerateBadCommandScript(), with
+ pass level limit flags and transformation level limit flags.
+
+ Args:
+ limit: transformation level limit for bad item.
+ pass_name: name of bad pass debugcounter from pass level bisect result.
+ pass_limit: pass level limit from pass level bisect result.
+ generate_ir: Whether to generate IR comparison.
+
+ Returns:
+ Total number of transformations if limit set to -1, else return 0.
+ """
+ counter_name = pass_name
+
+ os.environ["LIMIT_FLAGS"] = (
+ "-mllvm -opt-bisect-limit="
+ + str(pass_limit)
+ + " -mllvm -debug-counter="
+ + counter_name
+ + "-count="
+ + str(limit)
+ + " -mllvm -print-debug-counter"
+ )
+ if generate_ir:
+ os.environ["LIMIT_FLAGS"] += " -S -emit-llvm"
+ self.l.LogOutput(
+ "Limit flags: %s" % os.environ["LIMIT_FLAGS"],
+ print_to_console=self.verbose,
+ )
+ command = self.cmd_script
+ _, _, msg = self.ce.RunCommandWOutput(command, print_to_console=False)
+
+ if "Counters and values:" not in msg:
+ # Print pass level IR diff only if transformation level bisection does
+ # not work.
+ if self.ir_diff:
+ self.PrintIRDiff(pass_limit)
+ raise RuntimeError(
+ "No bisect info printed, DebugCounter may not be "
+ "supported by the compiler."
+ )
+
+ # With debugcounter enabled, there will be DebugCounter counting info in
+ # the output.
+ lines = msg.split("\n")
+ for l in lines:
+ if pass_name in l:
+ # Output of debugcounter will be like:
+ # instcombine-visit: {10, 0, 20}
+ # dce-transform: {1, 0, -1}
+ # which indicates {Count, Skip, StopAfter}.
+ # The last number should be the limit we set.
+ # We want the first number as the total transformation count.
+ # Split each line by ,|{|} and we can get l_list as:
+ # ['instcombine: ', '10', '0', '20', '']
+ # and we will need the second item in it.
+ l_list = re.split(",|{|}", l)
+ count = int(l_list[1])
+ if limit == -1:
+ return count
+ # The returned value is only useful when limit == -1, which shows total
+ # transformation count.
+ return 0
+
+ def PrintIRDiff(self, pass_index, pass_name=None, trans_index=-1):
+ bad_item = list(self.found_items)[0]
+ self.l.LogOutput(
+ "IR difference before and after bad pass/transformation:",
+ print_to_console=self.verbose,
+ )
+
+ if trans_index == -1:
+ # Pass level IR diff
+ self.BuildWithPassLimit(pass_index, self.ir_diff)
+ good_ir = os.path.join(tempfile.tempdir, "good.s")
+ shutil.copyfile(bad_item, good_ir)
+ pass_index += 1
+ self.BuildWithPassLimit(pass_index, self.ir_diff)
+ else:
+ # Transformation level IR diff
+ self.BuildWithTransformLimit(
+ trans_index, pass_name, pass_index, self.ir_diff
+ )
+ good_ir = os.path.join(tempfile.tempdir, "good.s")
+ shutil.copyfile(bad_item, good_ir)
+ trans_index += 1
+ self.BuildWithTransformLimit(
+ trans_index, pass_name, pass_index, self.ir_diff
+ )
+
+ bad_ir = os.path.join(tempfile.tempdir, "bad.s")
+ shutil.copyfile(bad_item, bad_ir)
+
+ command = "diff %s %s" % (good_ir, bad_ir)
+ _, _, _ = self.ce.RunCommandWOutput(
+ command, print_to_console=self.verbose
+ )
+
+ def DoSearchBadPass(self):
+ """Perform full search for bad pass of bad item."""
+ logger.GetLogger().LogOutput(
+ "Starting to bisect bad pass for bad item."
+ )
+
+ # Pass level bisection
+ self.mode = "pass"
+ self.binary_search = binary_search_perforce.BinarySearcherForPass(
+ logger_to_set=self.l
+ )
+ self.binary_search.total, _ = self.BuildWithPassLimit(-1)
+ logger.GetLogger().LogOutput(
+ "Total %s number: %d" % (self.mode, self.binary_search.total)
+ )
+
+ pass_index, pass_name = self.DoBinarySearchBadPass()
+
+ if not pass_name and pass_index == 0:
+ raise ValueError("Bisecting passes cannot reproduce good result.")
+ logger.GetLogger().LogOutput("Bad pass found: %s." % pass_name)
+
+ # Transformation level bisection.
+ logger.GetLogger().LogOutput(
+ "Starting to bisect at transformation level."
+ )
+
+ self.mode = "transform"
+ self.binary_search = binary_search_perforce.BinarySearcherForPass(
+ logger_to_set=self.l
+ )
+ self.binary_search.total = self.BuildWithTransformLimit(
+ -1, pass_name, pass_index
+ )
+ logger.GetLogger().LogOutput(
+ "Total %s number: %d" % (self.mode, self.binary_search.total)
+ )
+
+ trans_index, _ = self.DoBinarySearchBadPass(pass_index, pass_name)
+ if trans_index == 0:
+ raise ValueError(
+ "Bisecting %s cannot reproduce good result." % pass_name
+ )
+
+ if self.ir_diff:
+ self.PrintIRDiff(pass_index, pass_name, trans_index)
+
+ logger.GetLogger().LogOutput(
+ "Bisection result for bad item %s:\n"
+ "Bad pass: %s at number %d\n"
+ "Bad transformation number: %d"
+ % (self.found_items, pass_name, pass_index, trans_index)
+ )
+
+ def DoBinarySearchBadPass(self, pass_index=-1, pass_name=None):
+ """Perform single iteration of binary search at pass level
+
+ Args:
+ pass_index: Works for transformation level bisection, indicates the limit
+ number of pass from pass level bisecting result.
+ pass_name: Works for transformation level bisection, indicates
+ DebugCounter name of the bad pass from pass level bisecting
+ result.
+
+ Returns:
+ index: Index of problematic pass/transformation.
+ pass_name: Works for pass level bisection, returns DebugCounter name for
+ bad pass.
+ """
+ # If in resume mode don't reset search_cycles
+ if not self.resumed:
+ self.search_cycles = 0
+ else:
+ self.resumed = False
+
+ terminated = False
+ index = 0
+ while self.search_cycles < self.iterations and not terminated:
+ self.SaveState()
+ self.OutputIterationProgressBadPass()
+
+ self.search_cycles += 1
+ current = self.binary_search.GetNext()
+
+ if self.mode == "pass":
+ index, pass_name = self.BuildWithPassLimit(current)
+ else:
+ self.BuildWithTransformLimit(current, pass_name, pass_index)
+ index = current
+
+ # TODO: Newly generated object should not directly replace original
+ # one, need to put it somewhere and symbol link original one to it.
+ # Will update cmd_script to do it.
+
+ status = self.TestSetupScript()
+ assert status == 0, "Test setup should succeed."
+ status = self.TestScript()
+ terminated = self.binary_search.SetStatus(status)
+
+ if terminated:
+ self.l.LogOutput("Terminated!", print_to_console=self.verbose)
+ if not terminated:
+ self.l.LogOutput("Ran out of iterations searching...")
+ self.l.LogOutput(str(self), print_to_console=self.verbose)
+ return index, pass_name
+
+ def PopulateItemsUsingCommand(self, command):
+ """Update all_items and binary search logic from executable.
+
+ This method is mainly required for enumerating the initial list of items
+ from the get_initial_items script.
+
+ Args:
+ command: path to executable that will enumerate items.
+ """
+ ce = command_executer.GetCommandExecuter()
+ _, out, _ = ce.RunCommandWExceptionCleanup(
+ command, return_output=True, print_to_console=self.verbose
+ )
+ all_items = out.split()
+ self.PopulateItemsUsingList(all_items)
+
+ def PopulateItemsUsingList(self, all_items):
+ """Update all_items and binary searching logic from list.
+
+ Args:
+ all_items: new list of all_items
+ """
+ self.all_items = all_items
+ self.binary_search = binary_search_perforce.BinarySearcher(
+ logger_to_set=self.l
+ )
+ self.binary_search.SetSortedList(self.all_items)
+
+ def SaveState(self):
+ """Save state to STATE_FILE.
+
+ SaveState will create a new unique, hidden state file to hold data from
+ object. Then atomically overwrite the STATE_FILE symlink to point to the
+ new data.
+
+ Raises:
+ OSError if STATE_FILE already exists but is not a symlink.
+ """
+ ce, l = self.ce, self.l
+ self.ce, self.l, self.binary_search.logger = None, None, None
+ old_state = None
+
+ _, path = tempfile.mkstemp(prefix=HIDDEN_STATE_FILE, dir=".")
+ with open(path, "wb") as f:
+ pickle.dump(self, f)
+
+ if os.path.exists(STATE_FILE):
+ if os.path.islink(STATE_FILE):
+ old_state = os.readlink(STATE_FILE)
+ else:
+ raise OSError(
+ (
+ "%s already exists and is not a symlink!\n"
+ "State file saved to %s" % (STATE_FILE, path)
+ )
+ )
+
+ # Create new link and atomically overwrite old link
+ temp_link = "%s.link" % HIDDEN_STATE_FILE
+ os.symlink(path, temp_link)
+ os.rename(temp_link, STATE_FILE)
+
+ if old_state:
+ os.remove(old_state)
+
+ self.ce, self.l, self.binary_search.logger = ce, l, l
+
+ @classmethod
+ def LoadState(cls):
+ """Create BinarySearchState object from STATE_FILE."""
+ if not os.path.isfile(STATE_FILE):
+ return None
+ try:
+ with open(STATE_FILE, "rb") as f:
+ bss = pickle.load(f)
+ bss.l = logger.GetLogger()
+ bss.ce = command_executer.GetCommandExecuter()
+ bss.binary_search.logger = bss.l
+ bss.start_time = time.time()
+
+ # Set resumed to be True so we can enter DoBinarySearch without the
+ # method resetting our current search_cycles to 0.
+ bss.resumed = True
+
+ # Set currently_good_items and currently_bad_items to empty so that the
+ # first iteration after resuming will always be non-incremental. This
+ # is just in case the environment changes, the user makes manual
+ # changes, or a previous switch_script corrupted the environment.
+ bss.currently_good_items = set()
+ bss.currently_bad_items = set()
+
+ binary_search_perforce.verbose = bss.verbose
+ return bss
+ except Exception:
+ return None
+
+ def RemoveState(self):
+ """Remove STATE_FILE and its symlinked data from file system."""
+ if os.path.exists(STATE_FILE):
+ if os.path.islink(STATE_FILE):
+ real_file = os.readlink(STATE_FILE)
+ os.remove(real_file)
+ os.remove(STATE_FILE)
+
+ def GetNextItems(self):
+ """Get next items for binary search based on result of the last test run."""
+ border_item = self.binary_search.GetNext()
+ index = self.all_items.index(border_item)
+
+ next_bad_items = self.all_items[: index + 1]
+ next_good_items = self.all_items[index + 1 :] + list(self.known_good)
+
+ return [next_bad_items, next_good_items]
+
+ def ElapsedTimeString(self):
+ """Return h m s format of elapsed time since execution has started."""
+ diff = int(time.time() - self.start_time)
+ seconds = diff % 60
+ minutes = (diff // 60) % 60
+ hours = diff // (60 * 60)
+
+ seconds = str(seconds).rjust(2)
+ minutes = str(minutes).rjust(2)
+ hours = str(hours).rjust(2)
+
+ return "%sh %sm %ss" % (hours, minutes, seconds)
+
+ def _OutputProgress(self, progress_text):
+ """Output current progress of binary search to console and logs.
+
+ Args:
+ progress_text: The progress to display to the user.
+ """
+ progress = (
+ "\n***** PROGRESS (elapsed time: %s) *****\n"
+ "%s"
+ "************************************************"
+ )
+ progress = progress % (self.ElapsedTimeString(), progress_text)
+ self.l.LogOutput(progress)
+
+ def OutputIterationProgressBadItem(self):
+ out = (
+ "Search %d of estimated %d.\n"
+ "Prune %d of max %d.\n"
+ "Current bad items found:\n"
+ "%s\n"
+ )
+ out = out % (
+ self.search_cycles + 1,
+ math.ceil(math.log(len(self.all_items), 2)),
+ self.prune_cycles + 1,
+ self.prune_iterations,
+ ", ".join(self.found_items),
+ )
+ self._OutputProgress(out)
+
+ def OutputIterationProgressBadPass(self):
+ out = "Search %d of estimated %d.\n" "Current limit: %s\n"
+ out = out % (
+ self.search_cycles + 1,
+ math.ceil(math.log(self.binary_search.total, 2)),
+ self.binary_search.current,
+ )
+ self._OutputProgress(out)
+
+ def __str__(self):
+ ret = ""
+ ret += "all: %s\n" % str(self.all_items)
+ ret += "currently_good: %s\n" % str(self.currently_good_items)
+ ret += "currently_bad: %s\n" % str(self.currently_bad_items)
+ ret += str(self.binary_search)
+ return ret
- # If pass not found, return None
- return None
- def BuildWithPassLimit(self, limit, generate_ir=False):
- """Rebuild bad item with pass level bisect limit
+class MockBinarySearchState(BinarySearchState):
+ """Mock class for BinarySearchState."""
+
+ def __init__(self, **kwargs):
+ # Initialize all arguments to None
+ default_kwargs = {
+ "get_initial_items": 'echo "1"',
+ "switch_to_good": None,
+ "switch_to_bad": None,
+ "test_setup_script": None,
+ "test_script": None,
+ "incremental": True,
+ "prune": False,
+ "pass_bisect": None,
+ "ir_diff": False,
+ "iterations": 50,
+ "prune_iterations": 100,
+ "verify": True,
+ "file_args": False,
+ "verbose": False,
+ }
+ default_kwargs.update(kwargs)
+ super(MockBinarySearchState, self).__init__(**default_kwargs)
- Run command line script generated by GenerateBadCommandScript(), with
- pass level limit flags.
- Returns:
- pass_num: current number of the pass, or total number of passes if
- limit set to -1.
- pass_name: The debugcounter name of current limit pass.
- """
- os.environ['LIMIT_FLAGS'] = '-mllvm -opt-bisect-limit=' + str(limit)
- if generate_ir:
- os.environ['LIMIT_FLAGS'] += ' -S -emit-llvm'
- self.l.LogOutput(
- 'Limit flags: %s' % os.environ['LIMIT_FLAGS'],
- print_to_console=self.verbose)
- command = self.cmd_script
- _, _, msg = self.ce.RunCommandWOutput(command, print_to_console=False)
-
- # Massages we get will be like this:
- # BISECT: running pass (9) <Pass Description> on <function> (<file>)
- # BISECT: running pass (10) <Pass Description> on <module> (<file>)
- # BISECT: NOT running pass (11) <Pass Description> on <SCG> (<file>)
- # BISECT: NOT running pass (12) <Pass Description> on <SCG> (<file>)
- # We want to get the pass description of last running pass, to have
- # transformation level bisect on it.
- if 'BISECT: ' not in msg:
- raise RuntimeError('No bisect info printed, OptBisect may not be '
- 'supported by the compiler.')
-
- lines = msg.split('\n')
- pass_num = 0
- last_pass = ''
- for l in lines:
- if 'running pass' in l:
- # For situation of limit==-1, we want the total number of passes
- if limit != -1 and 'BISECT: NOT ' in l:
- break
- pass_num += 1
- last_pass = l
- if limit not in (-1, pass_num):
- raise ValueError('[Error] While building, limit number does not match.')
- return pass_num, self.CollectPassName(last_pass)
-
- def BuildWithTransformLimit(self,
- limit,
- pass_name=None,
- pass_limit=-1,
- generate_ir=False):
- """Rebuild bad item with transformation level bisect limit
-
- Run command line script generated by GenerateBadCommandScript(), with
- pass level limit flags and transformation level limit flags.
+def _CanonicalizeScript(script_name):
+ """Return canonical path to script.
Args:
- limit: transformation level limit for bad item.
- pass_name: name of bad pass debugcounter from pass level bisect result.
- pass_limit: pass level limit from pass level bisect result.
- generate_ir: Whether to generate IR comparison.
+ script_name: Relative or absolute path to script
Returns:
- Total number of transformations if limit set to -1, else return 0.
+ Canonicalized script path
"""
- counter_name = pass_name
-
- os.environ['LIMIT_FLAGS'] = '-mllvm -opt-bisect-limit=' + \
- str(pass_limit) + \
- ' -mllvm -debug-counter=' + counter_name + \
- '-count=' + str(limit) + \
- ' -mllvm -print-debug-counter'
- if generate_ir:
- os.environ['LIMIT_FLAGS'] += ' -S -emit-llvm'
- self.l.LogOutput(
- 'Limit flags: %s' % os.environ['LIMIT_FLAGS'],
- print_to_console=self.verbose)
- command = self.cmd_script
- _, _, msg = self.ce.RunCommandWOutput(command, print_to_console=False)
-
- if 'Counters and values:' not in msg:
- # Print pass level IR diff only if transformation level bisection does
- # not work.
- if self.ir_diff:
- self.PrintIRDiff(pass_limit)
- raise RuntimeError('No bisect info printed, DebugCounter may not be '
- 'supported by the compiler.')
-
- # With debugcounter enabled, there will be DebugCounter counting info in
- # the output.
- lines = msg.split('\n')
- for l in lines:
- if pass_name in l:
- # Output of debugcounter will be like:
- # instcombine-visit: {10, 0, 20}
- # dce-transform: {1, 0, -1}
- # which indicates {Count, Skip, StopAfter}.
- # The last number should be the limit we set.
- # We want the first number as the total transformation count.
- # Split each line by ,|{|} and we can get l_list as:
- # ['instcombine: ', '10', '0', '20', '']
- # and we will need the second item in it.
- l_list = re.split(',|{|}', l)
- count = int(l_list[1])
- if limit == -1:
- return count
- # The returned value is only useful when limit == -1, which shows total
- # transformation count.
- return 0
-
- def PrintIRDiff(self, pass_index, pass_name=None, trans_index=-1):
- bad_item = list(self.found_items)[0]
- self.l.LogOutput(
- 'IR difference before and after bad pass/transformation:',
- print_to_console=self.verbose)
-
- if trans_index == -1:
- # Pass level IR diff
- self.BuildWithPassLimit(pass_index, self.ir_diff)
- good_ir = os.path.join(tempfile.tempdir, 'good.s')
- shutil.copyfile(bad_item, good_ir)
- pass_index += 1
- self.BuildWithPassLimit(pass_index, self.ir_diff)
- else:
- # Transformation level IR diff
- self.BuildWithTransformLimit(trans_index, pass_name, pass_index,
- self.ir_diff)
- good_ir = os.path.join(tempfile.tempdir, 'good.s')
- shutil.copyfile(bad_item, good_ir)
- trans_index += 1
- self.BuildWithTransformLimit(trans_index, pass_name, pass_index,
- self.ir_diff)
-
- bad_ir = os.path.join(tempfile.tempdir, 'bad.s')
- shutil.copyfile(bad_item, bad_ir)
-
- command = 'diff %s %s' % (good_ir, bad_ir)
- _, _, _ = self.ce.RunCommandWOutput(command, print_to_console=self.verbose)
-
- def DoSearchBadPass(self):
- """Perform full search for bad pass of bad item."""
- logger.GetLogger().LogOutput('Starting to bisect bad pass for bad item.')
-
- # Pass level bisection
- self.mode = 'pass'
- self.binary_search = binary_search_perforce.BinarySearcherForPass(
- logger_to_set=self.l)
- self.binary_search.total, _ = self.BuildWithPassLimit(-1)
- logger.GetLogger().LogOutput(
- 'Total %s number: %d' % (self.mode, self.binary_search.total))
-
- pass_index, pass_name = self.DoBinarySearchBadPass()
-
- if (not pass_name and pass_index == 0):
- raise ValueError('Bisecting passes cannot reproduce good result.')
- logger.GetLogger().LogOutput('Bad pass found: %s.' % pass_name)
-
- # Transformation level bisection.
- logger.GetLogger().LogOutput('Starting to bisect at transformation level.')
-
- self.mode = 'transform'
- self.binary_search = binary_search_perforce.BinarySearcherForPass(
- logger_to_set=self.l)
- self.binary_search.total = self.BuildWithTransformLimit(
- -1, pass_name, pass_index)
- logger.GetLogger().LogOutput(
- 'Total %s number: %d' % (self.mode, self.binary_search.total))
-
- trans_index, _ = self.DoBinarySearchBadPass(pass_index, pass_name)
- if trans_index == 0:
- raise ValueError('Bisecting %s cannot reproduce good result.' % pass_name)
-
- if self.ir_diff:
- self.PrintIRDiff(pass_index, pass_name, trans_index)
-
- logger.GetLogger().LogOutput(
- 'Bisection result for bad item %s:\n'
- 'Bad pass: %s at number %d\n'
- 'Bad transformation number: %d' % (self.found_items, pass_name,
- pass_index, trans_index))
-
- def DoBinarySearchBadPass(self, pass_index=-1, pass_name=None):
- """Perform single iteration of binary search at pass level
+ script_name = os.path.expanduser(script_name)
+ if not script_name.startswith("/"):
+ return os.path.join(".", script_name)
+
+
+def Run(
+ get_initial_items,
+ switch_to_good,
+ switch_to_bad,
+ test_script,
+ test_setup_script=None,
+ iterations=50,
+ prune=False,
+ pass_bisect=None,
+ ir_diff=False,
+ noincremental=False,
+ file_args=False,
+ verify=True,
+ prune_iterations=100,
+ verbose=False,
+ resume=False,
+):
+ """Run binary search tool.
+
+ Equivalent to running through terminal.
Args:
- pass_index: Works for transformation level bisection, indicates the limit
- number of pass from pass level bisecting result.
- pass_name: Works for transformation level bisection, indicates
- DebugCounter name of the bad pass from pass level bisecting
- result.
+ get_initial_items: Script to enumerate all items being binary searched
+ switch_to_good: Script that will take items as input and switch them to good
+ set
+ switch_to_bad: Script that will take items as input and switch them to bad
+ set
+ test_script: Script that will determine if the current combination of good
+ and bad items make a "good" or "bad" result.
+ test_setup_script: Script to do necessary setup (building, compilation,
+ etc.) for test_script.
+ iterations: How many binary search iterations to run before exiting.
+ 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. This will turn on
+ pass/ transformation level bisection for the bad item. Requires that
+ 'prune' be set to False, and needs support of `-opt-bisect-limit`(pass)
+ and `-print-debug-counter`(transformation) from LLVM.
+ ir_diff: Whether to print IR differences before and after bad
+ pass/transformation to verbose output. Defaults to False, only works when
+ pass_bisect is enabled.
+ 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.
+ verify: If True, run tests to ensure initial good/bad sets actually produce
+ a good/bad result.
+ prune_iterations: Max number of bad items to search for.
+ verbose: If True will print extra debug information to user.
+ resume: If True will resume using STATE_FILE.
Returns:
- index: Index of problematic pass/transformation.
- pass_name: Works for pass level bisection, returns DebugCounter name for
- bad pass.
+ 0 for success, error otherwise
"""
- # If in resume mode don't reset search_cycles
- if not self.resumed:
- self.search_cycles = 0
+ # 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:
- self.resumed = False
-
- terminated = False
- index = 0
- while self.search_cycles < self.iterations and not terminated:
- self.SaveState()
- self.OutputIterationProgressBadPass()
-
- self.search_cycles += 1
- current = self.binary_search.GetNext()
-
- if self.mode == 'pass':
- index, pass_name = self.BuildWithPassLimit(current)
- else:
- self.BuildWithTransformLimit(current, pass_name, pass_index)
- index = current
-
- # TODO: Newly generated object should not directly replace original
- # one, need to put it somewhere and symbol link original one to it.
- # Will update cmd_script to do it.
-
- status = self.TestSetupScript()
- assert status == 0, 'Test setup should succeed.'
- status = self.TestScript()
- terminated = self.binary_search.SetStatus(status)
-
- if terminated:
- self.l.LogOutput('Terminated!', print_to_console=self.verbose)
- if not terminated:
- self.l.LogOutput('Ran out of iterations searching...')
- self.l.LogOutput(str(self), print_to_console=self.verbose)
- return index, pass_name
-
- def PopulateItemsUsingCommand(self, command):
- """Update all_items and binary search logic from executable.
-
- This method is mainly required for enumerating the initial list of items
- from the get_initial_items script.
-
- Args:
- command: path to executable that will enumerate items.
- """
- ce = command_executer.GetCommandExecuter()
- _, out, _ = ce.RunCommandWExceptionCleanup(
- command, return_output=True, print_to_console=self.verbose)
- all_items = out.split()
- self.PopulateItemsUsingList(all_items)
-
- def PopulateItemsUsingList(self, all_items):
- """Update all_items and binary searching logic from list.
-
- Args:
- all_items: new list of all_items
- """
- self.all_items = all_items
- self.binary_search = binary_search_perforce.BinarySearcher(
- logger_to_set=self.l)
- self.binary_search.SetSortedList(self.all_items)
-
- def SaveState(self):
- """Save state to STATE_FILE.
-
- SaveState will create a new unique, hidden state file to hold data from
- object. Then atomically overwrite the STATE_FILE symlink to point to the
- new data.
-
- Raises:
- OSError if STATE_FILE already exists but is not a symlink.
- """
- ce, l = self.ce, self.l
- self.ce, self.l, self.binary_search.logger = None, None, None
- old_state = None
-
- _, path = tempfile.mkstemp(prefix=HIDDEN_STATE_FILE, dir='.')
- with open(path, 'wb') as f:
- pickle.dump(self, f)
-
- if os.path.exists(STATE_FILE):
- if os.path.islink(STATE_FILE):
- old_state = os.readlink(STATE_FILE)
- else:
- raise OSError(('%s already exists and is not a symlink!\n'
- 'State file saved to %s' % (STATE_FILE, path)))
-
- # Create new link and atomically overwrite old link
- temp_link = '%s.link' % HIDDEN_STATE_FILE
- os.symlink(path, temp_link)
- os.rename(temp_link, STATE_FILE)
-
- if old_state:
- os.remove(old_state)
-
- self.ce, self.l, self.binary_search.logger = ce, l, l
-
- @classmethod
- def LoadState(cls):
- """Create BinarySearchState object from STATE_FILE."""
- if not os.path.isfile(STATE_FILE):
- return None
- try:
- with open(STATE_FILE, 'rb') as f:
- bss = pickle.load(f)
- bss.l = logger.GetLogger()
- bss.ce = command_executer.GetCommandExecuter()
- bss.binary_search.logger = bss.l
- bss.start_time = time.time()
-
- # Set resumed to be True so we can enter DoBinarySearch without the
- # method resetting our current search_cycles to 0.
- bss.resumed = True
-
- # Set currently_good_items and currently_bad_items to empty so that the
- # first iteration after resuming will always be non-incremental. This
- # is just in case the environment changes, the user makes manual
- # changes, or a previous switch_script corrupted the environment.
- bss.currently_good_items = set()
- bss.currently_bad_items = set()
-
- binary_search_perforce.verbose = bss.verbose
- return bss
- except Exception:
- return None
-
- def RemoveState(self):
- """Remove STATE_FILE and its symlinked data from file system."""
- if os.path.exists(STATE_FILE):
- if os.path.islink(STATE_FILE):
- real_file = os.readlink(STATE_FILE)
- os.remove(real_file)
- os.remove(STATE_FILE)
-
- def GetNextItems(self):
- """Get next items for binary search based on result of the last test run."""
- border_item = self.binary_search.GetNext()
- index = self.all_items.index(border_item)
-
- next_bad_items = self.all_items[:index + 1]
- next_good_items = self.all_items[index + 1:] + list(self.known_good)
-
- return [next_bad_items, next_good_items]
-
- def ElapsedTimeString(self):
- """Return h m s format of elapsed time since execution has started."""
- diff = int(time.time() - self.start_time)
- seconds = diff % 60
- minutes = (diff // 60) % 60
- hours = diff // (60 * 60)
-
- seconds = str(seconds).rjust(2)
- minutes = str(minutes).rjust(2)
- hours = str(hours).rjust(2)
-
- return '%sh %sm %ss' % (hours, minutes, seconds)
-
- def _OutputProgress(self, progress_text):
- """Output current progress of binary search to console and logs.
-
- Args:
- progress_text: The progress to display to the user.
- """
- progress = ('\n***** PROGRESS (elapsed time: %s) *****\n'
- '%s'
- '************************************************')
- progress = progress % (self.ElapsedTimeString(), progress_text)
- self.l.LogOutput(progress)
-
- def OutputIterationProgressBadItem(self):
- out = ('Search %d of estimated %d.\n'
- 'Prune %d of max %d.\n'
- 'Current bad items found:\n'
- '%s\n')
- out = out % (self.search_cycles + 1,
- math.ceil(math.log(len(self.all_items), 2)), self.prune_cycles
- + 1, self.prune_iterations, ', '.join(self.found_items))
- self._OutputProgress(out)
-
- def OutputIterationProgressBadPass(self):
- out = ('Search %d of estimated %d.\n' 'Current limit: %s\n')
- out = out % (self.search_cycles + 1,
- math.ceil(math.log(self.binary_search.total, 2)),
- self.binary_search.current)
- self._OutputProgress(out)
-
- def __str__(self):
- ret = ''
- ret += 'all: %s\n' % str(self.all_items)
- ret += 'currently_good: %s\n' % str(self.currently_good_items)
- ret += 'currently_bad: %s\n' % str(self.currently_bad_items)
- ret += str(self.binary_search)
- return ret
-
-
-class MockBinarySearchState(BinarySearchState):
- """Mock class for BinarySearchState."""
-
- def __init__(self, **kwargs):
- # Initialize all arguments to None
- default_kwargs = {
- 'get_initial_items': 'echo "1"',
- 'switch_to_good': None,
- 'switch_to_bad': None,
- 'test_setup_script': None,
- 'test_script': None,
- 'incremental': True,
- 'prune': False,
- 'pass_bisect': None,
- 'ir_diff': False,
- 'iterations': 50,
- 'prune_iterations': 100,
- 'verify': True,
- 'file_args': False,
- 'verbose': False
- }
- default_kwargs.update(kwargs)
- super(MockBinarySearchState, self).__init__(**default_kwargs)
-
-
-def _CanonicalizeScript(script_name):
- """Return canonical path to script.
-
- Args:
- script_name: Relative or absolute path to script
-
- Returns:
- Canonicalized script path
- """
- script_name = os.path.expanduser(script_name)
- if not script_name.startswith('/'):
- return os.path.join('.', script_name)
-
-
-def Run(get_initial_items,
- switch_to_good,
- switch_to_bad,
- test_script,
- test_setup_script=None,
- iterations=50,
- prune=False,
- pass_bisect=None,
- ir_diff=False,
- noincremental=False,
- file_args=False,
- verify=True,
- prune_iterations=100,
- verbose=False,
- resume=False):
- """Run binary search tool.
-
- Equivalent to running through terminal.
-
- Args:
- get_initial_items: Script to enumerate all items being binary searched
- switch_to_good: Script that will take items as input and switch them to good
- set
- switch_to_bad: Script that will take items as input and switch them to bad
- set
- test_script: Script that will determine if the current combination of good
- and bad items make a "good" or "bad" result.
- test_setup_script: Script to do necessary setup (building, compilation,
- etc.) for test_script.
- iterations: How many binary search iterations to run before exiting.
- 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. This will turn on
- pass/ transformation level bisection for the bad item. Requires that
- 'prune' be set to False, and needs support of `-opt-bisect-limit`(pass)
- and `-print-debug-counter`(transformation) from LLVM.
- ir_diff: Whether to print IR differences before and after bad
- pass/transformation to verbose output. Defaults to False, only works when
- pass_bisect is enabled.
- 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.
- verify: If True, run tests to ensure initial good/bad sets actually produce
- a good/bad result.
- prune_iterations: Max number of bad items to search for.
- verbose: If True will print extra debug information to user.
- resume: If True will resume using STATE_FILE.
-
- 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
- if not pass_bisect and ir_diff:
- logger.GetLogger().LogOutput('"--ir_diff" only works when '
- '"--pass_bisect" is enabled.')
-
- 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 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
+ if not pass_bisect and ir_diff:
+ logger.GetLogger().LogOutput(
+ '"--ir_diff" only works when ' '"--pass_bisect" is enabled.'
+ )
+
+ 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
+
+ binary_search_perforce.verbose = verbose
+
+ bss = BinarySearchState(
+ get_initial_items,
+ switch_to_good,
+ switch_to_bad,
+ test_setup_script,
+ test_script,
+ incremental,
+ prune,
+ pass_bisect,
+ ir_diff,
+ iterations,
+ prune_iterations,
+ verify,
+ file_args,
+ verbose,
+ )
+ bss.DoVerify()
+
+ bss.DoSearchBadItems()
if pass_bisect:
- pass_bisect = _CanonicalizeScript(pass_bisect)
- test_script = _CanonicalizeScript(test_script)
- get_initial_items = _CanonicalizeScript(get_initial_items)
- incremental = not noincremental
-
- binary_search_perforce.verbose = verbose
-
- bss = BinarySearchState(get_initial_items, switch_to_good, switch_to_bad,
- test_setup_script, test_script, incremental, prune,
- pass_bisect, ir_diff, iterations, prune_iterations,
- verify, file_args, verbose)
- bss.DoVerify()
-
- bss.DoSearchBadItems()
- if pass_bisect:
- bss.DoSearchBadPass()
- bss.RemoveState()
- logger.GetLogger().LogOutput(
- 'Total execution time: %s' % bss.ElapsedTimeString())
+ bss.DoSearchBadPass()
+ bss.RemoveState()
+ logger.GetLogger().LogOutput(
+ "Total execution time: %s" % bss.ElapsedTimeString()
+ )
- return 0
+ return 0
def Main(argv):
- """The main function."""
- # Common initializations
+ """The main function."""
+ # Common initializations
- parser = argparse.ArgumentParser()
- common.BuildArgParser(parser)
- logger.GetLogger().LogOutput(' '.join(argv))
- options = parser.parse_args(argv)
+ parser = argparse.ArgumentParser()
+ common.BuildArgParser(parser)
+ logger.GetLogger().LogOutput(" ".join(argv))
+ options = parser.parse_args(argv)
- # Get dictionary of all options
- args = vars(options)
- return Run(**args)
+ # Get dictionary of all options
+ args = vars(options)
+ return Run(**args)
-if __name__ == '__main__':
- sys.exit(Main(sys.argv[1:]))
+if __name__ == "__main__":
+ sys.exit(Main(sys.argv[1:]))
diff --git a/binary_search_tool/bisect_driver.py b/binary_search_tool/bisect_driver.py
index ac37ad9f..8feb1a37 100644
--- a/binary_search_tool/bisect_driver.py
+++ b/binary_search_tool/bisect_driver.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
@@ -19,400 +19,413 @@ Design doc:
https://docs.google.com/document/d/1yDgaUIa2O5w6dc3sSTe1ry-1ehKajTGJGQCbyn0fcEM
"""
-from __future__ import print_function
import contextlib
import fcntl
import os
import shutil
-import subprocess
import stat
+import subprocess
import sys
-VALID_MODES = ('POPULATE_GOOD', 'POPULATE_BAD', 'TRIAGE')
-GOOD_CACHE = 'good'
-BAD_CACHE = 'bad'
-LIST_FILE = os.path.join(GOOD_CACHE, '_LIST')
-CONTINUE_ON_MISSING = os.environ.get('BISECT_CONTINUE_ON_MISSING', None) == '1'
-CONTINUE_ON_REDUNDANCY = os.environ.get('BISECT_CONTINUE_ON_REDUNDANCY',
- None) == '1'
-WRAPPER_SAFE_MODE = os.environ.get('BISECT_WRAPPER_SAFE_MODE', None) == '1'
+VALID_MODES = ("POPULATE_GOOD", "POPULATE_BAD", "TRIAGE")
+GOOD_CACHE = "good"
+BAD_CACHE = "bad"
+LIST_FILE = os.path.join(GOOD_CACHE, "_LIST")
+
+CONTINUE_ON_MISSING = os.environ.get("BISECT_CONTINUE_ON_MISSING", None) == "1"
+CONTINUE_ON_REDUNDANCY = (
+ os.environ.get("BISECT_CONTINUE_ON_REDUNDANCY", None) == "1"
+)
+WRAPPER_SAFE_MODE = os.environ.get("BISECT_WRAPPER_SAFE_MODE", None) == "1"
class Error(Exception):
- """The general compiler wrapper error class."""
+ """The general compiler wrapper error class."""
@contextlib.contextmanager
def lock_file(path, mode):
- """Lock file and block if other process has lock on file.
-
- Acquire exclusive lock for file. Only blocks other processes if they attempt
- to also acquire lock through this method. If only reading (modes 'r' and 'rb')
- then the lock is shared (i.e. many reads can happen concurrently, but only one
- process may write at a time).
-
- This function is a contextmanager, meaning it's meant to be used with the
- "with" statement in Python. This is so cleanup and setup happens automatically
- and cleanly. Execution of the outer "with" statement happens at the "yield"
- statement. Execution resumes after the yield when the outer "with" statement
- ends.
-
- Args:
- path: path to file being locked
- mode: mode to open file with ('w', 'r', etc.)
- """
- with open(path, mode) as f:
- # Apply FD_CLOEXEC argument to fd. This ensures that the file descriptor
- # won't be leaked to any child processes.
- current_args = fcntl.fcntl(f.fileno(), fcntl.F_GETFD)
- fcntl.fcntl(f.fileno(), fcntl.F_SETFD, current_args | fcntl.FD_CLOEXEC)
-
- # Reads can share the lock as no race conditions exist. If write is needed,
- # give writing process exclusive access to the file.
- if f.mode == 'r' or f.mode == 'rb':
- lock_type = fcntl.LOCK_SH
- else:
- lock_type = fcntl.LOCK_EX
-
- try:
- fcntl.lockf(f, lock_type)
- yield f
- f.flush()
- finally:
- fcntl.lockf(f, fcntl.LOCK_UN)
+ """Lock file and block if other process has lock on file.
+
+ Acquire exclusive lock for file. Only blocks other processes if they attempt
+ to also acquire lock through this method. If only reading (modes 'r' and 'rb')
+ then the lock is shared (i.e. many reads can happen concurrently, but only one
+ process may write at a time).
+
+ This function is a contextmanager, meaning it's meant to be used with the
+ "with" statement in Python. This is so cleanup and setup happens automatically
+ and cleanly. Execution of the outer "with" statement happens at the "yield"
+ statement. Execution resumes after the yield when the outer "with" statement
+ ends.
+
+ Args:
+ path: path to file being locked
+ mode: mode to open file with ('w', 'r', etc.)
+ """
+ with open(path, mode) as f:
+ # Apply FD_CLOEXEC argument to fd. This ensures that the file descriptor
+ # won't be leaked to any child processes.
+ current_args = fcntl.fcntl(f.fileno(), fcntl.F_GETFD)
+ fcntl.fcntl(f.fileno(), fcntl.F_SETFD, current_args | fcntl.FD_CLOEXEC)
+
+ # Reads can share the lock as no race conditions exist. If write is needed,
+ # give writing process exclusive access to the file.
+ if f.mode == "r" or f.mode == "rb":
+ lock_type = fcntl.LOCK_SH
+ else:
+ lock_type = fcntl.LOCK_EX
+
+ try:
+ fcntl.lockf(f, lock_type)
+ yield f
+ f.flush()
+ finally:
+ fcntl.lockf(f, fcntl.LOCK_UN)
def log_to_file(path, execargs, link_from=None, link_to=None):
- """Common logging function.
+ """Common logging function.
- Log current working directory, current execargs, and a from-to relationship
- between files.
- """
- with lock_file(path, 'a') as log:
- log.write('cd: %s; %s\n' % (os.getcwd(), ' '.join(execargs)))
- if link_from and link_to:
- log.write('%s -> %s\n' % (link_from, link_to))
+ Log current working directory, current execargs, and a from-to relationship
+ between files.
+ """
+ with lock_file(path, "a") as log:
+ log.write("cd: %s; %s\n" % (os.getcwd(), " ".join(execargs)))
+ if link_from and link_to:
+ log.write("%s -> %s\n" % (link_from, link_to))
def exec_and_return(execargs):
- """Execute process and return.
+ """Execute process and return.
- Execute according to execargs and return immediately. Don't inspect
- stderr or stdout.
- """
- return subprocess.call(execargs)
+ Execute according to execargs and return immediately. Don't inspect
+ stderr or stdout.
+ """
+ return subprocess.call(execargs)
def which_cache(obj_file):
- """Determine which cache an object belongs to.
-
- The binary search tool creates two files for each search iteration listing
- the full set of bad objects and full set of good objects. We use this to
- determine where an object file should be linked from (good or bad).
- """
- bad_set_file = os.environ.get('BISECT_BAD_SET')
- if in_object_list(obj_file, bad_set_file):
- return BAD_CACHE
- else:
- return GOOD_CACHE
+ """Determine which cache an object belongs to.
+
+ The binary search tool creates two files for each search iteration listing
+ the full set of bad objects and full set of good objects. We use this to
+ determine where an object file should be linked from (good or bad).
+ """
+ bad_set_file = os.environ.get("BISECT_BAD_SET")
+ if in_object_list(obj_file, bad_set_file):
+ return BAD_CACHE
+ else:
+ return GOOD_CACHE
def makedirs(path):
- """Try to create directories in path."""
- try:
- os.makedirs(path)
- except os.error:
- if not os.path.isdir(path):
- raise
+ """Try to create directories in path."""
+ try:
+ os.makedirs(path)
+ except os.error:
+ if not os.path.isdir(path):
+ raise
def get_obj_path(execargs):
- """Get the object path for the object file in the list of arguments.
-
- Returns:
- Absolute object path from execution args (-o argument). If no object being
- outputted, then return empty string. -o argument is checked only if -c is
- also present.
- """
- try:
- i = execargs.index('-o')
- _ = execargs.index('-c')
- except ValueError:
- return ''
-
- obj_path = execargs[i + 1]
- # Ignore args that do not create a file.
- if obj_path in (
- '-',
- '/dev/null',
- ):
- return ''
- # Ignore files ending in .tmp.
- if obj_path.endswith(('.tmp',)):
- return ''
- # Ignore configuration files generated by Automake/Autoconf/CMake etc.
- if (obj_path.endswith('conftest.o') or
- obj_path.endswith('CMakeFiles/test.o') or
- obj_path.find('CMakeTmp') != -1 or
- os.path.abspath(obj_path).find('CMakeTmp') != -1):
- return ''
-
- return os.path.abspath(obj_path)
+ """Get the object path for the object file in the list of arguments.
+
+ Returns:
+ Absolute object path from execution args (-o argument). If no object being
+ outputted, then return empty string. -o argument is checked only if -c is
+ also present.
+ """
+ try:
+ i = execargs.index("-o")
+ _ = execargs.index("-c")
+ except ValueError:
+ return ""
+
+ obj_path = execargs[i + 1]
+ # Ignore args that do not create a file.
+ if obj_path in (
+ "-",
+ "/dev/null",
+ ):
+ return ""
+ # Ignore files ending in .tmp.
+ if obj_path.endswith((".tmp",)):
+ return ""
+ # Ignore configuration files generated by Automake/Autoconf/CMake etc.
+ if (
+ obj_path.endswith("conftest.o")
+ or obj_path.endswith("CMakeFiles/test.o")
+ or obj_path.find("CMakeTmp") != -1
+ or os.path.abspath(obj_path).find("CMakeTmp") != -1
+ ):
+ return ""
+
+ return os.path.abspath(obj_path)
def get_dep_path(execargs):
- """Get the dep file path for the dep file in the list of arguments.
+ """Get the dep file path for the dep file in the list of arguments.
- Returns:
- Absolute path of dependency file path from execution args (-o argument). If
- no dependency being outputted then return empty string.
- """
- if '-MD' not in execargs and '-MMD' not in execargs:
- return ''
+ Returns:
+ Absolute path of dependency file path from execution args (-o argument). If
+ no dependency being outputted then return empty string.
+ """
+ if "-MD" not in execargs and "-MMD" not in execargs:
+ return ""
- # If -MF is given this is the path of the dependency file. Otherwise the
- # dependency file is the value of -o but with a .d extension
- if '-MF' in execargs:
- i = execargs.index('-MF')
- dep_path = execargs[i + 1]
- return os.path.abspath(dep_path)
+ # If -MF is given this is the path of the dependency file. Otherwise the
+ # dependency file is the value of -o but with a .d extension
+ if "-MF" in execargs:
+ i = execargs.index("-MF")
+ dep_path = execargs[i + 1]
+ return os.path.abspath(dep_path)
- full_obj_path = get_obj_path(execargs)
- if not full_obj_path:
- return ''
+ full_obj_path = get_obj_path(execargs)
+ if not full_obj_path:
+ return ""
- return full_obj_path[:-2] + '.d'
+ return full_obj_path[:-2] + ".d"
def get_dwo_path(execargs):
- """Get the dwo file path for the dwo file in the list of arguments.
+ """Get the dwo file path for the dwo file in the list of arguments.
- Returns:
- Absolute dwo file path from execution args (-gsplit-dwarf argument) If no
- dwo file being outputted then return empty string.
- """
- if '-gsplit-dwarf' not in execargs:
- return ''
+ Returns:
+ Absolute dwo file path from execution args (-gsplit-dwarf argument) If no
+ dwo file being outputted then return empty string.
+ """
+ if "-gsplit-dwarf" not in execargs:
+ return ""
- full_obj_path = get_obj_path(execargs)
- if not full_obj_path:
- return ''
+ full_obj_path = get_obj_path(execargs)
+ if not full_obj_path:
+ return ""
- return full_obj_path[:-2] + '.dwo'
+ return full_obj_path[:-2] + ".dwo"
def in_object_list(obj_name, list_filename):
- """Check if object file name exist in file with object list."""
- if not obj_name:
- return False
+ """Check if object file name exist in file with object list."""
+ if not obj_name:
+ return False
- with lock_file(list_filename, 'r') as list_file:
- for line in list_file:
- if line.strip() == obj_name:
- return True
+ with lock_file(list_filename, "r") as list_file:
+ for line in list_file:
+ if line.strip() == obj_name:
+ return True
- return False
+ return False
def get_side_effects(execargs):
- """Determine side effects generated by compiler
+ """Determine side effects generated by compiler
- Returns:
- List of paths of objects that the compiler generates as side effects.
- """
- side_effects = []
+ Returns:
+ List of paths of objects that the compiler generates as side effects.
+ """
+ side_effects = []
- # Cache dependency files
- full_dep_path = get_dep_path(execargs)
- if full_dep_path:
- side_effects.append(full_dep_path)
+ # Cache dependency files
+ full_dep_path = get_dep_path(execargs)
+ if full_dep_path:
+ side_effects.append(full_dep_path)
- # Cache dwo files
- full_dwo_path = get_dwo_path(execargs)
- if full_dwo_path:
- side_effects.append(full_dwo_path)
+ # Cache dwo files
+ full_dwo_path = get_dwo_path(execargs)
+ if full_dwo_path:
+ side_effects.append(full_dwo_path)
- return side_effects
+ return side_effects
def cache_file(execargs, bisect_dir, cache, abs_file_path):
- """Cache compiler output file (.o/.d/.dwo).
-
- Args:
- execargs: compiler execution arguments.
- bisect_dir: The directory where bisection caches live.
- cache: Which cache the file will be cached to (GOOD/BAD).
- abs_file_path: Absolute path to file being cached.
-
- Returns:
- True if caching was successful, False otherwise.
- """
- # os.path.join fails with absolute paths, use + instead
- bisect_path = os.path.join(bisect_dir, cache) + abs_file_path
- bisect_path_dir = os.path.dirname(bisect_path)
- makedirs(bisect_path_dir)
- pop_log = os.path.join(bisect_dir, cache, '_POPULATE_LOG')
- log_to_file(pop_log, execargs, abs_file_path, bisect_path)
-
- try:
- if os.path.exists(abs_file_path):
- if os.path.exists(bisect_path):
- # File exists
- population_dir = os.path.join(bisect_dir, cache)
- with lock_file(os.path.join(population_dir, '_DUPS'),
- 'a') as dup_object_list:
- dup_object_list.write('%s\n' % abs_file_path)
- if CONTINUE_ON_REDUNDANCY:
- return True
- raise Exception(
- 'Trying to cache file %s multiple times. To avoid the error, set ' \
- 'BISECT_CONTINUE_ON_REDUNDANCY to 1. For reference, the list of ' \
- 'such files will be written to %s' % (abs_file_path, os.path.join(
- population_dir, '_DUPS')))
-
- shutil.copy2(abs_file_path, bisect_path)
- # Set cache object to be read-only so later compilations can't
- # accidentally overwrite it.
- os.chmod(bisect_path, 0o444)
- return True
- else:
- # File not found (happens when compilation fails but error code is still
- # 0)
- return False
- except Exception:
- print('Could not cache file %s' % abs_file_path, file=sys.stderr)
- raise
+ """Cache compiler output file (.o/.d/.dwo).
+
+ Args:
+ execargs: compiler execution arguments.
+ bisect_dir: The directory where bisection caches live.
+ cache: Which cache the file will be cached to (GOOD/BAD).
+ abs_file_path: Absolute path to file being cached.
+
+ Returns:
+ True if caching was successful, False otherwise.
+ """
+ # os.path.join fails with absolute paths, use + instead
+ bisect_path = os.path.join(bisect_dir, cache) + abs_file_path
+ bisect_path_dir = os.path.dirname(bisect_path)
+ makedirs(bisect_path_dir)
+ pop_log = os.path.join(bisect_dir, cache, "_POPULATE_LOG")
+ log_to_file(pop_log, execargs, abs_file_path, bisect_path)
+
+ try:
+ if os.path.exists(abs_file_path):
+ if os.path.exists(bisect_path):
+ # File exists
+ population_dir = os.path.join(bisect_dir, cache)
+ with lock_file(
+ os.path.join(population_dir, "_DUPS"), "a"
+ ) as dup_object_list:
+ dup_object_list.write("%s\n" % abs_file_path)
+ if CONTINUE_ON_REDUNDANCY:
+ return True
+ raise Exception(
+ "Trying to cache file %s multiple times. To avoid the error, set "
+ "BISECT_CONTINUE_ON_REDUNDANCY to 1. For reference, the list of "
+ "such files will be written to %s"
+ % (abs_file_path, os.path.join(population_dir, "_DUPS"))
+ )
+
+ shutil.copy2(abs_file_path, bisect_path)
+ # Set cache object to be read-only so later compilations can't
+ # accidentally overwrite it.
+ os.chmod(bisect_path, 0o444)
+ return True
+ else:
+ # File not found (happens when compilation fails but error code is still
+ # 0)
+ return False
+ except Exception:
+ print("Could not cache file %s" % abs_file_path, file=sys.stderr)
+ raise
def restore_file(bisect_dir, cache, abs_file_path):
- """Restore file from cache (.o/.d/.dwo).
-
- Args:
- bisect_dir: The directory where bisection caches live.
- cache: Which cache the file will be restored from (GOOD/BAD).
- abs_file_path: Absolute path to file being restored.
- """
- # os.path.join fails with absolute paths, use + instead
- cached_path = os.path.join(bisect_dir, cache) + abs_file_path
- if os.path.exists(cached_path):
- if os.path.exists(abs_file_path):
- os.remove(abs_file_path)
- shutil.copy2(cached_path, abs_file_path)
- # Add write permission to the restored object files as some packages
- # (such as kernels) may need write permission to delete files.
- os.chmod(abs_file_path, os.stat(abs_file_path).st_mode | stat.S_IWUSR)
- else:
- raise Error(('%s is missing from %s cache! Unsure how to proceed. Make '
- 'will now crash.' % (cache, cached_path)))
+ """Restore file from cache (.o/.d/.dwo).
+
+ Args:
+ bisect_dir: The directory where bisection caches live.
+ cache: Which cache the file will be restored from (GOOD/BAD).
+ abs_file_path: Absolute path to file being restored.
+ """
+ # os.path.join fails with absolute paths, use + instead
+ cached_path = os.path.join(bisect_dir, cache) + abs_file_path
+ if os.path.exists(cached_path):
+ if os.path.exists(abs_file_path):
+ os.remove(abs_file_path)
+ shutil.copy2(cached_path, abs_file_path)
+ # Add write permission to the restored object files as some packages
+ # (such as kernels) may need write permission to delete files.
+ os.chmod(abs_file_path, os.stat(abs_file_path).st_mode | stat.S_IWUSR)
+ else:
+ raise Error(
+ (
+ "%s is missing from %s cache! Unsure how to proceed. Make "
+ "will now crash." % (cache, cached_path)
+ )
+ )
def bisect_populate(execargs, bisect_dir, population_name):
- """Add necessary information to the bisect cache for the given execution.
-
- Extract the necessary information for bisection from the compiler
- execution arguments and put it into the bisection cache. This
- includes copying the created object file, adding the object
- file path to the cache list and keeping a log of the execution.
-
- Args:
- execargs: compiler execution arguments.
- bisect_dir: bisection directory.
- population_name: name of the cache being populated (good/bad).
- """
- retval = exec_and_return(execargs)
- if retval:
- return retval
-
- full_obj_path = get_obj_path(execargs)
- # This is not a normal compiler call because it doesn't have a -o argument,
- # or the -o argument has an unusable output file.
- # It's likely that this compiler call was actually made to invoke the linker,
- # or as part of a configuratoin test. In this case we want to simply call the
- # compiler and return.
- if not full_obj_path:
- return retval
-
- # Return if not able to cache the object file
- if not cache_file(execargs, bisect_dir, population_name, full_obj_path):
- return retval
+ """Add necessary information to the bisect cache for the given execution.
+
+ Extract the necessary information for bisection from the compiler
+ execution arguments and put it into the bisection cache. This
+ includes copying the created object file, adding the object
+ file path to the cache list and keeping a log of the execution.
+
+ Args:
+ execargs: compiler execution arguments.
+ bisect_dir: bisection directory.
+ population_name: name of the cache being populated (good/bad).
+ """
+ retval = exec_and_return(execargs)
+ if retval:
+ return retval
- population_dir = os.path.join(bisect_dir, population_name)
- with lock_file(os.path.join(population_dir, '_LIST'), 'a') as object_list:
- object_list.write('%s\n' % full_obj_path)
+ full_obj_path = get_obj_path(execargs)
+ # This is not a normal compiler call because it doesn't have a -o argument,
+ # or the -o argument has an unusable output file.
+ # It's likely that this compiler call was actually made to invoke the linker,
+ # or as part of a configuratoin test. In this case we want to simply call the
+ # compiler and return.
+ if not full_obj_path:
+ return retval
- for side_effect in get_side_effects(execargs):
- _ = cache_file(execargs, bisect_dir, population_name, side_effect)
+ # Return if not able to cache the object file
+ if not cache_file(execargs, bisect_dir, population_name, full_obj_path):
+ return retval
- return retval
+ population_dir = os.path.join(bisect_dir, population_name)
+ with lock_file(os.path.join(population_dir, "_LIST"), "a") as object_list:
+ object_list.write("%s\n" % full_obj_path)
+ for side_effect in get_side_effects(execargs):
+ _ = cache_file(execargs, bisect_dir, population_name, side_effect)
-def bisect_triage(execargs, bisect_dir):
- """Use object object file from appropriate cache (good/bad).
-
- Given a populated bisection directory, use the object file saved
- into one of the caches (good/bad) according to what is specified
- in the good/bad sets. The good/bad sets are generated by the
- high level binary search tool. Additionally restore any possible
- side effects of compiler.
-
- Args:
- execargs: compiler execution arguments.
- bisect_dir: populated bisection directory.
- """
- full_obj_path = get_obj_path(execargs)
- obj_list = os.path.join(bisect_dir, LIST_FILE)
-
- # If the output isn't an object file just call compiler
- if not full_obj_path:
- return exec_and_return(execargs)
-
- # If this isn't a bisected object just call compiler
- # This shouldn't happen!
- if not in_object_list(full_obj_path, obj_list):
- if CONTINUE_ON_MISSING:
- log_file = os.path.join(bisect_dir, '_MISSING_CACHED_OBJ_LOG')
- log_to_file(log_file, execargs, '? compiler', full_obj_path)
- return exec_and_return(execargs)
- else:
- raise Error(('%s is missing from cache! To ignore export '
- 'BISECT_CONTINUE_ON_MISSING=1. See documentation for more '
- 'details on this option.' % full_obj_path))
-
- cache = which_cache(full_obj_path)
-
- # If using safe WRAPPER_SAFE_MODE option call compiler and overwrite the
- # result from the good/bad cache. This option is safe and covers all compiler
- # side effects, but is very slow!
- if WRAPPER_SAFE_MODE:
- retval = exec_and_return(execargs)
- if retval:
- return retval
- os.remove(full_obj_path)
- restore_file(bisect_dir, cache, full_obj_path)
return retval
- # Generate compiler side effects. Trick Make into thinking compiler was
- # actually executed.
- for side_effect in get_side_effects(execargs):
- restore_file(bisect_dir, cache, side_effect)
- # If generated object file happened to be pruned/cleaned by Make then link it
- # over from cache again.
- if not os.path.exists(full_obj_path):
- restore_file(bisect_dir, cache, full_obj_path)
-
- return 0
+def bisect_triage(execargs, bisect_dir):
+ """Use object object file from appropriate cache (good/bad).
+
+ Given a populated bisection directory, use the object file saved
+ into one of the caches (good/bad) according to what is specified
+ in the good/bad sets. The good/bad sets are generated by the
+ high level binary search tool. Additionally restore any possible
+ side effects of compiler.
+
+ Args:
+ execargs: compiler execution arguments.
+ bisect_dir: populated bisection directory.
+ """
+ full_obj_path = get_obj_path(execargs)
+ obj_list = os.path.join(bisect_dir, LIST_FILE)
+
+ # If the output isn't an object file just call compiler
+ if not full_obj_path:
+ return exec_and_return(execargs)
+
+ # If this isn't a bisected object just call compiler
+ # This shouldn't happen!
+ if not in_object_list(full_obj_path, obj_list):
+ if CONTINUE_ON_MISSING:
+ log_file = os.path.join(bisect_dir, "_MISSING_CACHED_OBJ_LOG")
+ log_to_file(log_file, execargs, "? compiler", full_obj_path)
+ return exec_and_return(execargs)
+ else:
+ raise Error(
+ (
+ "%s is missing from cache! To ignore export "
+ "BISECT_CONTINUE_ON_MISSING=1. See documentation for more "
+ "details on this option." % full_obj_path
+ )
+ )
+
+ cache = which_cache(full_obj_path)
+
+ # If using safe WRAPPER_SAFE_MODE option call compiler and overwrite the
+ # result from the good/bad cache. This option is safe and covers all compiler
+ # side effects, but is very slow!
+ if WRAPPER_SAFE_MODE:
+ retval = exec_and_return(execargs)
+ if retval:
+ return retval
+ os.remove(full_obj_path)
+ restore_file(bisect_dir, cache, full_obj_path)
+ return retval
+
+ # Generate compiler side effects. Trick Make into thinking compiler was
+ # actually executed.
+ for side_effect in get_side_effects(execargs):
+ restore_file(bisect_dir, cache, side_effect)
+
+ # If generated object file happened to be pruned/cleaned by Make then link it
+ # over from cache again.
+ if not os.path.exists(full_obj_path):
+ restore_file(bisect_dir, cache, full_obj_path)
+
+ return 0
def bisect_driver(bisect_stage, bisect_dir, execargs):
- """Call appropriate bisection stage according to value in bisect_stage."""
- if bisect_stage == 'POPULATE_GOOD':
- return bisect_populate(execargs, bisect_dir, GOOD_CACHE)
- elif bisect_stage == 'POPULATE_BAD':
- return bisect_populate(execargs, bisect_dir, BAD_CACHE)
- elif bisect_stage == 'TRIAGE':
- return bisect_triage(execargs, bisect_dir)
- else:
- raise ValueError('wrong value for BISECT_STAGE: %s' % bisect_stage)
+ """Call appropriate bisection stage according to value in bisect_stage."""
+ if bisect_stage == "POPULATE_GOOD":
+ return bisect_populate(execargs, bisect_dir, GOOD_CACHE)
+ elif bisect_stage == "POPULATE_BAD":
+ return bisect_populate(execargs, bisect_dir, BAD_CACHE)
+ elif bisect_stage == "TRIAGE":
+ return bisect_triage(execargs, bisect_dir)
+ else:
+ raise ValueError("wrong value for BISECT_STAGE: %s" % bisect_stage)
diff --git a/binary_search_tool/common.py b/binary_search_tool/common.py
index 85cd478b..f6165847 100644
--- a/binary_search_tool/common.py
+++ b/binary_search_tool/common.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -21,276 +21,303 @@ ArgumentDict inherits OrderedDict in order to preserve the order the args are
created so the help text is made properly.
"""
-from __future__ import print_function
import collections
import os
import sys
+
# Programatically adding utils python path to PYTHONPATH
if os.path.isabs(sys.argv[0]):
- utils_pythonpath = os.path.abspath('{0}/..'.format(
- os.path.dirname(sys.argv[0])))
+ utils_pythonpath = os.path.abspath(
+ "{0}/..".format(os.path.dirname(sys.argv[0]))
+ )
else:
- wdir = os.getcwd()
- utils_pythonpath = os.path.abspath('{0}/{1}/..'.format(
- wdir, os.path.dirname(sys.argv[0])))
+ wdir = os.getcwd()
+ utils_pythonpath = os.path.abspath(
+ "{0}/{1}/..".format(wdir, os.path.dirname(sys.argv[0]))
+ )
sys.path.append(utils_pythonpath)
class ArgumentDict(collections.OrderedDict):
- """Wrapper around OrderedDict, represents CLI arguments for program.
-
- AddArgument enforces the following layout:
- {
- ['-n', '--iterations'] : {
- 'dest': 'iterations',
- 'type': int,
- 'help': 'Number of iterations to try in the search.',
- 'default': 50
- }
- [arg_name1, arg_name2, ...] : {
- arg_option1 : arg_option_val1,
- ...
- },
- ...
- }
- """
- _POSSIBLE_OPTIONS = [
- 'action', 'nargs', 'const', 'default', 'type', 'choices', 'required',
- 'help', 'metavar', 'dest'
- ]
-
- def AddArgument(self, *args, **kwargs):
- """Add argument to ArgsDict, has same signature as argparse.add_argument
-
- Emulates the the argparse.add_argument method so the internal OrderedDict
- can be safely and easily populated. Each call to this method will have a 1-1
- corresponding call to argparse.add_argument once BuildArgParser is called.
-
- Args:
- *args: The names for the argument (-V, --verbose, etc.)
- **kwargs: The options for the argument, corresponds to the args of
- argparse.add_argument
-
- Returns:
- None
-
- Raises:
- TypeError: if args is empty or if option in kwargs is not a valid
- option for argparse.add_argument.
+ """Wrapper around OrderedDict, represents CLI arguments for program.
+
+ AddArgument enforces the following layout:
+ {
+ ['-n', '--iterations'] : {
+ 'dest': 'iterations',
+ 'type': int,
+ 'help': 'Number of iterations to try in the search.',
+ 'default': 50
+ }
+ [arg_name1, arg_name2, ...] : {
+ arg_option1 : arg_option_val1,
+ ...
+ },
+ ...
+ }
"""
- if not args:
- raise TypeError('Argument needs at least one name')
-
- for key in kwargs:
- if key not in self._POSSIBLE_OPTIONS:
- raise TypeError('Invalid option "%s" for argument %s' % (key, args[0]))
- self[args] = kwargs
+ _POSSIBLE_OPTIONS = [
+ "action",
+ "nargs",
+ "const",
+ "default",
+ "type",
+ "choices",
+ "required",
+ "help",
+ "metavar",
+ "dest",
+ ]
+
+ def AddArgument(self, *args, **kwargs):
+ """Add argument to ArgsDict, has same signature as argparse.add_argument
+
+ Emulates the the argparse.add_argument method so the internal OrderedDict
+ can be safely and easily populated. Each call to this method will have a 1-1
+ corresponding call to argparse.add_argument once BuildArgParser is called.
+
+ Args:
+ *args: The names for the argument (-V, --verbose, etc.)
+ **kwargs: The options for the argument, corresponds to the args of
+ argparse.add_argument
+
+ Returns:
+ None
+
+ Raises:
+ TypeError: if args is empty or if option in kwargs is not a valid
+ option for argparse.add_argument.
+ """
+ if not args:
+ raise TypeError("Argument needs at least one name")
+
+ for key in kwargs:
+ if key not in self._POSSIBLE_OPTIONS:
+ raise TypeError(
+ 'Invalid option "%s" for argument %s' % (key, args[0])
+ )
+
+ self[args] = kwargs
_ArgsDict = ArgumentDict()
def GetArgsDict():
- """_ArgsDict singleton method"""
- if not _ArgsDict:
- _BuildArgsDict(_ArgsDict)
- return _ArgsDict
+ """_ArgsDict singleton method"""
+ if not _ArgsDict:
+ _BuildArgsDict(_ArgsDict)
+ return _ArgsDict
def BuildArgParser(parser, override=False):
- """Add all arguments from singleton ArgsDict to parser.
+ """Add all arguments from singleton ArgsDict to parser.
- Will take argparse parser and add all arguments in ArgsDict. Will ignore
- the default and required options if override is set to True.
+ Will take argparse parser and add all arguments in ArgsDict. Will ignore
+ the default and required options if override is set to True.
- Args:
- parser: type argparse.ArgumentParser, will call add_argument for every item
- in _ArgsDict
- override: True if being called from run_bisect.py. Used to say that default
- and required options are to be ignored
+ Args:
+ parser: type argparse.ArgumentParser, will call add_argument for every item
+ in _ArgsDict
+ override: True if being called from run_bisect.py. Used to say that default
+ and required options are to be ignored
- Returns:
- None
- """
- ArgsDict = GetArgsDict()
+ Returns:
+ None
+ """
+ ArgsDict = GetArgsDict()
- # Have no defaults when overriding
- for arg_names, arg_options in ArgsDict.items():
- if override:
- arg_options = arg_options.copy()
- arg_options.pop('default', None)
- arg_options.pop('required', None)
+ # Have no defaults when overriding
+ for arg_names, arg_options in ArgsDict.items():
+ if override:
+ arg_options = arg_options.copy()
+ arg_options.pop("default", None)
+ arg_options.pop("required", None)
- parser.add_argument(*arg_names, **arg_options)
+ parser.add_argument(*arg_names, **arg_options)
def StrToBool(str_in):
- if str_in.lower() in ['true', 't', '1']:
- return True
- if str_in.lower() in ['false', 'f', '0']:
- return False
+ if str_in.lower() in ["true", "t", "1"]:
+ return True
+ if str_in.lower() in ["false", "f", "0"]:
+ return False
- raise AttributeError('%s is not a valid boolean string' % str_in)
+ raise AttributeError("%s is not a valid boolean string" % str_in)
def _BuildArgsDict(args):
- """Populate ArgumentDict with all arguments"""
- args.AddArgument(
- '-n',
- '--iterations',
- dest='iterations',
- type=int,
- help='Number of iterations to try in the search.',
- default=50)
- args.AddArgument(
- '-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')
- 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')
- 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')
- 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.')
- args.AddArgument(
- '-t',
- '--test_script',
- dest='test_script',
- help='Script to run to test the '
- 'output after packages are built.')
- # No input (evals to False),
- # --prune (evals to True),
- # --prune=False,
- # --prune=True
- args.AddArgument(
- '-p',
- '--prune',
- dest='prune',
- nargs='?',
- const=True,
- default=False,
- type=StrToBool,
- metavar='bool',
- 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. '
- 'This will also turn on pass/transformation level bisection. '
- 'Needs support of `-opt-bisect-limit`(pass) and '
- '`-print-debug-counter`(transformation) from LLVM. '
- 'For now it only supports one single bad item, so to use it, '
- 'prune must be set to False.')
- # No input (evals to False),
- # --ir_diff (evals to True),
- # --ir_diff=False,
- # --ir_diff=True
- args.AddArgument(
- '-d',
- '--ir_diff',
- dest='ir_diff',
- nargs='?',
- const=True,
- default=False,
- type=StrToBool,
- metavar='bool',
- help='Whether to print IR differences before and after bad '
- 'pass/transformation to verbose output. Defaults to False, '
- 'only works when pass_bisect is enabled.')
- # No input (evals to False),
- # --noincremental (evals to True),
- # --noincremental=False,
- # --noincremental=True
- args.AddArgument(
- '-c',
- '--noincremental',
- dest='noincremental',
- nargs='?',
- const=True,
- default=False,
- type=StrToBool,
- metavar='bool',
- 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,
- # --file_args=True
- args.AddArgument(
- '-f',
- '--file_args',
- dest='file_args',
- nargs='?',
- const=True,
- default=False,
- type=StrToBool,
- metavar='bool',
- 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,
- # --verify=True
- args.AddArgument(
- '--verify',
- dest='verify',
- nargs='?',
- const=True,
- default=True,
- type=StrToBool,
- metavar='bool',
- help='Whether to run verify iterations before searching. '
- 'Defaults to True.')
- args.AddArgument(
- '-N',
- '--prune_iterations',
- dest='prune_iterations',
- type=int,
- help='Number of prune iterations to try in the search.',
- default=100)
- # No input (evals to False),
- # --verbose (evals to True),
- # --verbose=False,
- # --verbose=True
- args.AddArgument(
- '-V',
- '--verbose',
- dest='verbose',
- nargs='?',
- const=True,
- default=False,
- type=StrToBool,
- metavar='bool',
- help='If True, print full output to console.')
- args.AddArgument(
- '-r',
- '--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.')
+ """Populate ArgumentDict with all arguments"""
+ args.AddArgument(
+ "-n",
+ "--iterations",
+ dest="iterations",
+ type=int,
+ help="Number of iterations to try in the search.",
+ default=50,
+ )
+ args.AddArgument(
+ "-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",
+ )
+ 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",
+ )
+ 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",
+ )
+ 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.",
+ )
+ args.AddArgument(
+ "-t",
+ "--test_script",
+ dest="test_script",
+ help="Script to run to test the " "output after packages are built.",
+ )
+ # No input (evals to False),
+ # --prune (evals to True),
+ # --prune=False,
+ # --prune=True
+ args.AddArgument(
+ "-p",
+ "--prune",
+ dest="prune",
+ nargs="?",
+ const=True,
+ default=False,
+ type=StrToBool,
+ metavar="bool",
+ 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. "
+ "This will also turn on pass/transformation level bisection. "
+ "Needs support of `-opt-bisect-limit`(pass) and "
+ "`-print-debug-counter`(transformation) from LLVM. "
+ "For now it only supports one single bad item, so to use it, "
+ "prune must be set to False.",
+ )
+ # No input (evals to False),
+ # --ir_diff (evals to True),
+ # --ir_diff=False,
+ # --ir_diff=True
+ args.AddArgument(
+ "-d",
+ "--ir_diff",
+ dest="ir_diff",
+ nargs="?",
+ const=True,
+ default=False,
+ type=StrToBool,
+ metavar="bool",
+ help="Whether to print IR differences before and after bad "
+ "pass/transformation to verbose output. Defaults to False, "
+ "only works when pass_bisect is enabled.",
+ )
+ # No input (evals to False),
+ # --noincremental (evals to True),
+ # --noincremental=False,
+ # --noincremental=True
+ args.AddArgument(
+ "-c",
+ "--noincremental",
+ dest="noincremental",
+ nargs="?",
+ const=True,
+ default=False,
+ type=StrToBool,
+ metavar="bool",
+ 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,
+ # --file_args=True
+ args.AddArgument(
+ "-f",
+ "--file_args",
+ dest="file_args",
+ nargs="?",
+ const=True,
+ default=False,
+ type=StrToBool,
+ metavar="bool",
+ 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,
+ # --verify=True
+ args.AddArgument(
+ "--verify",
+ dest="verify",
+ nargs="?",
+ const=True,
+ default=True,
+ type=StrToBool,
+ metavar="bool",
+ help="Whether to run verify iterations before searching. "
+ "Defaults to True.",
+ )
+ args.AddArgument(
+ "-N",
+ "--prune_iterations",
+ dest="prune_iterations",
+ type=int,
+ help="Number of prune iterations to try in the search.",
+ default=100,
+ )
+ # No input (evals to False),
+ # --verbose (evals to True),
+ # --verbose=False,
+ # --verbose=True
+ args.AddArgument(
+ "-V",
+ "--verbose",
+ dest="verbose",
+ nargs="?",
+ const=True,
+ default=False,
+ type=StrToBool,
+ metavar="bool",
+ help="If True, print full output to console.",
+ )
+ args.AddArgument(
+ "-r",
+ "--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.",
+ )
diff --git a/binary_search_tool/common/boot_test.sh b/binary_search_tool/common/boot_test.sh
index 8f6d9a7d..384712b7 100755
--- a/binary_search_tool/common/boot_test.sh
+++ b/binary_search_tool/common/boot_test.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script pings the chromebook to determine if it has successfully booted.
#
diff --git a/binary_search_tool/common/hash_test.sh b/binary_search_tool/common/hash_test.sh
index 5450988e..338ee026 100755
--- a/binary_search_tool/common/hash_test.sh
+++ b/binary_search_tool/common/hash_test.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is intended to be used by binary_search_state.py. It is to
# be used for testing/development of the binary search triage tool
diff --git a/binary_search_tool/common/interactive_test.sh b/binary_search_tool/common/interactive_test.sh
index 8773dd12..05d47b7f 100755
--- a/binary_search_tool/common/interactive_test.sh
+++ b/binary_search_tool/common/interactive_test.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script pings the chromebook to determine if it successfully booted.
# It then asks the user if the image is good or not, allowing the user to
diff --git a/binary_search_tool/common/interactive_test_noping.sh b/binary_search_tool/common/interactive_test_noping.sh
index bb01b950..d4e77d7c 100755
--- a/binary_search_tool/common/interactive_test_noping.sh
+++ b/binary_search_tool/common/interactive_test_noping.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script asks the user if the image is good or not, allowing the user to
# conduct whatever tests the user wishes, and waiting for a response.
diff --git a/binary_search_tool/common/test_setup.sh b/binary_search_tool/common/test_setup.sh
index 3ea73272..06452346 100755
--- a/binary_search_tool/common/test_setup.sh
+++ b/binary_search_tool/common/test_setup.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Copyright 2021 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
diff --git a/binary_search_tool/compiler_wrapper.py b/binary_search_tool/compiler_wrapper.py
index 0fd92c67..c32826b0 100755
--- a/binary_search_tool/compiler_wrapper.py
+++ b/binary_search_tool/compiler_wrapper.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -20,7 +20,6 @@ Design doc:
https://docs.google.com/document/d/1yDgaUIa2O5w6dc3sSTe1ry-1ehKajTGJGQCbyn0fcEM
"""
-from __future__ import print_function
import os
import shlex
@@ -28,41 +27,42 @@ import sys
from binary_search_tool import bisect_driver
-WRAPPED = '%s.real' % sys.argv[0]
-BISECT_STAGE = os.environ.get('BISECT_STAGE')
-DEFAULT_BISECT_DIR = os.path.expanduser('~/ANDROID_BISECT')
-BISECT_DIR = os.environ.get('BISECT_DIR') or DEFAULT_BISECT_DIR
+
+WRAPPED = "%s.real" % sys.argv[0]
+BISECT_STAGE = os.environ.get("BISECT_STAGE")
+DEFAULT_BISECT_DIR = os.path.expanduser("~/ANDROID_BISECT")
+BISECT_DIR = os.environ.get("BISECT_DIR") or DEFAULT_BISECT_DIR
def ProcessArgFile(arg_file):
- args = []
- # Read in entire file at once and parse as if in shell
- with open(arg_file, 'r', encoding='utf-8') as f:
- args.extend(shlex.split(f.read()))
+ args = []
+ # Read in entire file at once and parse as if in shell
+ with open(arg_file, "r", encoding="utf-8") as f:
+ args.extend(shlex.split(f.read()))
- return args
+ return args
def Main(_):
- if not os.path.islink(sys.argv[0]):
- print("Compiler wrapper can't be called directly!")
- return 1
+ if not os.path.islink(sys.argv[0]):
+ print("Compiler wrapper can't be called directly!")
+ return 1
- execargs = [WRAPPED] + sys.argv[1:]
+ execargs = [WRAPPED] + sys.argv[1:]
- if BISECT_STAGE not in bisect_driver.VALID_MODES or '-o' not in execargs:
- os.execv(WRAPPED, [WRAPPED] + sys.argv[1:])
+ if BISECT_STAGE not in bisect_driver.VALID_MODES or "-o" not in execargs:
+ os.execv(WRAPPED, [WRAPPED] + sys.argv[1:])
- # Handle @file argument syntax with compiler
- for idx, _ in enumerate(execargs):
- # @file can be nested in other @file arguments, use While to re-evaluate
- # the first argument of the embedded file.
- while execargs[idx][0] == '@':
- args_in_file = ProcessArgFile(execargs[idx][1:])
- execargs = execargs[0:idx] + args_in_file + execargs[idx + 1:]
+ # Handle @file argument syntax with compiler
+ for idx, _ in enumerate(execargs):
+ # @file can be nested in other @file arguments, use While to re-evaluate
+ # the first argument of the embedded file.
+ while execargs[idx][0] == "@":
+ args_in_file = ProcessArgFile(execargs[idx][1:])
+ execargs = execargs[0:idx] + args_in_file + execargs[idx + 1 :]
- bisect_driver.bisect_driver(BISECT_STAGE, BISECT_DIR, execargs)
+ bisect_driver.bisect_driver(BISECT_STAGE, BISECT_DIR, execargs)
-if __name__ == '__main__':
- sys.exit(Main(sys.argv[1:]))
+if __name__ == "__main__":
+ sys.exit(Main(sys.argv[1:]))
diff --git a/binary_search_tool/cros_pkg/create_cleanup_script.py b/binary_search_tool/cros_pkg/create_cleanup_script.py
index 62ee38f1..abfea5eb 100755
--- a/binary_search_tool/cros_pkg/create_cleanup_script.py
+++ b/binary_search_tool/cros_pkg/create_cleanup_script.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -13,20 +13,19 @@ undo the changes made by setup.sh, returning everything to its
original state.
"""
-from __future__ import print_function
import argparse
import sys
def Usage(parser, msg):
- print('ERROR: ' + msg)
- parser.print_help()
- sys.exit(1)
+ print("ERROR: " + msg)
+ parser.print_help()
+ sys.exit(1)
def Main(argv):
- """Generate a script to undo changes done by setup.sh
+ """Generate a script to undo changes done by setup.sh
The script setup.sh makes a change that needs to be
undone, namely it creates a soft link making /build/${board} point
@@ -40,80 +39,91 @@ def Main(argv):
This function takes arguments that tell it exactly what setup.sh
actually did, then generates a script to undo those exact changes.
- """
-
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--board',
- dest='board',
- required=True,
- help='Chromeos board for packages/image.')
-
- parser.add_argument(
- '--old_tree_missing',
- dest='tree_existed',
- action='store_false',
- help='Did /build/${BOARD} exist.',
- default=True)
-
- parser.add_argument(
- '--renamed_tree',
- dest='renamed_tree',
- action='store_true',
- help='Was /build/${BOARD} saved & renamed.',
- default=False)
-
- parser.add_argument(
- '--old_link',
- dest='old_link',
- help=('The original build tree soft link.'))
-
- options = parser.parse_args(argv[1:])
-
- if options.old_link or options.renamed_tree:
- if not options.tree_existed:
- Usage(
- parser, 'If --tree_existed is False, cannot have '
- '--renamed_tree or --old_link')
-
- if options.old_link and options.renamed_tree:
- Usage(parser, '--old_link and --renamed_tree are incompatible options.')
-
- if options.tree_existed:
- if not options.old_link and not options.renamed_tree:
- Usage(
- parser, 'If --tree_existed is True, then must have either '
- '--old_link or --renamed_tree')
-
- out_filename = 'cros_pkg/' + options.board + '_cleanup.sh'
-
- with open(out_filename, 'w', encoding='utf-8') as out_file:
- out_file.write('#!/bin/bash\n\n')
- # First, remove the 'new' soft link.
- out_file.write('sudo rm /build/%s\n' % options.board)
+ """
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--board",
+ dest="board",
+ required=True,
+ help="Chromeos board for packages/image.",
+ )
+
+ parser.add_argument(
+ "--old_tree_missing",
+ dest="tree_existed",
+ action="store_false",
+ help="Did /build/${BOARD} exist.",
+ default=True,
+ )
+
+ parser.add_argument(
+ "--renamed_tree",
+ dest="renamed_tree",
+ action="store_true",
+ help="Was /build/${BOARD} saved & renamed.",
+ default=False,
+ )
+
+ parser.add_argument(
+ "--old_link",
+ dest="old_link",
+ help=("The original build tree soft link."),
+ )
+
+ options = parser.parse_args(argv[1:])
+
+ if options.old_link or options.renamed_tree:
+ if not options.tree_existed:
+ Usage(
+ parser,
+ "If --tree_existed is False, cannot have "
+ "--renamed_tree or --old_link",
+ )
+
+ if options.old_link and options.renamed_tree:
+ Usage(parser, "--old_link and --renamed_tree are incompatible options.")
+
if options.tree_existed:
- if options.renamed_tree:
- # Old build tree existed and was a real tree, so it got
- # renamed. Move the renamed tree back to the original tree.
- out_file.write('sudo mv /build/%s.save /build/%s\n' % (options.board,
- options.board))
- else:
- # Old tree existed and was already a soft link. Re-create the
- # original soft link.
- original_link = options.old_link
- if original_link[0] == "'":
- original_link = original_link[1:]
- if original_link[-1] == "'":
- original_link = original_link[:-1]
- out_file.write(
- 'sudo ln -s %s /build/%s\n' % (original_link, options.board))
- out_file.write('\n')
- # Remove common.sh file
- out_file.write('rm common/common.sh\n')
-
- return 0
-
-
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+ if not options.old_link and not options.renamed_tree:
+ Usage(
+ parser,
+ "If --tree_existed is True, then must have either "
+ "--old_link or --renamed_tree",
+ )
+
+ out_filename = "cros_pkg/" + options.board + "_cleanup.sh"
+
+ with open(out_filename, "w", encoding="utf-8") as out_file:
+ out_file.write("#!/bin/bash\n\n")
+ # First, remove the 'new' soft link.
+ out_file.write("sudo rm /build/%s\n" % options.board)
+ if options.tree_existed:
+ if options.renamed_tree:
+ # Old build tree existed and was a real tree, so it got
+ # renamed. Move the renamed tree back to the original tree.
+ out_file.write(
+ "sudo mv /build/%s.save /build/%s\n"
+ % (options.board, options.board)
+ )
+ else:
+ # Old tree existed and was already a soft link. Re-create the
+ # original soft link.
+ original_link = options.old_link
+ if original_link[0] == "'":
+ original_link = original_link[1:]
+ if original_link[-1] == "'":
+ original_link = original_link[:-1]
+ out_file.write(
+ "sudo ln -s %s /build/%s\n" % (original_link, options.board)
+ )
+ out_file.write("\n")
+ # Remove common.sh file
+ out_file.write("rm common/common.sh\n")
+
+ return 0
+
+
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/cros_pkg/get_initial_items.sh b/binary_search_tool/cros_pkg/get_initial_items.sh
index 49ca3d18..bc0fd2e6 100755
--- a/binary_search_tool/cros_pkg/get_initial_items.sh
+++ b/binary_search_tool/cros_pkg/get_initial_items.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2015 Google Inc. All Rights Reserved.
+# Copyright 2015 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on ChromeOS packages. This script
@@ -13,4 +13,3 @@ source common/common.sh
cd ${GOOD_BUILD}/packages
find . -name "*.tbz2"
-
diff --git a/binary_search_tool/cros_pkg/setup.sh b/binary_search_tool/cros_pkg/setup.sh
index ae31fa82..30a3a423 100755
--- a/binary_search_tool/cros_pkg/setup.sh
+++ b/binary_search_tool/cros_pkg/setup.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2015 Google Inc. All Rights Reserved.
+# Copyright 2015 Google LLC
#
# This script is part of the ChromeOS package binary search triage process.
# It should be the first script called by the user, after the user has set up
diff --git a/binary_search_tool/cros_pkg/switch_to_bad.sh b/binary_search_tool/cros_pkg/switch_to_bad.sh
index 126425f4..b4156a0e 100755
--- a/binary_search_tool/cros_pkg/switch_to_bad.sh
+++ b/binary_search_tool/cros_pkg/switch_to_bad.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2015 Google Inc. All Rights Reserved.
+# Copyright 2015 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on ChromeOS packages. This script
diff --git a/binary_search_tool/cros_pkg/switch_to_good.sh b/binary_search_tool/cros_pkg/switch_to_good.sh
index a9095e99..5f7c2d77 100755
--- a/binary_search_tool/cros_pkg/switch_to_good.sh
+++ b/binary_search_tool/cros_pkg/switch_to_good.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2015 Google Inc. All Rights Reserved.
+# Copyright 2015 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on ChromeOS packages. This script
diff --git a/binary_search_tool/cros_pkg/test_setup_usb.sh b/binary_search_tool/cros_pkg/test_setup_usb.sh
index fec66f8e..54d0baa1 100755
--- a/binary_search_tool/cros_pkg/test_setup_usb.sh
+++ b/binary_search_tool/cros_pkg/test_setup_usb.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This is a generic ChromeOS package/image test setup script. It is meant to
# be used for the package bisection tool, in particular when there is a booting
diff --git a/binary_search_tool/ndk/DO_BISECTION.sh b/binary_search_tool/ndk/DO_BISECTION.sh
index 298d5747..e6eed765 100755
--- a/binary_search_tool/ndk/DO_BISECTION.sh
+++ b/binary_search_tool/ndk/DO_BISECTION.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This is an example script to show users the steps for bisecting an NDK
# application for Android. Our example is the Teapot app that comes bundled with
diff --git a/binary_search_tool/ndk/boot_test.sh b/binary_search_tool/ndk/boot_test.sh
index b8c34aa5..0b66ddfa 100755
--- a/binary_search_tool/ndk/boot_test.sh
+++ b/binary_search_tool/ndk/boot_test.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script checks the android device to determine if the app is currently
# running. For our specific test case we will be checking if the Teapot app
diff --git a/binary_search_tool/ndk/get_initial_items.sh b/binary_search_tool/ndk/get_initial_items.sh
index bc2d05cd..5dd3396d 100755
--- a/binary_search_tool/ndk/get_initial_items.sh
+++ b/binary_search_tool/ndk/get_initial_items.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on the Android NDK apps. This script
@@ -9,4 +9,3 @@
#
cat ${BISECT_DIR}/good/_LIST
-
diff --git a/binary_search_tool/ndk/switch_to_good.sh b/binary_search_tool/ndk/switch_to_good.sh
index cb8d5fd9..c98de67c 100755
--- a/binary_search_tool/ndk/switch_to_good.sh
+++ b/binary_search_tool/ndk/switch_to_good.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on Android NDK apps. This script simply
@@ -43,4 +43,3 @@ fi
cat $OBJ_LIST_FILE | xargs rm
exit 0
-
diff --git a/binary_search_tool/ndk/test_setup.sh b/binary_search_tool/ndk/test_setup.sh
index 477bcb21..8f3ce04e 100755
--- a/binary_search_tool/ndk/test_setup.sh
+++ b/binary_search_tool/ndk/test_setup.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This is the setup script for generating and installing the ndk app.
#
diff --git a/binary_search_tool/pass_mapping.py b/binary_search_tool/pass_mapping.py
index 2678fd6d..33c023a9 100644
--- a/binary_search_tool/pass_mapping.py
+++ b/binary_search_tool/pass_mapping.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -15,20 +15,12 @@ pass_name = {
# For users who make local changes to passes, please add a map from pass
# description to newly introduced DebugCounter name for transformation
# level bisection purpose.
- 'Hoist/decompose integer division and remainder':
- 'div-rem-pairs-transform',
- 'Early CSE':
- 'early-cse',
- 'Falkor HW Prefetch Fix Late Phase':
- 'falkor-hwpf',
- 'Combine redundant instructions':
- 'instcombine-visit',
- 'Machine Copy Propagation Pass':
- 'machine-cp-fwd',
- 'Global Value Numbering':
- 'newgvn-phi',
- 'PredicateInfo Printer':
- 'predicateinfo-rename',
- 'SI Insert Waitcnts':
- 'si-insert-waitcnts-forceexp',
+ "Hoist/decompose integer division and remainder": "div-rem-pairs-transform",
+ "Early CSE": "early-cse",
+ "Falkor HW Prefetch Fix Late Phase": "falkor-hwpf",
+ "Combine redundant instructions": "instcombine-visit",
+ "Machine Copy Propagation Pass": "machine-cp-fwd",
+ "Global Value Numbering": "newgvn-phi",
+ "PredicateInfo Printer": "predicateinfo-rename",
+ "SI Insert Waitcnts": "si-insert-waitcnts-forceexp",
}
diff --git a/binary_search_tool/run_bisect.py b/binary_search_tool/run_bisect.py
index 249b9cf5..f54e00e1 100755
--- a/binary_search_tool/run_bisect.py
+++ b/binary_search_tool/run_bisect.py
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""The unified package/object bisecting tool."""
-from __future__ import print_function
import abc
import argparse
@@ -17,293 +16,336 @@ import sys
from binary_search_tool import binary_search_state
from binary_search_tool import common
-
from cros_utils import command_executer
from cros_utils import logger
class Bisector(object, metaclass=abc.ABCMeta):
- """The abstract base class for Bisectors."""
-
- def __init__(self, options, overrides=None):
- """Constructor for Bisector abstract base class
-
- Args:
- options: positional arguments for specific mode (board, remote, etc.)
- overrides: optional dict of overrides for argument defaults
- """
- self.options = options
- self.overrides = overrides
- if not overrides:
- self.overrides = {}
- self.logger = logger.GetLogger()
- self.ce = command_executer.GetCommandExecuter()
-
- def _PrettyPrintArgs(self, args, overrides):
- """Output arguments in a nice, human readable format
-
- Will print and log all arguments for the bisecting tool and make note of
- which arguments have been overridden.
-
- Example output:
- ./run_bisect.py package daisy 172.17.211.184 -I "" -t cros_pkg/my_test.sh
- Performing ChromeOS Package bisection
- Method Config:
- board : daisy
- remote : 172.17.211.184
-
- Bisection Config: (* = overridden)
- get_initial_items : cros_pkg/get_initial_items.sh
- switch_to_good : cros_pkg/switch_to_good.sh
- switch_to_bad : cros_pkg/switch_to_bad.sh
- * test_setup_script :
- * test_script : cros_pkg/my_test.sh
- prune : True
- noincremental : False
- file_args : True
-
- Args:
- args: The args to be given to binary_search_state.Run. This represents
- how the bisection tool will run (with overridden arguments already
- added in).
- overrides: The dict of overriden arguments provided by the user. This is
- provided so the user can be told which arguments were
- overriden and with what value.
- """
- # Output method config (board, remote, etc.)
- options = vars(self.options)
- out = '\nPerforming %s bisection\n' % self.method_name
- out += 'Method Config:\n'
- max_key_len = max([len(str(x)) for x in options.keys()])
- for key in sorted(options):
- val = options[key]
- key_str = str(key).rjust(max_key_len)
- val_str = str(val)
- out += ' %s : %s\n' % (key_str, val_str)
-
- # Output bisection config (scripts, prune, etc.)
- out += '\nBisection Config: (* = overridden)\n'
- max_key_len = max([len(str(x)) for x in args.keys()])
- # Print args in common._ArgsDict order
- args_order = [x['dest'] for x in common.GetArgsDict().values()]
- for key in sorted(args, key=args_order.index):
- val = args[key]
- key_str = str(key).rjust(max_key_len)
- val_str = str(val)
- changed_str = '*' if key in overrides else ' '
-
- out += ' %s %s : %s\n' % (changed_str, key_str, val_str)
-
- out += '\n'
- self.logger.LogOutput(out)
-
- def ArgOverride(self, args, overrides, pretty_print=True):
- """Override arguments based on given overrides and provide nice output
-
- Args:
- args: dict of arguments to be passed to binary_search_state.Run (runs
- dict.update, causing args to be mutated).
- overrides: dict of arguments to update args with
- pretty_print: if True print out args/overrides to user in pretty format
- """
- args.update(overrides)
- if pretty_print:
- self._PrettyPrintArgs(args, overrides)
-
- @abc.abstractmethod
- def PreRun(self):
- pass
-
- @abc.abstractmethod
- def Run(self):
- pass
-
- @abc.abstractmethod
- def PostRun(self):
- pass
+ """The abstract base class for Bisectors."""
+
+ def __init__(self, options, overrides=None):
+ """Constructor for Bisector abstract base class
+
+ Args:
+ options: positional arguments for specific mode (board, remote, etc.)
+ overrides: optional dict of overrides for argument defaults
+ """
+ self.options = options
+ self.overrides = overrides
+ if not overrides:
+ self.overrides = {}
+ self.logger = logger.GetLogger()
+ self.ce = command_executer.GetCommandExecuter()
+
+ def _PrettyPrintArgs(self, args, overrides):
+ """Output arguments in a nice, human readable format
+
+ Will print and log all arguments for the bisecting tool and make note of
+ which arguments have been overridden.
+
+ Example output:
+ ./run_bisect.py package daisy 172.17.211.184 -I "" -t cros_pkg/my_test.sh
+ Performing ChromeOS Package bisection
+ Method Config:
+ board : daisy
+ remote : 172.17.211.184
+
+ Bisection Config: (* = overridden)
+ get_initial_items : cros_pkg/get_initial_items.sh
+ switch_to_good : cros_pkg/switch_to_good.sh
+ switch_to_bad : cros_pkg/switch_to_bad.sh
+ * test_setup_script :
+ * test_script : cros_pkg/my_test.sh
+ prune : True
+ noincremental : False
+ file_args : True
+
+ Args:
+ args: The args to be given to binary_search_state.Run. This represents
+ how the bisection tool will run (with overridden arguments already
+ added in).
+ overrides: The dict of overriden arguments provided by the user. This is
+ provided so the user can be told which arguments were
+ overriden and with what value.
+ """
+ # Output method config (board, remote, etc.)
+ options = vars(self.options)
+ out = "\nPerforming %s bisection\n" % self.method_name
+ out += "Method Config:\n"
+ max_key_len = max([len(str(x)) for x in options.keys()])
+ for key in sorted(options):
+ val = options[key]
+ key_str = str(key).rjust(max_key_len)
+ val_str = str(val)
+ out += " %s : %s\n" % (key_str, val_str)
+
+ # Output bisection config (scripts, prune, etc.)
+ out += "\nBisection Config: (* = overridden)\n"
+ max_key_len = max([len(str(x)) for x in args.keys()])
+ # Print args in common._ArgsDict order
+ args_order = [x["dest"] for x in common.GetArgsDict().values()]
+ for key in sorted(args, key=args_order.index):
+ val = args[key]
+ key_str = str(key).rjust(max_key_len)
+ val_str = str(val)
+ changed_str = "*" if key in overrides else " "
+
+ out += " %s %s : %s\n" % (changed_str, key_str, val_str)
+
+ out += "\n"
+ self.logger.LogOutput(out)
+
+ def ArgOverride(self, args, overrides, pretty_print=True):
+ """Override arguments based on given overrides and provide nice output
+
+ Args:
+ args: dict of arguments to be passed to binary_search_state.Run (runs
+ dict.update, causing args to be mutated).
+ overrides: dict of arguments to update args with
+ pretty_print: if True print out args/overrides to user in pretty format
+ """
+ args.update(overrides)
+ if pretty_print:
+ self._PrettyPrintArgs(args, overrides)
+
+ @abc.abstractmethod
+ def PreRun(self):
+ pass
+
+ @abc.abstractmethod
+ def Run(self):
+ pass
+
+ @abc.abstractmethod
+ def PostRun(self):
+ pass
class BisectPackage(Bisector):
- """The class for package bisection steps."""
-
- cros_pkg_setup = 'cros_pkg/setup.sh'
- cros_pkg_cleanup = 'cros_pkg/%s_cleanup.sh'
-
- def __init__(self, options, overrides):
- super(BisectPackage, self).__init__(options, overrides)
- self.method_name = 'ChromeOS Package'
- self.default_kwargs = {
- 'get_initial_items': 'cros_pkg/get_initial_items.sh',
- 'switch_to_good': 'cros_pkg/switch_to_good.sh',
- 'switch_to_bad': 'cros_pkg/switch_to_bad.sh',
- 'test_setup_script': 'cros_pkg/test_setup.sh',
- 'test_script': 'cros_pkg/interactive_test.sh',
- 'noincremental': False,
- 'prune': True,
- 'file_args': True
- }
- self.setup_cmd = ' '.join(
- (self.cros_pkg_setup, self.options.board, self.options.remote))
- self.ArgOverride(self.default_kwargs, self.overrides)
-
- def PreRun(self):
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- self.setup_cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Package bisector setup failed w/ error %d' % ret)
- return 1
- return 0
-
- def Run(self):
- return binary_search_state.Run(**self.default_kwargs)
-
- def PostRun(self):
- cmd = self.cros_pkg_cleanup % self.options.board
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Package bisector cleanup failed w/ error %d' % ret)
- return 1
-
- self.logger.LogOutput(('Cleanup successful! To restore the bisection '
- 'environment run the following:\n'
- ' cd %s; %s') % (os.getcwd(), self.setup_cmd))
- return 0
+ """The class for package bisection steps."""
+
+ cros_pkg_setup = "cros_pkg/setup.sh"
+ cros_pkg_cleanup = "cros_pkg/%s_cleanup.sh"
+
+ def __init__(self, options, overrides):
+ super(BisectPackage, self).__init__(options, overrides)
+ self.method_name = "ChromeOS Package"
+ self.default_kwargs = {
+ "get_initial_items": "cros_pkg/get_initial_items.sh",
+ "switch_to_good": "cros_pkg/switch_to_good.sh",
+ "switch_to_bad": "cros_pkg/switch_to_bad.sh",
+ "test_setup_script": "cros_pkg/test_setup.sh",
+ "test_script": "cros_pkg/interactive_test.sh",
+ "noincremental": False,
+ "prune": True,
+ "file_args": True,
+ }
+ self.setup_cmd = " ".join(
+ (self.cros_pkg_setup, self.options.board, self.options.remote)
+ )
+ self.ArgOverride(self.default_kwargs, self.overrides)
+
+ def PreRun(self):
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ self.setup_cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Package bisector setup failed w/ error %d" % ret
+ )
+ return 1
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(**self.default_kwargs)
+
+ def PostRun(self):
+ cmd = self.cros_pkg_cleanup % self.options.board
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Package bisector cleanup failed w/ error %d" % ret
+ )
+ return 1
+
+ self.logger.LogOutput(
+ (
+ "Cleanup successful! To restore the bisection "
+ "environment run the following:\n"
+ " cd %s; %s"
+ )
+ % (os.getcwd(), self.setup_cmd)
+ )
+ return 0
class BisectObject(Bisector):
- """The class for object bisection steps."""
-
- sysroot_wrapper_setup = 'sysroot_wrapper/setup.sh'
- sysroot_wrapper_cleanup = 'sysroot_wrapper/cleanup.sh'
-
- def __init__(self, options, overrides):
- super(BisectObject, self).__init__(options, overrides)
- self.method_name = 'ChromeOS Object'
- self.default_kwargs = {
- 'get_initial_items': 'sysroot_wrapper/get_initial_items.sh',
- 'switch_to_good': 'sysroot_wrapper/switch_to_good.sh',
- 'switch_to_bad': 'sysroot_wrapper/switch_to_bad.sh',
- 'test_setup_script': 'sysroot_wrapper/test_setup.sh',
- 'test_script': 'sysroot_wrapper/interactive_test.sh',
- 'noincremental': False,
- 'prune': True,
- 'file_args': True
- }
- self.options = options
- if options.dir:
- os.environ['BISECT_DIR'] = options.dir
- self.options.dir = os.environ.get('BISECT_DIR', '/tmp/sysroot_bisect')
- self.setup_cmd = ' '.join(
- (self.sysroot_wrapper_setup, self.options.board, self.options.remote,
- self.options.package, str(self.options.reboot).lower(),
- shlex.quote(self.options.use_flags)))
-
- self.ArgOverride(self.default_kwargs, overrides)
-
- def PreRun(self):
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- self.setup_cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Object bisector setup failed w/ error %d' % ret)
- return 1
-
- os.environ['BISECT_STAGE'] = 'TRIAGE'
- return 0
-
- def Run(self):
- return binary_search_state.Run(**self.default_kwargs)
-
- def PostRun(self):
- cmd = self.sysroot_wrapper_cleanup
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Object bisector cleanup failed w/ error %d' % ret)
- return 1
- self.logger.LogOutput(('Cleanup successful! To restore the bisection '
- 'environment run the following:\n'
- ' cd %s; %s') % (os.getcwd(), self.setup_cmd))
- return 0
+ """The class for object bisection steps."""
+
+ sysroot_wrapper_setup = "sysroot_wrapper/setup.sh"
+ sysroot_wrapper_cleanup = "sysroot_wrapper/cleanup.sh"
+
+ def __init__(self, options, overrides):
+ super(BisectObject, self).__init__(options, overrides)
+ self.method_name = "ChromeOS Object"
+ self.default_kwargs = {
+ "get_initial_items": "sysroot_wrapper/get_initial_items.sh",
+ "switch_to_good": "sysroot_wrapper/switch_to_good.sh",
+ "switch_to_bad": "sysroot_wrapper/switch_to_bad.sh",
+ "test_setup_script": "sysroot_wrapper/test_setup.sh",
+ "test_script": "sysroot_wrapper/interactive_test.sh",
+ "noincremental": False,
+ "prune": True,
+ "file_args": True,
+ }
+ self.options = options
+ if options.dir:
+ os.environ["BISECT_DIR"] = options.dir
+ self.options.dir = os.environ.get("BISECT_DIR", "/tmp/sysroot_bisect")
+ self.setup_cmd = " ".join(
+ (
+ self.sysroot_wrapper_setup,
+ self.options.board,
+ self.options.remote,
+ self.options.package,
+ str(self.options.reboot).lower(),
+ shlex.quote(self.options.use_flags),
+ )
+ )
+
+ self.ArgOverride(self.default_kwargs, overrides)
+
+ def PreRun(self):
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ self.setup_cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Object bisector setup failed w/ error %d" % ret
+ )
+ return 1
+
+ os.environ["BISECT_STAGE"] = "TRIAGE"
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(**self.default_kwargs)
+
+ def PostRun(self):
+ cmd = self.sysroot_wrapper_cleanup
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Object bisector cleanup failed w/ error %d" % ret
+ )
+ return 1
+ self.logger.LogOutput(
+ (
+ "Cleanup successful! To restore the bisection "
+ "environment run the following:\n"
+ " cd %s; %s"
+ )
+ % (os.getcwd(), self.setup_cmd)
+ )
+ return 0
class BisectAndroid(Bisector):
- """The class for Android bisection steps."""
-
- android_setup = 'android/setup.sh'
- android_cleanup = 'android/cleanup.sh'
- default_dir = os.path.expanduser('~/ANDROID_BISECT')
-
- def __init__(self, options, overrides):
- super(BisectAndroid, self).__init__(options, overrides)
- self.method_name = 'Android'
- self.default_kwargs = {
- 'get_initial_items': 'android/get_initial_items.sh',
- 'switch_to_good': 'android/switch_to_good.sh',
- 'switch_to_bad': 'android/switch_to_bad.sh',
- 'test_setup_script': 'android/test_setup.sh',
- 'test_script': 'android/interactive_test.sh',
- 'prune': True,
- 'file_args': True,
- 'noincremental': False,
- }
- self.options = options
- if options.dir:
- os.environ['BISECT_DIR'] = options.dir
- self.options.dir = os.environ.get('BISECT_DIR', self.default_dir)
-
- num_jobs = "NUM_JOBS='%s'" % self.options.num_jobs
- device_id = ''
- if self.options.device_id:
- device_id = "ANDROID_SERIAL='%s'" % self.options.device_id
-
- self.setup_cmd = ' '.join(
- (num_jobs, device_id, self.android_setup, self.options.android_src))
-
- self.ArgOverride(self.default_kwargs, overrides)
-
- def PreRun(self):
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(
- self.setup_cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Android bisector setup failed w/ error %d' % ret)
- return 1
-
- os.environ['BISECT_STAGE'] = 'TRIAGE'
- return 0
-
- def Run(self):
- return binary_search_state.Run(**self.default_kwargs)
-
- def PostRun(self):
- cmd = self.android_cleanup
- ret, _, _ = self.ce.RunCommandWExceptionCleanup(cmd, print_to_console=True)
- if ret:
- self.logger.LogError('Android bisector cleanup failed w/ error %d' % ret)
- return 1
- self.logger.LogOutput(('Cleanup successful! To restore the bisection '
- 'environment run the following:\n'
- ' cd %s; %s') % (os.getcwd(), self.setup_cmd))
- return 0
+ """The class for Android bisection steps."""
+
+ android_setup = "android/setup.sh"
+ android_cleanup = "android/cleanup.sh"
+ default_dir = os.path.expanduser("~/ANDROID_BISECT")
+
+ def __init__(self, options, overrides):
+ super(BisectAndroid, self).__init__(options, overrides)
+ self.method_name = "Android"
+ self.default_kwargs = {
+ "get_initial_items": "android/get_initial_items.sh",
+ "switch_to_good": "android/switch_to_good.sh",
+ "switch_to_bad": "android/switch_to_bad.sh",
+ "test_setup_script": "android/test_setup.sh",
+ "test_script": "android/interactive_test.sh",
+ "prune": True,
+ "file_args": True,
+ "noincremental": False,
+ }
+ self.options = options
+ if options.dir:
+ os.environ["BISECT_DIR"] = options.dir
+ self.options.dir = os.environ.get("BISECT_DIR", self.default_dir)
+
+ num_jobs = "NUM_JOBS='%s'" % self.options.num_jobs
+ device_id = ""
+ if self.options.device_id:
+ device_id = "ANDROID_SERIAL='%s'" % self.options.device_id
+
+ self.setup_cmd = " ".join(
+ (num_jobs, device_id, self.android_setup, self.options.android_src)
+ )
+
+ self.ArgOverride(self.default_kwargs, overrides)
+
+ def PreRun(self):
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ self.setup_cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Android bisector setup failed w/ error %d" % ret
+ )
+ return 1
+
+ os.environ["BISECT_STAGE"] = "TRIAGE"
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(**self.default_kwargs)
+
+ def PostRun(self):
+ cmd = self.android_cleanup
+ ret, _, _ = self.ce.RunCommandWExceptionCleanup(
+ cmd, print_to_console=True
+ )
+ if ret:
+ self.logger.LogError(
+ "Android bisector cleanup failed w/ error %d" % ret
+ )
+ return 1
+ self.logger.LogOutput(
+ (
+ "Cleanup successful! To restore the bisection "
+ "environment run the following:\n"
+ " cd %s; %s"
+ )
+ % (os.getcwd(), self.setup_cmd)
+ )
+ return 0
def Run(bisector):
- log = logger.GetLogger()
+ log = logger.GetLogger()
- log.LogOutput('Setting up Bisection tool')
- ret = bisector.PreRun()
- if ret:
- return ret
+ log.LogOutput("Setting up Bisection tool")
+ ret = bisector.PreRun()
+ if ret:
+ return ret
- log.LogOutput('Running Bisection tool')
- ret = bisector.Run()
- if ret:
- return ret
+ log.LogOutput("Running Bisection tool")
+ ret = bisector.Run()
+ if ret:
+ return ret
- log.LogOutput('Cleaning up Bisection tool')
- ret = bisector.PostRun()
- if ret:
- return ret
+ log.LogOutput("Cleaning up Bisection tool")
+ ret = bisector.PostRun()
+ if ret:
+ return ret
- return 0
+ return 0
_HELP_EPILOG = """
@@ -318,92 +360,113 @@ See below for full override argument reference:
def Main(argv):
- override_parser = argparse.ArgumentParser(
- add_help=False,
- argument_default=argparse.SUPPRESS,
- usage='run_bisect.py {mode} [options]')
- common.BuildArgParser(override_parser, override=True)
-
- epilog = _HELP_EPILOG + override_parser.format_help()
- parser = argparse.ArgumentParser(
- epilog=epilog, formatter_class=RawTextHelpFormatter)
- subparsers = parser.add_subparsers(
- title='Bisect mode',
- description=('Which bisection method to '
- 'use. Each method has '
- 'specific setup and '
- 'arguments. Please consult '
- 'the README for more '
- 'information.'))
-
- parser_package = subparsers.add_parser('package')
- parser_package.add_argument('board', help='Board to target')
- parser_package.add_argument('remote', help='Remote machine to test on')
- parser_package.set_defaults(handler=BisectPackage)
-
- parser_object = subparsers.add_parser('object')
- parser_object.add_argument('board', help='Board to target')
- parser_object.add_argument('remote', help='Remote machine to test on')
- parser_object.add_argument('package', help='Package to emerge and test')
- parser_object.add_argument(
- '--use_flags',
- required=False,
- default='',
- help='Use flags passed to emerge')
- parser_object.add_argument(
- '--noreboot',
- action='store_false',
- dest='reboot',
- help='Do not reboot after updating the package (default: False)')
- parser_object.add_argument(
- '--dir',
- help=('Bisection directory to use, sets '
- '$BISECT_DIR if provided. Defaults to '
- 'current value of $BISECT_DIR (or '
- '/tmp/sysroot_bisect if $BISECT_DIR is '
- 'empty).'))
- parser_object.set_defaults(handler=BisectObject)
-
- parser_android = subparsers.add_parser('android')
- parser_android.add_argument('android_src', help='Path to android source tree')
- parser_android.add_argument(
- '--dir',
- help=('Bisection directory to use, sets '
- '$BISECT_DIR if provided. Defaults to '
- 'current value of $BISECT_DIR (or '
- '~/ANDROID_BISECT/ if $BISECT_DIR is '
- 'empty).'))
- parser_android.add_argument(
- '-j',
- '--num_jobs',
- type=int,
- default=1,
- help=('Number of jobs that make and various '
- 'scripts for bisector can spawn. Setting '
- 'this value too high can freeze up your '
- 'machine!'))
- parser_android.add_argument(
- '--device_id',
- default='',
- help=('Device id for device used for testing. '
- 'Use this if you have multiple Android '
- 'devices plugged into your machine.'))
- parser_android.set_defaults(handler=BisectAndroid)
-
- options, remaining = parser.parse_known_args(argv)
- if remaining:
- overrides = override_parser.parse_args(remaining)
- overrides = vars(overrides)
- else:
- overrides = {}
-
- subcmd = options.handler
- del options.handler
-
- bisector = subcmd(options, overrides)
- return Run(bisector)
-
-
-if __name__ == '__main__':
- os.chdir(os.path.dirname(__file__))
- sys.exit(Main(sys.argv[1:]))
+ override_parser = argparse.ArgumentParser(
+ add_help=False,
+ argument_default=argparse.SUPPRESS,
+ usage="run_bisect.py {mode} [options]",
+ )
+ common.BuildArgParser(override_parser, override=True)
+
+ epilog = _HELP_EPILOG + override_parser.format_help()
+ parser = argparse.ArgumentParser(
+ epilog=epilog, formatter_class=RawTextHelpFormatter
+ )
+ subparsers = parser.add_subparsers(
+ title="Bisect mode",
+ description=(
+ "Which bisection method to "
+ "use. Each method has "
+ "specific setup and "
+ "arguments. Please consult "
+ "the README for more "
+ "information."
+ ),
+ )
+
+ parser_package = subparsers.add_parser("package")
+ parser_package.add_argument("board", help="Board to target")
+ parser_package.add_argument("remote", help="Remote machine to test on")
+ parser_package.set_defaults(handler=BisectPackage)
+
+ parser_object = subparsers.add_parser("object")
+ parser_object.add_argument("board", help="Board to target")
+ parser_object.add_argument("remote", help="Remote machine to test on")
+ parser_object.add_argument("package", help="Package to emerge and test")
+ parser_object.add_argument(
+ "--use_flags",
+ required=False,
+ default="",
+ help="Use flags passed to emerge",
+ )
+ parser_object.add_argument(
+ "--noreboot",
+ action="store_false",
+ dest="reboot",
+ help="Do not reboot after updating the package (default: False)",
+ )
+ parser_object.add_argument(
+ "--dir",
+ help=(
+ "Bisection directory to use, sets "
+ "$BISECT_DIR if provided. Defaults to "
+ "current value of $BISECT_DIR (or "
+ "/tmp/sysroot_bisect if $BISECT_DIR is "
+ "empty)."
+ ),
+ )
+ parser_object.set_defaults(handler=BisectObject)
+
+ parser_android = subparsers.add_parser("android")
+ parser_android.add_argument(
+ "android_src", help="Path to android source tree"
+ )
+ parser_android.add_argument(
+ "--dir",
+ help=(
+ "Bisection directory to use, sets "
+ "$BISECT_DIR if provided. Defaults to "
+ "current value of $BISECT_DIR (or "
+ "~/ANDROID_BISECT/ if $BISECT_DIR is "
+ "empty)."
+ ),
+ )
+ parser_android.add_argument(
+ "-j",
+ "--num_jobs",
+ type=int,
+ default=1,
+ help=(
+ "Number of jobs that make and various "
+ "scripts for bisector can spawn. Setting "
+ "this value too high can freeze up your "
+ "machine!"
+ ),
+ )
+ parser_android.add_argument(
+ "--device_id",
+ default="",
+ help=(
+ "Device id for device used for testing. "
+ "Use this if you have multiple Android "
+ "devices plugged into your machine."
+ ),
+ )
+ parser_android.set_defaults(handler=BisectAndroid)
+
+ options, remaining = parser.parse_known_args(argv)
+ if remaining:
+ overrides = override_parser.parse_args(remaining)
+ overrides = vars(overrides)
+ else:
+ overrides = {}
+
+ subcmd = options.handler
+ del options.handler
+
+ bisector = subcmd(options, overrides)
+ return Run(bisector)
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(__file__))
+ sys.exit(Main(sys.argv[1:]))
diff --git a/binary_search_tool/run_bisect_tests.py b/binary_search_tool/run_bisect_tests.py
index 9172d678..ca7077d3 100755
--- a/binary_search_tool/run_bisect_tests.py
+++ b/binary_search_tool/run_bisect_tests.py
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run full bisection test."""
-from __future__ import print_function
import argparse
import os
@@ -14,81 +13,88 @@ import sys
from cros_utils import command_executer
-TEST_DIR = 'full_bisect_test'
-DEFAULT_BISECT_DIR = '/tmp/sysroot_bisect'
+TEST_DIR = "full_bisect_test"
+DEFAULT_BISECT_DIR = "/tmp/sysroot_bisect"
-def populate_good_files(top_dir, ce, bisect_dir=DEFAULT_BISECT_DIR):
- # 'make clean'
- work_dir = os.path.join(top_dir, TEST_DIR, 'work')
- cmd = 'rm -f %s/*.o' % work_dir
- status = ce.RunCommand(cmd)
- if status != 0:
- print('Error trying to clean out work directory: %s' % cmd)
- return status
- # set up the 'good' source files
- script = os.path.join(top_dir, TEST_DIR, 'make_sources_good.sh')
- status = ce.RunCommand(script)
- if status != 0:
- print('Error setting up "good" source files: %s' % script)
+def populate_good_files(top_dir, ce, bisect_dir=DEFAULT_BISECT_DIR):
+ # 'make clean'
+ work_dir = os.path.join(top_dir, TEST_DIR, "work")
+ cmd = "rm -f %s/*.o" % work_dir
+ status = ce.RunCommand(cmd)
+ if status != 0:
+ print("Error trying to clean out work directory: %s" % cmd)
+ return status
+
+ # set up the 'good' source files
+ script = os.path.join(top_dir, TEST_DIR, "make_sources_good.sh")
+ status = ce.RunCommand(script)
+ if status != 0:
+ print('Error setting up "good" source files: %s' % script)
+ return status
+
+ export_bisect = "export BISECT_DIR=%s; " % bisect_dir
+ # build the good source files
+ script_path = os.path.join(top_dir, TEST_DIR)
+ if os.path.exists("/usr/bin/x86_64-cros-linux-gnu-gcc"):
+ build_script = "chromeos_build.sh"
+ else:
+ build_script = "build.sh"
+ cmd = "%s export BISECT_STAGE=POPULATE_GOOD; pushd %s; ./%s; popd" % (
+ export_bisect,
+ script_path,
+ build_script,
+ )
+ status = ce.RunCommand(cmd)
return status
- export_bisect = 'export BISECT_DIR=%s; ' % bisect_dir
- # build the good source files
- script_path = os.path.join(top_dir, TEST_DIR)
- if os.path.exists('/usr/bin/x86_64-cros-linux-gnu-gcc'):
- build_script = 'chromeos_build.sh'
- else:
- build_script = 'build.sh'
- cmd = ('%s export BISECT_STAGE=POPULATE_GOOD; pushd %s; ./%s; popd' %
- (export_bisect, script_path, build_script))
- status = ce.RunCommand(cmd)
- return status
-
def populate_bad_files(top_dir, ce, bisect_dir=DEFAULT_BISECT_DIR):
- # 'make clean'
- work_dir = os.path.join(top_dir, TEST_DIR, 'work')
- cmd = 'rm -f %s/*.o' % work_dir
- status = ce.RunCommand(cmd)
- if status != 0:
- print('Error trying to clean out work directory: %s' % cmd)
+ # 'make clean'
+ work_dir = os.path.join(top_dir, TEST_DIR, "work")
+ cmd = "rm -f %s/*.o" % work_dir
+ status = ce.RunCommand(cmd)
+ if status != 0:
+ print("Error trying to clean out work directory: %s" % cmd)
+ return status
+
+ # set up the 'bad' source files
+ script = os.path.join(top_dir, TEST_DIR, "make_sources_bad.sh")
+ status = ce.RunCommand(script)
+ if status != 0:
+ print('Error setting up "bad" source files: %s' % script)
+ return status
+
+ export_bisect = "export BISECT_DIR=%s; " % bisect_dir
+ # build the bad source files
+ script_path = os.path.join(top_dir, TEST_DIR)
+ if os.path.exists("/usr/bin/x86_64-cros-linux-gnu-gcc"):
+ build_script = "chromeos_build.sh"
+ else:
+ build_script = "build.sh"
+ cmd = "%s export BISECT_STAGE=POPULATE_BAD; pushd %s; ./%s ; popd" % (
+ export_bisect,
+ script_path,
+ build_script,
+ )
+ status = ce.RunCommand(cmd)
return status
- # set up the 'bad' source files
- script = os.path.join(top_dir, TEST_DIR, 'make_sources_bad.sh')
- status = ce.RunCommand(script)
- if status != 0:
- print('Error setting up "bad" source files: %s' % script)
- return status
-
- export_bisect = 'export BISECT_DIR=%s; ' % bisect_dir
- # build the bad source files
- script_path = os.path.join(top_dir, TEST_DIR)
- if os.path.exists('/usr/bin/x86_64-cros-linux-gnu-gcc'):
- build_script = 'chromeos_build.sh'
- else:
- build_script = 'build.sh'
- cmd = ('%s export BISECT_STAGE=POPULATE_BAD; pushd %s; ./%s ; popd' %
- (export_bisect, script_path, build_script))
- status = ce.RunCommand(cmd)
- return status
-
def run_main_bisection_test(top_dir, ce):
- test_script = os.path.join(top_dir, TEST_DIR, 'main-bisect-test.sh')
- status = ce.RunCommand(test_script)
- return status
+ test_script = os.path.join(top_dir, TEST_DIR, "main-bisect-test.sh")
+ status = ce.RunCommand(test_script)
+ return status
def verify_compiler_and_wrapper():
- # We don't need to do any special setup if running inside a ChromeOS
- # chroot.
- if os.path.exists('/usr/bin/x86_64-cros-linux-gnu-gcc'):
- return True
+ # We don't need to do any special setup if running inside a ChromeOS
+ # chroot.
+ if os.path.exists("/usr/bin/x86_64-cros-linux-gnu-gcc"):
+ return True
- message = """
+ message = """
*** IMPORTANT --- READ THIS CAREFULLY!! ***
This test uses the command 'gcc' to compile the good/bad versions of the
@@ -100,78 +106,93 @@ thing".
Is your compiler wrapper properly set up? [Y/n]
"""
- print(message)
- inp = sys.stdin.readline()
- inp = inp.strip()
- inp = inp.lower()
- return not inp or inp == 'y' or inp == 'yes'
+ print(message)
+ inp = sys.stdin.readline()
+ inp = inp.strip()
+ inp = inp.lower()
+ return not inp or inp == "y" or inp == "yes"
def Main(argv):
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--dir',
- dest='directory',
- help='Bisection work tree, where good & bad object '
- 'files go. Default is /tmp/sysroot_bisect')
-
- options = parser.parse_args(argv)
-
- # Make sure the compiler wrapper & soft links are properly set up.
- wrapper_is_setup = verify_compiler_and_wrapper()
- if not wrapper_is_setup:
- print('Exiting now. Please re-run after you have set up the compiler '
- 'wrapper.')
- return 0
-
- # Make sure we're in the correct directory for running this test.
- cwd = os.getcwd()
- if not os.path.exists(os.path.join(cwd, 'full_bisect_test')):
- print('Error: Wrong directory. This script must be run from the top level'
- ' of the binary_search_tool tree (under toolchain_utils).')
- return 1
-
- ce = command_executer.GetCommandExecuter()
- bisect_dir = options.directory
- if not bisect_dir:
- bisect_dir = DEFAULT_BISECT_DIR
-
- # Make sure BISECT_DIR is clean
- if os.path.exists(bisect_dir):
- cmd = 'rm -Rf %s/*' % bisect_dir
- retv = ce.RunCommand(cmd)
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "--dir",
+ dest="directory",
+ help="Bisection work tree, where good & bad object "
+ "files go. Default is /tmp/sysroot_bisect",
+ )
+
+ options = parser.parse_args(argv)
+
+ # Make sure the compiler wrapper & soft links are properly set up.
+ wrapper_is_setup = verify_compiler_and_wrapper()
+ if not wrapper_is_setup:
+ print(
+ "Exiting now. Please re-run after you have set up the compiler "
+ "wrapper."
+ )
+ return 0
+
+ # Make sure we're in the correct directory for running this test.
+ cwd = os.getcwd()
+ if not os.path.exists(os.path.join(cwd, "full_bisect_test")):
+ print(
+ "Error: Wrong directory. This script must be run from the top level"
+ " of the binary_search_tool tree (under toolchain_utils)."
+ )
+ return 1
+
+ ce = command_executer.GetCommandExecuter()
+ bisect_dir = options.directory
+ if not bisect_dir:
+ bisect_dir = DEFAULT_BISECT_DIR
+
+ # Make sure BISECT_DIR is clean
+ if os.path.exists(bisect_dir):
+ cmd = "rm -Rf %s/*" % bisect_dir
+ retv = ce.RunCommand(cmd)
+ if retv != 0:
+ return retv
+
+ retv = populate_good_files(cwd, ce, bisect_dir)
if retv != 0:
- return retv
-
- retv = populate_good_files(cwd, ce, bisect_dir)
- if retv != 0:
- return retv
+ return retv
- retv = populate_bad_files(cwd, ce, bisect_dir)
- if retv != 0:
+ retv = populate_bad_files(cwd, ce, bisect_dir)
+ if retv != 0:
+ return retv
+
+ # Set up good/bad work soft links
+ cmd = "rm -f %s/%s/good-objects; ln -s %s/good %s/%s/good-objects" % (
+ cwd,
+ TEST_DIR,
+ bisect_dir,
+ cwd,
+ TEST_DIR,
+ )
+
+ status = ce.RunCommand(cmd)
+ if status != 0:
+ print("Error executing: %s; exiting now." % cmd)
+ return status
+
+ cmd = "rm -f %s/%s/bad-objects; ln -s %s/bad %s/%s/bad-objects" % (
+ cwd,
+ TEST_DIR,
+ bisect_dir,
+ cwd,
+ TEST_DIR,
+ )
+
+ status = ce.RunCommand(cmd)
+ if status != 0:
+ print("Error executing: %s; exiting now." % cmd)
+ return status
+
+ retv = run_main_bisection_test(cwd, ce)
return retv
- # Set up good/bad work soft links
- cmd = ('rm -f %s/%s/good-objects; ln -s %s/good %s/%s/good-objects' %
- (cwd, TEST_DIR, bisect_dir, cwd, TEST_DIR))
-
- status = ce.RunCommand(cmd)
- if status != 0:
- print('Error executing: %s; exiting now.' % cmd)
- return status
-
- cmd = ('rm -f %s/%s/bad-objects; ln -s %s/bad %s/%s/bad-objects' %
- (cwd, TEST_DIR, bisect_dir, cwd, TEST_DIR))
-
- status = ce.RunCommand(cmd)
- if status != 0:
- print('Error executing: %s; exiting now.' % cmd)
- return status
-
- retv = run_main_bisection_test(cwd, ce)
- return retv
-
-if __name__ == '__main__':
- retval = Main(sys.argv[1:])
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv[1:])
+ sys.exit(retval)
diff --git a/binary_search_tool/sysroot_wrapper/cleanup.sh b/binary_search_tool/sysroot_wrapper/cleanup.sh
index 5066d638..b3ae2dd9 100755
--- a/binary_search_tool/sysroot_wrapper/cleanup.sh
+++ b/binary_search_tool/sysroot_wrapper/cleanup.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2016 Google Inc. All Rights Reserved.
+# Copyright 2016 Google LLC
#
# This script is part of the ChromeOS object binary search triage process.
# It should be the last script called by the user, after the user has
diff --git a/binary_search_tool/sysroot_wrapper/interactive_test_host.sh b/binary_search_tool/sysroot_wrapper/interactive_test_host.sh
index 58adffc0..bd84936c 100755
--- a/binary_search_tool/sysroot_wrapper/interactive_test_host.sh
+++ b/binary_search_tool/sysroot_wrapper/interactive_test_host.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2017 Google Inc. All Rights Reserved.
+# Copyright 2017 Google LLC
#
# This script is intended to be used by binary_search_state.py, as
# part of the binary search triage on ChromeOS package and object files for a
diff --git a/binary_search_tool/sysroot_wrapper/setup.sh b/binary_search_tool/sysroot_wrapper/setup.sh
index 6b9b48f1..f9ecb0ea 100755
--- a/binary_search_tool/sysroot_wrapper/setup.sh
+++ b/binary_search_tool/sysroot_wrapper/setup.sh
@@ -1,6 +1,6 @@
#!/bin/bash -u
#
-# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Copyright 2021 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
diff --git a/binary_search_tool/sysroot_wrapper/test_setup_host.sh b/binary_search_tool/sysroot_wrapper/test_setup_host.sh
index b5169eee..e61bc367 100755
--- a/binary_search_tool/sysroot_wrapper/test_setup_host.sh
+++ b/binary_search_tool/sysroot_wrapper/test_setup_host.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2017 Google Inc. All Rights Reserved.
+# Copyright 2017 Google LLC
#
# This is a generic ChromeOS package/image test setup script. It is meant to
# be used for either the object file or package bisection tools. This script
diff --git a/binary_search_tool/sysroot_wrapper/testing_test.py b/binary_search_tool/sysroot_wrapper/testing_test.py
index b5ceec1f..af884be9 100755
--- a/binary_search_tool/sysroot_wrapper/testing_test.py
+++ b/binary_search_tool/sysroot_wrapper/testing_test.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -11,32 +11,35 @@ chromeos-chrome built for a daisy board, if you are using another package you
will need to change the base_path accordingly.
"""
-from __future__ import print_function
import subprocess
import sys
import os
-base_path = ('/var/cache/chromeos-chrome/chrome-src-internal/src/out_daisy/'
- 'Release/obj/')
+base_path = (
+ "/var/cache/chromeos-chrome/chrome-src-internal/src/out_daisy/"
+ "Release/obj/"
+)
bad_files = [
- os.path.join(base_path, 'base/base.cpu.o'),
- os.path.join(base_path, 'base/base.version.o'),
- os.path.join(base_path, 'apps/apps.launcher.o')
+ os.path.join(base_path, "base/base.cpu.o"),
+ os.path.join(base_path, "base/base.version.o"),
+ os.path.join(base_path, "apps/apps.launcher.o"),
]
-bisect_dir = os.environ.get('BISECT_DIR', '/tmp/sysroot_bisect')
+bisect_dir = os.environ.get("BISECT_DIR", "/tmp/sysroot_bisect")
def Main(_):
- for test_file in bad_files:
- test_file = test_file.strip()
- cmd = ['grep', test_file, os.path.join(bisect_dir, 'BAD_SET')]
- ret = subprocess.call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- if not ret:
- return 1
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(Main(sys.argv[1:]))
+ for test_file in bad_files:
+ test_file = test_file.strip()
+ cmd = ["grep", test_file, os.path.join(bisect_dir, "BAD_SET")]
+ ret = subprocess.call(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ )
+ if not ret:
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(Main(sys.argv[1:]))
diff --git a/binary_search_tool/test/__init__.py b/binary_search_tool/test/__init__.py
index 76500def..6e3ade4a 100644
--- a/binary_search_tool/test/__init__.py
+++ b/binary_search_tool/test/__init__.py
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
diff --git a/binary_search_tool/test/binary_search_tool_test.py b/binary_search_tool/test/binary_search_tool_test.py
index 6f5b514e..a79c9a1d 100755
--- a/binary_search_tool/test/binary_search_tool_test.py
+++ b/binary_search_tool/test/binary_search_tool_test.py
@@ -1,15 +1,13 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Tests for bisecting tool."""
-from __future__ import division
-from __future__ import print_function
-__author__ = 'shenhan@google.com (Han Shen)'
+__author__ = "shenhan@google.com (Han Shen)"
import os
import random
@@ -25,545 +23,597 @@ from binary_search_tool.test import gen_obj
def GenObj():
- obj_num = random.randint(100, 1000)
- bad_obj_num = random.randint(obj_num // 100, obj_num // 20)
- if bad_obj_num == 0:
- bad_obj_num = 1
- gen_obj.Main(['--obj_num', str(obj_num), '--bad_obj_num', str(bad_obj_num)])
+ obj_num = random.randint(100, 1000)
+ bad_obj_num = random.randint(obj_num // 100, obj_num // 20)
+ if bad_obj_num == 0:
+ bad_obj_num = 1
+ gen_obj.Main(["--obj_num", str(obj_num), "--bad_obj_num", str(bad_obj_num)])
def CleanObj():
- os.remove(common.OBJECTS_FILE)
- os.remove(common.WORKING_SET_FILE)
- print('Deleted "{0}" and "{1}"'.format(common.OBJECTS_FILE,
- common.WORKING_SET_FILE))
+ os.remove(common.OBJECTS_FILE)
+ os.remove(common.WORKING_SET_FILE)
+ print(
+ 'Deleted "{0}" and "{1}"'.format(
+ common.OBJECTS_FILE, common.WORKING_SET_FILE
+ )
+ )
class BisectTest(unittest.TestCase):
- """Tests for run_bisect.py"""
-
- def setUp(self):
- with open('./is_setup', 'w', encoding='utf-8'):
- pass
-
- try:
- os.remove(binary_search_state.STATE_FILE)
- except OSError:
- pass
-
- def tearDown(self):
- try:
- os.remove('./is_setup')
- os.remove(os.readlink(binary_search_state.STATE_FILE))
- os.remove(binary_search_state.STATE_FILE)
- except OSError:
- pass
-
- class FullBisector(run_bisect.Bisector):
- """Test bisector to test run_bisect.py with"""
-
- def __init__(self, options, overrides):
- super(BisectTest.FullBisector, self).__init__(options, overrides)
-
- def PreRun(self):
- GenObj()
- return 0
-
- def Run(self):
- return binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True)
-
- def PostRun(self):
- CleanObj()
- return 0
-
- def test_full_bisector(self):
- ret = run_bisect.Run(self.FullBisector({}, {}))
- self.assertEqual(ret, 0)
- self.assertFalse(os.path.exists(common.OBJECTS_FILE))
- self.assertFalse(os.path.exists(common.WORKING_SET_FILE))
-
- def check_output(self):
- _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
- ('grep "Bad items are: " logs/binary_search_tool_test.py.out | '
- 'tail -n1'))
- ls = out.splitlines()
- self.assertEqual(len(ls), 1)
- line = ls[0]
-
- _, _, bad_ones = line.partition('Bad items are: ')
- bad_ones = bad_ones.split()
- expected_result = common.ReadObjectsFile()
-
- # Reconstruct objects file from bad_ones and compare
- actual_result = [0] * len(expected_result)
- for bad_obj in bad_ones:
- actual_result[int(bad_obj)] = 1
-
- self.assertEqual(actual_result, expected_result)
+ """Tests for run_bisect.py"""
+
+ def setUp(self):
+ with open("./is_setup", "w", encoding="utf-8"):
+ pass
+
+ try:
+ os.remove(binary_search_state.STATE_FILE)
+ except OSError:
+ pass
+
+ def tearDown(self):
+ try:
+ os.remove("./is_setup")
+ os.remove(os.readlink(binary_search_state.STATE_FILE))
+ os.remove(binary_search_state.STATE_FILE)
+ except OSError:
+ pass
+
+ class FullBisector(run_bisect.Bisector):
+ """Test bisector to test run_bisect.py with"""
+
+ def __init__(self, options, overrides):
+ super(BisectTest.FullBisector, self).__init__(options, overrides)
+
+ def PreRun(self):
+ GenObj()
+ return 0
+
+ def Run(self):
+ return binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ )
+
+ def PostRun(self):
+ CleanObj()
+ return 0
+
+ def test_full_bisector(self):
+ ret = run_bisect.Run(self.FullBisector({}, {}))
+ self.assertEqual(ret, 0)
+ self.assertFalse(os.path.exists(common.OBJECTS_FILE))
+ self.assertFalse(os.path.exists(common.WORKING_SET_FILE))
+
+ def check_output(self):
+ _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
+ (
+ 'grep "Bad items are: " logs/binary_search_tool_test.py.out | '
+ "tail -n1"
+ )
+ )
+ ls = out.splitlines()
+ self.assertEqual(len(ls), 1)
+ line = ls[0]
+
+ _, _, bad_ones = line.partition("Bad items are: ")
+ bad_ones = bad_ones.split()
+ expected_result = common.ReadObjectsFile()
+
+ # Reconstruct objects file from bad_ones and compare
+ actual_result = [0] * len(expected_result)
+ for bad_obj in bad_ones:
+ actual_result[int(bad_obj)] = 1
+
+ self.assertEqual(actual_result, expected_result)
class BisectingUtilsTest(unittest.TestCase):
- """Tests for bisecting tool."""
-
- def setUp(self):
- """Generate [100-1000] object files, and 1-5% of which are bad ones."""
- GenObj()
-
- with open('./is_setup', 'w', encoding='utf-8'):
- pass
-
- try:
- os.remove(binary_search_state.STATE_FILE)
- except OSError:
- pass
-
- def tearDown(self):
- """Cleanup temp files."""
- CleanObj()
-
- try:
- os.remove(os.readlink(binary_search_state.STATE_FILE))
- except OSError:
- pass
-
- cleanup_list = [
- './is_setup', binary_search_state.STATE_FILE, 'noinc_prune_bad',
- 'noinc_prune_good', './cmd_script.sh'
- ]
- for f in cleanup_list:
- if os.path.exists(f):
- os.remove(f)
-
- def runTest(self):
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True)
- self.assertEqual(ret, 0)
- self.check_output()
-
- def test_arg_parse(self):
- args = [
- '--get_initial_items', './gen_init_list.py', '--switch_to_good',
- './switch_to_good.py', '--switch_to_bad', './switch_to_bad.py',
- '--test_script', './is_good.py', '--prune', '--file_args'
- ]
- ret = binary_search_state.Main(args)
- self.assertEqual(ret, 0)
- self.check_output()
-
- def test_test_setup_script(self):
- os.remove('./is_setup')
- with self.assertRaises(AssertionError):
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True)
-
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- test_setup_script='./test_setup.py',
- prune=True,
- file_args=True)
- self.assertEqual(ret, 0)
- self.check_output()
-
- def test_bad_test_setup_script(self):
- with self.assertRaises(AssertionError):
- binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- test_setup_script='./test_setup_bad.py',
- prune=True,
- file_args=True)
-
- def test_bad_save_state(self):
- state_file = binary_search_state.STATE_FILE
- hidden_state_file = os.path.basename(binary_search_state.HIDDEN_STATE_FILE)
-
- with open(state_file, 'w', encoding='utf-8') as f:
- f.write('test123')
-
- bss = binary_search_state.MockBinarySearchState()
- with self.assertRaises(OSError):
- bss.SaveState()
-
- with open(state_file, 'r', encoding='utf-8') as f:
- self.assertEqual(f.read(), 'test123')
-
- os.remove(state_file)
-
- # Cleanup generated save state that has no symlink
- files = os.listdir(os.getcwd())
- save_states = [x for x in files if x.startswith(hidden_state_file)]
- _ = [os.remove(x) for x in save_states]
-
- def test_save_state(self):
- state_file = binary_search_state.STATE_FILE
-
- bss = binary_search_state.MockBinarySearchState()
- bss.SaveState()
- self.assertTrue(os.path.exists(state_file))
- first_state = os.readlink(state_file)
-
- bss.SaveState()
- second_state = os.readlink(state_file)
- self.assertTrue(os.path.exists(state_file))
- self.assertTrue(second_state != first_state)
- self.assertFalse(os.path.exists(first_state))
-
- bss.RemoveState()
- self.assertFalse(os.path.islink(state_file))
- self.assertFalse(os.path.exists(second_state))
-
- def test_load_state(self):
- test_items = [1, 2, 3, 4, 5]
-
- bss = binary_search_state.MockBinarySearchState()
- bss.all_items = test_items
- bss.currently_good_items = set([1, 2, 3])
- bss.currently_bad_items = set([4, 5])
- bss.SaveState()
-
- bss = None
-
- bss2 = binary_search_state.MockBinarySearchState.LoadState()
- self.assertEqual(bss2.all_items, test_items)
- self.assertEqual(bss2.currently_good_items, set([]))
- self.assertEqual(bss2.currently_bad_items, set([]))
-
- def test_tmp_cleanup(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='echo "0\n1\n2\n3"',
- switch_to_good='./switch_tmp.py',
- file_args=True)
- bss.SwitchToGood(['0', '1', '2', '3'])
-
- tmp_file = None
- with open('tmp_file', 'r', encoding='utf-8') as f:
- tmp_file = f.read()
- os.remove('tmp_file')
-
- self.assertFalse(os.path.exists(tmp_file))
- ws = common.ReadWorkingSet()
- for i in range(3):
- self.assertEqual(ws[i], 42)
-
- def test_verify_fail(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_bad.py',
- switch_to_bad='./switch_to_good.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True,
- verify=True)
- with self.assertRaises(AssertionError):
- bss.DoVerify()
-
- def test_early_terminate(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True,
- iterations=1)
- bss.DoSearchBadItems()
- self.assertFalse(bss.found_items)
-
- def test_no_prune(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- test_setup_script='./test_setup.py',
- prune=False,
- file_args=True)
- bss.DoSearchBadItems()
- self.assertEqual(len(bss.found_items), 1)
-
- bad_objs = common.ReadObjectsFile()
- found_obj = int(bss.found_items.pop())
- self.assertEqual(bad_objs[found_obj], 1)
-
- def test_set_file(self):
- binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good_set_file.py',
- switch_to_bad='./switch_to_bad_set_file.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True,
- verify=True)
- self.check_output()
-
- def test_noincremental_prune(self):
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good_noinc_prune.py',
- switch_to_bad='./switch_to_bad_noinc_prune.py',
- test_script='./is_good_noinc_prune.py',
- test_setup_script='./test_setup.py',
- prune=True,
- noincremental=True,
- file_args=True,
- verify=False)
- self.assertEqual(ret, 0)
- self.check_output()
-
- def check_output(self):
- _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
- ('grep "Bad items are: " logs/binary_search_tool_test.py.out | '
- 'tail -n1'))
- ls = out.splitlines()
- self.assertEqual(len(ls), 1)
- line = ls[0]
-
- _, _, bad_ones = line.partition('Bad items are: ')
- bad_ones = bad_ones.split()
- expected_result = common.ReadObjectsFile()
-
- # Reconstruct objects file from bad_ones and compare
- actual_result = [0] * len(expected_result)
- for bad_obj in bad_ones:
- actual_result[int(bad_obj)] = 1
-
- self.assertEqual(actual_result, expected_result)
+ """Tests for bisecting tool."""
+
+ def setUp(self):
+ """Generate [100-1000] object files, and 1-5% of which are bad ones."""
+ GenObj()
+
+ with open("./is_setup", "w", encoding="utf-8"):
+ pass
+
+ try:
+ os.remove(binary_search_state.STATE_FILE)
+ except OSError:
+ pass
+
+ def tearDown(self):
+ """Cleanup temp files."""
+ CleanObj()
+
+ try:
+ os.remove(os.readlink(binary_search_state.STATE_FILE))
+ except OSError:
+ pass
+
+ cleanup_list = [
+ "./is_setup",
+ binary_search_state.STATE_FILE,
+ "noinc_prune_bad",
+ "noinc_prune_good",
+ "./cmd_script.sh",
+ ]
+ for f in cleanup_list:
+ if os.path.exists(f):
+ os.remove(f)
+
+ def runTest(self):
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ )
+ self.assertEqual(ret, 0)
+ self.check_output()
+
+ def test_arg_parse(self):
+ args = [
+ "--get_initial_items",
+ "./gen_init_list.py",
+ "--switch_to_good",
+ "./switch_to_good.py",
+ "--switch_to_bad",
+ "./switch_to_bad.py",
+ "--test_script",
+ "./is_good.py",
+ "--prune",
+ "--file_args",
+ ]
+ ret = binary_search_state.Main(args)
+ self.assertEqual(ret, 0)
+ self.check_output()
+
+ def test_test_setup_script(self):
+ os.remove("./is_setup")
+ with self.assertRaises(AssertionError):
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ )
+
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ test_setup_script="./test_setup.py",
+ prune=True,
+ file_args=True,
+ )
+ self.assertEqual(ret, 0)
+ self.check_output()
+
+ def test_bad_test_setup_script(self):
+ with self.assertRaises(AssertionError):
+ binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ test_setup_script="./test_setup_bad.py",
+ prune=True,
+ file_args=True,
+ )
+
+ def test_bad_save_state(self):
+ state_file = binary_search_state.STATE_FILE
+ hidden_state_file = os.path.basename(
+ binary_search_state.HIDDEN_STATE_FILE
+ )
+
+ with open(state_file, "w", encoding="utf-8") as f:
+ f.write("test123")
+
+ bss = binary_search_state.MockBinarySearchState()
+ with self.assertRaises(OSError):
+ bss.SaveState()
+
+ with open(state_file, "r", encoding="utf-8") as f:
+ self.assertEqual(f.read(), "test123")
+
+ os.remove(state_file)
+
+ # Cleanup generated save state that has no symlink
+ files = os.listdir(os.getcwd())
+ save_states = [x for x in files if x.startswith(hidden_state_file)]
+ _ = [os.remove(x) for x in save_states]
+
+ def test_save_state(self):
+ state_file = binary_search_state.STATE_FILE
+
+ bss = binary_search_state.MockBinarySearchState()
+ bss.SaveState()
+ self.assertTrue(os.path.exists(state_file))
+ first_state = os.readlink(state_file)
+
+ bss.SaveState()
+ second_state = os.readlink(state_file)
+ self.assertTrue(os.path.exists(state_file))
+ self.assertTrue(second_state != first_state)
+ self.assertFalse(os.path.exists(first_state))
+
+ bss.RemoveState()
+ self.assertFalse(os.path.islink(state_file))
+ self.assertFalse(os.path.exists(second_state))
+
+ def test_load_state(self):
+ test_items = [1, 2, 3, 4, 5]
+
+ bss = binary_search_state.MockBinarySearchState()
+ bss.all_items = test_items
+ bss.currently_good_items = set([1, 2, 3])
+ bss.currently_bad_items = set([4, 5])
+ bss.SaveState()
+
+ bss = None
+
+ bss2 = binary_search_state.MockBinarySearchState.LoadState()
+ self.assertEqual(bss2.all_items, test_items)
+ self.assertEqual(bss2.currently_good_items, set([]))
+ self.assertEqual(bss2.currently_bad_items, set([]))
+
+ def test_tmp_cleanup(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items='echo "0\n1\n2\n3"',
+ switch_to_good="./switch_tmp.py",
+ file_args=True,
+ )
+ bss.SwitchToGood(["0", "1", "2", "3"])
+
+ tmp_file = None
+ with open("tmp_file", "r", encoding="utf-8") as f:
+ tmp_file = f.read()
+ os.remove("tmp_file")
+
+ self.assertFalse(os.path.exists(tmp_file))
+ ws = common.ReadWorkingSet()
+ for i in range(3):
+ self.assertEqual(ws[i], 42)
+
+ def test_verify_fail(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_bad.py",
+ switch_to_bad="./switch_to_good.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ verify=True,
+ )
+ with self.assertRaises(AssertionError):
+ bss.DoVerify()
+
+ def test_early_terminate(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ iterations=1,
+ )
+ bss.DoSearchBadItems()
+ self.assertFalse(bss.found_items)
+
+ def test_no_prune(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ test_setup_script="./test_setup.py",
+ prune=False,
+ file_args=True,
+ )
+ bss.DoSearchBadItems()
+ self.assertEqual(len(bss.found_items), 1)
+
+ bad_objs = common.ReadObjectsFile()
+ found_obj = int(bss.found_items.pop())
+ self.assertEqual(bad_objs[found_obj], 1)
+
+ def test_set_file(self):
+ binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good_set_file.py",
+ switch_to_bad="./switch_to_bad_set_file.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ verify=True,
+ )
+ self.check_output()
+
+ def test_noincremental_prune(self):
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good_noinc_prune.py",
+ switch_to_bad="./switch_to_bad_noinc_prune.py",
+ test_script="./is_good_noinc_prune.py",
+ test_setup_script="./test_setup.py",
+ prune=True,
+ noincremental=True,
+ file_args=True,
+ verify=False,
+ )
+ self.assertEqual(ret, 0)
+ self.check_output()
+
+ def check_output(self):
+ _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
+ (
+ 'grep "Bad items are: " logs/binary_search_tool_test.py.out | '
+ "tail -n1"
+ )
+ )
+ ls = out.splitlines()
+ self.assertEqual(len(ls), 1)
+ line = ls[0]
+
+ _, _, bad_ones = line.partition("Bad items are: ")
+ bad_ones = bad_ones.split()
+ expected_result = common.ReadObjectsFile()
+
+ # Reconstruct objects file from bad_ones and compare
+ actual_result = [0] * len(expected_result)
+ for bad_obj in bad_ones:
+ actual_result[int(bad_obj)] = 1
+
+ self.assertEqual(actual_result, expected_result)
class BisectingUtilsPassTest(BisectingUtilsTest):
- """Tests for bisecting tool at pass/transformation level."""
-
- def check_pass_output(self, pass_name, pass_num, trans_num):
- _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
- ('grep "Bad pass: " logs/binary_search_tool_test.py.out | '
- 'tail -n1'))
- ls = out.splitlines()
- self.assertEqual(len(ls), 1)
- line = ls[0]
- _, _, bad_info = line.partition('Bad pass: ')
- actual_info = pass_name + ' at number ' + str(pass_num)
- self.assertEqual(actual_info, bad_info)
-
- _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
- ('grep "Bad transformation number: '
- '" logs/binary_search_tool_test.py.out | '
- 'tail -n1'))
- ls = out.splitlines()
- self.assertEqual(len(ls), 1)
- line = ls[0]
- _, _, bad_info = line.partition('Bad transformation number: ')
- actual_info = str(trans_num)
- self.assertEqual(actual_info, bad_info)
-
- def test_with_prune(self):
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=True,
- file_args=True)
- self.assertEqual(ret, 1)
-
- def test_gen_cmd_script(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=False,
- file_args=True)
- bss.DoSearchBadItems()
- cmd_script_path = bss.cmd_script
- self.assertTrue(os.path.exists(cmd_script_path))
-
- def test_no_pass_support(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=False,
- file_args=True)
- bss.cmd_script = './cmd_script_no_support.py'
- # No support for -opt-bisect-limit
- with self.assertRaises(RuntimeError):
- bss.BuildWithPassLimit(-1)
-
- def test_no_transform_support(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=False,
- file_args=True)
- bss.cmd_script = './cmd_script_no_support.py'
- # No support for -print-debug-counter
- with self.assertRaises(RuntimeError):
- bss.BuildWithTransformLimit(-1, 'counter_name')
-
- def test_pass_transform_bisect(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=False,
- file_args=True)
- pass_num = 4
- trans_num = 19
- bss.cmd_script = './cmd_script.py %d %d' % (pass_num, trans_num)
- bss.DoSearchBadPass()
- self.check_pass_output('instcombine-visit', pass_num, trans_num)
-
- def test_result_not_reproduced_pass(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=False,
- file_args=True)
- # Fails reproducing at pass level.
- pass_num = 0
- trans_num = 19
- bss.cmd_script = './cmd_script.py %d %d' % (pass_num, trans_num)
- with self.assertRaises(ValueError):
- bss.DoSearchBadPass()
-
- def test_result_not_reproduced_transform(self):
- bss = binary_search_state.MockBinarySearchState(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- pass_bisect='./generate_cmd.py',
- prune=False,
- file_args=True)
- # Fails reproducing at transformation level.
- pass_num = 4
- trans_num = 0
- bss.cmd_script = './cmd_script.py %d %d' % (pass_num, trans_num)
- with self.assertRaises(ValueError):
- bss.DoSearchBadPass()
+ """Tests for bisecting tool at pass/transformation level."""
+
+ def check_pass_output(self, pass_name, pass_num, trans_num):
+ _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
+ (
+ 'grep "Bad pass: " logs/binary_search_tool_test.py.out | '
+ "tail -n1"
+ )
+ )
+ ls = out.splitlines()
+ self.assertEqual(len(ls), 1)
+ line = ls[0]
+ _, _, bad_info = line.partition("Bad pass: ")
+ actual_info = pass_name + " at number " + str(pass_num)
+ self.assertEqual(actual_info, bad_info)
+
+ _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
+ (
+ 'grep "Bad transformation number: '
+ '" logs/binary_search_tool_test.py.out | '
+ "tail -n1"
+ )
+ )
+ ls = out.splitlines()
+ self.assertEqual(len(ls), 1)
+ line = ls[0]
+ _, _, bad_info = line.partition("Bad transformation number: ")
+ actual_info = str(trans_num)
+ self.assertEqual(actual_info, bad_info)
+
+ def test_with_prune(self):
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=True,
+ file_args=True,
+ )
+ self.assertEqual(ret, 1)
+
+ def test_gen_cmd_script(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=False,
+ file_args=True,
+ )
+ bss.DoSearchBadItems()
+ cmd_script_path = bss.cmd_script
+ self.assertTrue(os.path.exists(cmd_script_path))
+
+ def test_no_pass_support(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=False,
+ file_args=True,
+ )
+ bss.cmd_script = "./cmd_script_no_support.py"
+ # No support for -opt-bisect-limit
+ with self.assertRaises(RuntimeError):
+ bss.BuildWithPassLimit(-1)
+
+ def test_no_transform_support(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=False,
+ file_args=True,
+ )
+ bss.cmd_script = "./cmd_script_no_support.py"
+ # No support for -print-debug-counter
+ with self.assertRaises(RuntimeError):
+ bss.BuildWithTransformLimit(-1, "counter_name")
+
+ def test_pass_transform_bisect(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=False,
+ file_args=True,
+ )
+ pass_num = 4
+ trans_num = 19
+ bss.cmd_script = "./cmd_script.py %d %d" % (pass_num, trans_num)
+ bss.DoSearchBadPass()
+ self.check_pass_output("instcombine-visit", pass_num, trans_num)
+
+ def test_result_not_reproduced_pass(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=False,
+ file_args=True,
+ )
+ # Fails reproducing at pass level.
+ pass_num = 0
+ trans_num = 19
+ bss.cmd_script = "./cmd_script.py %d %d" % (pass_num, trans_num)
+ with self.assertRaises(ValueError):
+ bss.DoSearchBadPass()
+
+ def test_result_not_reproduced_transform(self):
+ bss = binary_search_state.MockBinarySearchState(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ pass_bisect="./generate_cmd.py",
+ prune=False,
+ file_args=True,
+ )
+ # Fails reproducing at transformation level.
+ pass_num = 4
+ trans_num = 0
+ bss.cmd_script = "./cmd_script.py %d %d" % (pass_num, trans_num)
+ with self.assertRaises(ValueError):
+ bss.DoSearchBadPass()
class BisectStressTest(unittest.TestCase):
- """Stress tests for bisecting tool."""
-
- def test_every_obj_bad(self):
- amt = 25
- gen_obj.Main(['--obj_num', str(amt), '--bad_obj_num', str(amt)])
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True,
- verify=False)
- self.assertEqual(ret, 0)
- self.check_output()
-
- def test_every_index_is_bad(self):
- amt = 25
- for i in range(amt):
- obj_list = ['0'] * amt
- obj_list[i] = '1'
- obj_list = ','.join(obj_list)
- gen_obj.Main(['--obj_list', obj_list])
- ret = binary_search_state.Run(
- get_initial_items='./gen_init_list.py',
- switch_to_good='./switch_to_good.py',
- switch_to_bad='./switch_to_bad.py',
- test_setup_script='./test_setup.py',
- test_script='./is_good.py',
- prune=True,
- file_args=True)
- self.assertEqual(ret, 0)
- self.check_output()
-
- def check_output(self):
- _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
- ('grep "Bad items are: " logs/binary_search_tool_test.py.out | '
- 'tail -n1'))
- ls = out.splitlines()
- self.assertEqual(len(ls), 1)
- line = ls[0]
-
- _, _, bad_ones = line.partition('Bad items are: ')
- bad_ones = bad_ones.split()
- expected_result = common.ReadObjectsFile()
-
- # Reconstruct objects file from bad_ones and compare
- actual_result = [0] * len(expected_result)
- for bad_obj in bad_ones:
- actual_result[int(bad_obj)] = 1
-
- self.assertEqual(actual_result, expected_result)
+ """Stress tests for bisecting tool."""
+
+ def test_every_obj_bad(self):
+ amt = 25
+ gen_obj.Main(["--obj_num", str(amt), "--bad_obj_num", str(amt)])
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ verify=False,
+ )
+ self.assertEqual(ret, 0)
+ self.check_output()
+
+ def test_every_index_is_bad(self):
+ amt = 25
+ for i in range(amt):
+ obj_list = ["0"] * amt
+ obj_list[i] = "1"
+ obj_list = ",".join(obj_list)
+ gen_obj.Main(["--obj_list", obj_list])
+ ret = binary_search_state.Run(
+ get_initial_items="./gen_init_list.py",
+ switch_to_good="./switch_to_good.py",
+ switch_to_bad="./switch_to_bad.py",
+ test_setup_script="./test_setup.py",
+ test_script="./is_good.py",
+ prune=True,
+ file_args=True,
+ )
+ self.assertEqual(ret, 0)
+ self.check_output()
+
+ def check_output(self):
+ _, out, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
+ (
+ 'grep "Bad items are: " logs/binary_search_tool_test.py.out | '
+ "tail -n1"
+ )
+ )
+ ls = out.splitlines()
+ self.assertEqual(len(ls), 1)
+ line = ls[0]
+
+ _, _, bad_ones = line.partition("Bad items are: ")
+ bad_ones = bad_ones.split()
+ expected_result = common.ReadObjectsFile()
+
+ # Reconstruct objects file from bad_ones and compare
+ actual_result = [0] * len(expected_result)
+ for bad_obj in bad_ones:
+ actual_result[int(bad_obj)] = 1
+
+ self.assertEqual(actual_result, expected_result)
def Main(argv):
- num_tests = 2
- if len(argv) > 1:
- num_tests = int(argv[1])
-
- suite = unittest.TestSuite()
- for _ in range(0, num_tests):
- suite.addTest(BisectingUtilsTest())
- suite.addTest(BisectingUtilsTest('test_arg_parse'))
- suite.addTest(BisectingUtilsTest('test_test_setup_script'))
- suite.addTest(BisectingUtilsTest('test_bad_test_setup_script'))
- suite.addTest(BisectingUtilsTest('test_bad_save_state'))
- suite.addTest(BisectingUtilsTest('test_save_state'))
- suite.addTest(BisectingUtilsTest('test_load_state'))
- suite.addTest(BisectingUtilsTest('test_tmp_cleanup'))
- suite.addTest(BisectingUtilsTest('test_verify_fail'))
- suite.addTest(BisectingUtilsTest('test_early_terminate'))
- suite.addTest(BisectingUtilsTest('test_no_prune'))
- suite.addTest(BisectingUtilsTest('test_set_file'))
- suite.addTest(BisectingUtilsTest('test_noincremental_prune'))
- suite.addTest(BisectingUtilsPassTest('test_with_prune'))
- suite.addTest(BisectingUtilsPassTest('test_gen_cmd_script'))
- suite.addTest(BisectingUtilsPassTest('test_no_pass_support'))
- suite.addTest(BisectingUtilsPassTest('test_no_transform_support'))
- suite.addTest(BisectingUtilsPassTest('test_pass_transform_bisect'))
- suite.addTest(BisectingUtilsPassTest('test_result_not_reproduced_pass'))
- suite.addTest(BisectingUtilsPassTest('test_result_not_reproduced_transform'))
- suite.addTest(BisectTest('test_full_bisector'))
- suite.addTest(BisectStressTest('test_every_obj_bad'))
- suite.addTest(BisectStressTest('test_every_index_is_bad'))
- runner = unittest.TextTestRunner()
- runner.run(suite)
-
-
-if __name__ == '__main__':
- Main(sys.argv)
+ num_tests = 2
+ if len(argv) > 1:
+ num_tests = int(argv[1])
+
+ suite = unittest.TestSuite()
+ for _ in range(0, num_tests):
+ suite.addTest(BisectingUtilsTest())
+ suite.addTest(BisectingUtilsTest("test_arg_parse"))
+ suite.addTest(BisectingUtilsTest("test_test_setup_script"))
+ suite.addTest(BisectingUtilsTest("test_bad_test_setup_script"))
+ suite.addTest(BisectingUtilsTest("test_bad_save_state"))
+ suite.addTest(BisectingUtilsTest("test_save_state"))
+ suite.addTest(BisectingUtilsTest("test_load_state"))
+ suite.addTest(BisectingUtilsTest("test_tmp_cleanup"))
+ suite.addTest(BisectingUtilsTest("test_verify_fail"))
+ suite.addTest(BisectingUtilsTest("test_early_terminate"))
+ suite.addTest(BisectingUtilsTest("test_no_prune"))
+ suite.addTest(BisectingUtilsTest("test_set_file"))
+ suite.addTest(BisectingUtilsTest("test_noincremental_prune"))
+ suite.addTest(BisectingUtilsPassTest("test_with_prune"))
+ suite.addTest(BisectingUtilsPassTest("test_gen_cmd_script"))
+ suite.addTest(BisectingUtilsPassTest("test_no_pass_support"))
+ suite.addTest(BisectingUtilsPassTest("test_no_transform_support"))
+ suite.addTest(BisectingUtilsPassTest("test_pass_transform_bisect"))
+ suite.addTest(BisectingUtilsPassTest("test_result_not_reproduced_pass"))
+ suite.addTest(
+ BisectingUtilsPassTest("test_result_not_reproduced_transform")
+ )
+ suite.addTest(BisectTest("test_full_bisector"))
+ suite.addTest(BisectStressTest("test_every_obj_bad"))
+ suite.addTest(BisectStressTest("test_every_index_is_bad"))
+ runner = unittest.TextTestRunner()
+ runner.run(suite)
+
+
+if __name__ == "__main__":
+ Main(sys.argv)
diff --git a/binary_search_tool/test/cmd_script.py b/binary_search_tool/test/cmd_script.py
index bfd56052..b0475c70 100755
--- a/binary_search_tool/test/cmd_script.py
+++ b/binary_search_tool/test/cmd_script.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -11,7 +11,6 @@ It assumes that -opt-bisect-limit and -print-debug-counter are supported by the
compiler.
"""
-from __future__ import print_function
import os
import sys
@@ -20,57 +19,62 @@ from binary_search_tool.test import common
def Main(argv):
- if not os.path.exists('./is_setup'):
- return 1
-
- if len(argv) != 3:
- return 1
-
- limit_flags = os.environ['LIMIT_FLAGS']
- opt_bisect_exist = False
- debug_counter_exist = False
-
- for option in limit_flags.split():
- if '-opt-bisect-limit' in option:
- opt_bisect_limit = int(option.split('=')[-1])
- opt_bisect_exist = True
- if '-debug-counter=' in option:
- debug_counter = int(option.split('=')[-1])
- debug_counter_exist = True
-
- if not opt_bisect_exist:
- return 1
-
- # Manually set total number and bad number
- total_pass = 10
- total_transform = 20
- bad_pass = int(argv[1])
- bad_transform = int(argv[2])
-
- if opt_bisect_limit == -1:
- opt_bisect_limit = total_pass
-
- for i in range(1, total_pass + 1):
- bisect_str = 'BISECT: %srunning pass (%d) Combine redundant ' \
- 'instructions on function (f1)' \
- % ('NOT ' if i > opt_bisect_limit else '', i)
- print(bisect_str, file=sys.stderr)
-
- if debug_counter_exist:
- print('Counters and values:', file=sys.stderr)
- print(
- 'instcombine-visit : {%d, 0, %d}' % (total_transform, debug_counter),
- file=sys.stderr)
-
- if opt_bisect_limit > bad_pass or \
- (debug_counter_exist and debug_counter > bad_transform):
- common.WriteWorkingSet([1])
- else:
- common.WriteWorkingSet([0])
-
- return 0
-
-
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+ if not os.path.exists("./is_setup"):
+ return 1
+
+ if len(argv) != 3:
+ return 1
+
+ limit_flags = os.environ["LIMIT_FLAGS"]
+ opt_bisect_exist = False
+ debug_counter_exist = False
+
+ for option in limit_flags.split():
+ if "-opt-bisect-limit" in option:
+ opt_bisect_limit = int(option.split("=")[-1])
+ opt_bisect_exist = True
+ if "-debug-counter=" in option:
+ debug_counter = int(option.split("=")[-1])
+ debug_counter_exist = True
+
+ if not opt_bisect_exist:
+ return 1
+
+ # Manually set total number and bad number
+ total_pass = 10
+ total_transform = 20
+ bad_pass = int(argv[1])
+ bad_transform = int(argv[2])
+
+ if opt_bisect_limit == -1:
+ opt_bisect_limit = total_pass
+
+ for i in range(1, total_pass + 1):
+ bisect_str = (
+ "BISECT: %srunning pass (%d) Combine redundant "
+ "instructions on function (f1)"
+ % ("NOT " if i > opt_bisect_limit else "", i)
+ )
+ print(bisect_str, file=sys.stderr)
+
+ if debug_counter_exist:
+ print("Counters and values:", file=sys.stderr)
+ print(
+ "instcombine-visit : {%d, 0, %d}"
+ % (total_transform, debug_counter),
+ file=sys.stderr,
+ )
+
+ if opt_bisect_limit > bad_pass or (
+ debug_counter_exist and debug_counter > bad_transform
+ ):
+ common.WriteWorkingSet([1])
+ else:
+ common.WriteWorkingSet([0])
+
+ return 0
+
+
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/cmd_script_no_support.py b/binary_search_tool/test/cmd_script_no_support.py
index badbedc8..f1c2bcbe 100644
--- a/binary_search_tool/test/cmd_script_no_support.py
+++ b/binary_search_tool/test/cmd_script_no_support.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -9,21 +9,21 @@ This script generates a pseudo log when certain bisecting flags are not
supported by compiler.
"""
-from __future__ import print_function
import os
import sys
def Main():
- if not os.path.exists('./is_setup'):
- return 1
- print(
- 'No support for -opt-bisect-limit or -print-debug-counter.',
- file=sys.stderr)
- return 0
-
-
-if __name__ == '__main__':
- retval = Main()
- sys.exit(retval)
+ if not os.path.exists("./is_setup"):
+ return 1
+ print(
+ "No support for -opt-bisect-limit or -print-debug-counter.",
+ file=sys.stderr,
+ )
+ return 0
+
+
+if __name__ == "__main__":
+ retval = Main()
+ sys.exit(retval)
diff --git a/binary_search_tool/test/common.py b/binary_search_tool/test/common.py
index cf5300f5..6632a4c7 100755
--- a/binary_search_tool/test/common.py
+++ b/binary_search_tool/test/common.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -8,35 +8,35 @@
DEFAULT_OBJECT_NUMBER = 1238
DEFAULT_BAD_OBJECT_NUMBER = 23
-OBJECTS_FILE = 'objects.txt'
-WORKING_SET_FILE = 'working_set.txt'
+OBJECTS_FILE = "objects.txt"
+WORKING_SET_FILE = "working_set.txt"
def ReadWorkingSet():
- working_set = []
- with open(WORKING_SET_FILE, 'r', encoding='utf-8') as f:
- for l in f:
- working_set.append(int(l))
- return working_set
+ working_set = []
+ with open(WORKING_SET_FILE, "r", encoding="utf-8") as f:
+ for l in f:
+ working_set.append(int(l))
+ return working_set
def WriteWorkingSet(working_set):
- with open(WORKING_SET_FILE, 'w', encoding='utf-8') as f:
- for o in working_set:
- f.write('{0}\n'.format(o))
+ with open(WORKING_SET_FILE, "w", encoding="utf-8") as f:
+ for o in working_set:
+ f.write("{0}\n".format(o))
def ReadObjectsFile():
- objects_file = []
- with open(OBJECTS_FILE, 'r', encoding='utf-8') as f:
- for l in f:
- objects_file.append(int(l))
- return objects_file
+ objects_file = []
+ with open(OBJECTS_FILE, "r", encoding="utf-8") as f:
+ for l in f:
+ objects_file.append(int(l))
+ return objects_file
def ReadObjectIndex(filename):
- object_index = []
- with open(filename, 'r', encoding='utf-8') as f:
- for o in f:
- object_index.append(int(o))
- return object_index
+ object_index = []
+ with open(filename, "r", encoding="utf-8") as f:
+ for o in f:
+ object_index.append(int(o))
+ return object_index
diff --git a/binary_search_tool/test/gen_init_list.py b/binary_search_tool/test/gen_init_list.py
index bc5dd8fe..138e949c 100755
--- a/binary_search_tool/test/gen_init_list.py
+++ b/binary_search_tool/test/gen_init_list.py
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Prints out index for every object file, starting from 0."""
-from __future__ import print_function
import sys
@@ -15,13 +14,14 @@ from binary_search_tool.test import common
def Main():
- ce = command_executer.GetCommandExecuter()
- _, l, _ = ce.RunCommandWOutput(
- 'cat {0} | wc -l'.format(common.OBJECTS_FILE), print_to_console=False)
- for i in range(0, int(l)):
- print(i)
+ ce = command_executer.GetCommandExecuter()
+ _, l, _ = ce.RunCommandWOutput(
+ "cat {0} | wc -l".format(common.OBJECTS_FILE), print_to_console=False
+ )
+ for i in range(0, int(l)):
+ print(i)
-if __name__ == '__main__':
- Main()
- sys.exit(0)
+if __name__ == "__main__":
+ Main()
+ sys.exit(0)
diff --git a/binary_search_tool/test/gen_obj.py b/binary_search_tool/test/gen_obj.py
index 4f65c71b..394445f0 100755
--- a/binary_search_tool/test/gen_obj.py
+++ b/binary_search_tool/test/gen_obj.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -10,7 +10,6 @@
1 represents a bad object file.
"""
-from __future__ import print_function
import argparse
import os
@@ -21,81 +20,91 @@ from binary_search_tool.test import common
def Main(argv):
- """Generates a list, the value of each element is 0 or 1.
-
- The number of 1s in the list is specified by bad_obj_num.
- The others are all 0s. The total number of 0s and 1s is specified by obj_num.
-
- Args:
- argv: argument from command line
-
- Returns:
- 0 always.
- """
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '-n',
- '--obj_num',
- dest='obj_num',
- default=common.DEFAULT_OBJECT_NUMBER,
- help=('Number of total objects.'))
- parser.add_argument(
- '-b',
- '--bad_obj_num',
- dest='bad_obj_num',
- default=common.DEFAULT_BAD_OBJECT_NUMBER,
- help=('Number of bad objects. Must be great than or '
- 'equal to zero and less than total object '
- 'number.'))
- parser.add_argument(
- '-o',
- '--obj_list',
- dest='obj_list',
- default='',
- help=('List of comma seperated objects to generate. '
- 'A 0 means the object is good, a 1 means the '
- 'object is bad.'))
- options = parser.parse_args(argv)
-
- obj_num = int(options.obj_num)
- bad_obj_num = int(options.bad_obj_num)
- bad_to_gen = int(options.bad_obj_num)
- obj_list = options.obj_list
- if not obj_list:
- obj_list = []
- for i in range(obj_num):
- if bad_to_gen > 0 and random.randint(1, obj_num) <= bad_obj_num:
- obj_list.append(1)
- bad_to_gen -= 1
- else:
- obj_list.append(0)
- while bad_to_gen > 0:
- t = random.randint(0, obj_num - 1)
- if obj_list[t] == 0:
- obj_list[t] = 1
- bad_to_gen -= 1
- else:
- obj_list = obj_list.split(',')
-
- if os.path.isfile(common.OBJECTS_FILE):
- os.remove(common.OBJECTS_FILE)
- if os.path.isfile(common.WORKING_SET_FILE):
- os.remove(common.WORKING_SET_FILE)
-
- with open(common.OBJECTS_FILE, 'w', encoding='utf-8') as f:
- with open(common.WORKING_SET_FILE, 'w', encoding='utf-8') as w:
- for i in obj_list:
- f.write('{0}\n'.format(i))
- w.write('{0}\n'.format(i))
-
- obj_num = len(obj_list)
- bad_obj_num = obj_list.count(1)
- print('Generated {0} object files, with {1} bad ones.'.format(
- obj_num, bad_obj_num))
-
- return 0
-
-
-if __name__ == '__main__':
- retval = Main(sys.argv[1:])
- sys.exit(retval)
+ """Generates a list, the value of each element is 0 or 1.
+
+ The number of 1s in the list is specified by bad_obj_num.
+ The others are all 0s. The total number of 0s and 1s is specified by obj_num.
+
+ Args:
+ argv: argument from command line
+
+ Returns:
+ 0 always.
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-n",
+ "--obj_num",
+ dest="obj_num",
+ default=common.DEFAULT_OBJECT_NUMBER,
+ help=("Number of total objects."),
+ )
+ parser.add_argument(
+ "-b",
+ "--bad_obj_num",
+ dest="bad_obj_num",
+ default=common.DEFAULT_BAD_OBJECT_NUMBER,
+ help=(
+ "Number of bad objects. Must be great than or "
+ "equal to zero and less than total object "
+ "number."
+ ),
+ )
+ parser.add_argument(
+ "-o",
+ "--obj_list",
+ dest="obj_list",
+ default="",
+ help=(
+ "List of comma seperated objects to generate. "
+ "A 0 means the object is good, a 1 means the "
+ "object is bad."
+ ),
+ )
+ options = parser.parse_args(argv)
+
+ obj_num = int(options.obj_num)
+ bad_obj_num = int(options.bad_obj_num)
+ bad_to_gen = int(options.bad_obj_num)
+ obj_list = options.obj_list
+ if not obj_list:
+ obj_list = []
+ for i in range(obj_num):
+ if bad_to_gen > 0 and random.randint(1, obj_num) <= bad_obj_num:
+ obj_list.append(1)
+ bad_to_gen -= 1
+ else:
+ obj_list.append(0)
+ while bad_to_gen > 0:
+ t = random.randint(0, obj_num - 1)
+ if obj_list[t] == 0:
+ obj_list[t] = 1
+ bad_to_gen -= 1
+ else:
+ obj_list = obj_list.split(",")
+
+ if os.path.isfile(common.OBJECTS_FILE):
+ os.remove(common.OBJECTS_FILE)
+ if os.path.isfile(common.WORKING_SET_FILE):
+ os.remove(common.WORKING_SET_FILE)
+
+ with open(common.OBJECTS_FILE, "w", encoding="utf-8") as f:
+ with open(common.WORKING_SET_FILE, "w", encoding="utf-8") as w:
+ for i in obj_list:
+ f.write("{0}\n".format(i))
+ w.write("{0}\n".format(i))
+
+ obj_num = len(obj_list)
+ bad_obj_num = obj_list.count(1)
+ print(
+ "Generated {0} object files, with {1} bad ones.".format(
+ obj_num, bad_obj_num
+ )
+ )
+
+ return 0
+
+
+if __name__ == "__main__":
+ retval = Main(sys.argv[1:])
+ sys.exit(retval)
diff --git a/binary_search_tool/test/generate_cmd.py b/binary_search_tool/test/generate_cmd.py
index 51b36b0a..96fa720c 100755
--- a/binary_search_tool/test/generate_cmd.py
+++ b/binary_search_tool/test/generate_cmd.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -10,21 +10,20 @@ This is a required argument for pass level bisecting. For unit test, we use
this script to verify if cmd_script.sh is generated correctly.
"""
-from __future__ import print_function
import os
import sys
def Main():
- if not os.path.exists('./is_setup'):
- return 1
- file_name = 'cmd_script.sh'
- with open(file_name, 'w', encoding='utf-8') as f:
- f.write('Generated by generate_cmd.py')
- return 0
+ if not os.path.exists("./is_setup"):
+ return 1
+ file_name = "cmd_script.sh"
+ with open(file_name, "w", encoding="utf-8") as f:
+ f.write("Generated by generate_cmd.py")
+ return 0
-if __name__ == '__main__':
- retval = Main()
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main()
+ sys.exit(retval)
diff --git a/binary_search_tool/test/is_good.py b/binary_search_tool/test/is_good.py
index 662921e8..fd3f908f 100755
--- a/binary_search_tool/test/is_good.py
+++ b/binary_search_tool/test/is_good.py
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Check to see if the working set produces a good executable."""
-from __future__ import print_function
import os
import sys
@@ -15,15 +14,15 @@ from binary_search_tool.test import common
def Main():
- if not os.path.exists('./is_setup'):
- return 1
- working_set = common.ReadWorkingSet()
- for w in working_set:
- if w == 1:
- return 1 ## False, linking failure
- return 0
-
-
-if __name__ == '__main__':
- retval = Main()
- sys.exit(retval)
+ if not os.path.exists("./is_setup"):
+ return 1
+ working_set = common.ReadWorkingSet()
+ for w in working_set:
+ if w == 1:
+ return 1 ## False, linking failure
+ return 0
+
+
+if __name__ == "__main__":
+ retval = Main()
+ sys.exit(retval)
diff --git a/binary_search_tool/test/is_good_noinc_prune.py b/binary_search_tool/test/is_good_noinc_prune.py
index c0e42bb1..654fcd25 100755
--- a/binary_search_tool/test/is_good_noinc_prune.py
+++ b/binary_search_tool/test/is_good_noinc_prune.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -12,7 +12,6 @@ to the switch scripts is equals to the actual number of items (i.e. checking
that noincremental always holds).
"""
-from __future__ import print_function
import os
import sys
@@ -21,31 +20,31 @@ from binary_search_tool.test import common
def Main():
- working_set = common.ReadWorkingSet()
+ working_set = common.ReadWorkingSet()
- with open('noinc_prune_good', 'r', encoding='utf-8') as good_args:
- num_good_args = len(good_args.readlines())
+ with open("noinc_prune_good", "r", encoding="utf-8") as good_args:
+ num_good_args = len(good_args.readlines())
- with open('noinc_prune_bad', 'r', encoding='utf-8') as bad_args:
- num_bad_args = len(bad_args.readlines())
+ with open("noinc_prune_bad", "r", encoding="utf-8") as bad_args:
+ num_bad_args = len(bad_args.readlines())
- num_args = num_good_args + num_bad_args
- if num_args != len(working_set):
- print('Only %d args, expected %d' % (num_args, len(working_set)))
- print('%d good args, %d bad args' % (num_good_args, num_bad_args))
- return 3
+ num_args = num_good_args + num_bad_args
+ if num_args != len(working_set):
+ print("Only %d args, expected %d" % (num_args, len(working_set)))
+ print("%d good args, %d bad args" % (num_good_args, num_bad_args))
+ return 3
- os.remove('noinc_prune_bad')
- os.remove('noinc_prune_good')
+ os.remove("noinc_prune_bad")
+ os.remove("noinc_prune_good")
- if not os.path.exists('./is_setup'):
- return 1
- for w in working_set:
- if w == 1:
- return 1 ## False, linking failure
- return 0
+ if not os.path.exists("./is_setup"):
+ return 1
+ for w in working_set:
+ if w == 1:
+ return 1 ## False, linking failure
+ return 0
-if __name__ == '__main__':
- retval = Main()
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main()
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_tmp.py b/binary_search_tool/test/switch_tmp.py
index 0f3c4234..acc0393d 100755
--- a/binary_search_tool/test/switch_tmp.py
+++ b/binary_search_tool/test/switch_tmp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -12,7 +12,6 @@ this script) content. Every line in the file is an object index, which will be
set to good (mark as 42).
"""
-from __future__ import print_function
import sys
@@ -20,20 +19,20 @@ from binary_search_tool.test import common
def Main(argv):
- working_set = common.ReadWorkingSet()
- object_index = common.ReadObjectIndex(argv[1])
+ working_set = common.ReadWorkingSet()
+ object_index = common.ReadObjectIndex(argv[1])
- # Random number so the results can be checked
- for oi in object_index:
- working_set[int(oi)] = 42
+ # Random number so the results can be checked
+ for oi in object_index:
+ working_set[int(oi)] = 42
- common.WriteWorkingSet(working_set)
- with open('tmp_file', 'w', encoding='utf-8') as f:
- f.write(argv[1])
+ common.WriteWorkingSet(working_set)
+ with open("tmp_file", "w", encoding="utf-8") as f:
+ f.write(argv[1])
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_to_bad.py b/binary_search_tool/test/switch_to_bad.py
index e3553eb6..bc32f3cc 100755
--- a/binary_search_tool/test/switch_to_bad.py
+++ b/binary_search_tool/test/switch_to_bad.py
@@ -1,12 +1,11 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Switch part of the objects file in working set to (possible) bad ones."""
-from __future__ import print_function
import sys
@@ -14,19 +13,19 @@ from binary_search_tool.test import common
def Main(argv):
- """Switch part of the objects file in working set to (possible) bad ones."""
- working_set = common.ReadWorkingSet()
- objects_file = common.ReadObjectsFile()
- object_index = common.ReadObjectIndex(argv[1])
+ """Switch part of the objects file in working set to (possible) bad ones."""
+ working_set = common.ReadWorkingSet()
+ objects_file = common.ReadObjectsFile()
+ object_index = common.ReadObjectIndex(argv[1])
- for oi in object_index:
- working_set[oi] = objects_file[oi]
+ for oi in object_index:
+ working_set[oi] = objects_file[oi]
- common.WriteWorkingSet(working_set)
+ common.WriteWorkingSet(working_set)
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_to_bad_noinc_prune.py b/binary_search_tool/test/switch_to_bad_noinc_prune.py
index 81b558e1..e5574f95 100755
--- a/binary_search_tool/test/switch_to_bad_noinc_prune.py
+++ b/binary_search_tool/test/switch_to_bad_noinc_prune.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -18,7 +18,6 @@ that noincremental always holds).
Warning: This switch script assumes the --file_args option
"""
-from __future__ import print_function
import shutil
import sys
@@ -27,21 +26,21 @@ from binary_search_tool.test import common
def Main(argv):
- """Switch part of the objects file in working set to (possible) bad ones."""
- working_set = common.ReadWorkingSet()
- objects_file = common.ReadObjectsFile()
- object_index = common.ReadObjectIndex(argv[1])
+ """Switch part of the objects file in working set to (possible) bad ones."""
+ working_set = common.ReadWorkingSet()
+ objects_file = common.ReadObjectsFile()
+ object_index = common.ReadObjectIndex(argv[1])
- for oi in object_index:
- working_set[oi] = objects_file[oi]
+ for oi in object_index:
+ working_set[oi] = objects_file[oi]
- shutil.copy(argv[1], './noinc_prune_bad')
+ shutil.copy(argv[1], "./noinc_prune_bad")
- common.WriteWorkingSet(working_set)
+ common.WriteWorkingSet(working_set)
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_to_bad_set_file.py b/binary_search_tool/test/switch_to_bad_set_file.py
index 5b941c62..9d4bee6f 100755
--- a/binary_search_tool/test/switch_to_bad_set_file.py
+++ b/binary_search_tool/test/switch_to_bad_set_file.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -10,7 +10,6 @@ This script is meant to be specifically used with the set_file test. This uses
the set files generated by binary_search_state to do the switching.
"""
-from __future__ import print_function
import os
import sys
@@ -19,24 +18,24 @@ from binary_search_tool.test import common
def Main(_):
- """Switch part of the objects file in working set to (possible) bad ones."""
- working_set = common.ReadWorkingSet()
- objects_file = common.ReadObjectsFile()
+ """Switch part of the objects file in working set to (possible) bad ones."""
+ working_set = common.ReadWorkingSet()
+ objects_file = common.ReadObjectsFile()
- if not os.path.exists(os.environ['BISECT_BAD_SET']):
- print('Bad set file does not exist!')
- return 1
+ if not os.path.exists(os.environ["BISECT_BAD_SET"]):
+ print("Bad set file does not exist!")
+ return 1
- object_index = common.ReadObjectIndex(os.environ['BISECT_BAD_SET'])
+ object_index = common.ReadObjectIndex(os.environ["BISECT_BAD_SET"])
- for oi in object_index:
- working_set[int(oi)] = objects_file[oi]
+ for oi in object_index:
+ working_set[int(oi)] = objects_file[oi]
- common.WriteWorkingSet(working_set)
+ common.WriteWorkingSet(working_set)
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_to_good.py b/binary_search_tool/test/switch_to_good.py
index 97479329..61a59a2a 100755
--- a/binary_search_tool/test/switch_to_good.py
+++ b/binary_search_tool/test/switch_to_good.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -11,7 +11,6 @@ this script) content. Every line in the file is an object index, which will be
set to good (mark as 0).
"""
-from __future__ import print_function
import sys
@@ -19,17 +18,17 @@ from binary_search_tool.test import common
def Main(argv):
- working_set = common.ReadWorkingSet()
- object_index = common.ReadObjectIndex(argv[1])
+ working_set = common.ReadWorkingSet()
+ object_index = common.ReadObjectIndex(argv[1])
- for oi in object_index:
- working_set[int(oi)] = 0
+ for oi in object_index:
+ working_set[int(oi)] = 0
- common.WriteWorkingSet(working_set)
+ common.WriteWorkingSet(working_set)
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_to_good_noinc_prune.py b/binary_search_tool/test/switch_to_good_noinc_prune.py
index 0b91a0d8..3bda1d78 100755
--- a/binary_search_tool/test/switch_to_good_noinc_prune.py
+++ b/binary_search_tool/test/switch_to_good_noinc_prune.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -18,7 +18,6 @@ that noincremental always holds).
Warning: This switch script assumes the --file_args option
"""
-from __future__ import print_function
import shutil
import sys
@@ -27,19 +26,19 @@ from binary_search_tool.test import common
def Main(argv):
- working_set = common.ReadWorkingSet()
- object_index = common.ReadObjectIndex(argv[1])
+ working_set = common.ReadWorkingSet()
+ object_index = common.ReadObjectIndex(argv[1])
- for oi in object_index:
- working_set[int(oi)] = 0
+ for oi in object_index:
+ working_set[int(oi)] = 0
- shutil.copy(argv[1], './noinc_prune_good')
+ shutil.copy(argv[1], "./noinc_prune_good")
- common.WriteWorkingSet(working_set)
+ common.WriteWorkingSet(working_set)
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/switch_to_good_set_file.py b/binary_search_tool/test/switch_to_good_set_file.py
index 1cb05e0c..b83cbe3f 100755
--- a/binary_search_tool/test/switch_to_good_set_file.py
+++ b/binary_search_tool/test/switch_to_good_set_file.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -14,7 +14,6 @@ This script is meant to be specifically used with the set_file test. This uses
the set files generated by binary_search_state to do the switching.
"""
-from __future__ import print_function
import os
import sys
@@ -23,22 +22,22 @@ from binary_search_tool.test import common
def Main(_):
- working_set = common.ReadWorkingSet()
+ working_set = common.ReadWorkingSet()
- if not os.path.exists(os.environ['BISECT_GOOD_SET']):
- print('Good set file does not exist!')
- return 1
+ if not os.path.exists(os.environ["BISECT_GOOD_SET"]):
+ print("Good set file does not exist!")
+ return 1
- object_index = common.ReadObjectIndex(os.environ['BISECT_GOOD_SET'])
+ object_index = common.ReadObjectIndex(os.environ["BISECT_GOOD_SET"])
- for oi in object_index:
- working_set[int(oi)] = 0
+ for oi in object_index:
+ working_set[int(oi)] = 0
- common.WriteWorkingSet(working_set)
+ common.WriteWorkingSet(working_set)
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main(sys.argv)
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/binary_search_tool/test/test_setup.py b/binary_search_tool/test/test_setup.py
index ecc8eb97..52486a28 100755
--- a/binary_search_tool/test/test_setup.py
+++ b/binary_search_tool/test/test_setup.py
@@ -1,24 +1,23 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Emulate running of test setup script, is_good.py should fail without this."""
-from __future__ import print_function
import sys
def Main():
- # create ./is_setup
- with open('./is_setup', 'w', encoding='utf-8'):
- pass
+ # create ./is_setup
+ with open("./is_setup", "w", encoding="utf-8"):
+ pass
- return 0
+ return 0
-if __name__ == '__main__':
- retval = Main()
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main()
+ sys.exit(retval)
diff --git a/binary_search_tool/test/test_setup_bad.py b/binary_search_tool/test/test_setup_bad.py
index cbca3c21..518a69fd 100755
--- a/binary_search_tool/test/test_setup_bad.py
+++ b/binary_search_tool/test/test_setup_bad.py
@@ -1,20 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Copyright 2020 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Emulate test setup that fails (i.e. failed flash to device)"""
-from __future__ import print_function
import sys
def Main():
- return 1 ## False, flashing failure
+ return 1 ## False, flashing failure
-if __name__ == '__main__':
- retval = Main()
- sys.exit(retval)
+if __name__ == "__main__":
+ retval = Main()
+ sys.exit(retval)