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

Side by Side Diff: tools/bisect-perf-regression.py

Issue 418113003: Extract SourceControl to module; extract common methods to bisect_utils. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Performance Test Bisect Tool 6 """Performance Test Bisect Tool
7 7
8 This script bisects a series of changelists using binary search. It starts at 8 This script bisects a series of changelists using binary search. It starts at
9 a bad revision where a performance metric has regressed, and asks for a last 9 a bad revision where a performance metric has regressed, and asks for a last
10 known-good revision. It will then binary search across this revision range by 10 known-good revision. It will then binary search across this revision range by
(...skipping 28 matching lines...) Expand all
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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())
OLDNEW
« tools/auto_bisect/source_control.py ('K') | « tools/auto_bisect/source_control.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698