| OLD | NEW |
| 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 28 matching lines...) Expand all Loading... |
| 39 import datetime | 39 import datetime |
| 40 import errno | 40 import errno |
| 41 import hashlib | 41 import hashlib |
| 42 import math | 42 import math |
| 43 import optparse | 43 import optparse |
| 44 import os | 44 import os |
| 45 import re | 45 import re |
| 46 import shlex | 46 import shlex |
| 47 import shutil | 47 import shutil |
| 48 import StringIO | 48 import StringIO |
| 49 import subprocess | |
| 50 import sys | 49 import sys |
| 51 import time | 50 import time |
| 52 import zipfile | 51 import zipfile |
| 53 | 52 |
| 54 sys.path.append(os.path.join(os.path.dirname(__file__), 'telemetry')) | 53 sys.path.append(os.path.join(os.path.dirname(__file__), 'telemetry')) |
| 55 | 54 |
| 56 from auto_bisect import bisect_utils | 55 from auto_bisect import bisect_utils |
| 57 from auto_bisect import post_perf_builder_job as bisect_builder | 56 from auto_bisect import post_perf_builder_job as bisect_builder |
| 57 from auto_bisect import source_control as source_control_module |
| 58 from telemetry.util import cloud_storage | 58 from telemetry.util import cloud_storage |
| 59 | 59 |
| 60 # The additional repositories that might need to be bisected. | 60 # The additional repositories that might need to be bisected. |
| 61 # If the repository has any dependant repositories (such as skia/src needs | 61 # If the repository has any dependant repositories (such as skia/src needs |
| 62 # skia/include and skia/gyp to be updated), specify them in the 'depends' | 62 # skia/include and skia/gyp to be updated), specify them in the 'depends' |
| 63 # so that they're synced appropriately. | 63 # so that they're synced appropriately. |
| 64 # Format is: | 64 # Format is: |
| 65 # src: path to the working directory. | 65 # src: path to the working directory. |
| 66 # recurse: True if this repositry will get bisected. | 66 # recurse: True if this repositry will get bisected. |
| 67 # depends: A list of other repositories that are actually part of the same | 67 # depends: A list of other repositories that are actually part of the same |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 136 "recurse" : False, | 136 "recurse" : False, |
| 137 "svn" : "http://skia.googlecode.com/svn/trunk/gyp", | 137 "svn" : "http://skia.googlecode.com/svn/trunk/gyp", |
| 138 "depends" : None, | 138 "depends" : None, |
| 139 "from" : ['chromium'], | 139 "from" : ['chromium'], |
| 140 'viewvc': 'https://code.google.com/p/skia/source/detail?r=', | 140 'viewvc': 'https://code.google.com/p/skia/source/detail?r=', |
| 141 'deps_var': 'None' | 141 'deps_var': 'None' |
| 142 }, | 142 }, |
| 143 } | 143 } |
| 144 | 144 |
| 145 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() | 145 DEPOT_NAMES = DEPOT_DEPS_NAME.keys() |
| 146 |
| 146 CROS_SDK_PATH = os.path.join('..', 'cros', 'chromite', 'bin', 'cros_sdk') | 147 CROS_SDK_PATH = os.path.join('..', 'cros', 'chromite', 'bin', 'cros_sdk') |
| 147 CROS_VERSION_PATTERN = 'new version number from %s' | |
| 148 CROS_CHROMEOS_PATTERN = 'chromeos-base/chromeos-chrome' | 148 CROS_CHROMEOS_PATTERN = 'chromeos-base/chromeos-chrome' |
| 149 CROS_TEST_KEY_PATH = os.path.join('..', 'cros', 'chromite', 'ssh_keys', | 149 CROS_TEST_KEY_PATH = os.path.join('..', 'cros', 'chromite', 'ssh_keys', |
| 150 'testing_rsa') | 150 'testing_rsa') |
| 151 CROS_SCRIPT_KEY_PATH = os.path.join('..', 'cros', 'src', 'scripts', | 151 CROS_SCRIPT_KEY_PATH = os.path.join('..', 'cros', 'src', 'scripts', |
| 152 'mod_for_test_scripts', 'ssh_keys', | 152 'mod_for_test_scripts', 'ssh_keys', |
| 153 'testing_rsa') | 153 'testing_rsa') |
| 154 | 154 |
| 155 BUILD_RESULT_SUCCEED = 0 | 155 BUILD_RESULT_SUCCEED = 0 |
| 156 BUILD_RESULT_FAIL = 1 | 156 BUILD_RESULT_FAIL = 1 |
| 157 BUILD_RESULT_SKIPPED = 2 | 157 BUILD_RESULT_SKIPPED = 2 |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 def CalculateStandardError(values): | 406 def CalculateStandardError(values): |
| 407 """Calculates the standard error of a list of values.""" | 407 """Calculates the standard error of a list of values.""" |
| 408 if len(values) <= 1: | 408 if len(values) <= 1: |
| 409 return 0.0 | 409 return 0.0 |
| 410 | 410 |
| 411 std_dev = CalculateStandardDeviation(values) | 411 std_dev = CalculateStandardDeviation(values) |
| 412 | 412 |
| 413 return std_dev / math.sqrt(len(values)) | 413 return std_dev / math.sqrt(len(values)) |
| 414 | 414 |
| 415 | 415 |
| 416 def IsStringFloat(string_to_check): | |
| 417 """Checks whether or not the given string can be converted to a floating | |
| 418 point number. | |
| 419 | |
| 420 Args: | |
| 421 string_to_check: Input string to check if it can be converted to a float. | |
| 422 | |
| 423 Returns: | |
| 424 True if the string can be converted to a float. | |
| 425 """ | |
| 426 try: | |
| 427 float(string_to_check) | |
| 428 | |
| 429 return True | |
| 430 except ValueError: | |
| 431 return False | |
| 432 | |
| 433 | |
| 434 def IsStringInt(string_to_check): | |
| 435 """Checks whether or not the given string can be converted to a integer. | |
| 436 | |
| 437 Args: | |
| 438 string_to_check: Input string to check if it can be converted to an int. | |
| 439 | |
| 440 Returns: | |
| 441 True if the string can be converted to an int. | |
| 442 """ | |
| 443 try: | |
| 444 int(string_to_check) | |
| 445 | |
| 446 return True | |
| 447 except ValueError: | |
| 448 return False | |
| 449 | |
| 450 | |
| 451 def IsWindowsHost(): | |
| 452 """Checks whether or not the script is running on Windows. | |
| 453 | |
| 454 Returns: | |
| 455 True if running on Windows. | |
| 456 """ | |
| 457 return sys.platform == 'cygwin' or sys.platform.startswith('win') | |
| 458 | |
| 459 | |
| 460 def Is64BitWindows(): | |
| 461 """Returns whether or not Windows is a 64-bit version. | |
| 462 | |
| 463 Returns: | |
| 464 True if Windows is 64-bit, False if 32-bit. | |
| 465 """ | |
| 466 platform = os.environ['PROCESSOR_ARCHITECTURE'] | |
| 467 try: | |
| 468 platform = os.environ['PROCESSOR_ARCHITEW6432'] | |
| 469 except KeyError: | |
| 470 # Must not be running in WoW64, so PROCESSOR_ARCHITECTURE is correct | |
| 471 pass | |
| 472 | |
| 473 return platform in ['AMD64', 'I64'] | |
| 474 | |
| 475 | |
| 476 def IsLinuxHost(): | |
| 477 """Checks whether or not the script is running on Linux. | |
| 478 | |
| 479 Returns: | |
| 480 True if running on Linux. | |
| 481 """ | |
| 482 return sys.platform.startswith('linux') | |
| 483 | |
| 484 | |
| 485 def IsMacHost(): | |
| 486 """Checks whether or not the script is running on Mac. | |
| 487 | |
| 488 Returns: | |
| 489 True if running on Mac. | |
| 490 """ | |
| 491 return sys.platform.startswith('darwin') | |
| 492 | 416 |
| 493 | 417 |
| 494 def GetSHA1HexDigest(contents): | 418 def GetSHA1HexDigest(contents): |
| 495 """Returns secured hash containing hexadecimal for the given contents.""" | 419 """Returns secured hash containing hexadecimal for the given contents.""" |
| 496 return hashlib.sha1(contents).hexdigest() | 420 return hashlib.sha1(contents).hexdigest() |
| 497 | 421 |
| 498 | 422 |
| 499 def GetZipFileName(build_revision=None, target_arch='ia32', patch_sha=None): | 423 def GetZipFileName(build_revision=None, target_arch='ia32', patch_sha=None): |
| 500 """Gets the archive file name for the given revision.""" | 424 """Gets the archive file name for the given revision.""" |
| 501 def PlatformName(): | 425 def PlatformName(): |
| 502 """Return a string to be used in paths for the platform.""" | 426 """Return a string to be used in paths for the platform.""" |
| 503 if IsWindowsHost(): | 427 if bisect_utils.IsWindowsHost(): |
| 504 # Build archive for x64 is still stored with 'win32'suffix | 428 # Build archive for x64 is still stored with 'win32'suffix |
| 505 # (chromium_utils.PlatformName()). | 429 # (chromium_utils.PlatformName()). |
| 506 if Is64BitWindows() and target_arch == 'x64': | 430 if bisect_utils.Is64BitWindows() and target_arch == 'x64': |
| 507 return 'win32' | 431 return 'win32' |
| 508 return 'win32' | 432 return 'win32' |
| 509 if IsLinuxHost(): | 433 if bisect_utils.IsLinuxHost(): |
| 510 # Android builds too are archived with full-build-linux* prefix. | 434 # Android builds too are archived with full-build-linux* prefix. |
| 511 return 'linux' | 435 return 'linux' |
| 512 if IsMacHost(): | 436 if bisect_utils.IsMacHost(): |
| 513 return 'mac' | 437 return 'mac' |
| 514 raise NotImplementedError('Unknown platform "%s".' % sys.platform) | 438 raise NotImplementedError('Unknown platform "%s".' % sys.platform) |
| 515 | 439 |
| 516 base_name = 'full-build-%s' % PlatformName() | 440 base_name = 'full-build-%s' % PlatformName() |
| 517 if not build_revision: | 441 if not build_revision: |
| 518 return base_name | 442 return base_name |
| 519 if patch_sha: | 443 if patch_sha: |
| 520 build_revision = '%s_%s' % (build_revision , patch_sha) | 444 build_revision = '%s_%s' % (build_revision , patch_sha) |
| 521 return '%s_%s.zip' % (base_name, build_revision) | 445 return '%s_%s.zip' % (base_name, build_revision) |
| 522 | 446 |
| 523 | 447 |
| 524 def GetRemoteBuildPath(build_revision, target_platform='chromium', | 448 def GetRemoteBuildPath(build_revision, target_platform='chromium', |
| 525 target_arch='ia32', patch_sha=None): | 449 target_arch='ia32', patch_sha=None): |
| 526 """Compute the url to download the build from.""" | 450 """Compute the url to download the build from.""" |
| 527 def GetGSRootFolderName(target_platform): | 451 def GetGSRootFolderName(target_platform): |
| 528 """Gets Google Cloud Storage root folder names""" | 452 """Gets Google Cloud Storage root folder names""" |
| 529 if IsWindowsHost(): | 453 if bisect_utils.IsWindowsHost(): |
| 530 if Is64BitWindows() and target_arch == 'x64': | 454 if bisect_utils.Is64BitWindows() and target_arch == 'x64': |
| 531 return 'Win x64 Builder' | 455 return 'Win x64 Builder' |
| 532 return 'Win Builder' | 456 return 'Win Builder' |
| 533 if IsLinuxHost(): | 457 if bisect_utils.IsLinuxHost(): |
| 534 if target_platform == 'android': | 458 if target_platform == 'android': |
| 535 return 'android_perf_rel' | 459 return 'android_perf_rel' |
| 536 return 'Linux Builder' | 460 return 'Linux Builder' |
| 537 if IsMacHost(): | 461 if bisect_utils.IsMacHost(): |
| 538 return 'Mac Builder' | 462 return 'Mac Builder' |
| 539 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform) | 463 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform) |
| 540 | 464 |
| 541 base_filename = GetZipFileName( | 465 base_filename = GetZipFileName( |
| 542 build_revision, target_arch, patch_sha) | 466 build_revision, target_arch, patch_sha) |
| 543 builder_folder = GetGSRootFolderName(target_platform) | 467 builder_folder = GetGSRootFolderName(target_platform) |
| 544 return '%s/%s' % (builder_folder, base_filename) | 468 return '%s/%s' % (builder_folder, base_filename) |
| 545 | 469 |
| 546 | 470 |
| 547 def FetchFromCloudStorage(bucket_name, source_path, destination_path): | 471 def FetchFromCloudStorage(bucket_name, source_path, destination_path): |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 593 # handle links and file bits (executable), which is much | 517 # handle links and file bits (executable), which is much |
| 594 # easier then trying to do that with ZipInfo options. | 518 # easier then trying to do that with ZipInfo options. |
| 595 # | 519 # |
| 596 # The Mac Version of unzip unfortunately does not support Zip64, whereas | 520 # The Mac Version of unzip unfortunately does not support Zip64, whereas |
| 597 # the python module does, so we have to fallback to the python zip module | 521 # the python module does, so we have to fallback to the python zip module |
| 598 # on Mac if the filesize is greater than 4GB. | 522 # on Mac if the filesize is greater than 4GB. |
| 599 # | 523 # |
| 600 # On Windows, try to use 7z if it is installed, otherwise fall back to python | 524 # On Windows, try to use 7z if it is installed, otherwise fall back to python |
| 601 # zip module and pray we don't have files larger than 512MB to unzip. | 525 # zip module and pray we don't have files larger than 512MB to unzip. |
| 602 unzip_cmd = None | 526 unzip_cmd = None |
| 603 if ((IsMacHost() and os.path.getsize(filename) < 4 * 1024 * 1024 * 1024) | 527 if ((bisect_utils.IsMacHost() |
| 604 or IsLinuxHost()): | 528 and os.path.getsize(filename) < 4 * 1024 * 1024 * 1024) |
| 529 or bisect_utils.IsLinuxHost()): |
| 605 unzip_cmd = ['unzip', '-o'] | 530 unzip_cmd = ['unzip', '-o'] |
| 606 elif IsWindowsHost() and os.path.exists('C:\\Program Files\\7-Zip\\7z.exe'): | 531 elif (bisect_utils.IsWindowsHost() |
| 532 and os.path.exists('C:\\Program Files\\7-Zip\\7z.exe')): |
| 607 unzip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'x', '-y'] | 533 unzip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'x', '-y'] |
| 608 | 534 |
| 609 if unzip_cmd: | 535 if unzip_cmd: |
| 610 # Make sure path is absolute before changing directories. | 536 # Make sure path is absolute before changing directories. |
| 611 filepath = os.path.abspath(filename) | 537 filepath = os.path.abspath(filename) |
| 612 saved_dir = os.getcwd() | 538 saved_dir = os.getcwd() |
| 613 os.chdir(output_dir) | 539 os.chdir(output_dir) |
| 614 command = unzip_cmd + [filepath] | 540 command = unzip_cmd + [filepath] |
| 615 result = RunProcess(command) | 541 result = bisect_utils.RunProcess(command) |
| 616 os.chdir(saved_dir) | 542 os.chdir(saved_dir) |
| 617 if result: | 543 if result: |
| 618 raise IOError('unzip failed: %s => %s' % (str(command), result)) | 544 raise IOError('unzip failed: %s => %s' % (str(command), result)) |
| 619 else: | 545 else: |
| 620 assert IsWindowsHost() or IsMacHost() | 546 assert bisect_utils.IsWindowsHost() or bisect_utils.IsMacHost() |
| 621 zf = zipfile.ZipFile(filename) | 547 zf = zipfile.ZipFile(filename) |
| 622 for name in zf.namelist(): | 548 for name in zf.namelist(): |
| 623 if verbose: | 549 if verbose: |
| 624 print 'Extracting %s' % name | 550 print 'Extracting %s' % name |
| 625 zf.extract(name, output_dir) | 551 zf.extract(name, output_dir) |
| 626 if IsMacHost(): | 552 if bisect_utils.IsMacHost(): |
| 627 # Restore permission bits. | 553 # Restore permission bits. |
| 628 os.chmod(os.path.join(output_dir, name), | 554 os.chmod(os.path.join(output_dir, name), |
| 629 zf.getinfo(name).external_attr >> 16L) | 555 zf.getinfo(name).external_attr >> 16L) |
| 630 | 556 |
| 631 | 557 |
| 632 def RunProcess(command): | |
| 633 """Runs an arbitrary command. | |
| 634 | |
| 635 If output from the call is needed, use RunProcessAndRetrieveOutput instead. | |
| 636 | |
| 637 Args: | |
| 638 command: A list containing the command and args to execute. | |
| 639 | |
| 640 Returns: | |
| 641 The return code of the call. | |
| 642 """ | |
| 643 # On Windows, use shell=True to get PATH interpretation. | |
| 644 shell = IsWindowsHost() | |
| 645 return subprocess.call(command, shell=shell) | |
| 646 | |
| 647 | |
| 648 def RunProcessAndRetrieveOutput(command, cwd=None): | |
| 649 """Runs an arbitrary command, returning its output and return code. | |
| 650 | |
| 651 Since output is collected via communicate(), there will be no output until | |
| 652 the call terminates. If you need output while the program runs (ie. so | |
| 653 that the buildbot doesn't terminate the script), consider RunProcess(). | |
| 654 | |
| 655 Args: | |
| 656 command: A list containing the command and args to execute. | |
| 657 cwd: A directory to change to while running the command. The command can be | |
| 658 relative to this directory. If this is None, the command will be run in | |
| 659 the current directory. | |
| 660 | |
| 661 Returns: | |
| 662 A tuple of the output and return code. | |
| 663 """ | |
| 664 if cwd: | |
| 665 original_cwd = os.getcwd() | |
| 666 os.chdir(cwd) | |
| 667 | |
| 668 # On Windows, use shell=True to get PATH interpretation. | |
| 669 shell = IsWindowsHost() | |
| 670 proc = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE) | |
| 671 (output, _) = proc.communicate() | |
| 672 | |
| 673 if cwd: | |
| 674 os.chdir(original_cwd) | |
| 675 | |
| 676 return (output, proc.returncode) | |
| 677 | |
| 678 | |
| 679 def RunGit(command, cwd=None): | |
| 680 """Run a git subcommand, returning its output and return code. | |
| 681 | |
| 682 Args: | |
| 683 command: A list containing the args to git. | |
| 684 cwd: A directory to change to while running the git command (optional). | |
| 685 | |
| 686 Returns: | |
| 687 A tuple of the output and return code. | |
| 688 """ | |
| 689 command = ['git'] + command | |
| 690 | |
| 691 return RunProcessAndRetrieveOutput(command, cwd=cwd) | |
| 692 | |
| 693 | |
| 694 def CheckRunGit(command, cwd=None): | |
| 695 """Run a git subcommand, returning its output and return code. Asserts if | |
| 696 the return code of the call is non-zero. | |
| 697 | |
| 698 Args: | |
| 699 command: A list containing the args to git. | |
| 700 | |
| 701 Returns: | |
| 702 A tuple of the output and return code. | |
| 703 """ | |
| 704 (output, return_code) = RunGit(command, cwd=cwd) | |
| 705 | |
| 706 assert not return_code, 'An error occurred while running'\ | |
| 707 ' "git %s"' % ' '.join(command) | |
| 708 return output | |
| 709 | 558 |
| 710 | 559 |
| 711 def SetBuildSystemDefault(build_system, use_goma, goma_dir): | 560 def SetBuildSystemDefault(build_system, use_goma, goma_dir): |
| 712 """Sets up any environment variables needed to build with the specified build | 561 """Sets up any environment variables needed to build with the specified build |
| 713 system. | 562 system. |
| 714 | 563 |
| 715 Args: | 564 Args: |
| 716 build_system: A string specifying build system. Currently only 'ninja' or | 565 build_system: A string specifying build system. Currently only 'ninja' or |
| 717 'make' are supported.""" | 566 'make' are supported.""" |
| 718 if build_system == 'ninja': | 567 if build_system == 'ninja': |
| 719 gyp_var = os.getenv('GYP_GENERATORS') | 568 gyp_var = os.getenv('GYP_GENERATORS') |
| 720 | 569 |
| 721 if not gyp_var or not 'ninja' in gyp_var: | 570 if not gyp_var or not 'ninja' in gyp_var: |
| 722 if gyp_var: | 571 if gyp_var: |
| 723 os.environ['GYP_GENERATORS'] = gyp_var + ',ninja' | 572 os.environ['GYP_GENERATORS'] = gyp_var + ',ninja' |
| 724 else: | 573 else: |
| 725 os.environ['GYP_GENERATORS'] = 'ninja' | 574 os.environ['GYP_GENERATORS'] = 'ninja' |
| 726 | 575 |
| 727 if IsWindowsHost(): | 576 if bisect_utils.IsWindowsHost(): |
| 728 os.environ['GYP_DEFINES'] = 'component=shared_library '\ | 577 os.environ['GYP_DEFINES'] = 'component=shared_library '\ |
| 729 'incremental_chrome_dll=1 disable_nacl=1 fastbuild=1 '\ | 578 'incremental_chrome_dll=1 disable_nacl=1 fastbuild=1 '\ |
| 730 'chromium_win_pch=0' | 579 'chromium_win_pch=0' |
| 731 | 580 |
| 732 elif build_system == 'make': | 581 elif build_system == 'make': |
| 733 os.environ['GYP_GENERATORS'] = 'make' | 582 os.environ['GYP_GENERATORS'] = 'make' |
| 734 else: | 583 else: |
| 735 raise RuntimeError('%s build not supported.' % build_system) | 584 raise RuntimeError('%s build not supported.' % build_system) |
| 736 | 585 |
| 737 if use_goma: | 586 if use_goma: |
| 738 os.environ['GYP_DEFINES'] = '%s %s' % (os.getenv('GYP_DEFINES', ''), | 587 os.environ['GYP_DEFINES'] = '%s %s' % (os.getenv('GYP_DEFINES', ''), |
| 739 'use_goma=1') | 588 'use_goma=1') |
| 740 if goma_dir: | 589 if goma_dir: |
| 741 os.environ['GYP_DEFINES'] += ' gomadir=%s' % goma_dir | 590 os.environ['GYP_DEFINES'] += ' gomadir=%s' % goma_dir |
| 742 | 591 |
| 743 | 592 |
| 744 def BuildWithMake(threads, targets, build_type='Release'): | 593 def BuildWithMake(threads, targets, build_type='Release'): |
| 745 cmd = ['make', 'BUILDTYPE=%s' % build_type] | 594 cmd = ['make', 'BUILDTYPE=%s' % build_type] |
| 746 | 595 |
| 747 if threads: | 596 if threads: |
| 748 cmd.append('-j%d' % threads) | 597 cmd.append('-j%d' % threads) |
| 749 | 598 |
| 750 cmd += targets | 599 cmd += targets |
| 751 | 600 |
| 752 return_code = RunProcess(cmd) | 601 return_code = bisect_utils.RunProcess(cmd) |
| 753 | 602 |
| 754 return not return_code | 603 return not return_code |
| 755 | 604 |
| 756 | 605 |
| 757 def BuildWithNinja(threads, targets, build_type='Release'): | 606 def BuildWithNinja(threads, targets, build_type='Release'): |
| 758 cmd = ['ninja', '-C', os.path.join('out', build_type)] | 607 cmd = ['ninja', '-C', os.path.join('out', build_type)] |
| 759 | 608 |
| 760 if threads: | 609 if threads: |
| 761 cmd.append('-j%d' % threads) | 610 cmd.append('-j%d' % threads) |
| 762 | 611 |
| 763 cmd += targets | 612 cmd += targets |
| 764 | 613 |
| 765 return_code = RunProcess(cmd) | 614 return_code = bisect_utils.RunProcess(cmd) |
| 766 | 615 |
| 767 return not return_code | 616 return not return_code |
| 768 | 617 |
| 769 | 618 |
| 770 def BuildWithVisualStudio(targets, build_type='Release'): | 619 def BuildWithVisualStudio(targets, build_type='Release'): |
| 771 path_to_devenv = os.path.abspath( | 620 path_to_devenv = os.path.abspath( |
| 772 os.path.join(os.environ['VS100COMNTOOLS'], '..', 'IDE', 'devenv.com')) | 621 os.path.join(os.environ['VS100COMNTOOLS'], '..', 'IDE', 'devenv.com')) |
| 773 path_to_sln = os.path.join(os.getcwd(), 'chrome', 'chrome.sln') | 622 path_to_sln = os.path.join(os.getcwd(), 'chrome', 'chrome.sln') |
| 774 cmd = [path_to_devenv, '/build', build_type, path_to_sln] | 623 cmd = [path_to_devenv, '/build', build_type, path_to_sln] |
| 775 | 624 |
| 776 for t in targets: | 625 for t in targets: |
| 777 cmd.extend(['/Project', t]) | 626 cmd.extend(['/Project', t]) |
| 778 | 627 |
| 779 return_code = RunProcess(cmd) | 628 return_code = bisect_utils.RunProcess(cmd) |
| 780 | 629 |
| 781 return not return_code | 630 return not return_code |
| 782 | 631 |
| 783 | 632 |
| 784 def WriteStringToFile(text, file_name): | 633 def WriteStringToFile(text, file_name): |
| 785 try: | 634 try: |
| 786 with open(file_name, "wb") as f: | 635 with open(file_name, "wb") as f: |
| 787 f.write(text) | 636 f.write(text) |
| 788 except IOError as e: | 637 except IOError as e: |
| 789 raise RuntimeError('Error writing to file [%s]' % file_name ) | 638 raise RuntimeError('Error writing to file [%s]' % file_name ) |
| (...skipping 21 matching lines...) Expand all Loading... |
| 811 | 660 |
| 812 class Builder(object): | 661 class Builder(object): |
| 813 """Builder is used by the bisect script to build relevant targets and deploy. | 662 """Builder is used by the bisect script to build relevant targets and deploy. |
| 814 """ | 663 """ |
| 815 def __init__(self, opts): | 664 def __init__(self, opts): |
| 816 """Performs setup for building with target build system. | 665 """Performs setup for building with target build system. |
| 817 | 666 |
| 818 Args: | 667 Args: |
| 819 opts: Options parsed from command line. | 668 opts: Options parsed from command line. |
| 820 """ | 669 """ |
| 821 if IsWindowsHost(): | 670 if bisect_utils.IsWindowsHost(): |
| 822 if not opts.build_preference: | 671 if not opts.build_preference: |
| 823 opts.build_preference = 'msvs' | 672 opts.build_preference = 'msvs' |
| 824 | 673 |
| 825 if opts.build_preference == 'msvs': | 674 if opts.build_preference == 'msvs': |
| 826 if not os.getenv('VS100COMNTOOLS'): | 675 if not os.getenv('VS100COMNTOOLS'): |
| 827 raise RuntimeError( | 676 raise RuntimeError( |
| 828 'Path to visual studio could not be determined.') | 677 'Path to visual studio could not be determined.') |
| 829 else: | 678 else: |
| 830 SetBuildSystemDefault(opts.build_preference, opts.use_goma, | 679 SetBuildSystemDefault(opts.build_preference, opts.use_goma, |
| 831 opts.goma_dir) | 680 opts.goma_dir) |
| (...skipping 24 matching lines...) Expand all Loading... |
| 856 | 705 |
| 857 def Build(self, depot, opts): | 706 def Build(self, depot, opts): |
| 858 raise NotImplementedError() | 707 raise NotImplementedError() |
| 859 | 708 |
| 860 def GetBuildOutputDirectory(self, opts, src_dir=None): | 709 def GetBuildOutputDirectory(self, opts, src_dir=None): |
| 861 """Returns the path to the build directory, relative to the checkout root. | 710 """Returns the path to the build directory, relative to the checkout root. |
| 862 | 711 |
| 863 Assumes that the current working directory is the checkout root. | 712 Assumes that the current working directory is the checkout root. |
| 864 """ | 713 """ |
| 865 src_dir = src_dir or 'src' | 714 src_dir = src_dir or 'src' |
| 866 if opts.build_preference == 'ninja' or IsLinuxHost(): | 715 if opts.build_preference == 'ninja' or bisect_utils.IsLinuxHost(): |
| 867 return os.path.join(src_dir, 'out') | 716 return os.path.join(src_dir, 'out') |
| 868 if IsMacHost(): | 717 if bisect_utils.IsMacHost(): |
| 869 return os.path.join(src_dir, 'xcodebuild') | 718 return os.path.join(src_dir, 'xcodebuild') |
| 870 if IsWindowsHost(): | 719 if bisect_utils.IsWindowsHost(): |
| 871 return os.path.join(src_dir, 'build') | 720 return os.path.join(src_dir, 'build') |
| 872 raise NotImplementedError('Unexpected platform %s' % sys.platform) | 721 raise NotImplementedError('Unexpected platform %s' % sys.platform) |
| 873 | 722 |
| 874 | 723 |
| 875 class DesktopBuilder(Builder): | 724 class DesktopBuilder(Builder): |
| 876 """DesktopBuilder is used to build Chromium on linux/mac/windows.""" | 725 """DesktopBuilder is used to build Chromium on linux/mac/windows.""" |
| 877 def __init__(self, opts): | 726 def __init__(self, opts): |
| 878 super(DesktopBuilder, self).__init__(opts) | 727 super(DesktopBuilder, self).__init__(opts) |
| 879 | 728 |
| 880 def Build(self, depot, opts): | 729 def Build(self, depot, opts): |
| (...skipping 12 matching lines...) Expand all Loading... |
| 893 threads = None | 742 threads = None |
| 894 if opts.use_goma: | 743 if opts.use_goma: |
| 895 threads = 64 | 744 threads = 64 |
| 896 | 745 |
| 897 build_success = False | 746 build_success = False |
| 898 if opts.build_preference == 'make': | 747 if opts.build_preference == 'make': |
| 899 build_success = BuildWithMake(threads, targets, opts.target_build_type) | 748 build_success = BuildWithMake(threads, targets, opts.target_build_type) |
| 900 elif opts.build_preference == 'ninja': | 749 elif opts.build_preference == 'ninja': |
| 901 build_success = BuildWithNinja(threads, targets, opts.target_build_type) | 750 build_success = BuildWithNinja(threads, targets, opts.target_build_type) |
| 902 elif opts.build_preference == 'msvs': | 751 elif opts.build_preference == 'msvs': |
| 903 assert IsWindowsHost(), 'msvs is only supported on Windows.' | 752 assert bisect_utils.IsWindowsHost(), 'msvs is only supported on Windows.' |
| 904 build_success = BuildWithVisualStudio(targets, opts.target_build_type) | 753 build_success = BuildWithVisualStudio(targets, opts.target_build_type) |
| 905 else: | 754 else: |
| 906 assert False, 'No build system defined.' | 755 assert False, 'No build system defined.' |
| 907 return build_success | 756 return build_success |
| 908 | 757 |
| 909 | 758 |
| 910 class AndroidBuilder(Builder): | 759 class AndroidBuilder(Builder): |
| 911 """AndroidBuilder is used to build on android.""" | 760 """AndroidBuilder is used to build on android.""" |
| 912 def __init__(self, opts): | 761 def __init__(self, opts): |
| 913 super(AndroidBuilder, self).__init__(opts) | 762 super(AndroidBuilder, self).__init__(opts) |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 965 True if successful. | 814 True if successful. |
| 966 """ | 815 """ |
| 967 try: | 816 try: |
| 968 # Keys will most likely be set to 0640 after wiping the chroot. | 817 # Keys will most likely be set to 0640 after wiping the chroot. |
| 969 os.chmod(CROS_SCRIPT_KEY_PATH, 0600) | 818 os.chmod(CROS_SCRIPT_KEY_PATH, 0600) |
| 970 os.chmod(CROS_TEST_KEY_PATH, 0600) | 819 os.chmod(CROS_TEST_KEY_PATH, 0600) |
| 971 cmd = [CROS_SDK_PATH, '--', './bin/cros_image_to_target.py', | 820 cmd = [CROS_SDK_PATH, '--', './bin/cros_image_to_target.py', |
| 972 '--remote=%s' % opts.cros_remote_ip, | 821 '--remote=%s' % opts.cros_remote_ip, |
| 973 '--board=%s' % opts.cros_board, '--test', '--verbose'] | 822 '--board=%s' % opts.cros_board, '--test', '--verbose'] |
| 974 | 823 |
| 975 return_code = RunProcess(cmd) | 824 return_code = bisect_utils.RunProcess(cmd) |
| 976 return not return_code | 825 return not return_code |
| 977 except OSError, e: | 826 except OSError, e: |
| 978 return False | 827 return False |
| 979 | 828 |
| 980 def BuildPackages(self, opts, depot): | 829 def BuildPackages(self, opts, depot): |
| 981 """Builds packages for cros. | 830 """Builds packages for cros. |
| 982 | 831 |
| 983 Args: | 832 Args: |
| 984 opts: Program options containing cros_board. | 833 opts: Program options containing cros_board. |
| 985 depot: The depot being bisected. | 834 depot: The depot being bisected. |
| 986 | 835 |
| 987 Returns: | 836 Returns: |
| 988 True if successful. | 837 True if successful. |
| 989 """ | 838 """ |
| 990 cmd = [CROS_SDK_PATH] | 839 cmd = [CROS_SDK_PATH] |
| 991 | 840 |
| 992 if depot != 'cros': | 841 if depot != 'cros': |
| 993 path_to_chrome = os.path.join(os.getcwd(), '..') | 842 path_to_chrome = os.path.join(os.getcwd(), '..') |
| 994 cmd += ['--chrome_root=%s' % path_to_chrome] | 843 cmd += ['--chrome_root=%s' % path_to_chrome] |
| 995 | 844 |
| 996 cmd += ['--'] | 845 cmd += ['--'] |
| 997 | 846 |
| 998 if depot != 'cros': | 847 if depot != 'cros': |
| 999 cmd += ['CHROME_ORIGIN=LOCAL_SOURCE'] | 848 cmd += ['CHROME_ORIGIN=LOCAL_SOURCE'] |
| 1000 | 849 |
| 1001 cmd += ['BUILDTYPE=%s' % opts.target_build_type, './build_packages', | 850 cmd += ['BUILDTYPE=%s' % opts.target_build_type, './build_packages', |
| 1002 '--board=%s' % opts.cros_board] | 851 '--board=%s' % opts.cros_board] |
| 1003 return_code = RunProcess(cmd) | 852 return_code = bisect_utils.RunProcess(cmd) |
| 1004 | 853 |
| 1005 return not return_code | 854 return not return_code |
| 1006 | 855 |
| 1007 def BuildImage(self, opts, depot): | 856 def BuildImage(self, opts, depot): |
| 1008 """Builds test image for cros. | 857 """Builds test image for cros. |
| 1009 | 858 |
| 1010 Args: | 859 Args: |
| 1011 opts: Program options containing cros_board. | 860 opts: Program options containing cros_board. |
| 1012 depot: The depot being bisected. | 861 depot: The depot being bisected. |
| 1013 | 862 |
| 1014 Returns: | 863 Returns: |
| 1015 True if successful. | 864 True if successful. |
| 1016 """ | 865 """ |
| 1017 cmd = [CROS_SDK_PATH] | 866 cmd = [CROS_SDK_PATH] |
| 1018 | 867 |
| 1019 if depot != 'cros': | 868 if depot != 'cros': |
| 1020 path_to_chrome = os.path.join(os.getcwd(), '..') | 869 path_to_chrome = os.path.join(os.getcwd(), '..') |
| 1021 cmd += ['--chrome_root=%s' % path_to_chrome] | 870 cmd += ['--chrome_root=%s' % path_to_chrome] |
| 1022 | 871 |
| 1023 cmd += ['--'] | 872 cmd += ['--'] |
| 1024 | 873 |
| 1025 if depot != 'cros': | 874 if depot != 'cros': |
| 1026 cmd += ['CHROME_ORIGIN=LOCAL_SOURCE'] | 875 cmd += ['CHROME_ORIGIN=LOCAL_SOURCE'] |
| 1027 | 876 |
| 1028 cmd += ['BUILDTYPE=%s' % opts.target_build_type, '--', './build_image', | 877 cmd += ['BUILDTYPE=%s' % opts.target_build_type, '--', './build_image', |
| 1029 '--board=%s' % opts.cros_board, 'test'] | 878 '--board=%s' % opts.cros_board, 'test'] |
| 1030 | 879 |
| 1031 return_code = RunProcess(cmd) | 880 return_code = bisect_utils.RunProcess(cmd) |
| 1032 | 881 |
| 1033 return not return_code | 882 return not return_code |
| 1034 | 883 |
| 1035 def Build(self, depot, opts): | 884 def Build(self, depot, opts): |
| 1036 """Builds targets using options passed into the script. | 885 """Builds targets using options passed into the script. |
| 1037 | 886 |
| 1038 Args: | 887 Args: |
| 1039 depot: Current depot being bisected. | 888 depot: Current depot being bisected. |
| 1040 opts: The options parsed from the command line. | 889 opts: The options parsed from the command line. |
| 1041 | 890 |
| 1042 Returns: | 891 Returns: |
| 1043 True if build was successful. | 892 True if build was successful. |
| 1044 """ | 893 """ |
| 1045 if self.BuildPackages(opts, depot): | 894 if self.BuildPackages(opts, depot): |
| 1046 if self.BuildImage(opts, depot): | 895 if self.BuildImage(opts, depot): |
| 1047 return self.ImageToTarget(opts) | 896 return self.ImageToTarget(opts) |
| 1048 return False | 897 return False |
| 1049 | 898 |
| 1050 | 899 |
| 1051 class SourceControl(object): | |
| 1052 """SourceControl is an abstraction over the underlying source control | |
| 1053 system used for chromium. For now only git is supported, but in the | |
| 1054 future, the svn workflow could be added as well.""" | |
| 1055 def __init__(self): | |
| 1056 super(SourceControl, self).__init__() | |
| 1057 | |
| 1058 def SyncToRevisionWithGClient(self, revision): | |
| 1059 """Uses gclient to sync to the specified revision. | |
| 1060 | |
| 1061 ie. gclient sync --revision <revision> | |
| 1062 | |
| 1063 Args: | |
| 1064 revision: The git SHA1 or svn CL (depending on workflow). | |
| 1065 | |
| 1066 Returns: | |
| 1067 The return code of the call. | |
| 1068 """ | |
| 1069 return bisect_utils.RunGClient(['sync', '--verbose', '--reset', '--force', | |
| 1070 '--delete_unversioned_trees', '--nohooks', '--revision', revision]) | |
| 1071 | |
| 1072 def SyncToRevisionWithRepo(self, timestamp): | |
| 1073 """Uses repo to sync all the underlying git depots to the specified | |
| 1074 time. | |
| 1075 | |
| 1076 Args: | |
| 1077 timestamp: The unix timestamp to sync to. | |
| 1078 | |
| 1079 Returns: | |
| 1080 The return code of the call. | |
| 1081 """ | |
| 1082 return bisect_utils.RunRepoSyncAtTimestamp(timestamp) | |
| 1083 | |
| 1084 | |
| 1085 class GitSourceControl(SourceControl): | |
| 1086 """GitSourceControl is used to query the underlying source control. """ | |
| 1087 def __init__(self, opts): | |
| 1088 super(GitSourceControl, self).__init__() | |
| 1089 self.opts = opts | |
| 1090 | |
| 1091 def IsGit(self): | |
| 1092 return True | |
| 1093 | |
| 1094 def GetRevisionList(self, revision_range_end, revision_range_start, cwd=None): | |
| 1095 """Retrieves a list of revisions between |revision_range_start| and | |
| 1096 |revision_range_end|. | |
| 1097 | |
| 1098 Args: | |
| 1099 revision_range_end: The SHA1 for the end of the range. | |
| 1100 revision_range_start: The SHA1 for the beginning of the range. | |
| 1101 | |
| 1102 Returns: | |
| 1103 A list of the revisions between |revision_range_start| and | |
| 1104 |revision_range_end| (inclusive). | |
| 1105 """ | |
| 1106 revision_range = '%s..%s' % (revision_range_start, revision_range_end) | |
| 1107 cmd = ['log', '--format=%H', '-10000', '--first-parent', revision_range] | |
| 1108 log_output = CheckRunGit(cmd, cwd=cwd) | |
| 1109 | |
| 1110 revision_hash_list = log_output.split() | |
| 1111 revision_hash_list.append(revision_range_start) | |
| 1112 | |
| 1113 return revision_hash_list | |
| 1114 | |
| 1115 def SyncToRevision(self, revision, sync_client=None): | |
| 1116 """Syncs to the specified revision. | |
| 1117 | |
| 1118 Args: | |
| 1119 revision: The revision to sync to. | |
| 1120 use_gclient: Specifies whether or not we should sync using gclient or | |
| 1121 just use source control directly. | |
| 1122 | |
| 1123 Returns: | |
| 1124 True if successful. | |
| 1125 """ | |
| 1126 | |
| 1127 if not sync_client: | |
| 1128 results = RunGit(['checkout', revision])[1] | |
| 1129 elif sync_client == 'gclient': | |
| 1130 results = self.SyncToRevisionWithGClient(revision) | |
| 1131 elif sync_client == 'repo': | |
| 1132 results = self.SyncToRevisionWithRepo(revision) | |
| 1133 | |
| 1134 return not results | |
| 1135 | |
| 1136 def ResolveToRevision(self, revision_to_check, depot, search, cwd=None): | |
| 1137 """If an SVN revision is supplied, try to resolve it to a git SHA1. | |
| 1138 | |
| 1139 Args: | |
| 1140 revision_to_check: The user supplied revision string that may need to be | |
| 1141 resolved to a git SHA1. | |
| 1142 depot: The depot the revision_to_check is from. | |
| 1143 search: The number of changelists to try if the first fails to resolve | |
| 1144 to a git hash. If the value is negative, the function will search | |
| 1145 backwards chronologically, otherwise it will search forward. | |
| 1146 | |
| 1147 Returns: | |
| 1148 A string containing a git SHA1 hash, otherwise None. | |
| 1149 """ | |
| 1150 # Android-chrome is git only, so no need to resolve this to anything else. | |
| 1151 if depot == 'android-chrome': | |
| 1152 return revision_to_check | |
| 1153 | |
| 1154 if depot != 'cros': | |
| 1155 if not IsStringInt(revision_to_check): | |
| 1156 return revision_to_check | |
| 1157 | |
| 1158 depot_svn = 'svn://svn.chromium.org/chrome/trunk/src' | |
| 1159 | |
| 1160 if depot != 'chromium': | |
| 1161 depot_svn = DEPOT_DEPS_NAME[depot]['svn'] | |
| 1162 | |
| 1163 svn_revision = int(revision_to_check) | |
| 1164 git_revision = None | |
| 1165 | |
| 1166 if search > 0: | |
| 1167 search_range = xrange(svn_revision, svn_revision + search, 1) | |
| 1168 else: | |
| 1169 search_range = xrange(svn_revision, svn_revision + search, -1) | |
| 1170 | |
| 1171 for i in search_range: | |
| 1172 svn_pattern = 'git-svn-id: %s@%d' % (depot_svn, i) | |
| 1173 cmd = ['log', '--format=%H', '-1', '--grep', svn_pattern, | |
| 1174 'origin/master'] | |
| 1175 | |
| 1176 (log_output, return_code) = RunGit(cmd, cwd=cwd) | |
| 1177 | |
| 1178 assert not return_code, 'An error occurred while running'\ | |
| 1179 ' "git %s"' % ' '.join(cmd) | |
| 1180 | |
| 1181 if not return_code: | |
| 1182 log_output = log_output.strip() | |
| 1183 | |
| 1184 if log_output: | |
| 1185 git_revision = log_output | |
| 1186 | |
| 1187 break | |
| 1188 | |
| 1189 return git_revision | |
| 1190 else: | |
| 1191 if IsStringInt(revision_to_check): | |
| 1192 return int(revision_to_check) | |
| 1193 else: | |
| 1194 cwd = os.getcwd() | |
| 1195 os.chdir(os.path.join(os.getcwd(), 'src', 'third_party', | |
| 1196 'chromiumos-overlay')) | |
| 1197 pattern = CROS_VERSION_PATTERN % revision_to_check | |
| 1198 cmd = ['log', '--format=%ct', '-1', '--grep', pattern] | |
| 1199 | |
| 1200 git_revision = None | |
| 1201 | |
| 1202 log_output = CheckRunGit(cmd, cwd=cwd) | |
| 1203 if log_output: | |
| 1204 git_revision = log_output | |
| 1205 git_revision = int(log_output.strip()) | |
| 1206 os.chdir(cwd) | |
| 1207 | |
| 1208 return git_revision | |
| 1209 | |
| 1210 def IsInProperBranch(self): | |
| 1211 """Confirms they're in the master branch for performing the bisection. | |
| 1212 This is needed or gclient will fail to sync properly. | |
| 1213 | |
| 1214 Returns: | |
| 1215 True if the current branch on src is 'master' | |
| 1216 """ | |
| 1217 cmd = ['rev-parse', '--abbrev-ref', 'HEAD'] | |
| 1218 log_output = CheckRunGit(cmd) | |
| 1219 log_output = log_output.strip() | |
| 1220 | |
| 1221 return log_output == "master" | |
| 1222 | |
| 1223 def SVNFindRev(self, revision, cwd=None): | |
| 1224 """Maps directly to the 'git svn find-rev' command. | |
| 1225 | |
| 1226 Args: | |
| 1227 revision: The git SHA1 to use. | |
| 1228 | |
| 1229 Returns: | |
| 1230 An integer changelist #, otherwise None. | |
| 1231 """ | |
| 1232 | |
| 1233 cmd = ['svn', 'find-rev', revision] | |
| 1234 | |
| 1235 output = CheckRunGit(cmd, cwd) | |
| 1236 svn_revision = output.strip() | |
| 1237 | |
| 1238 if IsStringInt(svn_revision): | |
| 1239 return int(svn_revision) | |
| 1240 | |
| 1241 return None | |
| 1242 | |
| 1243 def QueryRevisionInfo(self, revision, cwd=None): | |
| 1244 """Gathers information on a particular revision, such as author's name, | |
| 1245 email, subject, and date. | |
| 1246 | |
| 1247 Args: | |
| 1248 revision: Revision you want to gather information on. | |
| 1249 Returns: | |
| 1250 A dict in the following format: | |
| 1251 { | |
| 1252 'author': %s, | |
| 1253 'email': %s, | |
| 1254 'date': %s, | |
| 1255 'subject': %s, | |
| 1256 'body': %s, | |
| 1257 } | |
| 1258 """ | |
| 1259 commit_info = {} | |
| 1260 | |
| 1261 formats = ['%cN', '%cE', '%s', '%cD', '%b'] | |
| 1262 targets = ['author', 'email', 'subject', 'date', 'body'] | |
| 1263 | |
| 1264 for i in xrange(len(formats)): | |
| 1265 cmd = ['log', '--format=%s' % formats[i], '-1', revision] | |
| 1266 output = CheckRunGit(cmd, cwd=cwd) | |
| 1267 commit_info[targets[i]] = output.rstrip() | |
| 1268 | |
| 1269 return commit_info | |
| 1270 | |
| 1271 def CheckoutFileAtRevision(self, file_name, revision, cwd=None): | |
| 1272 """Performs a checkout on a file at the given revision. | |
| 1273 | |
| 1274 Returns: | |
| 1275 True if successful. | |
| 1276 """ | |
| 1277 return not RunGit(['checkout', revision, file_name], cwd=cwd)[1] | |
| 1278 | |
| 1279 def RevertFileToHead(self, file_name): | |
| 1280 """Unstages a file and returns it to HEAD. | |
| 1281 | |
| 1282 Returns: | |
| 1283 True if successful. | |
| 1284 """ | |
| 1285 # Reset doesn't seem to return 0 on success. | |
| 1286 RunGit(['reset', 'HEAD', file_name]) | |
| 1287 | |
| 1288 return not RunGit(['checkout', bisect_utils.FILE_DEPS_GIT])[1] | |
| 1289 | |
| 1290 def QueryFileRevisionHistory(self, filename, revision_start, revision_end): | |
| 1291 """Returns a list of commits that modified this file. | |
| 1292 | |
| 1293 Args: | |
| 1294 filename: Name of file. | |
| 1295 revision_start: Start of revision range. | |
| 1296 revision_end: End of revision range. | |
| 1297 | |
| 1298 Returns: | |
| 1299 Returns a list of commits that touched this file. | |
| 1300 """ | |
| 1301 cmd = ['log', '--format=%H', '%s~1..%s' % (revision_start, revision_end), | |
| 1302 filename] | |
| 1303 output = CheckRunGit(cmd) | |
| 1304 | |
| 1305 return [o for o in output.split('\n') if o] | |
| 1306 | 900 |
| 1307 | 901 |
| 1308 class BisectPerformanceMetrics(object): | 902 class BisectPerformanceMetrics(object): |
| 1309 """This class contains functionality to perform a bisection of a range of | 903 """This class contains functionality to perform a bisection of a range of |
| 1310 revisions to narrow down where performance regressions may have occurred. | 904 revisions to narrow down where performance regressions may have occurred. |
| 1311 | 905 |
| 1312 The main entry-point is the Run method. | 906 The main entry-point is the Run method. |
| 1313 """ | 907 """ |
| 1314 | 908 |
| 1315 def __init__(self, source_control, opts): | 909 def __init__(self, source_control, opts): |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1356 cwd = os.getcwd() | 950 cwd = os.getcwd() |
| 1357 self.ChangeToDepotWorkingDirectory('cros') | 951 self.ChangeToDepotWorkingDirectory('cros') |
| 1358 | 952 |
| 1359 # Print the commit timestamps for every commit in the revision time | 953 # Print the commit timestamps for every commit in the revision time |
| 1360 # range. We'll sort them and bisect by that. There is a remote chance that | 954 # range. We'll sort them and bisect by that. There is a remote chance that |
| 1361 # 2 (or more) commits will share the exact same timestamp, but it's | 955 # 2 (or more) commits will share the exact same timestamp, but it's |
| 1362 # probably safe to ignore that case. | 956 # probably safe to ignore that case. |
| 1363 cmd = ['repo', 'forall', '-c', | 957 cmd = ['repo', 'forall', '-c', |
| 1364 'git log --format=%%ct --before=%d --after=%d' % ( | 958 'git log --format=%%ct --before=%d --after=%d' % ( |
| 1365 revision_range_end, revision_range_start)] | 959 revision_range_end, revision_range_start)] |
| 1366 (output, return_code) = RunProcessAndRetrieveOutput(cmd) | 960 (output, return_code) = bisect_utils.RunProcessAndRetrieveOutput(cmd) |
| 1367 | 961 |
| 1368 assert not return_code, 'An error occurred while running'\ | 962 assert not return_code, 'An error occurred while running'\ |
| 1369 ' "%s"' % ' '.join(cmd) | 963 ' "%s"' % ' '.join(cmd) |
| 1370 | 964 |
| 1371 os.chdir(cwd) | 965 os.chdir(cwd) |
| 1372 | 966 |
| 1373 revision_work_list = list(set( | 967 revision_work_list = list(set( |
| 1374 [int(o) for o in output.split('\n') if IsStringInt(o)])) | 968 [int(o) for o in output.split('\n') if bisect_utils.IsStringInt(o)])) |
| 1375 revision_work_list = sorted(revision_work_list, reverse=True) | 969 revision_work_list = sorted(revision_work_list, reverse=True) |
| 1376 else: | 970 else: |
| 1377 cwd = self._GetDepotDirectory(depot) | 971 cwd = self._GetDepotDirectory(depot) |
| 1378 revision_work_list = self.source_control.GetRevisionList(bad_revision, | 972 revision_work_list = self.source_control.GetRevisionList(bad_revision, |
| 1379 good_revision, cwd=cwd) | 973 good_revision, cwd=cwd) |
| 1380 | 974 |
| 1381 return revision_work_list | 975 return revision_work_list |
| 1382 | 976 |
| 1383 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): | 977 def _GetV8BleedingEdgeFromV8TrunkIfMappable(self, revision): |
| 1384 svn_revision = self.source_control.SVNFindRev(revision) | 978 svn_revision = self.source_control.SVNFindRev(revision) |
| 1385 | 979 |
| 1386 if IsStringInt(svn_revision): | 980 if bisect_utils.IsStringInt(svn_revision): |
| 1387 # V8 is tricky to bisect, in that there are only a few instances when | 981 # V8 is tricky to bisect, in that there are only a few instances when |
| 1388 # we can dive into bleeding_edge and get back a meaningful result. | 982 # we can dive into bleeding_edge and get back a meaningful result. |
| 1389 # Try to detect a V8 "business as usual" case, which is when: | 983 # Try to detect a V8 "business as usual" case, which is when: |
| 1390 # 1. trunk revision N has description "Version X.Y.Z" | 984 # 1. trunk revision N has description "Version X.Y.Z" |
| 1391 # 2. bleeding_edge revision (N-1) has description "Prepare push to | 985 # 2. bleeding_edge revision (N-1) has description "Prepare push to |
| 1392 # trunk. Now working on X.Y.(Z+1)." | 986 # trunk. Now working on X.Y.(Z+1)." |
| 1393 # | 987 # |
| 1394 # As of 01/24/2014, V8 trunk descriptions are formatted: | 988 # As of 01/24/2014, V8 trunk descriptions are formatted: |
| 1395 # "Version 3.X.Y (based on bleeding_edge revision rZ)" | 989 # "Version 3.X.Y (based on bleeding_edge revision rZ)" |
| 1396 # So we can just try parsing that out first and fall back to the old way. | 990 # So we can just try parsing that out first and fall back to the old way. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1407 if regex_results: | 1001 if regex_results: |
| 1408 git_revision = None | 1002 git_revision = None |
| 1409 | 1003 |
| 1410 # Look for "based on bleeding_edge" and parse out revision | 1004 # Look for "based on bleeding_edge" and parse out revision |
| 1411 if 'based on bleeding_edge' in revision_info['subject']: | 1005 if 'based on bleeding_edge' in revision_info['subject']: |
| 1412 try: | 1006 try: |
| 1413 bleeding_edge_revision = revision_info['subject'].split( | 1007 bleeding_edge_revision = revision_info['subject'].split( |
| 1414 'bleeding_edge revision r')[1] | 1008 'bleeding_edge revision r')[1] |
| 1415 bleeding_edge_revision = int(bleeding_edge_revision.split(')')[0]) | 1009 bleeding_edge_revision = int(bleeding_edge_revision.split(')')[0]) |
| 1416 git_revision = self.source_control.ResolveToRevision( | 1010 git_revision = self.source_control.ResolveToRevision( |
| 1417 bleeding_edge_revision, 'v8_bleeding_edge', 1, | 1011 bleeding_edge_revision, 'v8_bleeding_edge', DEPOT_DEPS_NAME, 1, |
| 1418 cwd=v8_bleeding_edge_dir) | 1012 cwd=v8_bleeding_edge_dir) |
| 1419 return git_revision | 1013 return git_revision |
| 1420 except (IndexError, ValueError): | 1014 except (IndexError, ValueError): |
| 1421 pass | 1015 pass |
| 1422 | 1016 |
| 1423 if not git_revision: | 1017 if not git_revision: |
| 1424 # Wasn't successful, try the old way of looking for "Prepare push to" | 1018 # Wasn't successful, try the old way of looking for "Prepare push to" |
| 1425 git_revision = self.source_control.ResolveToRevision( | 1019 git_revision = self.source_control.ResolveToRevision( |
| 1426 int(svn_revision) - 1, 'v8_bleeding_edge', -1, | 1020 int(svn_revision) - 1, 'v8_bleeding_edge', DEPOT_DEPS_NAME, -1, |
| 1427 cwd=v8_bleeding_edge_dir) | 1021 cwd=v8_bleeding_edge_dir) |
| 1428 | 1022 |
| 1429 if git_revision: | 1023 if git_revision: |
| 1430 revision_info = self.source_control.QueryRevisionInfo(git_revision, | 1024 revision_info = self.source_control.QueryRevisionInfo(git_revision, |
| 1431 cwd=v8_bleeding_edge_dir) | 1025 cwd=v8_bleeding_edge_dir) |
| 1432 | 1026 |
| 1433 if 'Prepare push to trunk' in revision_info['subject']: | 1027 if 'Prepare push to trunk' in revision_info['subject']: |
| 1434 return git_revision | 1028 return git_revision |
| 1435 return None | 1029 return None |
| 1436 | 1030 |
| 1437 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True): | 1031 def _GetNearestV8BleedingEdgeFromTrunk(self, revision, search_forward=True): |
| 1438 cwd = self._GetDepotDirectory('v8') | 1032 cwd = self._GetDepotDirectory('v8') |
| 1439 cmd = ['log', '--format=%ct', '-1', revision] | 1033 cmd = ['log', '--format=%ct', '-1', revision] |
| 1440 output = CheckRunGit(cmd, cwd=cwd) | 1034 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) |
| 1441 commit_time = int(output) | 1035 commit_time = int(output) |
| 1442 commits = [] | 1036 commits = [] |
| 1443 | 1037 |
| 1444 if search_forward: | 1038 if search_forward: |
| 1445 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time, | 1039 cmd = ['log', '--format=%H', '-10', '--after=%d' % commit_time, |
| 1446 'origin/master'] | 1040 'origin/master'] |
| 1447 output = CheckRunGit(cmd, cwd=cwd) | 1041 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) |
| 1448 output = output.split() | 1042 output = output.split() |
| 1449 commits = output | 1043 commits = output |
| 1450 commits = reversed(commits) | 1044 commits = reversed(commits) |
| 1451 else: | 1045 else: |
| 1452 cmd = ['log', '--format=%H', '-10', '--before=%d' % commit_time, | 1046 cmd = ['log', '--format=%H', '-10', '--before=%d' % commit_time, |
| 1453 'origin/master'] | 1047 'origin/master'] |
| 1454 output = CheckRunGit(cmd, cwd=cwd) | 1048 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) |
| 1455 output = output.split() | 1049 output = output.split() |
| 1456 commits = output | 1050 commits = output |
| 1457 | 1051 |
| 1458 bleeding_edge_revision = None | 1052 bleeding_edge_revision = None |
| 1459 | 1053 |
| 1460 for c in commits: | 1054 for c in commits: |
| 1461 bleeding_edge_revision = self._GetV8BleedingEdgeFromV8TrunkIfMappable(c) | 1055 bleeding_edge_revision = self._GetV8BleedingEdgeFromV8TrunkIfMappable(c) |
| 1462 if bleeding_edge_revision: | 1056 if bleeding_edge_revision: |
| 1463 break | 1057 break |
| 1464 | 1058 |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1557 | 1151 |
| 1558 results = {} | 1152 results = {} |
| 1559 | 1153 |
| 1560 if depot == 'chromium' or depot == 'android-chrome': | 1154 if depot == 'chromium' or depot == 'android-chrome': |
| 1561 results = self._ParseRevisionsFromDEPSFile(depot) | 1155 results = self._ParseRevisionsFromDEPSFile(depot) |
| 1562 os.chdir(cwd) | 1156 os.chdir(cwd) |
| 1563 elif depot == 'cros': | 1157 elif depot == 'cros': |
| 1564 cmd = [CROS_SDK_PATH, '--', 'portageq-%s' % self.opts.cros_board, | 1158 cmd = [CROS_SDK_PATH, '--', 'portageq-%s' % self.opts.cros_board, |
| 1565 'best_visible', '/build/%s' % self.opts.cros_board, 'ebuild', | 1159 'best_visible', '/build/%s' % self.opts.cros_board, 'ebuild', |
| 1566 CROS_CHROMEOS_PATTERN] | 1160 CROS_CHROMEOS_PATTERN] |
| 1567 (output, return_code) = RunProcessAndRetrieveOutput(cmd) | 1161 (output, return_code) = bisect_utils.RunProcessAndRetrieveOutput(cmd) |
| 1568 | 1162 |
| 1569 assert not return_code, 'An error occurred while running' \ | 1163 assert not return_code, 'An error occurred while running' \ |
| 1570 ' "%s"' % ' '.join(cmd) | 1164 ' "%s"' % ' '.join(cmd) |
| 1571 | 1165 |
| 1572 if len(output) > CROS_CHROMEOS_PATTERN: | 1166 if len(output) > CROS_CHROMEOS_PATTERN: |
| 1573 output = output[len(CROS_CHROMEOS_PATTERN):] | 1167 output = output[len(CROS_CHROMEOS_PATTERN):] |
| 1574 | 1168 |
| 1575 if len(output) > 1: | 1169 if len(output) > 1: |
| 1576 output = output.split('_')[0] | 1170 output = output.split('_')[0] |
| 1577 | 1171 |
| 1578 if len(output) > 3: | 1172 if len(output) > 3: |
| 1579 contents = output.split('.') | 1173 contents = output.split('.') |
| 1580 | 1174 |
| 1581 version = contents[2] | 1175 version = contents[2] |
| 1582 | 1176 |
| 1583 if contents[3] != '0': | 1177 if contents[3] != '0': |
| 1584 warningText = 'Chrome version: %s.%s but using %s.0 to bisect.' % \ | 1178 warningText = 'Chrome version: %s.%s but using %s.0 to bisect.' % \ |
| 1585 (version, contents[3], version) | 1179 (version, contents[3], version) |
| 1586 if not warningText in self.warnings: | 1180 if not warningText in self.warnings: |
| 1587 self.warnings.append(warningText) | 1181 self.warnings.append(warningText) |
| 1588 | 1182 |
| 1589 cwd = os.getcwd() | 1183 cwd = os.getcwd() |
| 1590 self.ChangeToDepotWorkingDirectory('chromium') | 1184 self.ChangeToDepotWorkingDirectory('chromium') |
| 1591 return_code = CheckRunGit(['log', '-1', '--format=%H', | 1185 cmd = ['log', '-1', '--format=%H', |
| 1592 '--author=chrome-release@google.com', '--grep=to %s' % version, | 1186 '--author=chrome-release@google.com', |
| 1593 'origin/master']) | 1187 '--grep=to %s' % version, 'origin/master'] |
| 1188 return_code = bisect_utils.CheckRunGit(cmd) |
| 1594 os.chdir(cwd) | 1189 os.chdir(cwd) |
| 1595 | 1190 |
| 1596 results['chromium'] = output.strip() | 1191 results['chromium'] = output.strip() |
| 1597 elif depot == 'v8': | 1192 elif depot == 'v8': |
| 1598 # We can't try to map the trunk revision to bleeding edge yet, because | 1193 # We can't try to map the trunk revision to bleeding edge yet, because |
| 1599 # we don't know which direction to try to search in. Have to wait until | 1194 # we don't know which direction to try to search in. Have to wait until |
| 1600 # the bisect has narrowed the results down to 2 v8 rolls. | 1195 # the bisect has narrowed the results down to 2 v8 rolls. |
| 1601 results['v8_bleeding_edge'] = None | 1196 results['v8_bleeding_edge'] = None |
| 1602 | 1197 |
| 1603 return results | 1198 return results |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1797 # Get SVN revision for the given SHA. | 1392 # Get SVN revision for the given SHA. |
| 1798 svn_revision = self.source_control.SVNFindRev(revision) | 1393 svn_revision = self.source_control.SVNFindRev(revision) |
| 1799 if not svn_revision: | 1394 if not svn_revision: |
| 1800 raise RuntimeError( | 1395 raise RuntimeError( |
| 1801 'Failed to determine SVN revision for %s' % revision) | 1396 'Failed to determine SVN revision for %s' % revision) |
| 1802 | 1397 |
| 1803 def GetBuilderNameAndBuildTime(target_platform, target_arch='ia32'): | 1398 def GetBuilderNameAndBuildTime(target_platform, target_arch='ia32'): |
| 1804 """Gets builder bot name and buildtime in seconds based on platform.""" | 1399 """Gets builder bot name and buildtime in seconds based on platform.""" |
| 1805 # Bot names should match the one listed in tryserver.chromium's | 1400 # Bot names should match the one listed in tryserver.chromium's |
| 1806 # master.cfg which produces builds for bisect. | 1401 # master.cfg which produces builds for bisect. |
| 1807 if IsWindowsHost(): | 1402 if bisect_utils.IsWindowsHost(): |
| 1808 if Is64BitWindows() and target_arch == 'x64': | 1403 if bisect_utils.Is64BitWindows() and target_arch == 'x64': |
| 1809 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME) | 1404 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME) |
| 1810 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME) | 1405 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME) |
| 1811 if IsLinuxHost(): | 1406 if bisect_utils.IsLinuxHost(): |
| 1812 if target_platform == 'android': | 1407 if target_platform == 'android': |
| 1813 return ('android_perf_bisect_builder', MAX_LINUX_BUILD_TIME) | 1408 return ('android_perf_bisect_builder', MAX_LINUX_BUILD_TIME) |
| 1814 return ('linux_perf_bisect_builder', MAX_LINUX_BUILD_TIME) | 1409 return ('linux_perf_bisect_builder', MAX_LINUX_BUILD_TIME) |
| 1815 if IsMacHost(): | 1410 if bisect_utils.IsMacHost(): |
| 1816 return ('mac_perf_bisect_builder', MAX_MAC_BUILD_TIME) | 1411 return ('mac_perf_bisect_builder', MAX_MAC_BUILD_TIME) |
| 1817 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform) | 1412 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform) |
| 1818 if not fetch_build: | 1413 if not fetch_build: |
| 1819 return False | 1414 return False |
| 1820 | 1415 |
| 1821 bot_name, build_timeout = GetBuilderNameAndBuildTime( | 1416 bot_name, build_timeout = GetBuilderNameAndBuildTime( |
| 1822 self.opts.target_platform, self.opts.target_arch) | 1417 self.opts.target_platform, self.opts.target_arch) |
| 1823 builder_host = self.opts.builder_host | 1418 builder_host = self.opts.builder_host |
| 1824 builder_port = self.opts.builder_port | 1419 builder_port = self.opts.builder_port |
| 1825 # Create a unique ID for each build request posted to tryserver builders. | 1420 # Create a unique ID for each build request posted to tryserver builders. |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2001 depot: Current depot being bisected. | 1596 depot: Current depot being bisected. |
| 2002 revision: A git hash revision of the dependency repository. | 1597 revision: A git hash revision of the dependency repository. |
| 2003 | 1598 |
| 2004 Returns: | 1599 Returns: |
| 2005 A tuple with git hash of chromium revision and DEPS patch text. | 1600 A tuple with git hash of chromium revision and DEPS patch text. |
| 2006 """ | 1601 """ |
| 2007 deps_file_path = os.path.join(self.src_cwd, bisect_utils.FILE_DEPS) | 1602 deps_file_path = os.path.join(self.src_cwd, bisect_utils.FILE_DEPS) |
| 2008 if not os.path.exists(deps_file_path): | 1603 if not os.path.exists(deps_file_path): |
| 2009 raise RuntimeError('DEPS file does not exists.[%s]' % deps_file_path) | 1604 raise RuntimeError('DEPS file does not exists.[%s]' % deps_file_path) |
| 2010 # Get current chromium revision (git hash). | 1605 # Get current chromium revision (git hash). |
| 2011 chromium_sha = CheckRunGit(['rev-parse', 'HEAD']).strip() | 1606 cmd = ['rev-parse', 'HEAD'] |
| 1607 chromium_sha = bisect_utils.CheckRunGit(cmd).strip() |
| 2012 if not chromium_sha: | 1608 if not chromium_sha: |
| 2013 raise RuntimeError('Failed to determine Chromium revision for %s' % | 1609 raise RuntimeError('Failed to determine Chromium revision for %s' % |
| 2014 revision) | 1610 revision) |
| 2015 if ('chromium' in DEPOT_DEPS_NAME[depot]['from'] or | 1611 if ('chromium' in DEPOT_DEPS_NAME[depot]['from'] or |
| 2016 'v8' in DEPOT_DEPS_NAME[depot]['from']): | 1612 'v8' in DEPOT_DEPS_NAME[depot]['from']): |
| 2017 # Checkout DEPS file for the current chromium revision. | 1613 # Checkout DEPS file for the current chromium revision. |
| 2018 if self.source_control.CheckoutFileAtRevision(bisect_utils.FILE_DEPS, | 1614 if self.source_control.CheckoutFileAtRevision(bisect_utils.FILE_DEPS, |
| 2019 chromium_sha, | 1615 chromium_sha, |
| 2020 cwd=self.src_cwd): | 1616 cwd=self.src_cwd): |
| 2021 if self.UpdateDeps(revision, depot, deps_file_path): | 1617 if self.UpdateDeps(revision, depot, deps_file_path): |
| 2022 diff_command = ['diff', | 1618 diff_command = ['diff', |
| 2023 '--src-prefix=src/', | 1619 '--src-prefix=src/', |
| 2024 '--dst-prefix=src/', | 1620 '--dst-prefix=src/', |
| 2025 '--no-ext-diff', | 1621 '--no-ext-diff', |
| 2026 bisect_utils.FILE_DEPS] | 1622 bisect_utils.FILE_DEPS] |
| 2027 diff_text = CheckRunGit(diff_command, cwd=self.src_cwd) | 1623 diff_text = bisect_utils.CheckRunGit( |
| 1624 diff_command, cwd=self.src_cwd) |
| 2028 return (chromium_sha, ChangeBackslashToSlashInPatch(diff_text)) | 1625 return (chromium_sha, ChangeBackslashToSlashInPatch(diff_text)) |
| 2029 else: | 1626 else: |
| 2030 raise RuntimeError('Failed to update DEPS file for chromium: [%s]' % | 1627 raise RuntimeError('Failed to update DEPS file for chromium: [%s]' % |
| 2031 chromium_sha) | 1628 chromium_sha) |
| 2032 else: | 1629 else: |
| 2033 raise RuntimeError('DEPS checkout Failed for chromium revision : [%s]' % | 1630 raise RuntimeError('DEPS checkout Failed for chromium revision : [%s]' % |
| 2034 chromium_sha) | 1631 chromium_sha) |
| 2035 return (None, None) | 1632 return (None, None) |
| 2036 | 1633 |
| 2037 def BuildCurrentRevision(self, depot, revision=None): | 1634 def BuildCurrentRevision(self, depot, revision=None): |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2150 single_result_match.group('VALUE')): | 1747 single_result_match.group('VALUE')): |
| 2151 values_list += [single_result_match.group('VALUE')] | 1748 values_list += [single_result_match.group('VALUE')] |
| 2152 elif (not multi_results_match is None and | 1749 elif (not multi_results_match is None and |
| 2153 multi_results_match.group('VALUES')): | 1750 multi_results_match.group('VALUES')): |
| 2154 metric_values = multi_results_match.group('VALUES') | 1751 metric_values = multi_results_match.group('VALUES') |
| 2155 values_list += metric_values.split(',') | 1752 values_list += metric_values.split(',') |
| 2156 elif (not mean_stddev_match is None and | 1753 elif (not mean_stddev_match is None and |
| 2157 mean_stddev_match.group('MEAN')): | 1754 mean_stddev_match.group('MEAN')): |
| 2158 values_list += [mean_stddev_match.group('MEAN')] | 1755 values_list += [mean_stddev_match.group('MEAN')] |
| 2159 | 1756 |
| 2160 values_list = [float(v) for v in values_list if IsStringFloat(v)] | 1757 values_list = [float(v) for v in values_list |
| 1758 if bisect_utils.IsStringFloat(v)] |
| 2161 | 1759 |
| 2162 # If the metric is times/t, we need to sum the timings in order to get | 1760 # If the metric is times/t, we need to sum the timings in order to get |
| 2163 # similar regression results as the try-bots. | 1761 # similar regression results as the try-bots. |
| 2164 metrics_to_sum = [['times', 't'], ['times', 'page_load_time'], | 1762 metrics_to_sum = [['times', 't'], ['times', 'page_load_time'], |
| 2165 ['cold_times', 'page_load_time'], ['warm_times', 'page_load_time']] | 1763 ['cold_times', 'page_load_time'], ['warm_times', 'page_load_time']] |
| 2166 | 1764 |
| 2167 if metric in metrics_to_sum: | 1765 if metric in metrics_to_sum: |
| 2168 if values_list: | 1766 if values_list: |
| 2169 values_list = [reduce(lambda x, y: float(x) + float(y), values_list)] | 1767 values_list = [reduce(lambda x, y: float(x) + float(y), values_list)] |
| 2170 | 1768 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2214 # Check 2 cases, --arg=<val> and --arg <val> | 1812 # Check 2 cases, --arg=<val> and --arg <val> |
| 2215 if len(current_arg_split) == 2: | 1813 if len(current_arg_split) == 2: |
| 2216 arg_dict[arg_to_parse] = current_arg_split[1] | 1814 arg_dict[arg_to_parse] = current_arg_split[1] |
| 2217 elif i + 1 < len(command_args): | 1815 elif i + 1 < len(command_args): |
| 2218 arg_dict[arg_to_parse] = command_args[i+1] | 1816 arg_dict[arg_to_parse] = command_args[i+1] |
| 2219 | 1817 |
| 2220 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') | 1818 path_to_generate = os.path.join('tools', 'perf', 'generate_profile') |
| 2221 | 1819 |
| 2222 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'): | 1820 if arg_dict.has_key('--profile-dir') and arg_dict.has_key('--browser'): |
| 2223 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) | 1821 profile_path, profile_type = os.path.split(arg_dict['--profile-dir']) |
| 2224 return not RunProcess(['python', path_to_generate, | 1822 return not bisect_utils.RunProcess(['python', path_to_generate, |
| 2225 '--profile-type-to-generate', profile_type, | 1823 '--profile-type-to-generate', profile_type, |
| 2226 '--browser', arg_dict['--browser'], '--output-dir', profile_path]) | 1824 '--browser', arg_dict['--browser'], '--output-dir', profile_path]) |
| 2227 return False | 1825 return False |
| 2228 return True | 1826 return True |
| 2229 | 1827 |
| 2230 def _IsBisectModeUsingMetric(self): | 1828 def _IsBisectModeUsingMetric(self): |
| 2231 return self.opts.bisect_mode in [BISECT_MODE_MEAN, BISECT_MODE_STD_DEV] | 1829 return self.opts.bisect_mode in [BISECT_MODE_MEAN, BISECT_MODE_STD_DEV] |
| 2232 | 1830 |
| 2233 def _IsBisectModeReturnCode(self): | 1831 def _IsBisectModeReturnCode(self): |
| 2234 return self.opts.bisect_mode in [BISECT_MODE_RETURN_CODE] | 1832 return self.opts.bisect_mode in [BISECT_MODE_RETURN_CODE] |
| 2235 | 1833 |
| 2236 def _IsBisectModeStandardDeviation(self): | 1834 def _IsBisectModeStandardDeviation(self): |
| 2237 return self.opts.bisect_mode in [BISECT_MODE_STD_DEV] | 1835 return self.opts.bisect_mode in [BISECT_MODE_STD_DEV] |
| 2238 | 1836 |
| 2239 def GetCompatibleCommand(self, command_to_run, revision, depot): | 1837 def GetCompatibleCommand(self, command_to_run, revision, depot): |
| 2240 # Prior to crrev.com/274857 *only* android-chromium-testshell | 1838 # Prior to crrev.com/274857 *only* android-chromium-testshell |
| 2241 # Then until crrev.com/276628 *both* (android-chromium-testshell and | 1839 # Then until crrev.com/276628 *both* (android-chromium-testshell and |
| 2242 # android-chrome-shell) work. After that rev 276628 *only* | 1840 # android-chrome-shell) work. After that rev 276628 *only* |
| 2243 # android-chrome-shell works. bisect-perf-reggresion.py script should | 1841 # android-chrome-shell works. bisect-perf-reggresion.py script should |
| 2244 # handle these cases and set appropriate browser type based on revision. | 1842 # handle these cases and set appropriate browser type based on revision. |
| 2245 if self.opts.target_platform in ['android']: | 1843 if self.opts.target_platform in ['android']: |
| 2246 # When its a third_party depot, get the chromium revision. | 1844 # When its a third_party depot, get the chromium revision. |
| 2247 if depot != 'chromium': | 1845 if depot != 'chromium': |
| 2248 revision = CheckRunGit(['rev-parse', 'HEAD'], cwd=self.src_cwd).strip() | 1846 revision = bisect_utils.CheckRunGit( |
| 1847 ['rev-parse', 'HEAD'], cwd=self.src_cwd).strip() |
| 2249 svn_revision = self.source_control.SVNFindRev(revision, cwd=self.src_cwd) | 1848 svn_revision = self.source_control.SVNFindRev(revision, cwd=self.src_cwd) |
| 2250 if not svn_revision: | 1849 if not svn_revision: |
| 2251 return command_to_run | 1850 return command_to_run |
| 2252 cmd_re = re.compile('--browser=(?P<browser_type>\S+)') | 1851 cmd_re = re.compile('--browser=(?P<browser_type>\S+)') |
| 2253 matches = cmd_re.search(command_to_run) | 1852 matches = cmd_re.search(command_to_run) |
| 2254 if IsStringInt(svn_revision) and matches: | 1853 if bisect_utils.IsStringInt(svn_revision) and matches: |
| 2255 cmd_browser = matches.group('browser_type') | 1854 cmd_browser = matches.group('browser_type') |
| 2256 if svn_revision <= 274857 and cmd_browser == 'android-chrome-shell': | 1855 if svn_revision <= 274857 and cmd_browser == 'android-chrome-shell': |
| 2257 return command_to_run.replace(cmd_browser, | 1856 return command_to_run.replace(cmd_browser, |
| 2258 'android-chromium-testshell') | 1857 'android-chromium-testshell') |
| 2259 elif (svn_revision >= 276628 and | 1858 elif (svn_revision >= 276628 and |
| 2260 cmd_browser == 'android-chromium-testshell'): | 1859 cmd_browser == 'android-chromium-testshell'): |
| 2261 return command_to_run.replace(cmd_browser, | 1860 return command_to_run.replace(cmd_browser, |
| 2262 'android-chrome-shell') | 1861 'android-chrome-shell') |
| 2263 return command_to_run | 1862 return command_to_run |
| 2264 | 1863 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2290 'mean': 0.0, | 1889 'mean': 0.0, |
| 2291 'std_err': 0.0, | 1890 'std_err': 0.0, |
| 2292 'std_dev': 0.0, | 1891 'std_dev': 0.0, |
| 2293 'values': [0.0] | 1892 'values': [0.0] |
| 2294 } | 1893 } |
| 2295 return (fake_results, success_code) | 1894 return (fake_results, success_code) |
| 2296 | 1895 |
| 2297 # For Windows platform set posix=False, to parse windows paths correctly. | 1896 # For Windows platform set posix=False, to parse windows paths correctly. |
| 2298 # On Windows, path separators '\' or '\\' are replace by '' when posix=True, | 1897 # On Windows, path separators '\' or '\\' are replace by '' when posix=True, |
| 2299 # refer to http://bugs.python.org/issue1724822. By default posix=True. | 1898 # refer to http://bugs.python.org/issue1724822. By default posix=True. |
| 2300 args = shlex.split(command_to_run, posix=not IsWindowsHost()) | 1899 args = shlex.split(command_to_run, posix=not bisect_utils.IsWindowsHost()) |
| 2301 | 1900 |
| 2302 if not self._GenerateProfileIfNecessary(args): | 1901 if not self._GenerateProfileIfNecessary(args): |
| 2303 err_text = 'Failed to generate profile for performance test.' | 1902 err_text = 'Failed to generate profile for performance test.' |
| 2304 return (err_text, failure_code) | 1903 return (err_text, failure_code) |
| 2305 | 1904 |
| 2306 # If running a Telemetry test for Chrome OS, insert the remote IP and | 1905 # If running a Telemetry test for Chrome OS, insert the remote IP and |
| 2307 # identity parameters. | 1906 # identity parameters. |
| 2308 is_telemetry = bisect_utils.IsTelemetryCommand(command_to_run) | 1907 is_telemetry = bisect_utils.IsTelemetryCommand(command_to_run) |
| 2309 if self.opts.target_platform == 'cros' and is_telemetry: | 1908 if self.opts.target_platform == 'cros' and is_telemetry: |
| 2310 args.append('--remote=%s' % self.opts.cros_remote_ip) | 1909 args.append('--remote=%s' % self.opts.cros_remote_ip) |
| 2311 args.append('--identity=%s' % CROS_TEST_KEY_PATH) | 1910 args.append('--identity=%s' % CROS_TEST_KEY_PATH) |
| 2312 | 1911 |
| 2313 start_time = time.time() | 1912 start_time = time.time() |
| 2314 | 1913 |
| 2315 metric_values = [] | 1914 metric_values = [] |
| 2316 output_of_all_runs = '' | 1915 output_of_all_runs = '' |
| 2317 for i in xrange(self.opts.repeat_test_count): | 1916 for i in xrange(self.opts.repeat_test_count): |
| 2318 # Can ignore the return code since if the tests fail, it won't return 0. | 1917 # Can ignore the return code since if the tests fail, it won't return 0. |
| 2319 current_args = copy.copy(args) | 1918 current_args = copy.copy(args) |
| 2320 if is_telemetry: | 1919 if is_telemetry: |
| 2321 if i == 0 and reset_on_first_run: | 1920 if i == 0 and reset_on_first_run: |
| 2322 current_args.append('--reset-results') | 1921 current_args.append('--reset-results') |
| 2323 elif i == self.opts.repeat_test_count - 1 and upload_on_last_run: | 1922 elif i == self.opts.repeat_test_count - 1 and upload_on_last_run: |
| 2324 current_args.append('--upload-results') | 1923 current_args.append('--upload-results') |
| 2325 if results_label: | 1924 if results_label: |
| 2326 current_args.append('--results-label=%s' % results_label) | 1925 current_args.append('--results-label=%s' % results_label) |
| 2327 try: | 1926 try: |
| 2328 (output, return_code) = RunProcessAndRetrieveOutput(current_args, | 1927 (output, return_code) = bisect_utils.RunProcessAndRetrieveOutput( |
| 2329 cwd=self.src_cwd) | 1928 current_args, cwd=self.src_cwd) |
| 2330 except OSError, e: | 1929 except OSError, e: |
| 2331 if e.errno == errno.ENOENT: | 1930 if e.errno == errno.ENOENT: |
| 2332 err_text = ('Something went wrong running the performance test. ' | 1931 err_text = ('Something went wrong running the performance test. ' |
| 2333 'Please review the command line:\n\n') | 1932 'Please review the command line:\n\n') |
| 2334 if 'src/' in ' '.join(args): | 1933 if 'src/' in ' '.join(args): |
| 2335 err_text += ('Check that you haven\'t accidentally specified a ' | 1934 err_text += ('Check that you haven\'t accidentally specified a ' |
| 2336 'path with src/ in the command.\n\n') | 1935 'path with src/ in the command.\n\n') |
| 2337 err_text += ' '.join(args) | 1936 err_text += ' '.join(args) |
| 2338 err_text += '\n' | 1937 err_text += '\n' |
| 2339 | 1938 |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2428 # guarantee that the SVN revision will exist for each of the dependant | 2027 # guarantee that the SVN revision will exist for each of the dependant |
| 2429 # depots, so we have to grep the git logs and grab the next earlier one. | 2028 # depots, so we have to grep the git logs and grab the next earlier one. |
| 2430 if not is_base and\ | 2029 if not is_base and\ |
| 2431 DEPOT_DEPS_NAME[depot]['depends'] and\ | 2030 DEPOT_DEPS_NAME[depot]['depends'] and\ |
| 2432 self.source_control.IsGit(): | 2031 self.source_control.IsGit(): |
| 2433 svn_rev = self.source_control.SVNFindRev(revision) | 2032 svn_rev = self.source_control.SVNFindRev(revision) |
| 2434 | 2033 |
| 2435 for d in DEPOT_DEPS_NAME[depot]['depends']: | 2034 for d in DEPOT_DEPS_NAME[depot]['depends']: |
| 2436 self.ChangeToDepotWorkingDirectory(d) | 2035 self.ChangeToDepotWorkingDirectory(d) |
| 2437 | 2036 |
| 2438 dependant_rev = self.source_control.ResolveToRevision(svn_rev, d, -1000) | 2037 dependant_rev = self.source_control.ResolveToRevision( |
| 2038 svn_rev, d, DEPOT_DEPS_NAME, -1000) |
| 2439 | 2039 |
| 2440 if dependant_rev: | 2040 if dependant_rev: |
| 2441 revisions_to_sync.append([d, dependant_rev]) | 2041 revisions_to_sync.append([d, dependant_rev]) |
| 2442 | 2042 |
| 2443 num_resolved = len(revisions_to_sync) | 2043 num_resolved = len(revisions_to_sync) |
| 2444 num_needed = len(DEPOT_DEPS_NAME[depot]['depends']) | 2044 num_needed = len(DEPOT_DEPS_NAME[depot]['depends']) |
| 2445 | 2045 |
| 2446 self.ChangeToDepotWorkingDirectory(depot) | 2046 self.ChangeToDepotWorkingDirectory(depot) |
| 2447 | 2047 |
| 2448 if not ((num_resolved - 1) == num_needed): | 2048 if not ((num_resolved - 1) == num_needed): |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2494 | 2094 |
| 2495 def PerformCrosChrootCleanup(self): | 2095 def PerformCrosChrootCleanup(self): |
| 2496 """Deletes the chroot. | 2096 """Deletes the chroot. |
| 2497 | 2097 |
| 2498 Returns: | 2098 Returns: |
| 2499 True if successful. | 2099 True if successful. |
| 2500 """ | 2100 """ |
| 2501 cwd = os.getcwd() | 2101 cwd = os.getcwd() |
| 2502 self.ChangeToDepotWorkingDirectory('cros') | 2102 self.ChangeToDepotWorkingDirectory('cros') |
| 2503 cmd = [CROS_SDK_PATH, '--delete'] | 2103 cmd = [CROS_SDK_PATH, '--delete'] |
| 2504 return_code = RunProcess(cmd) | 2104 return_code = bisect_utils.RunProcess(cmd) |
| 2505 os.chdir(cwd) | 2105 os.chdir(cwd) |
| 2506 return not return_code | 2106 return not return_code |
| 2507 | 2107 |
| 2508 def CreateCrosChroot(self): | 2108 def CreateCrosChroot(self): |
| 2509 """Creates a new chroot. | 2109 """Creates a new chroot. |
| 2510 | 2110 |
| 2511 Returns: | 2111 Returns: |
| 2512 True if successful. | 2112 True if successful. |
| 2513 """ | 2113 """ |
| 2514 cwd = os.getcwd() | 2114 cwd = os.getcwd() |
| 2515 self.ChangeToDepotWorkingDirectory('cros') | 2115 self.ChangeToDepotWorkingDirectory('cros') |
| 2516 cmd = [CROS_SDK_PATH, '--create'] | 2116 cmd = [CROS_SDK_PATH, '--create'] |
| 2517 return_code = RunProcess(cmd) | 2117 return_code = bisect_utils.RunProcess(cmd) |
| 2518 os.chdir(cwd) | 2118 os.chdir(cwd) |
| 2519 return not return_code | 2119 return not return_code |
| 2520 | 2120 |
| 2521 def PerformPreSyncCleanup(self, revision, depot): | 2121 def PerformPreSyncCleanup(self, revision, depot): |
| 2522 """Performs any necessary cleanup before syncing. | 2122 """Performs any necessary cleanup before syncing. |
| 2523 | 2123 |
| 2524 Returns: | 2124 Returns: |
| 2525 True if successful. | 2125 True if successful. |
| 2526 """ | 2126 """ |
| 2527 if depot == 'chromium' or depot == 'android-chrome': | 2127 if depot == 'chromium' or depot == 'android-chrome': |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2567 Args: | 2167 Args: |
| 2568 depot: The depot being bisected. | 2168 depot: The depot being bisected. |
| 2569 revision: Current revision we're synced to. | 2169 revision: Current revision we're synced to. |
| 2570 | 2170 |
| 2571 Returns: | 2171 Returns: |
| 2572 True if we should skip building/testing this revision. | 2172 True if we should skip building/testing this revision. |
| 2573 """ | 2173 """ |
| 2574 if depot == 'chromium': | 2174 if depot == 'chromium': |
| 2575 if self.source_control.IsGit(): | 2175 if self.source_control.IsGit(): |
| 2576 cmd = ['diff-tree', '--no-commit-id', '--name-only', '-r', revision] | 2176 cmd = ['diff-tree', '--no-commit-id', '--name-only', '-r', revision] |
| 2577 output = CheckRunGit(cmd) | 2177 output = bisect_utils.CheckRunGit(cmd) |
| 2578 | 2178 |
| 2579 files = output.splitlines() | 2179 files = output.splitlines() |
| 2580 | 2180 |
| 2581 if len(files) == 1 and files[0] == 'DEPS': | 2181 if len(files) == 1 and files[0] == 'DEPS': |
| 2582 return True | 2182 return True |
| 2583 | 2183 |
| 2584 return False | 2184 return False |
| 2585 | 2185 |
| 2586 def SyncBuildAndRunRevision(self, revision, depot, command_to_run, metric, | 2186 def SyncBuildAndRunRevision(self, revision, depot, command_to_run, metric, |
| 2587 skippable=False): | 2187 skippable=False): |
| (...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2924 if changes_to_deps: | 2524 if changes_to_deps: |
| 2925 # DEPS file was changed, search from the oldest change to DEPS file to | 2525 # DEPS file was changed, search from the oldest change to DEPS file to |
| 2926 # bad_revision to see if there are matching .DEPS.git changes. | 2526 # bad_revision to see if there are matching .DEPS.git changes. |
| 2927 oldest_deps_change = changes_to_deps[-1] | 2527 oldest_deps_change = changes_to_deps[-1] |
| 2928 changes_to_gitdeps = self.source_control.QueryFileRevisionHistory( | 2528 changes_to_gitdeps = self.source_control.QueryFileRevisionHistory( |
| 2929 bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) | 2529 bisect_utils.FILE_DEPS_GIT, oldest_deps_change, bad_revision) |
| 2930 | 2530 |
| 2931 if len(changes_to_deps) != len(changes_to_gitdeps): | 2531 if len(changes_to_deps) != len(changes_to_gitdeps): |
| 2932 # Grab the timestamp of the last DEPS change | 2532 # Grab the timestamp of the last DEPS change |
| 2933 cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] | 2533 cmd = ['log', '--format=%ct', '-1', changes_to_deps[0]] |
| 2934 output = CheckRunGit(cmd) | 2534 output = bisect_utils.CheckRunGit(cmd) |
| 2935 commit_time = int(output) | 2535 commit_time = int(output) |
| 2936 | 2536 |
| 2937 # Try looking for a commit that touches the .DEPS.git file in the | 2537 # Try looking for a commit that touches the .DEPS.git file in the |
| 2938 # next 15 minutes after the DEPS file change. | 2538 # next 15 minutes after the DEPS file change. |
| 2939 cmd = ['log', '--format=%H', '-1', | 2539 cmd = ['log', '--format=%H', '-1', |
| 2940 '--before=%d' % (commit_time + 900), '--after=%d' % commit_time, | 2540 '--before=%d' % (commit_time + 900), '--after=%d' % commit_time, |
| 2941 'origin/master', bisect_utils.FILE_DEPS_GIT] | 2541 'origin/master', bisect_utils.FILE_DEPS_GIT] |
| 2942 output = CheckRunGit(cmd) | 2542 output = bisect_utils.CheckRunGit(cmd) |
| 2943 output = output.strip() | 2543 output = output.strip() |
| 2944 if output: | 2544 if output: |
| 2945 self.warnings.append('Detected change to DEPS and modified ' | 2545 self.warnings.append('Detected change to DEPS and modified ' |
| 2946 'revision range to include change to .DEPS.git') | 2546 'revision range to include change to .DEPS.git') |
| 2947 return (output, good_revision) | 2547 return (output, good_revision) |
| 2948 else: | 2548 else: |
| 2949 self.warnings.append('Detected change to DEPS but couldn\'t find ' | 2549 self.warnings.append('Detected change to DEPS but couldn\'t find ' |
| 2950 'matching change to .DEPS.git') | 2550 'matching change to .DEPS.git') |
| 2951 return (bad_revision, good_revision) | 2551 return (bad_revision, good_revision) |
| 2952 | 2552 |
| 2953 def CheckIfRevisionsInProperOrder(self, | 2553 def CheckIfRevisionsInProperOrder(self, |
| 2954 target_depot, | 2554 target_depot, |
| 2955 good_revision, | 2555 good_revision, |
| 2956 bad_revision): | 2556 bad_revision): |
| 2957 """Checks that |good_revision| is an earlier revision than |bad_revision|. | 2557 """Checks that |good_revision| is an earlier revision than |bad_revision|. |
| 2958 | 2558 |
| 2959 Args: | 2559 Args: |
| 2960 good_revision: Number/tag of the known good revision. | 2560 good_revision: Number/tag of the known good revision. |
| 2961 bad_revision: Number/tag of the known bad revision. | 2561 bad_revision: Number/tag of the known bad revision. |
| 2962 | 2562 |
| 2963 Returns: | 2563 Returns: |
| 2964 True if the revisions are in the proper order (good earlier than bad). | 2564 True if the revisions are in the proper order (good earlier than bad). |
| 2965 """ | 2565 """ |
| 2966 if self.source_control.IsGit() and target_depot != 'cros': | 2566 if self.source_control.IsGit() and target_depot != 'cros': |
| 2967 cmd = ['log', '--format=%ct', '-1', good_revision] | 2567 cmd = ['log', '--format=%ct', '-1', good_revision] |
| 2968 cwd = self._GetDepotDirectory(target_depot) | 2568 cwd = self._GetDepotDirectory(target_depot) |
| 2969 | 2569 |
| 2970 output = CheckRunGit(cmd, cwd=cwd) | 2570 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) |
| 2971 good_commit_time = int(output) | 2571 good_commit_time = int(output) |
| 2972 | 2572 |
| 2973 cmd = ['log', '--format=%ct', '-1', bad_revision] | 2573 cmd = ['log', '--format=%ct', '-1', bad_revision] |
| 2974 output = CheckRunGit(cmd, cwd=cwd) | 2574 output = bisect_utils.CheckRunGit(cmd, cwd=cwd) |
| 2975 bad_commit_time = int(output) | 2575 bad_commit_time = int(output) |
| 2976 | 2576 |
| 2977 return good_commit_time <= bad_commit_time | 2577 return good_commit_time <= bad_commit_time |
| 2978 else: | 2578 else: |
| 2979 # Cros/svn use integers | 2579 # Cros/svn use integers |
| 2980 return int(good_revision) <= int(bad_revision) | 2580 return int(good_revision) <= int(bad_revision) |
| 2981 | 2581 |
| 2982 def CanPerformBisect(self, revision_to_check): | 2582 def CanPerformBisect(self, revision_to_check): |
| 2983 """Checks whether a given revision is bisectable. | 2583 """Checks whether a given revision is bisectable. |
| 2984 | 2584 |
| 2985 Note: At present it checks whether a given revision is bisectable on | 2585 Note: At present it checks whether a given revision is bisectable on |
| 2986 android bots(refer crbug.com/385324). | 2586 android bots(refer crbug.com/385324). |
| 2987 | 2587 |
| 2988 Args: | 2588 Args: |
| 2989 revision_to_check: Known good revision. | 2589 revision_to_check: Known good revision. |
| 2990 | 2590 |
| 2991 Returns: | 2591 Returns: |
| 2992 A dictionary indicating the result. If revision is not bisectable, | 2592 A dictionary indicating the result. If revision is not bisectable, |
| 2993 this will contain the field "error", otherwise None. | 2593 this will contain the field "error", otherwise None. |
| 2994 """ | 2594 """ |
| 2995 if self.opts.target_platform == 'android': | 2595 if self.opts.target_platform == 'android': |
| 2996 revision_to_check = self.source_control.SVNFindRev(revision_to_check) | 2596 revision_to_check = self.source_control.SVNFindRev(revision_to_check) |
| 2997 if IsStringInt(revision_to_check) and revision_to_check < 265549: | 2597 if (bisect_utils.IsStringInt(revision_to_check) |
| 2598 and revision_to_check < 265549): |
| 2998 return {'error': ( | 2599 return {'error': ( |
| 2999 'Bisect cannot conitnue for the given revision range.\n' | 2600 'Bisect cannot conitnue for the given revision range.\n' |
| 3000 'It is impossible to bisect Android regressions ' | 2601 'It is impossible to bisect Android regressions ' |
| 3001 'prior to r265549, which allows the bisect bot to ' | 2602 'prior to r265549, which allows the bisect bot to ' |
| 3002 'rely on Telemetry to do apk installation of the most recently ' | 2603 'rely on Telemetry to do apk installation of the most recently ' |
| 3003 'built local ChromeShell(refer to crbug.com/385324).\n' | 2604 'built local ChromeShell(refer to crbug.com/385324).\n' |
| 3004 'Please try bisecting revisions greater than or equal to r265549.')} | 2605 'Please try bisecting revisions greater than or equal to r265549.')} |
| 3005 return None | 2606 return None |
| 3006 | 2607 |
| 3007 def Run(self, command_to_run, bad_revision_in, good_revision_in, metric): | 2608 def Run(self, command_to_run, bad_revision_in, good_revision_in, metric): |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3054 target_depot = 'chromium' | 2655 target_depot = 'chromium' |
| 3055 if self.opts.target_platform == 'cros': | 2656 if self.opts.target_platform == 'cros': |
| 3056 target_depot = 'cros' | 2657 target_depot = 'cros' |
| 3057 elif self.opts.target_platform == 'android-chrome': | 2658 elif self.opts.target_platform == 'android-chrome': |
| 3058 target_depot = 'android-chrome' | 2659 target_depot = 'android-chrome' |
| 3059 | 2660 |
| 3060 cwd = os.getcwd() | 2661 cwd = os.getcwd() |
| 3061 self.ChangeToDepotWorkingDirectory(target_depot) | 2662 self.ChangeToDepotWorkingDirectory(target_depot) |
| 3062 | 2663 |
| 3063 # If they passed SVN CL's, etc... we can try match them to git SHA1's. | 2664 # If they passed SVN CL's, etc... we can try match them to git SHA1's. |
| 3064 bad_revision = self.source_control.ResolveToRevision(bad_revision_in, | 2665 bad_revision = self.source_control.ResolveToRevision( |
| 3065 target_depot, 100) | 2666 bad_revision_in, target_depot, DEPOT_DEPS_NAME, 100) |
| 3066 good_revision = self.source_control.ResolveToRevision(good_revision_in, | 2667 good_revision = self.source_control.ResolveToRevision( |
| 3067 target_depot, -100) | 2668 good_revision_in, target_depot, DEPOT_DEPS_NAME, -100) |
| 3068 | 2669 |
| 3069 os.chdir(cwd) | 2670 os.chdir(cwd) |
| 3070 | 2671 |
| 3071 | 2672 |
| 3072 if bad_revision is None: | 2673 if bad_revision is None: |
| 3073 results['error'] = 'Could\'t resolve [%s] to SHA1.' % (bad_revision_in,) | 2674 results['error'] = 'Could\'t resolve [%s] to SHA1.' % (bad_revision_in,) |
| 3074 return results | 2675 return results |
| 3075 | 2676 |
| 3076 if good_revision is None: | 2677 if good_revision is None: |
| 3077 results['error'] = 'Could\'t resolve [%s] to SHA1.' % (good_revision_in,) | 2678 results['error'] = 'Could\'t resolve [%s] to SHA1.' % (good_revision_in,) |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3649 cwd = os.getcwd() | 3250 cwd = os.getcwd() |
| 3650 self.ChangeToDepotWorkingDirectory( | 3251 self.ChangeToDepotWorkingDirectory( |
| 3651 revision_data[last_broken_revision]['depot']) | 3252 revision_data[last_broken_revision]['depot']) |
| 3652 | 3253 |
| 3653 if revision_data[last_broken_revision]['depot'] == 'cros': | 3254 if revision_data[last_broken_revision]['depot'] == 'cros': |
| 3654 # Want to get a list of all the commits and what depots they belong | 3255 # Want to get a list of all the commits and what depots they belong |
| 3655 # to so that we can grab info about each. | 3256 # to so that we can grab info about each. |
| 3656 cmd = ['repo', 'forall', '-c', | 3257 cmd = ['repo', 'forall', '-c', |
| 3657 'pwd ; git log --pretty=oneline --before=%d --after=%d' % ( | 3258 'pwd ; git log --pretty=oneline --before=%d --after=%d' % ( |
| 3658 last_broken_revision, first_working_revision + 1)] | 3259 last_broken_revision, first_working_revision + 1)] |
| 3659 (output, return_code) = RunProcessAndRetrieveOutput(cmd) | 3260 (output, return_code) = bisect_utils.RunProcessAndRetrieveOutput(cmd) |
| 3660 | 3261 |
| 3661 changes = [] | 3262 changes = [] |
| 3662 assert not return_code, 'An error occurred while running'\ | 3263 assert not return_code, 'An error occurred while running'\ |
| 3663 ' "%s"' % ' '.join(cmd) | 3264 ' "%s"' % ' '.join(cmd) |
| 3664 last_depot = None | 3265 last_depot = None |
| 3665 cwd = os.getcwd() | 3266 cwd = os.getcwd() |
| 3666 for l in output.split('\n'): | 3267 for l in output.split('\n'): |
| 3667 if l: | 3268 if l: |
| 3668 # Output will be in form: | 3269 # Output will be in form: |
| 3669 # /path_to_depot | 3270 # /path_to_depot |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3777 results_dict['first_working_revision'], | 3378 results_dict['first_working_revision'], |
| 3778 results_dict['last_broken_revision'], | 3379 results_dict['last_broken_revision'], |
| 3779 results_dict['confidence']) | 3380 results_dict['confidence']) |
| 3780 self._PrintStepTime(revision_data_sorted) | 3381 self._PrintStepTime(revision_data_sorted) |
| 3781 self._PrintReproSteps() | 3382 self._PrintReproSteps() |
| 3782 self._PrintThankYou() | 3383 self._PrintThankYou() |
| 3783 if self.opts.output_buildbot_annotations: | 3384 if self.opts.output_buildbot_annotations: |
| 3784 bisect_utils.OutputAnnotationStepClosed() | 3385 bisect_utils.OutputAnnotationStepClosed() |
| 3785 | 3386 |
| 3786 | 3387 |
| 3787 def DetermineAndCreateSourceControl(opts): | |
| 3788 """Attempts to determine the underlying source control workflow and returns | |
| 3789 a SourceControl object. | |
| 3790 | |
| 3791 Returns: | |
| 3792 An instance of a SourceControl object, or None if the current workflow | |
| 3793 is unsupported. | |
| 3794 """ | |
| 3795 | |
| 3796 (output, _) = RunGit(['rev-parse', '--is-inside-work-tree']) | |
| 3797 | |
| 3798 if output.strip() == 'true': | |
| 3799 return GitSourceControl(opts) | |
| 3800 | |
| 3801 return None | |
| 3802 | |
| 3803 | |
| 3804 def IsPlatformSupported(opts): | 3388 def IsPlatformSupported(opts): |
| 3805 """Checks that this platform and build system are supported. | 3389 """Checks that this platform and build system are supported. |
| 3806 | 3390 |
| 3807 Args: | 3391 Args: |
| 3808 opts: The options parsed from the command line. | 3392 opts: The options parsed from the command line. |
| 3809 | 3393 |
| 3810 Returns: | 3394 Returns: |
| 3811 True if the platform and build system are supported. | 3395 True if the platform and build system are supported. |
| 3812 """ | 3396 """ |
| 3813 # Haven't tested the script out on any other platforms yet. | 3397 # Haven't tested the script out on any other platforms yet. |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4057 if not opts.builder_host: | 3641 if not opts.builder_host: |
| 4058 raise RuntimeError('Must specify try server hostname, when ' | 3642 raise RuntimeError('Must specify try server hostname, when ' |
| 4059 'gs_bucket is used: --builder_host') | 3643 'gs_bucket is used: --builder_host') |
| 4060 if not opts.builder_port: | 3644 if not opts.builder_port: |
| 4061 raise RuntimeError('Must specify try server port number, when ' | 3645 raise RuntimeError('Must specify try server port number, when ' |
| 4062 'gs_bucket is used: --builder_port') | 3646 'gs_bucket is used: --builder_port') |
| 4063 if opts.target_platform == 'cros': | 3647 if opts.target_platform == 'cros': |
| 4064 # Run sudo up front to make sure credentials are cached for later. | 3648 # Run sudo up front to make sure credentials are cached for later. |
| 4065 print 'Sudo is required to build cros:' | 3649 print 'Sudo is required to build cros:' |
| 4066 print | 3650 print |
| 4067 RunProcess(['sudo', 'true']) | 3651 bisect_utils.RunProcess(['sudo', 'true']) |
| 4068 | 3652 |
| 4069 if not opts.cros_board: | 3653 if not opts.cros_board: |
| 4070 raise RuntimeError('missing required parameter: --cros_board') | 3654 raise RuntimeError('missing required parameter: --cros_board') |
| 4071 | 3655 |
| 4072 if not opts.cros_remote_ip: | 3656 if not opts.cros_remote_ip: |
| 4073 raise RuntimeError('missing required parameter: --cros_remote_ip') | 3657 raise RuntimeError('missing required parameter: --cros_remote_ip') |
| 4074 | 3658 |
| 4075 if not opts.working_directory: | 3659 if not opts.working_directory: |
| 4076 raise RuntimeError('missing required parameter: --working_directory') | 3660 raise RuntimeError('missing required parameter: --working_directory') |
| 4077 | 3661 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4145 bisect_utils.CreateBisectDirectoryAndSetupDepot(opts, custom_deps) | 3729 bisect_utils.CreateBisectDirectoryAndSetupDepot(opts, custom_deps) |
| 4146 | 3730 |
| 4147 os.chdir(os.path.join(os.getcwd(), 'src')) | 3731 os.chdir(os.path.join(os.getcwd(), 'src')) |
| 4148 | 3732 |
| 4149 if not RemoveBuildFiles(opts.target_build_type): | 3733 if not RemoveBuildFiles(opts.target_build_type): |
| 4150 raise RuntimeError('Something went wrong removing the build files.') | 3734 raise RuntimeError('Something went wrong removing the build files.') |
| 4151 | 3735 |
| 4152 if not IsPlatformSupported(opts): | 3736 if not IsPlatformSupported(opts): |
| 4153 raise RuntimeError("Sorry, this platform isn't supported yet.") | 3737 raise RuntimeError("Sorry, this platform isn't supported yet.") |
| 4154 | 3738 |
| 4155 # Check what source control method they're using. Only support git workflow | 3739 # Check what source control method is being used, and create a |
| 4156 # at the moment. | 3740 # SourceControl object if possible. |
| 4157 source_control = DetermineAndCreateSourceControl(opts) | 3741 source_control = source_control_module.DetermineAndCreateSourceControl(opts) |
| 4158 | 3742 |
| 4159 if not source_control: | 3743 if not source_control: |
| 4160 raise RuntimeError("Sorry, only the git workflow is supported at the " | 3744 raise RuntimeError("Sorry, only the git workflow is supported at the " |
| 4161 "moment.") | 3745 "moment.") |
| 4162 | 3746 |
| 4163 # gClient sync seems to fail if you're not in master branch. | 3747 # gClient sync seems to fail if you're not in master branch. |
| 4164 if (not source_control.IsInProperBranch() and | 3748 if (not source_control.IsInProperBranch() and |
| 4165 not opts.debug_ignore_sync and | 3749 not opts.debug_ignore_sync and |
| 4166 not opts.working_directory): | 3750 not opts.working_directory): |
| 4167 raise RuntimeError("You must switch to master branch to run bisection.") | 3751 raise RuntimeError("You must switch to master branch to run bisection.") |
| (...skipping 14 matching lines...) Expand all Loading... |
| 4182 # The perf dashboard scrapes the "results" step in order to comment on | 3766 # The perf dashboard scrapes the "results" step in order to comment on |
| 4183 # bugs. If you change this, please update the perf dashboard as well. | 3767 # bugs. If you change this, please update the perf dashboard as well. |
| 4184 bisect_utils.OutputAnnotationStepStart('Results') | 3768 bisect_utils.OutputAnnotationStepStart('Results') |
| 4185 print 'Error: %s' % e.message | 3769 print 'Error: %s' % e.message |
| 4186 if opts.output_buildbot_annotations: | 3770 if opts.output_buildbot_annotations: |
| 4187 bisect_utils.OutputAnnotationStepClosed() | 3771 bisect_utils.OutputAnnotationStepClosed() |
| 4188 return 1 | 3772 return 1 |
| 4189 | 3773 |
| 4190 if __name__ == '__main__': | 3774 if __name__ == '__main__': |
| 4191 sys.exit(main()) | 3775 sys.exit(main()) |
| OLD | NEW |