Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(32)

Side by Side Diff: tools/auto_bisect/bisect_perf_regression.py

Issue 691553002: Refactoring auto-bisect bot (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/auto_bisect/bisect.cfg ('k') | tools/auto_bisect/bisect_perf_regression_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Performance Test Bisect Tool 6 """Performance Test Bisect Tool
7 7
8 This script bisects a series of changelists using binary search. It starts at 8 This script bisects a series of changelists using binary search. It starts at
9 a bad revision where a performance metric has regressed, and asks for a last 9 a bad revision where a performance metric has regressed, and asks for a last
10 known-good revision. It will then binary search across this revision range by 10 known-good revision. It will then binary search across this revision range by
(...skipping 18 matching lines...) Expand all
29 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\ 29 "out/Release/performance_ui_tests --gtest_filter=ShutdownTest.SimpleUserQuit"\
30 -g 1f6e67861535121c5c819c16a666f2436c207e7b\ 30 -g 1f6e67861535121c5c819c16a666f2436c207e7b\
31 -b b732f23b4f81c382db0b23b9035f3dadc7d925bb\ 31 -b b732f23b4f81c382db0b23b9035f3dadc7d925bb\
32 -m shutdown/simple-user-quit 32 -m shutdown/simple-user-quit
33 """ 33 """
34 34
35 import copy 35 import copy
36 import errno 36 import errno
37 import hashlib 37 import hashlib
38 import logging 38 import logging
39 import optparse 39 import argparse
40 import os 40 import os
41 import re 41 import re
42 import shlex 42 import shlex
43 import shutil 43 import shutil
44 import StringIO 44 import StringIO
45 import sys 45 import sys
46 import time 46 import time
47 import zipfile 47 import zipfile
48 48
49 sys.path.append(os.path.join( 49 sys.path.append(os.path.join(
50 os.path.dirname(__file__), os.path.pardir, 'telemetry')) 50 os.path.dirname(__file__), os.path.pardir, 'telemetry'))
51 51
52 from bisect_printer import BisectPrinter 52 from bisect_printer import BisectPrinter
53 from bisect_results import BisectResults 53 from bisect_results import BisectResults
54 from bisect_state import BisectState 54 from bisect_state import BisectState
55 import bisect_utils 55 import bisect_utils
56 import builder 56 import builder
57 import math_utils 57 import math_utils
58 import request_build 58 import request_build
59 import source_control 59 import source_control
60 from telemetry.util import cloud_storage 60 from telemetry.util import cloud_storage
61 61
62 # The script is in chromium/src/tools/auto_bisect. Throughout this script, 62 # The script is in chromium/src/tools/auto_bisect. Throughout this script,
63 # we use paths to other things in the chromium/src repository. 63 # we use paths to other things in the chromium/src repository.
64 64
65 CROS_CHROMEOS_PATTERN = 'chromeos-base/chromeos-chrome'
66
67 # Possible return values from BisectPerformanceMetrics.RunTest. 65 # Possible return values from BisectPerformanceMetrics.RunTest.
68 BUILD_RESULT_SUCCEED = 0 66 BUILD_RESULT_SUCCEED = 0
69 BUILD_RESULT_FAIL = 1 67 BUILD_RESULT_FAIL = 1
70 BUILD_RESULT_SKIPPED = 2 68 BUILD_RESULT_SKIPPED = 2
71 69
72 # Maximum time in seconds to wait after posting build request to the try server. 70 # Maximum time in seconds to wait after posting build request to the try server.
73 # TODO: Change these values based on the actual time taken by buildbots on 71 # TODO: Change these values based on the actual time taken by buildbots on
74 # the try server. 72 # the try server.
75 MAX_MAC_BUILD_TIME = 14400 73 MAX_MAC_BUILD_TIME = 14400
76 MAX_WIN_BUILD_TIME = 14400 74 MAX_WIN_BUILD_TIME = 14400
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after
656 654
657 def __init__(self, src_cwd): 655 def __init__(self, src_cwd):
658 self.depot_cwd = {} 656 self.depot_cwd = {}
659 for depot in bisect_utils.DEPOT_NAMES: 657 for depot in bisect_utils.DEPOT_NAMES:
660 # The working directory of each depot is just the path to the depot, but 658 # The working directory of each depot is just the path to the depot, but
661 # since we're already in 'src', we can skip that part. 659 # since we're already in 'src', we can skip that part.
662 path_in_src = bisect_utils.DEPOT_DEPS_NAME[depot]['src'][4:] 660 path_in_src = bisect_utils.DEPOT_DEPS_NAME[depot]['src'][4:]
663 self.SetDepotDir(depot, os.path.join(src_cwd, path_in_src)) 661 self.SetDepotDir(depot, os.path.join(src_cwd, path_in_src))
664 662
665 self.SetDepotDir('chromium', src_cwd) 663 self.SetDepotDir('chromium', src_cwd)
666 self.SetDepotDir('cros', os.path.join(src_cwd, 'tools', 'cros'))
667 664
668 def SetDepotDir(self, depot_name, depot_dir): 665 def SetDepotDir(self, depot_name, depot_dir):
669 self.depot_cwd[depot_name] = depot_dir 666 self.depot_cwd[depot_name] = depot_dir
670 667
671 def GetDepotDir(self, depot_name): 668 def GetDepotDir(self, depot_name):
672 if depot_name in self.depot_cwd: 669 if depot_name in self.depot_cwd:
673 return self.depot_cwd[depot_name] 670 return self.depot_cwd[depot_name]
674 else: 671 else:
675 assert False, ('Unknown depot [ %s ] encountered. Possibly a new one ' 672 assert False, ('Unknown depot [ %s ] encountered. Possibly a new one '
676 'was added without proper support?' % depot_name) 673 'was added without proper support?' % depot_name)
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 bisect_utils.RunGit(['branch', '-D', BISECT_TRYJOB_BRANCH]) 770 bisect_utils.RunGit(['branch', '-D', BISECT_TRYJOB_BRANCH])
774 771
775 772
776 class BisectPerformanceMetrics(object): 773 class BisectPerformanceMetrics(object):
777 """This class contains functionality to perform a bisection of a range of 774 """This class contains functionality to perform a bisection of a range of
778 revisions to narrow down where performance regressions may have occurred. 775 revisions to narrow down where performance regressions may have occurred.
779 776
780 The main entry-point is the Run method. 777 The main entry-point is the Run method.
781 """ 778 """
782 779
783 def __init__(self, opts): 780 def __init__(self, opts, src_cwd):
781 """Constructs a BisectPerformancesMetrics object.
782
783 Args:
784 opts: BisectOptions object containing parsed options.
785 src_cwd: Root src/ directory of the test repository (inside bisect/ dir).
786 """
784 super(BisectPerformanceMetrics, self).__init__() 787 super(BisectPerformanceMetrics, self).__init__()
785 788
786 self.opts = opts 789 self.opts = opts
787 790 self.src_cwd = src_cwd
788 # The src directory here is NOT the src/ directory for the repository
789 # where the bisect script is running from. Instead, it's the src/ directory
790 # inside the bisect/ directory which is created before running.
791 self.src_cwd = os.getcwd()
792
793 self.depot_registry = DepotDirectoryRegistry(self.src_cwd) 791 self.depot_registry = DepotDirectoryRegistry(self.src_cwd)
792 self.printer = BisectPrinter(self.opts, self.depot_registry)
794 self.cleanup_commands = [] 793 self.cleanup_commands = []
795 self.warnings = [] 794 self.warnings = []
796 self.builder = builder.Builder.FromOpts(opts) 795 self.builder = builder.Builder.FromOpts(opts)
797 796
798 def PerformCleanup(self): 797 def PerformCleanup(self):
799 """Performs cleanup when script is finished.""" 798 """Performs cleanup when script is finished."""
800 os.chdir(self.src_cwd) 799 os.chdir(self.src_cwd)
801 for c in self.cleanup_commands: 800 for c in self.cleanup_commands:
802 if c[0] == 'mv': 801 if c[0] == 'mv':
803 shutil.move(c[1], c[2]) 802 shutil.move(c[1], c[2])
804 else: 803 else:
805 assert False, 'Invalid cleanup command.' 804 assert False, 'Invalid cleanup command.'
806 805
807 def GetRevisionList(self, depot, bad_revision, good_revision): 806 def GetRevisionList(self, depot, bad_revision, good_revision):
808 """Retrieves a list of all the commits between the bad revision and 807 """Retrieves a list of all the commits between the bad revision and
809 last known good revision.""" 808 last known good revision."""
810 809
811 revision_work_list = [] 810 cwd = self.depot_registry.GetDepotDir(depot)
812 811 return source_control.GetRevisionList(bad_revision, good_revision, cwd=cwd)
813 if depot == 'cros':
814 revision_range_start = good_revision
815 revision_range_end = bad_revision
816
817 cwd = os.getcwd()
818 self.depot_registry.ChangeToDepotDir('cros')
819
820 # Print the commit timestamps for every commit in the revision time
821 # range. We'll sort them and bisect by that. There is a remote chance that
822 # 2 (or more) commits will share the exact same timestamp, but it's
823 # probably safe to ignore that case.
824 cmd = ['repo', 'forall', '-c',
825 'git log --format=%%ct --before=%d --after=%d' % (
826 revision_range_end, revision_range_start)]
827 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
828
829 assert not return_code, ('An error occurred while running '
830 '"%s"' % ' '.join(cmd))
831
832 os.chdir(cwd)
833
834 revision_work_list = list(set(
835 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)]))
836 revision_work_list = sorted(revision_work_list, reverse=True)
837 else:
838 cwd = self.depot_registry.GetDepotDir(depot)
839 revision_work_list = source_control.GetRevisionList(bad_revision,
840 good_revision, cwd=cwd)
841
842 return revision_work_list
843 812
844 def _ParseRevisionsFromDEPSFile(self, depot): 813 def _ParseRevisionsFromDEPSFile(self, depot):
845 """Parses the local DEPS file to determine blink/skia/v8 revisions which may 814 """Parses the local DEPS file to determine blink/skia/v8 revisions which may
846 be needed if the bisect recurses into those depots later. 815 be needed if the bisect recurses into those depots later.
847 816
848 Args: 817 Args:
849 depot: Name of depot being bisected. 818 depot: Name of depot being bisected.
850 819
851 Returns: 820 Returns:
852 A dict in the format {depot:revision} if successful, otherwise None. 821 A dict in the format {depot:revision} if successful, otherwise None.
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 """ 882 """
914 cwd = os.getcwd() 883 cwd = os.getcwd()
915 self.depot_registry.ChangeToDepotDir(depot) 884 self.depot_registry.ChangeToDepotDir(depot)
916 885
917 results = {} 886 results = {}
918 887
919 if depot == 'chromium' or depot == 'android-chrome': 888 if depot == 'chromium' or depot == 'android-chrome':
920 results = self._ParseRevisionsFromDEPSFile(depot) 889 results = self._ParseRevisionsFromDEPSFile(depot)
921 os.chdir(cwd) 890 os.chdir(cwd)
922 891
923 if depot == 'cros':
924 cmd = [
925 bisect_utils.CROS_SDK_PATH,
926 '--',
927 'portageq-%s' % self.opts.cros_board,
928 'best_visible',
929 '/build/%s' % self.opts.cros_board,
930 'ebuild',
931 CROS_CHROMEOS_PATTERN
932 ]
933 output, return_code = bisect_utils.RunProcessAndRetrieveOutput(cmd)
934
935 assert not return_code, ('An error occurred while running '
936 '"%s"' % ' '.join(cmd))
937
938 if len(output) > CROS_CHROMEOS_PATTERN:
939 output = output[len(CROS_CHROMEOS_PATTERN):]
940
941 if len(output) > 1:
942 output = output.split('_')[0]
943
944 if len(output) > 3:
945 contents = output.split('.')
946
947 version = contents[2]
948
949 if contents[3] != '0':
950 warningText = ('Chrome version: %s.%s but using %s.0 to bisect.' %
951 (version, contents[3], version))
952 if not warningText in self.warnings:
953 self.warnings.append(warningText)
954
955 cwd = os.getcwd()
956 self.depot_registry.ChangeToDepotDir('chromium')
957 cmd = ['log', '-1', '--format=%H',
958 '--author=chrome-release@google.com',
959 '--grep=to %s' % version, 'origin/master']
960 return_code = bisect_utils.CheckRunGit(cmd)
961 os.chdir(cwd)
962
963 results['chromium'] = output.strip()
964
965 if depot == 'v8': 892 if depot == 'v8':
966 # We can't try to map the trunk revision to bleeding edge yet, because 893 # We can't try to map the trunk revision to bleeding edge yet, because
967 # we don't know which direction to try to search in. Have to wait until 894 # we don't know which direction to try to search in. Have to wait until
968 # the bisect has narrowed the results down to 2 v8 rolls. 895 # the bisect has narrowed the results down to 2 v8 rolls.
969 results['v8_bleeding_edge'] = None 896 results['v8_bleeding_edge'] = None
970 897
971 return results 898 return results
972 899
973 def BackupOrRestoreOutputDirectory(self, restore=False, build_type='Release'): 900 def BackupOrRestoreOutputDirectory(self, restore=False, build_type='Release'):
974 """Backs up or restores build output directory based on restore argument. 901 """Backs up or restores build output directory based on restore argument.
(...skipping 497 matching lines...) Expand 10 before | Expand all | Expand 10 after
1472 # refer to http://bugs.python.org/issue1724822. By default posix=True. 1399 # refer to http://bugs.python.org/issue1724822. By default posix=True.
1473 args = shlex.split(command_to_run, posix=not bisect_utils.IsWindowsHost()) 1400 args = shlex.split(command_to_run, posix=not bisect_utils.IsWindowsHost())
1474 1401
1475 if not _GenerateProfileIfNecessary(args): 1402 if not _GenerateProfileIfNecessary(args):
1476 err_text = 'Failed to generate profile for performance test.' 1403 err_text = 'Failed to generate profile for performance test.'
1477 return (err_text, failure_code) 1404 return (err_text, failure_code)
1478 1405
1479 # If running a Telemetry test for Chrome OS, insert the remote IP and 1406 # If running a Telemetry test for Chrome OS, insert the remote IP and
1480 # identity parameters. 1407 # identity parameters.
1481 is_telemetry = bisect_utils.IsTelemetryCommand(command_to_run) 1408 is_telemetry = bisect_utils.IsTelemetryCommand(command_to_run)
1482 if self.opts.target_platform == 'cros' and is_telemetry:
1483 args.append('--remote=%s' % self.opts.cros_remote_ip)
1484 args.append('--identity=%s' % bisect_utils.CROS_TEST_KEY_PATH)
1485 1409
1486 start_time = time.time() 1410 start_time = time.time()
1487 1411
1488 metric_values = [] 1412 metric_values = []
1489 output_of_all_runs = '' 1413 output_of_all_runs = ''
1490 for i in xrange(self.opts.repeat_test_count): 1414 for i in xrange(self.opts.repeat_test_count):
1491 # Can ignore the return code since if the tests fail, it won't return 0. 1415 # Can ignore the return code since if the tests fail, it won't return 0.
1492 current_args = copy.copy(args) 1416 current_args = copy.copy(args)
1493 if is_telemetry: 1417 if is_telemetry:
1494 if i == 0 and reset_on_first_run: 1418 if i == 0 and reset_on_first_run:
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
1588 1512
1589 Args: 1513 Args:
1590 revision: The revision to sync to. 1514 revision: The revision to sync to.
1591 depot: The depot in use at the moment (probably skia). 1515 depot: The depot in use at the moment (probably skia).
1592 1516
1593 Returns: 1517 Returns:
1594 A list of [depot, revision] pairs that need to be synced. 1518 A list of [depot, revision] pairs that need to be synced.
1595 """ 1519 """
1596 revisions_to_sync = [[depot, revision]] 1520 revisions_to_sync = [[depot, revision]]
1597 1521
1598 is_base = ((depot == 'chromium') or (depot == 'cros') or 1522 is_base = ((depot == 'chromium') or (depot == 'android-chrome'))
1599 (depot == 'android-chrome'))
1600 1523
1601 # Some SVN depots were split into multiple git depots, so we need to 1524 # Some SVN depots were split into multiple git depots, so we need to
1602 # figure out for each mirror which git revision to grab. There's no 1525 # figure out for each mirror which git revision to grab. There's no
1603 # guarantee that the SVN revision will exist for each of the dependent 1526 # guarantee that the SVN revision will exist for each of the dependent
1604 # depots, so we have to grep the git logs and grab the next earlier one. 1527 # depots, so we have to grep the git logs and grab the next earlier one.
1605 if not is_base and bisect_utils.DEPOT_DEPS_NAME[depot]['depends']: 1528 if not is_base and bisect_utils.DEPOT_DEPS_NAME[depot]['depends']:
1606 commit_position = source_control.GetCommitPosition(revision) 1529 commit_position = source_control.GetCommitPosition(revision)
1607 1530
1608 for d in bisect_utils.DEPOT_DEPS_NAME[depot]['depends']: 1531 for d in bisect_utils.DEPOT_DEPS_NAME[depot]['depends']:
1609 self.depot_registry.ChangeToDepotDir(d) 1532 self.depot_registry.ChangeToDepotDir(d)
(...skipping 19 matching lines...) Expand all
1629 print 'Cleaning up between runs.' 1552 print 'Cleaning up between runs.'
1630 print 1553 print
1631 1554
1632 # Leaving these .pyc files around between runs may disrupt some perf tests. 1555 # Leaving these .pyc files around between runs may disrupt some perf tests.
1633 for (path, _, files) in os.walk(self.src_cwd): 1556 for (path, _, files) in os.walk(self.src_cwd):
1634 for cur_file in files: 1557 for cur_file in files:
1635 if cur_file.endswith('.pyc'): 1558 if cur_file.endswith('.pyc'):
1636 path_to_file = os.path.join(path, cur_file) 1559 path_to_file = os.path.join(path, cur_file)
1637 os.remove(path_to_file) 1560 os.remove(path_to_file)
1638 1561
1639 def PerformCrosChrootCleanup(self): 1562 def _RunPostSync(self, _depot):
1640 """Deletes the chroot.
1641
1642 Returns:
1643 True if successful.
1644 """
1645 cwd = os.getcwd()
1646 self.depot_registry.ChangeToDepotDir('cros')
1647 cmd = [bisect_utils.CROS_SDK_PATH, '--delete']
1648 return_code = bisect_utils.RunProcess(cmd)
1649 os.chdir(cwd)
1650 return not return_code
1651
1652 def CreateCrosChroot(self):
1653 """Creates a new chroot.
1654
1655 Returns:
1656 True if successful.
1657 """
1658 cwd = os.getcwd()
1659 self.depot_registry.ChangeToDepotDir('cros')
1660 cmd = [bisect_utils.CROS_SDK_PATH, '--create']
1661 return_code = bisect_utils.RunProcess(cmd)
1662 os.chdir(cwd)
1663 return not return_code
1664
1665 def _PerformPreSyncCleanup(self, depot):
1666 """Performs any necessary cleanup before syncing.
1667
1668 Args:
1669 depot: Depot name.
1670
1671 Returns:
1672 True if successful.
1673 """
1674 if depot == 'cros':
1675 return self.PerformCrosChrootCleanup()
1676 return True
1677
1678 def _RunPostSync(self, depot):
1679 """Performs any work after syncing. 1563 """Performs any work after syncing.
1680 1564
1681 Args: 1565 Args:
1682 depot: Depot name. 1566 depot: Depot name.
1683 1567
1684 Returns: 1568 Returns:
1685 True if successful. 1569 True if successful.
1686 """ 1570 """
1687 if self.opts.target_platform == 'android': 1571 if self.opts.target_platform == 'android':
1688 if not builder.SetupAndroidBuildEnvironment(self.opts, 1572 if not builder.SetupAndroidBuildEnvironment(self.opts,
1689 path_to_src=self.src_cwd): 1573 path_to_src=self.src_cwd):
1690 return False 1574 return False
1691 1575
1692 if depot == 'cros': 1576 return self.RunGClientHooks()
1693 return self.CreateCrosChroot()
1694 else:
1695 return self.RunGClientHooks()
1696 return True
1697 1577
1698 @staticmethod 1578 @staticmethod
1699 def ShouldSkipRevision(depot, revision): 1579 def ShouldSkipRevision(depot, revision):
1700 """Checks whether a particular revision can be safely skipped. 1580 """Checks whether a particular revision can be safely skipped.
1701 1581
1702 Some commits can be safely skipped (such as a DEPS roll), since the tool 1582 Some commits can be safely skipped (such as a DEPS roll), since the tool
1703 is git based those changes would have no effect. 1583 is git based those changes would have no effect.
1704 1584
1705 Args: 1585 Args:
1706 depot: The depot being bisected. 1586 depot: The depot being bisected.
(...skipping 23 matching lines...) Expand all
1730 metric: The performance metric being tested. 1610 metric: The performance metric being tested.
1731 1611
1732 Returns: 1612 Returns:
1733 On success, a tuple containing the results of the performance test. 1613 On success, a tuple containing the results of the performance test.
1734 Otherwise, a tuple with the error message. 1614 Otherwise, a tuple with the error message.
1735 """ 1615 """
1736 # Decide which sync program to use. 1616 # Decide which sync program to use.
1737 sync_client = None 1617 sync_client = None
1738 if depot == 'chromium' or depot == 'android-chrome': 1618 if depot == 'chromium' or depot == 'android-chrome':
1739 sync_client = 'gclient' 1619 sync_client = 'gclient'
1740 elif depot == 'cros':
1741 sync_client = 'repo'
1742 1620
1743 # Decide what depots will need to be synced to what revisions. 1621 # Decide what depots will need to be synced to what revisions.
1744 revisions_to_sync = self._FindAllRevisionsToSync(revision, depot) 1622 revisions_to_sync = self._FindAllRevisionsToSync(revision, depot)
1745 if not revisions_to_sync: 1623 if not revisions_to_sync:
1746 return ('Failed to resolve dependent depots.', BUILD_RESULT_FAIL) 1624 return ('Failed to resolve dependent depots.', BUILD_RESULT_FAIL)
1747 1625
1748 if not self._PerformPreSyncCleanup(depot):
1749 return ('Failed to perform pre-sync cleanup.', BUILD_RESULT_FAIL)
1750
1751 # Do the syncing for all depots. 1626 # Do the syncing for all depots.
1752 if not self.opts.debug_ignore_sync: 1627 if not self.opts.debug_ignore_sync:
1753 if not self._SyncAllRevisions(revisions_to_sync, sync_client): 1628 if not self._SyncAllRevisions(revisions_to_sync, sync_client):
1754 return ('Failed to sync: [%s]' % str(revision), BUILD_RESULT_FAIL) 1629 return ('Failed to sync: [%s]' % str(revision), BUILD_RESULT_FAIL)
1755 1630
1756 # Try to do any post-sync steps. This may include "gclient runhooks". 1631 # Try to do any post-sync steps. This may include "gclient runhooks".
1757 if not self._RunPostSync(depot): 1632 if not self._RunPostSync(depot):
1758 return ('Failed to run [gclient runhooks].', BUILD_RESULT_FAIL) 1633 return ('Failed to run [gclient runhooks].', BUILD_RESULT_FAIL)
1759 1634
1760 # Skip this revision if it can be skipped. 1635 # Skip this revision if it can be skipped.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1795 start_build_time) 1670 start_build_time)
1796 else: 1671 else:
1797 return ('Failed to parse DEPS file for external revisions.', 1672 return ('Failed to parse DEPS file for external revisions.',
1798 BUILD_RESULT_FAIL) 1673 BUILD_RESULT_FAIL)
1799 1674
1800 def _SyncAllRevisions(self, revisions_to_sync, sync_client): 1675 def _SyncAllRevisions(self, revisions_to_sync, sync_client):
1801 """Syncs multiple depots to particular revisions. 1676 """Syncs multiple depots to particular revisions.
1802 1677
1803 Args: 1678 Args:
1804 revisions_to_sync: A list of (depot, revision) pairs to be synced. 1679 revisions_to_sync: A list of (depot, revision) pairs to be synced.
1805 sync_client: Program used to sync, e.g. "gclient", "repo". Can be None. 1680 sync_client: Program used to sync, e.g. "gclient". Can be None.
1806 1681
1807 Returns: 1682 Returns:
1808 True if successful, False otherwise. 1683 True if successful, False otherwise.
1809 """ 1684 """
1810 for depot, revision in revisions_to_sync: 1685 for depot, revision in revisions_to_sync:
1811 self.depot_registry.ChangeToDepotDir(depot) 1686 self.depot_registry.ChangeToDepotDir(depot)
1812 1687
1813 if sync_client: 1688 if sync_client:
1814 self.PerformPreBuildCleanup() 1689 self.PerformPreBuildCleanup()
1815 1690
(...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after
2202 self, target_depot, good_revision, bad_revision): 2077 self, target_depot, good_revision, bad_revision):
2203 """Checks that |good_revision| is an earlier revision than |bad_revision|. 2078 """Checks that |good_revision| is an earlier revision than |bad_revision|.
2204 2079
2205 Args: 2080 Args:
2206 good_revision: Number/tag of the known good revision. 2081 good_revision: Number/tag of the known good revision.
2207 bad_revision: Number/tag of the known bad revision. 2082 bad_revision: Number/tag of the known bad revision.
2208 2083
2209 Returns: 2084 Returns:
2210 True if the revisions are in the proper order (good earlier than bad). 2085 True if the revisions are in the proper order (good earlier than bad).
2211 """ 2086 """
2212 if target_depot != 'cros': 2087 cwd = self.depot_registry.GetDepotDir(target_depot)
2213 cwd = self.depot_registry.GetDepotDir(target_depot) 2088 good_position = source_control.GetCommitPosition(good_revision, cwd)
2214 good_position = source_control.GetCommitPosition(good_revision, cwd) 2089 bad_position = source_control.GetCommitPosition(bad_revision, cwd)
2215 bad_position = source_control.GetCommitPosition(bad_revision, cwd)
2216 else:
2217 # CrOS and SVN use integers.
2218 good_position = int(good_revision)
2219 bad_position = int(bad_revision)
2220 2090
2221 return good_position <= bad_position 2091 return good_position <= bad_position
2222 2092
2223 def CanPerformBisect(self, good_revision, bad_revision): 2093 def CanPerformBisect(self, good_revision, bad_revision):
2224 """Checks whether a given revision is bisectable. 2094 """Checks whether a given revision is bisectable.
2225 2095
2226 Checks for following: 2096 Checks for following:
2227 1. Non-bisectable revsions for android bots (refer to crbug.com/385324). 2097 1. Non-bisectable revsions for android bots (refer to crbug.com/385324).
2228 2. Non-bisectable revsions for Windows bots (refer to crbug.com/405274). 2098 2. Non-bisectable revsions for Windows bots (refer to crbug.com/405274).
2229 2099
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
2269 command_to_run: Specify the command to execute the performance test. 2139 command_to_run: Specify the command to execute the performance test.
2270 good_revision: Number/tag of the known good revision. 2140 good_revision: Number/tag of the known good revision.
2271 bad_revision: Number/tag of the known bad revision. 2141 bad_revision: Number/tag of the known bad revision.
2272 metric: The performance metric to monitor. 2142 metric: The performance metric to monitor.
2273 2143
2274 Returns: 2144 Returns:
2275 A BisectResults object. 2145 A BisectResults object.
2276 """ 2146 """
2277 # Choose depot to bisect first 2147 # Choose depot to bisect first
2278 target_depot = 'chromium' 2148 target_depot = 'chromium'
2279 if self.opts.target_platform == 'cros': 2149 if self.opts.target_platform == 'android-chrome':
2280 target_depot = 'cros'
2281 elif self.opts.target_platform == 'android-chrome':
2282 target_depot = 'android-chrome' 2150 target_depot = 'android-chrome'
2283 2151
2284 cwd = os.getcwd() 2152 cwd = os.getcwd()
2285 self.depot_registry.ChangeToDepotDir(target_depot) 2153 self.depot_registry.ChangeToDepotDir(target_depot)
2286 2154
2287 # If they passed SVN revisions, we can try match them to git SHA1 hashes. 2155 # If they passed SVN revisions, we can try match them to git SHA1 hashes.
2288 bad_revision = source_control.ResolveToRevision( 2156 bad_revision = source_control.ResolveToRevision(
2289 bad_revision_in, target_depot, bisect_utils.DEPOT_DEPS_NAME, 100) 2157 bad_revision_in, target_depot, bisect_utils.DEPOT_DEPS_NAME, 100)
2290 good_revision = source_control.ResolveToRevision( 2158 good_revision = source_control.ResolveToRevision(
2291 good_revision_in, target_depot, bisect_utils.DEPOT_DEPS_NAME, -100) 2159 good_revision_in, target_depot, bisect_utils.DEPOT_DEPS_NAME, -100)
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
2398 bad_revision_state.passed = False 2266 bad_revision_state.passed = False
2399 bad_revision_state.value = known_bad_value 2267 bad_revision_state.value = known_bad_value
2400 2268
2401 good_revision_state = revision_states[max_revision] 2269 good_revision_state = revision_states[max_revision]
2402 good_revision_state.external = good_results[2] 2270 good_revision_state.external = good_results[2]
2403 good_revision_state.perf_time = good_results[3] 2271 good_revision_state.perf_time = good_results[3]
2404 good_revision_state.build_time = good_results[4] 2272 good_revision_state.build_time = good_results[4]
2405 good_revision_state.passed = True 2273 good_revision_state.passed = True
2406 good_revision_state.value = known_good_value 2274 good_revision_state.value = known_good_value
2407 2275
2408 bisect_printer = BisectPrinter(self.opts, self.depot_registry)
2409
2410 # Check how likely it is that the good and bad results are different 2276 # Check how likely it is that the good and bad results are different
2411 # beyond chance-induced variation. 2277 # beyond chance-induced variation.
2412 confidence_error = False 2278 confidence_error = False
2413 if not self.opts.debug_ignore_regression_confidence: 2279 if not self.opts.debug_ignore_regression_confidence:
2414 confidence_error = _CheckRegressionConfidenceError(good_revision, 2280 confidence_error = _CheckRegressionConfidenceError(good_revision,
2415 bad_revision, 2281 bad_revision,
2416 known_good_value, 2282 known_good_value,
2417 known_bad_value) 2283 known_bad_value)
2418 if confidence_error: 2284 if confidence_error:
2419 self.warnings.append(confidence_error) 2285 self.warnings.append(confidence_error)
2420 bad_revision_state.passed = True # Marking the 'bad' revision as good. 2286 bad_revision_state.passed = True # Marking the 'bad' revision as good.
2421 return BisectResults(bisect_state, self.depot_registry, self.opts, 2287 return BisectResults(bisect_state, self.depot_registry, self.opts,
2422 self.warnings) 2288 self.warnings)
2423 2289
2424 while True: 2290 while True:
2425 if not revision_states: 2291 if not revision_states:
2426 break 2292 break
2427 2293
2428 if max_revision - min_revision <= 1: 2294 if max_revision - min_revision <= 1:
2429 min_revision_state = revision_states[min_revision] 2295 min_revision_state = revision_states[min_revision]
2430 max_revision_state = revision_states[max_revision] 2296 max_revision_state = revision_states[max_revision]
2431 current_depot = min_revision_state.depot 2297 current_depot = min_revision_state.depot
2432 # TODO(sergiyb): Under which conditions can first two branches be hit? 2298 # TODO(sergiyb): Under which conditions can first two branches be hit?
2433 if min_revision_state.passed == '?': 2299 if min_revision_state.passed == '?':
2434 next_revision_index = min_revision 2300 next_revision_index = min_revision
2435 elif max_revision_state.passed == '?': 2301 elif max_revision_state.passed == '?':
2436 next_revision_index = max_revision 2302 next_revision_index = max_revision
2437 elif current_depot in ['android-chrome', 'cros', 'chromium', 'v8']: 2303 elif current_depot in ['android-chrome', 'chromium', 'v8']:
2438 previous_revision = revision_states[min_revision].revision 2304 previous_revision = revision_states[min_revision].revision
2439 # If there were changes to any of the external libraries we track, 2305 # If there were changes to any of the external libraries we track,
2440 # should bisect the changes there as well. 2306 # should bisect the changes there as well.
2441 external_depot = self._FindNextDepotToBisect( 2307 external_depot = self._FindNextDepotToBisect(
2442 current_depot, min_revision_state, max_revision_state) 2308 current_depot, min_revision_state, max_revision_state)
2443 # If there was no change in any of the external depots, the search 2309 # If there was no change in any of the external depots, the search
2444 # is over. 2310 # is over.
2445 if not external_depot: 2311 if not external_depot:
2446 if current_depot == 'v8': 2312 if current_depot == 'v8':
2447 self.warnings.append('Unfortunately, V8 bisection couldn\'t ' 2313 self.warnings.append('Unfortunately, V8 bisection couldn\'t '
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
2524 next_revision_state.passed = 'Build Failed' 2390 next_revision_state.passed = 'Build Failed'
2525 2391
2526 print run_results[0] 2392 print run_results[0]
2527 2393
2528 # If the build is broken, remove it and redo search. 2394 # If the build is broken, remove it and redo search.
2529 revision_states.pop(next_revision_index) 2395 revision_states.pop(next_revision_index)
2530 2396
2531 max_revision -= 1 2397 max_revision -= 1
2532 2398
2533 if self.opts.output_buildbot_annotations: 2399 if self.opts.output_buildbot_annotations:
2534 bisect_printer.PrintPartialResults(bisect_state) 2400 self.printer.PrintPartialResults(bisect_state)
2535 bisect_utils.OutputAnnotationStepClosed() 2401 bisect_utils.OutputAnnotationStepClosed()
2536 2402
2537 return BisectResults(bisect_state, self.depot_registry, self.opts, 2403 return BisectResults(bisect_state, self.depot_registry, self.opts,
2538 self.warnings) 2404 self.warnings)
2539 else: 2405 else:
2540 # Weren't able to sync and retrieve the revision range. 2406 # Weren't able to sync and retrieve the revision range.
2541 error = ('An error occurred attempting to retrieve revision range: ' 2407 error = ('An error occurred attempting to retrieve revision range: '
2542 '[%s..%s]' % (good_revision, bad_revision)) 2408 '[%s..%s]' % (good_revision, bad_revision))
2543 return BisectResults(error=error) 2409 return BisectResults(error=error)
2544 2410
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
2590 def __init__(self): 2456 def __init__(self):
2591 super(BisectOptions, self).__init__() 2457 super(BisectOptions, self).__init__()
2592 2458
2593 self.target_platform = 'chromium' 2459 self.target_platform = 'chromium'
2594 self.build_preference = None 2460 self.build_preference = None
2595 self.good_revision = None 2461 self.good_revision = None
2596 self.bad_revision = None 2462 self.bad_revision = None
2597 self.use_goma = None 2463 self.use_goma = None
2598 self.goma_dir = None 2464 self.goma_dir = None
2599 self.goma_threads = 64 2465 self.goma_threads = 64
2600 self.cros_board = None
2601 self.cros_remote_ip = None
2602 self.repeat_test_count = 20 2466 self.repeat_test_count = 20
2603 self.truncate_percent = 25 2467 self.truncate_percent = 25
2604 self.max_time_minutes = 20 2468 self.max_time_minutes = 20
2605 self.metric = None 2469 self.metric = None
2606 self.command = None 2470 self.command = None
2607 self.output_buildbot_annotations = None 2471 self.output_buildbot_annotations = None
2608 self.no_custom_deps = False 2472 self.no_custom_deps = False
2609 self.working_directory = None 2473 self.working_directory = None
2610 self.extra_src = None 2474 self.extra_src = None
2611 self.debug_ignore_build = None 2475 self.debug_ignore_build = None
2612 self.debug_ignore_sync = None 2476 self.debug_ignore_sync = None
2613 self.debug_ignore_perf_test = None 2477 self.debug_ignore_perf_test = None
2614 self.debug_ignore_regression_confidence = None 2478 self.debug_ignore_regression_confidence = None
2615 self.debug_fake_first_test_mean = 0 2479 self.debug_fake_first_test_mean = 0
2616 self.gs_bucket = None 2480 self.gs_bucket = None
2617 self.target_arch = 'ia32' 2481 self.target_arch = 'ia32'
2618 self.target_build_type = 'Release' 2482 self.target_build_type = 'Release'
2619 self.builder_host = None 2483 self.builder_host = None
2620 self.builder_port = None 2484 self.builder_port = None
2621 self.bisect_mode = bisect_utils.BISECT_MODE_MEAN 2485 self.bisect_mode = bisect_utils.BISECT_MODE_MEAN
2622 self.improvement_direction = 0 2486 self.improvement_direction = 0
2623 2487
2624 @staticmethod 2488 @staticmethod
2625 def _CreateCommandLineParser(): 2489 def _AddBisectOptionsGroup(parser):
2490 group = parser.add_argument_group('Bisect options')
2491 group.add_argument('-c', '--command', required=True,
2492 help='A command to execute your performance test at '
2493 'each point in the bisection.')
2494 group.add_argument('-b', '--bad_revision', required=True,
2495 help='A bad revision to start bisection. Must be later '
2496 'than good revision. May be either a git or svn '
2497 'revision.')
2498 group.add_argument('-g', '--good_revision', required=True,
2499 help='A revision to start bisection where performance '
2500 'test is known to pass. Must be earlier than the '
2501 'bad revision. May be either a git or a svn '
2502 'revision.')
2503 group.add_argument('-m', '--metric',
2504 help='The desired metric to bisect on. For example '
2505 '"vm_rss_final_b/vm_rss_f_b"')
2506 group.add_argument('-d', '--improvement_direction', type=int, default=0,
2507 help='An integer number representing the direction of '
2508 'improvement. 1 for higher is better, -1 for lower '
2509 'is better, 0 for ignore (default).')
2510 group.add_argument('-r', '--repeat_test_count', type=int, default=20,
2511 choices=range(1, 101),
2512 help='The number of times to repeat the performance '
2513 'test. Values will be clamped to range [1, 100]. '
2514 'Default value is 20.')
2515 group.add_argument('--max_time_minutes', type=int, default=20,
2516 choices=range(1, 61),
2517 help='The maximum time (in minutes) to take running the '
2518 'performance tests. The script will run the '
2519 'performance tests according to '
2520 '--repeat_test_count, so long as it doesn\'t exceed'
2521 ' --max_time_minutes. Values will be clamped to '
2522 'range [1, 60]. Default value is 20.')
2523 group.add_argument('-t', '--truncate_percent', type=int, default=25,
2524 help='The highest/lowest % are discarded to form a '
2525 'truncated mean. Values will be clamped to range '
2526 '[0, 25]. Default value is 25 (highest/lowest 25% '
2527 'will be discarded).')
2528 group.add_argument('--bisect_mode', default=bisect_utils.BISECT_MODE_MEAN,
2529 choices=[bisect_utils.BISECT_MODE_MEAN,
2530 bisect_utils.BISECT_MODE_STD_DEV,
2531 bisect_utils.BISECT_MODE_RETURN_CODE],
2532 help='The bisect mode. Choices are to bisect on the '
2533 'difference in mean, std_dev, or return_code.')
2534
2535 @staticmethod
2536 def _AddBuildOptionsGroup(parser):
2537 group = parser.add_argument_group('Build options')
2538 group.add_argument('-w', '--working_directory',
2539 help='Path to the working directory where the script '
2540 'will do an initial checkout of the chromium depot. The '
2541 'files will be placed in a subdirectory "bisect" under '
2542 'working_directory and that will be used to perform the '
2543 'bisection. This parameter is optional, if it is not '
2544 'supplied, the script will work from the current depot.')
2545 group.add_argument('--build_preference', type='choice',
2546 choices=['msvs', 'ninja', 'make'],
2547 help='The preferred build system to use. On linux/mac '
2548 'the options are make/ninja. On Windows, the '
2549 'options are msvs/ninja.')
2550 group.add_argument('--target_platform', type='choice', default='chromium',
2551 choices=['chromium', 'android', 'android-chrome'],
2552 help='The target platform. Choices are "chromium" '
2553 '(current platform), or "android". If you specify '
2554 'something other than "chromium", you must be '
2555 'properly set up to build that platform.')
2556 group.add_argument('--no_custom_deps', dest='no_custom_deps',
2557 action='store_true', default=False,
2558 help='Run the script with custom_deps or not.')
2559 group.add_argument('--extra_src',
2560 help='Path to a script which can be used to modify the '
2561 'bisect script\'s behavior.')
2562 group.add_argument('--use_goma', action='store_true',
2563 help='Add a bunch of extra threads for goma, and enable '
2564 'goma')
2565 group.add_argument('--goma_dir',
2566 help='Path to goma tools (or system default if not '
2567 'specified).')
2568 group.add_argument('--goma_threads', type=int, default='64',
2569 help='Number of threads for goma, only if using goma.')
2570 group.add_argument('--output_buildbot_annotations', action='store_true',
2571 help='Add extra annotation output for buildbot.')
2572 group.add_argument('--gs_bucket', default='', dest='gs_bucket',
2573 help='Name of Google Storage bucket to upload or '
2574 'download build. e.g., chrome-perf')
2575 group.add_argument('--target_arch', type='choice', default='ia32',
2576 dest='target_arch', choices=['ia32', 'x64', 'arm'],
2577 help='The target build architecture. Choices are "ia32" '
2578 '(default), "x64" or "arm".')
2579 group.add_argument('--target_build_type', type='choice', default='Release',
2580 choices=['Release', 'Debug'],
2581 help='The target build type. Choices are "Release" '
2582 '(default), or "Debug".')
2583 group.add_argument('--builder_host', dest='builder_host',
2584 help='Host address of server to produce build by '
2585 'posting try job request.')
2586 group.add_argument('--builder_port', dest='builder_port', type='int',
2587 help='HTTP port of the server to produce build by '
2588 'posting try job request.')
2589
2590 @staticmethod
2591 def _AddDebugOptionsGroup(parser):
2592 group = parser.add_argument_group('Debug options')
2593 group.add_argument('--debug_ignore_build', action='store_true',
2594 help='DEBUG: Don\'t perform builds.')
2595 group.add_argument('--debug_ignore_sync', action='store_true',
2596 help='DEBUG: Don\'t perform syncs.')
2597 group.add_argument('--debug_ignore_perf_test', action='store_true',
2598 help='DEBUG: Don\'t perform performance tests.')
2599 group.add_argument('--debug_ignore_regression_confidence',
2600 action='store_true',
2601 help='DEBUG: Don\'t score the confidence of the initial '
2602 'good and bad revisions\' test results.')
2603 group.add_argument('--debug_fake_first_test_mean', type='int', default='0',
2604 help='DEBUG: When faking performance tests, return this '
2605 'value as the mean of the first performance test, '
2606 'and return a mean of 0.0 for further tests.')
2607 return group
2608
2609 @classmethod
2610 def _CreateCommandLineParser(cls):
2626 """Creates a parser with bisect options. 2611 """Creates a parser with bisect options.
2627 2612
2628 Returns: 2613 Returns:
2629 An instance of optparse.OptionParser. 2614 An instance of optparse.OptionParser.
2630 """ 2615 """
2631 usage = ('%prog [options] [-- chromium-options]\n' 2616 usage = ('%prog [options] [-- chromium-options]\n'
2632 'Perform binary search on revision history to find a minimal ' 2617 'Perform binary search on revision history to find a minimal '
2633 'range of revisions where a performance metric regressed.\n') 2618 'range of revisions where a performance metric regressed.\n')
2634 2619
2635 parser = optparse.OptionParser(usage=usage) 2620 parser = argparse.ArgumentParser(usage=usage)
2636 2621 cls._AddBisectOptionsGroup(parser)
2637 group = optparse.OptionGroup(parser, 'Bisect options') 2622 cls._AddBuildOptionsGroup(parser)
2638 group.add_option('-c', '--command', 2623 cls._AddDebugOptionsGroup(parser)
2639 type='str',
2640 help='A command to execute your performance test at' +
2641 ' each point in the bisection.')
2642 group.add_option('-b', '--bad_revision',
2643 type='str',
2644 help='A bad revision to start bisection. ' +
2645 'Must be later than good revision. May be either a git' +
2646 ' or svn revision.')
2647 group.add_option('-g', '--good_revision',
2648 type='str',
2649 help='A revision to start bisection where performance' +
2650 ' test is known to pass. Must be earlier than the ' +
2651 'bad revision. May be either a git or svn revision.')
2652 group.add_option('-m', '--metric',
2653 type='str',
2654 help='The desired metric to bisect on. For example ' +
2655 '"vm_rss_final_b/vm_rss_f_b"')
2656 group.add_option('-d', '--improvement_direction',
2657 type='int',
2658 default=0,
2659 help='An integer number representing the direction of ' +
2660 'improvement. 1 for higher is better, -1 for lower is ' +
2661 'better, 0 for ignore (default).')
2662 group.add_option('-r', '--repeat_test_count',
2663 type='int',
2664 default=20,
2665 help='The number of times to repeat the performance '
2666 'test. Values will be clamped to range [1, 100]. '
2667 'Default value is 20.')
2668 group.add_option('--max_time_minutes',
2669 type='int',
2670 default=20,
2671 help='The maximum time (in minutes) to take running the '
2672 'performance tests. The script will run the performance '
2673 'tests according to --repeat_test_count, so long as it '
2674 'doesn\'t exceed --max_time_minutes. Values will be '
2675 'clamped to range [1, 60].'
2676 'Default value is 20.')
2677 group.add_option('-t', '--truncate_percent',
2678 type='int',
2679 default=25,
2680 help='The highest/lowest % are discarded to form a '
2681 'truncated mean. Values will be clamped to range [0, '
2682 '25]. Default value is 25 (highest/lowest 25% will be '
2683 'discarded).')
2684 group.add_option('--bisect_mode',
2685 type='choice',
2686 choices=[bisect_utils.BISECT_MODE_MEAN,
2687 bisect_utils.BISECT_MODE_STD_DEV,
2688 bisect_utils.BISECT_MODE_RETURN_CODE],
2689 default=bisect_utils.BISECT_MODE_MEAN,
2690 help='The bisect mode. Choices are to bisect on the '
2691 'difference in mean, std_dev, or return_code.')
2692 parser.add_option_group(group)
2693
2694 group = optparse.OptionGroup(parser, 'Build options')
2695 group.add_option('-w', '--working_directory',
2696 type='str',
2697 help='Path to the working directory where the script '
2698 'will do an initial checkout of the chromium depot. The '
2699 'files will be placed in a subdirectory "bisect" under '
2700 'working_directory and that will be used to perform the '
2701 'bisection. This parameter is optional, if it is not '
2702 'supplied, the script will work from the current depot.')
2703 group.add_option('--build_preference',
2704 type='choice',
2705 choices=['msvs', 'ninja', 'make'],
2706 help='The preferred build system to use. On linux/mac '
2707 'the options are make/ninja. On Windows, the options '
2708 'are msvs/ninja.')
2709 group.add_option('--target_platform',
2710 type='choice',
2711 choices=['chromium', 'cros', 'android', 'android-chrome'],
2712 default='chromium',
2713 help='The target platform. Choices are "chromium" '
2714 '(current platform), "cros", or "android". If you '
2715 'specify something other than "chromium", you must be '
2716 'properly set up to build that platform.')
2717 group.add_option('--no_custom_deps',
2718 dest='no_custom_deps',
2719 action='store_true',
2720 default=False,
2721 help='Run the script with custom_deps or not.')
2722 group.add_option('--extra_src',
2723 type='str',
2724 help='Path to a script which can be used to modify '
2725 'the bisect script\'s behavior.')
2726 group.add_option('--cros_board',
2727 type='str',
2728 help='The cros board type to build.')
2729 group.add_option('--cros_remote_ip',
2730 type='str',
2731 help='The remote machine to image to.')
2732 group.add_option('--use_goma',
2733 action='store_true',
2734 help='Add a bunch of extra threads for goma, and enable '
2735 'goma')
2736 group.add_option('--goma_dir',
2737 help='Path to goma tools (or system default if not '
2738 'specified).')
2739 group.add_option('--goma_threads',
2740 type='int',
2741 default='64',
2742 help='Number of threads for goma, only if using goma.')
2743 group.add_option('--output_buildbot_annotations',
2744 action='store_true',
2745 help='Add extra annotation output for buildbot.')
2746 group.add_option('--gs_bucket',
2747 default='',
2748 dest='gs_bucket',
2749 type='str',
2750 help=('Name of Google Storage bucket to upload or '
2751 'download build. e.g., chrome-perf'))
2752 group.add_option('--target_arch',
2753 type='choice',
2754 choices=['ia32', 'x64', 'arm'],
2755 default='ia32',
2756 dest='target_arch',
2757 help=('The target build architecture. Choices are "ia32" '
2758 '(default), "x64" or "arm".'))
2759 group.add_option('--target_build_type',
2760 type='choice',
2761 choices=['Release', 'Debug'],
2762 default='Release',
2763 help='The target build type. Choices are "Release" '
2764 '(default), or "Debug".')
2765 group.add_option('--builder_host',
2766 dest='builder_host',
2767 type='str',
2768 help=('Host address of server to produce build by posting'
2769 ' try job request.'))
2770 group.add_option('--builder_port',
2771 dest='builder_port',
2772 type='int',
2773 help=('HTTP port of the server to produce build by posting'
2774 ' try job request.'))
2775 parser.add_option_group(group)
2776
2777 group = optparse.OptionGroup(parser, 'Debug options')
2778 group.add_option('--debug_ignore_build',
2779 action='store_true',
2780 help='DEBUG: Don\'t perform builds.')
2781 group.add_option('--debug_ignore_sync',
2782 action='store_true',
2783 help='DEBUG: Don\'t perform syncs.')
2784 group.add_option('--debug_ignore_perf_test',
2785 action='store_true',
2786 help='DEBUG: Don\'t perform performance tests.')
2787 group.add_option('--debug_ignore_regression_confidence',
2788 action='store_true',
2789 help='DEBUG: Don\'t score the confidence of the initial '
2790 'good and bad revisions\' test results.')
2791 group.add_option('--debug_fake_first_test_mean',
2792 type='int',
2793 default='0',
2794 help=('DEBUG: When faking performance tests, return this '
2795 'value as the mean of the first performance test, '
2796 'and return a mean of 0.0 for further tests.'))
2797 parser.add_option_group(group)
2798 return parser 2624 return parser
2799 2625
2800 def ParseCommandLine(self): 2626 def ParseCommandLine(self):
2801 """Parses the command line for bisect options.""" 2627 """Parses the command line for bisect options."""
2802 parser = self._CreateCommandLineParser() 2628 parser = self._CreateCommandLineParser()
2803 opts, _ = parser.parse_args() 2629 opts, _ = parser.parse_args()
2804 2630
2805 try: 2631 try:
2806 if not opts.command:
2807 raise RuntimeError('missing required parameter: --command')
2808
2809 if not opts.good_revision:
2810 raise RuntimeError('missing required parameter: --good_revision')
2811
2812 if not opts.bad_revision:
2813 raise RuntimeError('missing required parameter: --bad_revision')
2814
2815 if (not opts.metric and 2632 if (not opts.metric and
2816 opts.bisect_mode != bisect_utils.BISECT_MODE_RETURN_CODE): 2633 opts.bisect_mode != bisect_utils.BISECT_MODE_RETURN_CODE):
2817 raise RuntimeError('missing required parameter: --metric') 2634 raise RuntimeError('missing required parameter: --metric')
2818 2635
2819 if opts.gs_bucket: 2636 if opts.gs_bucket:
2820 if not cloud_storage.List(opts.gs_bucket): 2637 if not cloud_storage.List(opts.gs_bucket):
2821 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket) 2638 raise RuntimeError('Invalid Google Storage: gs://%s' % opts.gs_bucket)
2822 if not opts.builder_host: 2639 if not opts.builder_host:
2823 raise RuntimeError('Must specify try server host name using ' 2640 raise RuntimeError('Must specify try server host name using '
2824 '--builder_host when gs_bucket is used.') 2641 '--builder_host when gs_bucket is used.')
2825 if not opts.builder_port: 2642 if not opts.builder_port:
2826 raise RuntimeError('Must specify try server port number using ' 2643 raise RuntimeError('Must specify try server port number using '
2827 '--builder_port when gs_bucket is used.') 2644 '--builder_port when gs_bucket is used.')
2828 if opts.target_platform == 'cros':
2829 # Run sudo up front to make sure credentials are cached for later.
2830 print 'Sudo is required to build cros:'
2831 print
2832 bisect_utils.RunProcess(['sudo', 'true'])
2833
2834 if not opts.cros_board:
2835 raise RuntimeError('missing required parameter: --cros_board')
2836
2837 if not opts.cros_remote_ip:
2838 raise RuntimeError('missing required parameter: --cros_remote_ip')
2839
2840 if not opts.working_directory:
2841 raise RuntimeError('missing required parameter: --working_directory')
2842 2645
2843 if opts.bisect_mode != bisect_utils.BISECT_MODE_RETURN_CODE: 2646 if opts.bisect_mode != bisect_utils.BISECT_MODE_RETURN_CODE:
2844 metric_values = opts.metric.split('/') 2647 metric_values = opts.metric.split('/')
2845 if len(metric_values) != 2: 2648 if len(metric_values) != 2:
2846 raise RuntimeError('Invalid metric specified: [%s]' % opts.metric) 2649 raise RuntimeError('Invalid metric specified: [%s]' % opts.metric)
2847 opts.metric = metric_values 2650 opts.metric = metric_values
2848 2651
2849 opts.repeat_test_count = min(max(opts.repeat_test_count, 1), 100) 2652 opts.truncate_percent = min(max(opts.truncate_percent, 0), 25) / 100.0
2850 opts.max_time_minutes = min(max(opts.max_time_minutes, 1), 60)
2851 opts.truncate_percent = min(max(opts.truncate_percent, 0), 25)
2852 opts.truncate_percent = opts.truncate_percent / 100.0
2853 2653
2854 for k, v in opts.__dict__.iteritems(): 2654 for k, v in opts.__dict__.iteritems():
2855 assert hasattr(self, k), 'Invalid %s attribute in BisectOptions.' % k 2655 assert hasattr(self, k), 'Invalid %s attribute in BisectOptions.' % k
2856 setattr(self, k, v) 2656 setattr(self, k, v)
2857 except RuntimeError, e: 2657 except RuntimeError, e:
2858 output_string = StringIO.StringIO() 2658 output_string = StringIO.StringIO()
2859 parser.print_help(file=output_string) 2659 parser.print_help(file=output_string)
2860 error_message = '%s\n\n%s' % (e.message, output_string.getvalue()) 2660 error_message = '%s\n\n%s' % (e.message, output_string.getvalue())
2861 output_string.close() 2661 output_string.close()
2862 raise RuntimeError(error_message) 2662 raise RuntimeError(error_message)
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
2929 2729
2930 if not source_control.IsInGitRepository(): 2730 if not source_control.IsInGitRepository():
2931 raise RuntimeError( 2731 raise RuntimeError(
2932 'Sorry, only the git workflow is supported at the moment.') 2732 'Sorry, only the git workflow is supported at the moment.')
2933 2733
2934 # gClient sync seems to fail if you're not in master branch. 2734 # gClient sync seems to fail if you're not in master branch.
2935 if (not source_control.IsInProperBranch() and 2735 if (not source_control.IsInProperBranch() and
2936 not opts.debug_ignore_sync and 2736 not opts.debug_ignore_sync and
2937 not opts.working_directory): 2737 not opts.working_directory):
2938 raise RuntimeError('You must switch to master branch to run bisection.') 2738 raise RuntimeError('You must switch to master branch to run bisection.')
2939 bisect_test = BisectPerformanceMetrics(opts) 2739 bisect_test = BisectPerformanceMetrics(opts, os.getcwd())
2940 bisect_printer = BisectPrinter(opts, bisect_test.depot_registry)
2941 try: 2740 try:
2942 results = bisect_test.Run(opts.command, opts.bad_revision, 2741 results = bisect_test.Run(opts.command, opts.bad_revision,
2943 opts.good_revision, opts.metric) 2742 opts.good_revision, opts.metric)
2944 if results.error: 2743 if results.error:
2945 raise RuntimeError(results.error) 2744 raise RuntimeError(results.error)
2946 bisect_printer.FormatAndPrintResults(results) 2745 bisect_test.printer.FormatAndPrintResults(results)
2947 return 0 2746 return 0
2948 finally: 2747 finally:
2949 bisect_test.PerformCleanup() 2748 bisect_test.PerformCleanup()
2950 except RuntimeError, e: 2749 except RuntimeError, e:
2951 if opts.output_buildbot_annotations: 2750 if opts.output_buildbot_annotations:
2952 # The perf dashboard scrapes the "results" step in order to comment on 2751 # The perf dashboard scrapes the "results" step in order to comment on
2953 # bugs. If you change this, please update the perf dashboard as well. 2752 # bugs. If you change this, please update the perf dashboard as well.
2954 bisect_utils.OutputAnnotationStepStart('Results') 2753 bisect_utils.OutputAnnotationStepStart('Results')
2955 print 'Error: ', e.message 2754 print 'Error: ', e.message
2956 logging.warn('A RuntimeError was caught: %s', e.message) 2755 logging.warn('A RuntimeError was caught: %s', e.message)
2957 if opts.output_buildbot_annotations: 2756 if opts.output_buildbot_annotations:
2958 bisect_utils.OutputAnnotationStepClosed() 2757 bisect_utils.OutputAnnotationStepClosed()
2959 return 1 2758 return 1
2960 2759
2961 2760
2962 if __name__ == '__main__': 2761 if __name__ == '__main__':
2963 sys.exit(main()) 2762 sys.exit(main())
OLDNEW
« no previous file with comments | « tools/auto_bisect/bisect.cfg ('k') | tools/auto_bisect/bisect_perf_regression_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698