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

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

Issue 344123002: Download archives contain git or svn revisions in the filename. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Performance Test Bisect Tool 6 """Performance Test Bisect Tool
7 7
8 This script bisects a series of changelists using binary search. It starts at 8 This script bisects a series of changelists using binary search. It starts at
9 a bad revision where a performance metric has regressed, and asks for a last 9 a bad revision where a performance metric has regressed, and asks for a last
10 known-good revision. It will then binary search across this revision range by 10 known-good revision. It will then binary search across this revision range by
(...skipping 464 matching lines...) Expand 10 before | Expand all | Expand 10 after
475 475
476 def FetchFromCloudStorage(bucket_name, source_path, destination_path): 476 def FetchFromCloudStorage(bucket_name, source_path, destination_path):
477 """Fetches file(s) from the Google Cloud Storage. 477 """Fetches file(s) from the Google Cloud Storage.
478 478
479 Args: 479 Args:
480 bucket_name: Google Storage bucket name. 480 bucket_name: Google Storage bucket name.
481 source_path: Source file path. 481 source_path: Source file path.
482 destination_path: Destination file path. 482 destination_path: Destination file path.
483 483
484 Returns: 484 Returns:
485 True if the fetching succeeds, otherwise False. 485 Downloaded file path if exisits, otherwise None.
486 """ 486 """
487 target_file = os.path.join(destination_path, os.path.basename(source_path)) 487 target_file = os.path.join(destination_path, os.path.basename(source_path))
488 try: 488 try:
489 if cloud_storage.Exists(bucket_name, source_path): 489 if cloud_storage.Exists(bucket_name, source_path):
490 print 'Fetching file from gs//%s/%s ...' % (bucket_name, source_path) 490 print 'Fetching file from gs//%s/%s ...' % (bucket_name, source_path)
491 cloud_storage.Get(bucket_name, source_path, destination_path) 491 cloud_storage.Get(bucket_name, source_path, destination_path)
492 if os.path.exists(target_file): 492 if os.path.exists(target_file):
493 return True 493 return target_file
494 else: 494 else:
495 print ('File gs://%s/%s not found in cloud storage.' % ( 495 print ('File gs://%s/%s not found in cloud storage.' % (
496 bucket_name, source_path)) 496 bucket_name, source_path))
497 except Exception as e: 497 except Exception as e:
498 print 'Something went wrong while fetching file from cloud: %s' % e 498 print 'Something went wrong while fetching file from cloud: %s' % e
499 if os.path.exists(target_file): 499 if os.path.exists(target_file):
500 os.remove(target_file) 500 os.remove(target_file)
501 return False 501 return None
502 502
503 503
504 # This is copied from Chromium's project build/scripts/common/chromium_utils.py. 504 # This is copied from Chromium's project build/scripts/common/chromium_utils.py.
505 def MaybeMakeDirectory(*path): 505 def MaybeMakeDirectory(*path):
506 """Creates an entire path, if it doesn't already exist.""" 506 """Creates an entire path, if it doesn't already exist."""
507 file_path = os.path.join(*path) 507 file_path = os.path.join(*path)
508 try: 508 try:
509 os.makedirs(file_path) 509 os.makedirs(file_path)
510 except OSError, e: 510 except OSError, e:
511 if e.errno != errno.EEXIST: 511 if e.errno != errno.EEXIST:
(...skipping 1034 matching lines...) Expand 10 before | Expand all | Expand 10 after
1546 source_dir = os.path.join(build_dir, build_type) 1546 source_dir = os.path.join(build_dir, build_type)
1547 destination_dir = os.path.join(build_dir, '%s.bak' % build_type) 1547 destination_dir = os.path.join(build_dir, '%s.bak' % build_type)
1548 if restore: 1548 if restore:
1549 source_dir, destination_dir = destination_dir, source_dir 1549 source_dir, destination_dir = destination_dir, source_dir
1550 if os.path.exists(source_dir): 1550 if os.path.exists(source_dir):
1551 RmTreeAndMkDir(destination_dir, skip_makedir=True) 1551 RmTreeAndMkDir(destination_dir, skip_makedir=True)
1552 shutil.move(source_dir, destination_dir) 1552 shutil.move(source_dir, destination_dir)
1553 return destination_dir 1553 return destination_dir
1554 return None 1554 return None
1555 1555
1556 def GetBuildArchiveForRevision(self, revision, gs_bucket, target_arch,
1557 patch_sha, out_dir):
1558 """Checks and downloads build archive for a given revision.
1559
1560 Checks for build archive with Git hash or SVN revision. If either of the
1561 file exists, then downloads the archive file.
1562
1563 Args:
1564 revision: A Git hash revision.
1565 gs_bucket: Cloud storage bucket name
1566 target_arch: 32 or 64 bit build target
1567 patch: A DEPS patch (used while bisecting 3rd party repositories).
1568 out_dir: Build output directory where downloaded file is stored.
1569
1570 Returns:
1571 Downloaded archive file path if exists, otherwise None.
1572 """
1573 # Source archive file path on cloud storage using Git revision.
1574 source_file = GetRemoteBuildPath(revision, target_arch, patch_sha)
1575 downloaded_archive = FetchFromCloudStorage(gs_bucket, source_file, out_dir)
1576 if not downloaded_archive:
1577 # Get SVN revision for the given SHA.
1578 svn_revision = self.source_control.SVNFindRev(revision)
1579 if svn_revision:
1580 # Source archive file path on cloud storage using SVN revision.
1581 source_file = GetRemoteBuildPath(svn_revision, target_arch, patch_sha)
1582 return FetchFromCloudStorage(gs_bucket, source_file, out_dir)
1583 return downloaded_archive
1584
1556 def DownloadCurrentBuild(self, revision, build_type='Release', patch=None): 1585 def DownloadCurrentBuild(self, revision, build_type='Release', patch=None):
1557 """Downloads the build archive for the given revision. 1586 """Downloads the build archive for the given revision.
1558 1587
1559 Args: 1588 Args:
1560 revision: The SVN revision to build. 1589 revision: The Git revision to download or build.
1561 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.) 1590 build_type: Target build type ('Release', 'Debug', 'Release_x64' etc.)
1591 patch: A DEPS patch (used while bisecting 3rd party repositories).
1562 1592
1563 Returns: 1593 Returns:
1564 True if download succeeds, otherwise False. 1594 True if download succeeds, otherwise False.
1565 """ 1595 """
1566 patch_sha = None 1596 patch_sha = None
1567 if patch: 1597 if patch:
1568 # Get the SHA of the DEPS changes patch. 1598 # Get the SHA of the DEPS changes patch.
1569 patch_sha = GetSHA1HexDigest(patch) 1599 patch_sha = GetSHA1HexDigest(patch)
1570 1600
1571 # Update the DEPS changes patch with a patch to create a new file named 1601 # Update the DEPS changes patch with a patch to create a new file named
1572 # 'DEPS.sha' and add patch_sha evaluated above to it. 1602 # 'DEPS.sha' and add patch_sha evaluated above to it.
1573 patch = '%s\n%s' % (patch, DEPS_SHA_PATCH % {'deps_sha': patch_sha}) 1603 patch = '%s\n%s' % (patch, DEPS_SHA_PATCH % {'deps_sha': patch_sha})
1574 1604
1575 # Source archive file path on cloud storage.
1576 source_file = GetRemoteBuildPath(revision, self.opts.target_arch, patch_sha)
1577
1578 # Get Build output directory 1605 # Get Build output directory
1579 abs_build_dir = os.path.abspath( 1606 abs_build_dir = os.path.abspath(
1580 self.builder.GetBuildOutputDirectory(self.opts, self.src_cwd)) 1607 self.builder.GetBuildOutputDirectory(self.opts, self.src_cwd))
1581 # Downloaded archive file path.
1582 downloaded_file = os.path.join(
1583 abs_build_dir,
1584 GetZipFileName(revision, self.opts.target_arch, patch_sha))
1585 1608
1586 fetch_build_func = lambda: FetchFromCloudStorage(self.opts.gs_bucket, 1609 fetch_build_func = lambda: self.GetBuildArchiveForRevision(
1587 source_file, 1610 revision, self.opts.gs_bucket, self.opts.target_arch,
1588 abs_build_dir) 1611 patch_sha, abs_build_dir)
1589 1612
1590 if not fetch_build_func(): 1613 # Downloaded archive file path, downloads build archive for given revision.
1591 if not self.PostBuildRequestAndWait(revision, 1614 downloaded_file = fetch_build_func()
1592 fetch_build=fetch_build_func, 1615
1593 patch=patch): 1616 # When build archive doesn't exists, post a build request to tryserver
1594 raise RuntimeError('Somewthing went wrong while processing build' 1617 # and wait for the build to be produced.
1595 'request for: %s' % revision) 1618 if not downloaded_file:
1619 downloaded_file = self.PostBuildRequestAndWait(
1620 revision, fetch_build=fetch_build_func, patch=patch)
1621 if not downloaded_file:
1622 return False
1623
1596 # Generic name for the archive, created when archive file is extracted. 1624 # Generic name for the archive, created when archive file is extracted.
1597 output_dir = os.path.join( 1625 output_dir = os.path.join(
1598 abs_build_dir, GetZipFileName(target_arch=self.opts.target_arch)) 1626 abs_build_dir, GetZipFileName(target_arch=self.opts.target_arch))
1599 # Unzip build archive directory. 1627 # Unzip build archive directory.
1600 try: 1628 try:
1601 RmTreeAndMkDir(output_dir, skip_makedir=True) 1629 RmTreeAndMkDir(output_dir, skip_makedir=True)
1602 ExtractZip(downloaded_file, abs_build_dir) 1630 ExtractZip(downloaded_file, abs_build_dir)
1603 if os.path.exists(output_dir): 1631 if os.path.exists(output_dir):
1604 self.BackupOrRestoreOutputdirectory(restore=False) 1632 self.BackupOrRestoreOutputdirectory(restore=False)
1605 # Build output directory based on target(e.g. out/Release, out/Debug). 1633 # Build output directory based on target(e.g. out/Release, out/Debug).
(...skipping 21 matching lines...) Expand all
1627 1655
1628 Args: 1656 Args:
1629 fetch_build: Function to check and download build from cloud storage. 1657 fetch_build: Function to check and download build from cloud storage.
1630 bot_name: Builder bot name on tryserver. 1658 bot_name: Builder bot name on tryserver.
1631 builder_host Tryserver hostname. 1659 builder_host Tryserver hostname.
1632 builder_port: Tryserver port. 1660 builder_port: Tryserver port.
1633 build_request_id: A unique ID of the build request posted to tryserver. 1661 build_request_id: A unique ID of the build request posted to tryserver.
1634 max_timeout: Maximum time to wait for the build. 1662 max_timeout: Maximum time to wait for the build.
1635 1663
1636 Returns: 1664 Returns:
1637 True if build exists and download is successful, otherwise throws 1665 Downloaded archive file path if exists, otherwise None.
1638 RuntimeError exception when time elapse.
1639 """ 1666 """
1640 # Build number on the tryserver. 1667 # Build number on the tryserver.
1641 build_num = None 1668 build_num = None
1642 # Interval to check build on cloud storage. 1669 # Interval to check build on cloud storage.
1643 poll_interval = 60 1670 poll_interval = 60
1644 # Interval to check build status on tryserver. 1671 # Interval to check build status on tryserver.
1645 status_check_interval = 600 1672 status_check_interval = 600
1646 last_status_check = time.time() 1673 last_status_check = time.time()
1647 start_time = time.time() 1674 start_time = time.time()
1648 while True: 1675 while True:
1649 # Checks for build on gs://chrome-perf and download if exists. 1676 # Checks for build on gs://chrome-perf and download if exists.
1650 res = fetch_build() 1677 res = fetch_build()
1651 if res: 1678 if res:
1652 return (res, 'Build successfully found') 1679 return (res, 'Build successfully found')
1653 elapsed_status_check = time.time() - last_status_check 1680 elapsed_status_check = time.time() - last_status_check
1654 # To avoid overloading tryserver with status check requests, we check 1681 # To avoid overloading tryserver with status check requests, we check
1655 # build status for every 10 mins. 1682 # build status for every 10 mins.
1656 if elapsed_status_check > status_check_interval: 1683 if elapsed_status_check > status_check_interval:
1657 last_status_check = time.time() 1684 last_status_check = time.time()
1658 if not build_num: 1685 if not build_num:
1659 # Get the build number on tryserver for the current build. 1686 # Get the build number on tryserver for the current build.
1660 build_num = bisect_builder.GetBuildNumFromBuilder( 1687 build_num = bisect_builder.GetBuildNumFromBuilder(
1661 build_request_id, bot_name, builder_host, builder_port) 1688 build_request_id, bot_name, builder_host, builder_port)
1662 # Check the status of build using the build number. 1689 # Check the status of build using the build number.
1663 # Note: Build is treated as PENDING if build number is not found 1690 # Note: Build is treated as PENDING if build number is not found
1664 # on the the tryserver. 1691 # on the the tryserver.
1665 build_status, status_link = bisect_builder.GetBuildStatus( 1692 build_status, status_link = bisect_builder.GetBuildStatus(
1666 build_num, bot_name, builder_host, builder_port) 1693 build_num, bot_name, builder_host, builder_port)
1667 if build_status == bisect_builder.FAILED: 1694 if build_status == bisect_builder.FAILED:
1668 return (False, 'Failed to produce build, log: %s' % status_link) 1695 return (None, 'Failed to produce build, log: %s' % status_link)
1669 elapsed_time = time.time() - start_time 1696 elapsed_time = time.time() - start_time
1670 if elapsed_time > max_timeout: 1697 if elapsed_time > max_timeout:
1671 return (False, 'Timed out: %ss without build' % max_timeout) 1698 return (None, 'Timed out: %ss without build' % max_timeout)
1672 1699
1673 print 'Time elapsed: %ss without build.' % elapsed_time 1700 print 'Time elapsed: %ss without build.' % elapsed_time
1674 time.sleep(poll_interval) 1701 time.sleep(poll_interval)
1675 1702
1676 def PostBuildRequestAndWait(self, revision, fetch_build, patch=None): 1703 def PostBuildRequestAndWait(self, revision, fetch_build, patch=None):
1677 """POSTs the build request job to the tryserver instance.""" 1704 """POSTs the build request job to the tryserver instance.
1705
1706 A try job build request is posted to tryserver.chromium.perf master,
1707 and waits for the binaries to be produced and archived on cloud storage.
1708 Once the build is ready and stored onto cloud, build archive is downloaded
1709 into the output folder.
1710
1711 Args:
1712 revision: A Git hash revision.
1713 fetch_build: Function to check and download build from cloud storage.
1714 patch: A DEPS patch (used while bisecting 3rd party repositories).
1715
1716 Returns:
1717 Downloaded archive file path when requested build exists and download is
1718 successful, otherwise None.
1719 """
1720 # Get SVN revision for the given SHA.
1721 svn_revision = self.source_control.SVNFindRev(revision)
1722 if not svn_revision:
1723 raise RuntimeError(
1724 'Failed to determine SVN revision for %s' % revision)
1678 1725
1679 def GetBuilderNameAndBuildTime(target_arch='ia32'): 1726 def GetBuilderNameAndBuildTime(target_arch='ia32'):
1680 """Gets builder bot name and buildtime in seconds based on platform.""" 1727 """Gets builder bot name and buildtime in seconds based on platform."""
1681 # Bot names should match the one listed in tryserver.chromium's 1728 # Bot names should match the one listed in tryserver.chromium's
1682 # master.cfg which produces builds for bisect. 1729 # master.cfg which produces builds for bisect.
1683 if IsWindows(): 1730 if IsWindows():
1684 if Is64BitWindows() and target_arch == 'x64': 1731 if Is64BitWindows() and target_arch == 'x64':
1685 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME) 1732 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME)
1686 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME) 1733 return ('win_perf_bisect_builder', MAX_WIN_BUILD_TIME)
1687 if IsLinux(): 1734 if IsLinux():
1688 return ('linux_perf_bisect_builder', MAX_LINUX_BUILD_TIME) 1735 return ('linux_perf_bisect_builder', MAX_LINUX_BUILD_TIME)
1689 if IsMac(): 1736 if IsMac():
1690 return ('mac_perf_bisect_builder', MAX_MAC_BUILD_TIME) 1737 return ('mac_perf_bisect_builder', MAX_MAC_BUILD_TIME)
1691 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform) 1738 raise NotImplementedError('Unsupported Platform "%s".' % sys.platform)
1692 if not fetch_build: 1739 if not fetch_build:
1693 return False 1740 return False
1694 1741
1695 bot_name, build_timeout = GetBuilderNameAndBuildTime(self.opts.target_arch) 1742 bot_name, build_timeout = GetBuilderNameAndBuildTime(self.opts.target_arch)
1696 builder_host = self.opts.builder_host 1743 builder_host = self.opts.builder_host
1697 builder_port = self.opts.builder_port 1744 builder_port = self.opts.builder_port
1698 # Create a unique ID for each build request posted to tryserver builders. 1745 # Create a unique ID for each build request posted to tryserver builders.
1699 # This ID is added to "Reason" property in build's json. 1746 # This ID is added to "Reason" property in build's json.
1700 build_request_id = GetSHA1HexDigest( 1747 build_request_id = GetSHA1HexDigest(
1701 '%s-%s-%s' % (revision, patch, time.time())) 1748 '%s-%s-%s' % (svn_revision, patch, time.time()))
1702 1749
1703 # Creates a try job description. 1750 # Creates a try job description.
1704 job_args = {'host': builder_host, 1751 job_args = {'host': builder_host,
1705 'port': builder_port, 1752 'port': builder_port,
1706 'revision': 'src@%s' % revision, 1753 'revision': 'src@%s' % svn_revision,
1707 'bot': bot_name, 1754 'bot': bot_name,
1708 'name': build_request_id 1755 'name': build_request_id
1709 } 1756 }
1710 # Update patch information if supplied. 1757 # Update patch information if supplied.
1711 if patch: 1758 if patch:
1712 job_args['patch'] = patch 1759 job_args['patch'] = patch
1713 # Posts job to build the revision on the server. 1760 # Posts job to build the revision on the server.
1714 if bisect_builder.PostTryJob(job_args): 1761 if bisect_builder.PostTryJob(job_args):
1715 status, error_msg = self.WaitUntilBuildIsReady(fetch_build, 1762 target_file, error_msg = self.WaitUntilBuildIsReady(fetch_build,
1716 bot_name, 1763 bot_name,
1717 builder_host, 1764 builder_host,
1718 builder_port, 1765 builder_port,
1719 build_request_id, 1766 build_request_id,
1720 build_timeout) 1767 build_timeout)
1721 if not status: 1768 if not target_file:
1722 raise RuntimeError('%s [revision: %s]' % (error_msg, revision)) 1769 print '%s [revision: %s]' % (error_msg, svn_revision)
1723 return True 1770 return None
1724 return False 1771 return target_file
1772 print 'Failed to post build request for revision: [%s]' % svn_revision
1773 return None
1725 1774
1726 def IsDownloadable(self, depot): 1775 def IsDownloadable(self, depot):
1727 """Checks if build is downloadable based on target platform and depot.""" 1776 """Checks if build is downloadable based on target platform and depot."""
1728 if self.opts.target_platform in ['chromium'] and self.opts.gs_bucket: 1777 if self.opts.target_platform in ['chromium'] and self.opts.gs_bucket:
1729 return (depot == 'chromium' or 1778 return (depot == 'chromium' or
1730 'chromium' in DEPOT_DEPS_NAME[depot]['from'] or 1779 'chromium' in DEPOT_DEPS_NAME[depot]['from'] or
1731 'v8' in DEPOT_DEPS_NAME[depot]['from']) 1780 'v8' in DEPOT_DEPS_NAME[depot]['from'])
1732 return False 1781 return False
1733 1782
1734 def UpdateDeps(self, revision, depot, deps_file): 1783 def UpdateDeps(self, revision, depot, deps_file):
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
1914 return True 1963 return True
1915 cwd = os.getcwd() 1964 cwd = os.getcwd()
1916 os.chdir(self.src_cwd) 1965 os.chdir(self.src_cwd)
1917 # Fetch build archive for the given revision from the cloud storage when 1966 # Fetch build archive for the given revision from the cloud storage when
1918 # the storage bucket is passed. 1967 # the storage bucket is passed.
1919 if self.IsDownloadable(depot) and revision: 1968 if self.IsDownloadable(depot) and revision:
1920 deps_patch = None 1969 deps_patch = None
1921 if depot != 'chromium': 1970 if depot != 'chromium':
1922 # Create a DEPS patch with new revision for dependency repository. 1971 # Create a DEPS patch with new revision for dependency repository.
1923 (revision, deps_patch) = self.CreateDEPSPatch(depot, revision) 1972 (revision, deps_patch) = self.CreateDEPSPatch(depot, revision)
1924 # Get SVN revision for the given SHA, since builds are archived using SVN 1973 if self.DownloadCurrentBuild(revision, patch=deps_patch):
1925 # revision.
1926 chromium_revision = self.source_control.SVNFindRev(revision)
1927 if not chromium_revision:
1928 raise RuntimeError(
1929 'Failed to determine SVN revision for %s' % revision)
1930 if self.DownloadCurrentBuild(chromium_revision, patch=deps_patch):
1931 os.chdir(cwd) 1974 os.chdir(cwd)
1932 if deps_patch: 1975 if deps_patch:
1933 # Reverts the changes to DEPS file. 1976 # Reverts the changes to DEPS file.
1934 self.source_control.CheckoutFileAtRevision(bisect_utils.FILE_DEPS, 1977 self.source_control.CheckoutFileAtRevision(bisect_utils.FILE_DEPS,
1935 revision, 1978 revision,
1936 cwd=self.src_cwd) 1979 cwd=self.src_cwd)
1937 return True 1980 return True
1938 raise RuntimeError('Failed to download build archive for revision %s.\n' 1981 return False
1939 'Unfortunately, bisection couldn\'t continue any '
1940 'further. Please try running script without '
1941 '--gs_bucket flag to produce local builds.' % revision)
1942 1982
1943 1983 # These codes are executed when bisect bots builds binaries locally.
1944 build_success = self.builder.Build(depot, self.opts) 1984 build_success = self.builder.Build(depot, self.opts)
1945 os.chdir(cwd) 1985 os.chdir(cwd)
1946 return build_success 1986 return build_success
1947 1987
1948 def RunGClientHooks(self): 1988 def RunGClientHooks(self):
1949 """Runs gclient with runhooks command. 1989 """Runs gclient with runhooks command.
1950 1990
1951 Returns: 1991 Returns:
1952 True if gclient reports no errors. 1992 True if gclient reports no errors.
1953 """ 1993 """
(...skipping 2023 matching lines...) Expand 10 before | Expand all | Expand 10 after
3977 # The perf dashboard scrapes the "results" step in order to comment on 4017 # The perf dashboard scrapes the "results" step in order to comment on
3978 # bugs. If you change this, please update the perf dashboard as well. 4018 # bugs. If you change this, please update the perf dashboard as well.
3979 bisect_utils.OutputAnnotationStepStart('Results') 4019 bisect_utils.OutputAnnotationStepStart('Results')
3980 print 'Error: %s' % e.message 4020 print 'Error: %s' % e.message
3981 if opts.output_buildbot_annotations: 4021 if opts.output_buildbot_annotations:
3982 bisect_utils.OutputAnnotationStepClosed() 4022 bisect_utils.OutputAnnotationStepClosed()
3983 return 1 4023 return 1
3984 4024
3985 if __name__ == '__main__': 4025 if __name__ == '__main__':
3986 sys.exit(main()) 4026 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698