Changeset 220537 in webkit


Ignore:
Timestamp:
Aug 10, 2017 11:57:22 AM (7 years ago)
Author:
Lucas Forschler
Message:

Fix duplicated code from r220534.

Unreviewed cleanup.

  • Scripts/bisect-builds:
Location:
trunk/Tools
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r220536 r220537  
     12017-08-10  Lucas Forschler  <lforschler@apple.com>
     2
     3        Fix duplicated code in r220534.
     4
     5        Unreviewed cleanup.
     6
     7        * Scripts/bisect-builds:
     8
    192017-08-08  Jiewen Tan  <jiewen_tan@apple.com>
    210
  • trunk/Tools/Scripts/bisect-builds

    r220534 r220537  
    241241    finally:
    242242        shutil.rmtree(webkit_output_dir, ignore_errors=True)
    243 #!/usr/bin/env python
    244 
    245 # Copyright (C) 2017 Apple Inc. All rights reserved.
    246 #
    247 # Redistribution and use in source and binary forms, with or without
    248 # modification, are permitted provided that the following conditions
    249 # are met:
    250 #
    251 # 1.  Redistributions of source code must retain the above copyright
    252 #     notice, this list of conditions and the following disclaimer.
    253 # 2.  Redistributions in binary form must reproduce the above copyright
    254 #     notice, this list of conditions and the following disclaimer in the
    255 #     documentation and/or other materials provided with the distribution.
    256 # 3.  Neither the name of Apple Inc. ("Apple") nor the names of
    257 #     its contributors may be used to endorse or promote products derived
    258 #     from this software without specific prior written permission.
    259 #
    260 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
    261 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    262 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    263 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
    264 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    265 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    266 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    267 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    268 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    269 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    270 
    271 import argparse
    272 import bisect
    273 import math
    274 import os
    275 import requests
    276 import shutil
    277 import subprocess
    278 import sys
    279 import tempfile
    280 import urlparse
    281 
    282 REST_API_URL = 'https://q1tzqfy48e.execute-api.us-west-2.amazonaws.com/v1/'
    283 REST_API_ENDPOINT = 'archives/'
    284 REST_API_MINIFIED_ENDPOINT = 'minified-archives/'
    285 
    286 
    287 def bisect_builds(revision_list, start_index, end_index, options):
    288     while True:
    289         index_to_test = pick_next_build(revision_list, start_index, end_index)
    290         if index_to_test == None:
    291             print('No more builds to test...')
    292             exit(1)
    293         download_archive(options, revision_list[index_to_test])
    294         extract_archive(options)
    295         reproduces = test_archive(options, revision_list[index_to_test])
    296 
    297         if reproduces:          # bisect left
    298             index_to_test -= 1  # We can remove this from the end of the list of builds to test
    299             bisect_builds(revision_list, start_index, index_to_test, options)
    300         if not reproduces:      # bisect right
    301             index_to_test += 1  # We can remove this from the start of the list of builds to test
    302             bisect_builds(revision_list, index_to_test, end_index, options)
    303 
    304 
    305 def download_archive(options, revision):
    306     api_url = get_api_url(options)
    307     s3_url = get_s3_location_for_revision(api_url, revision)
    308     print('Archive URL: {}'.format(s3_url))
    309     command = ['python', '../BuildSlaveSupport/download-built-product', '--{}'.format(options.configuration), '--platform', options.platform, s3_url]
    310     print('Downloading revision: {}'.format(revision))
    311     subprocess.check_call(command)
    312 
    313 
    314 def extract_archive(options):
    315     command = ['python', '../BuildSlaveSupport/built-product-archive', '--platform', options.platform, '--%s' % options.configuration, 'extract']
    316     print('Extracting archive: {}'.format(command))
    317     subprocess.check_call(command)
    318 
    319 
    320 # ---- bisect helpers from https://docs.python.org/2/library/bisect.html ----
    321 def find_le(a, x):
    322     """Find rightmost value less than or equal to x"""
    323     i = bisect.bisect_right(a, x)
    324     if i:
    325         return i - 1
    326     raise ValueError
    327 
    328 
    329 def find_ge(a, x):
    330     """Find leftmost item greater than or equal to x"""
    331     i = bisect.bisect_left(a, x)
    332     if i != len(a):
    333         return i
    334     raise ValueError
    335 # ---- end bisect helpers ----
    336 
    337 
    338 def get_api_url(options):
    339     if options.full:
    340         base_url = urlparse.urljoin(REST_API_URL, REST_API_ENDPOINT)
    341     else:
    342         base_url = urlparse.urljoin(REST_API_URL, REST_API_MINIFIED_ENDPOINT)
    343 
    344     api_url = urlparse.urljoin(base_url, '-'.join([options.platform, options.architecture, options.configuration]))
    345     return api_url
    346 
    347 
    348 def get_indices_from_revisions(revision_list, start_revision, end_revision):
    349     if start_revision is None:
    350         print('WARNING: No starting revision was given, defaulting to first available for this configuration')
    351         start_index = 0
    352     else:
    353         start_index = find_ge(revision_list, start_revision)
    354 
    355     if end_revision is None:
    356         print('WARNING: No ending revision was given, defaulting to last avialable for this configuration')
    357         end_index = len(revision_list) - 1
    358     else:
    359         end_index = find_le(revision_list, end_revision)
    360 
    361     return start_index, end_index
    362 
    363 
    364 def get_sorted_revisions(revisions_dict):
    365     revisions = [int(revision['revision']) for revision in revisions_dict['revisions']]
    366     return sorted(revisions)
    367    
    368 
    369 def get_s3_location_for_revision(url, revision):
    370     url = '/'.join([url, str(revision)])
    371     r = requests.get(url)
    372     for archive in r.json()['archive']:
    373         s3_url = archive['s3_url']
    374     return s3_url
    375 
    376 
    377 def parse_args(args):
    378     parser = argparse.ArgumentParser(description='Perform a bisection against existing WebKit archives.')
    379     parser.add_argument('-c', '--configuration', default='release', help='The configuration to query [release | debug]')
    380     parser.add_argument('-a', '--architecture', default='x86_64', help='The architecture to query [x86_64 | i386]')
    381     parser.add_argument('-p', '--platform', default='None', required=True, help='The platform to query [mac-sierra | gtk | ios-simulator | win]')
    382     parser.add_argument('-f', '--full', action='store_true', default=False, help='Use full archives containing debug symbols. These are significantly larger files!')
    383     parser.add_argument('-s', '--start', default=None, type=int, help='The starting revision to bisect.')
    384     parser.add_argument('-e', '--end', default=None, type=int, help='The ending revision to bisect')
    385     return parser.parse_args(args)
    386 
    387 
    388 def pick_next_build(revision_list, start_index, end_index):
    389     revisions_remaining = (end_index - start_index) + 1
    390     print('Found {} revisions in this range to test...'.format(revisions_remaining))
    391 
    392     if start_index >= end_index:
    393         print('No archives available between {} and {}'.format(revision_list[end_index], revision_list[start_index]))
    394         return None
    395 
    396     middleIndex = (start_index + end_index) / 2
    397     return int(math.ceil(middleIndex))
    398 
    399 
    400 def prompt_did_reproduce():
    401     var = raw_input('\nDid the error reproduce? [y/n]: ')
    402     var = var.lower()
    403     if 'y' in var:
    404         return True
    405     if 'n' in var:
    406         return False
    407     else:
    408         prompt_did_reproduce()
    409    
    410 
    411 def set_webkit_output_dir(temp_dir):
    412     print('Setting environment variable WEBKIT_OUTPUTDIR to {}'.format(temp_dir))
    413     os.environ['WEBKIT_OUTPUTDIR'] = temp_dir
    414 
    415 
    416 def test_archive(options, revision):
    417     print('Testing revision {}...'.format(revision))
    418     command = []
    419     if 'mac' in options.platform:
    420         command = ['./run-safari']
    421     elif 'ios' in options.platform:
    422         command = ['./run-safari', '--simulator']
    423     else:
    424         print('Default test behavior for this platform is not implemented...'.format(options.platform))
    425 
    426     if command:
    427         subprocess.call(command)
    428     return prompt_did_reproduce()
    429    
    430 
    431 def minified_platforms():
    432     # FIXME: query this dynamically from API
    433     return  ['mac-elcapitan', 'mac-sierra']
    434 
    435 
    436 def unminified_platforms():
    437     # FIXME: query this dynamically from API
    438     return ['gtk', 'ios-simulator-10', 'mac-elcapitan', 'mac-sierra', 'win', 'wpe']
    439 
    440 
    441 def is_supported_platform(options):
    442     if options.full:
    443         return options.platform in unminified_platforms()
    444     return options.platform in minified_platforms()
    445 
    446 
    447 def validate_options(options):
    448     if not is_supported_platform(options):
    449         print('Unsupported platform: [{}], exiting...'.format(options.platform))
    450         if options.full:
    451             print('Available Unminified platforms: {}'.format(unminified_platforms()))
    452         else:
    453             print('Available Minified platforms: {}'.format(minified_platforms()))
    454             print('INFO: pass --full to try against full archives')
    455         exit(1)
    456 
    457 
    458 def main(options):
    459     validate_options(options)
    460 
    461     url = get_api_url(options)
    462     r = requests.get(url)
    463     revision_list = get_sorted_revisions(r.json())
    464    
    465     start_index, end_index = get_indices_from_revisions(revision_list, options.start, options.end)
    466     print('Bisecting between {} and {}'.format(revision_list[start_index], revision_list[end_index]))
    467    
    468     # from here forward, use indices instead of revisions
    469     bisect_builds(revision_list, start_index, end_index, options)
    470 
    471 
    472 if __name__ == '__main__':
    473     options = parse_args(sys.argv[1:])
    474     script_path = os.path.abspath(__file__)
    475     script_directory = os.path.dirname(script_path)
    476     os.chdir(script_directory)
    477     webkit_output_dir = tempfile.mkdtemp()
    478     set_webkit_output_dir(webkit_output_dir)
    479     try:
    480         main(options)   
    481     except KeyboardInterrupt:
    482         exit("Aborting.")
    483     finally:
    484         shutil.rmtree(webkit_output_dir, ignore_errors=True)
    485 #!/usr/bin/env python
    486 
    487 # Copyright (C) 2017 Apple Inc. All rights reserved.
    488 #
    489 # Redistribution and use in source and binary forms, with or without
    490 # modification, are permitted provided that the following conditions
    491 # are met:
    492 #
    493 # 1.  Redistributions of source code must retain the above copyright
    494 #     notice, this list of conditions and the following disclaimer.
    495 # 2.  Redistributions in binary form must reproduce the above copyright
    496 #     notice, this list of conditions and the following disclaimer in the
    497 #     documentation and/or other materials provided with the distribution.
    498 # 3.  Neither the name of Apple Inc. ("Apple") nor the names of
    499 #     its contributors may be used to endorse or promote products derived
    500 #     from this software without specific prior written permission.
    501 #
    502 # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
    503 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    504 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    505 # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
    506 # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    507 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    508 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    509 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    510 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    511 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    512 
    513 import argparse
    514 import bisect
    515 import math
    516 import os
    517 import requests
    518 import shutil
    519 import subprocess
    520 import sys
    521 import tempfile
    522 import urlparse
    523 
    524 REST_API_URL = 'https://q1tzqfy48e.execute-api.us-west-2.amazonaws.com/v1/'
    525 REST_API_ENDPOINT = 'archives/'
    526 REST_API_MINIFIED_ENDPOINT = 'minified-archives/'
    527 
    528 
    529 def bisect_builds(revision_list, start_index, end_index, options):
    530     while True:
    531         index_to_test = pick_next_build(revision_list, start_index, end_index)
    532         if index_to_test == None:
    533             print('No more builds to test...')
    534             exit(1)
    535         download_archive(options, revision_list[index_to_test])
    536         extract_archive(options)
    537         reproduces = test_archive(options, revision_list[index_to_test])
    538 
    539         if reproduces:          # bisect left
    540             index_to_test -= 1  # We can remove this from the end of the list of builds to test
    541             bisect_builds(revision_list, start_index, index_to_test, options)
    542         if not reproduces:      # bisect right
    543             index_to_test += 1  # We can remove this from the start of the list of builds to test
    544             bisect_builds(revision_list, index_to_test, end_index, options)
    545 
    546 
    547 def download_archive(options, revision):
    548     api_url = get_api_url(options)
    549     s3_url = get_s3_location_for_revision(api_url, revision)
    550     print('Archive URL: {}'.format(s3_url))
    551     command = ['python', '../BuildSlaveSupport/download-built-product', '--{}'.format(options.configuration), '--platform', options.platform, s3_url]
    552     print('Downloading revision: {}'.format(revision))
    553     subprocess.check_call(command)
    554 
    555 
    556 def extract_archive(options):
    557     command = ['python', '../BuildSlaveSupport/built-product-archive', '--platform', options.platform, '--%s' % options.configuration, 'extract']
    558     print('Extracting archive: {}'.format(command))
    559     subprocess.check_call(command)
    560 
    561 
    562 # ---- bisect helpers from https://docs.python.org/2/library/bisect.html ----
    563 def find_le(a, x):
    564     """Find rightmost value less than or equal to x"""
    565     i = bisect.bisect_right(a, x)
    566     if i:
    567         return i - 1
    568     raise ValueError
    569 
    570 
    571 def find_ge(a, x):
    572     """Find leftmost item greater than or equal to x"""
    573     i = bisect.bisect_left(a, x)
    574     if i != len(a):
    575         return i
    576     raise ValueError
    577 # ---- end bisect helpers ----
    578 
    579 
    580 def get_api_url(options):
    581     if options.full:
    582         base_url = urlparse.urljoin(REST_API_URL, REST_API_ENDPOINT)
    583     else:
    584         base_url = urlparse.urljoin(REST_API_URL, REST_API_MINIFIED_ENDPOINT)
    585 
    586     api_url = urlparse.urljoin(base_url, '-'.join([options.platform, options.architecture, options.configuration]))
    587     return api_url
    588 
    589 
    590 def get_indices_from_revisions(revision_list, start_revision, end_revision):
    591     if start_revision is None:
    592         print('WARNING: No starting revision was given, defaulting to first available for this configuration')
    593         start_index = 0
    594     else:
    595         start_index = find_ge(revision_list, start_revision)
    596 
    597     if end_revision is None:
    598         print('WARNING: No ending revision was given, defaulting to last avialable for this configuration')
    599         end_index = len(revision_list) - 1
    600     else:
    601         end_index = find_le(revision_list, end_revision)
    602 
    603     return start_index, end_index
    604 
    605 
    606 def get_sorted_revisions(revisions_dict):
    607     revisions = [int(revision['revision']) for revision in revisions_dict['revisions']]
    608     return sorted(revisions)
    609    
    610 
    611 def get_s3_location_for_revision(url, revision):
    612     url = '/'.join([url, str(revision)])
    613     r = requests.get(url)
    614     for archive in r.json()['archive']:
    615         s3_url = archive['s3_url']
    616     return s3_url
    617 
    618 
    619 def parse_args(args):
    620     parser = argparse.ArgumentParser(description='Perform a bisection against existing WebKit archives.')
    621     parser.add_argument('-c', '--configuration', default='release', help='The configuration to query [release | debug]')
    622     parser.add_argument('-a', '--architecture', default='x86_64', help='The architecture to query [x86_64 | i386]')
    623     parser.add_argument('-p', '--platform', default='None', required=True, help='The platform to query [mac-sierra | gtk | ios-simulator | win]')
    624     parser.add_argument('-f', '--full', action='store_true', default=False, help='Use full archives containing debug symbols. These are significantly larger files!')
    625     parser.add_argument('-s', '--start', default=None, type=int, help='The starting revision to bisect.')
    626     parser.add_argument('-e', '--end', default=None, type=int, help='The ending revision to bisect')
    627     return parser.parse_args(args)
    628 
    629 
    630 def pick_next_build(revision_list, start_index, end_index):
    631     revisions_remaining = (end_index - start_index) + 1
    632     print('Found {} revisions in this range to test...'.format(revisions_remaining))
    633 
    634     if start_index >= end_index:
    635         print('No archives available between {} and {}'.format(revision_list[end_index], revision_list[start_index]))
    636         return None
    637 
    638     middleIndex = (start_index + end_index) / 2
    639     return int(math.ceil(middleIndex))
    640 
    641 
    642 def prompt_did_reproduce():
    643     var = raw_input('\nDid the error reproduce? [y/n]: ')
    644     var = var.lower()
    645     if 'y' in var:
    646         return True
    647     if 'n' in var:
    648         return False
    649     else:
    650         prompt_did_reproduce()
    651    
    652 
    653 def set_webkit_output_dir(temp_dir):
    654     print('Setting environment variable WEBKIT_OUTPUTDIR to {}'.format(temp_dir))
    655     os.environ['WEBKIT_OUTPUTDIR'] = temp_dir
    656 
    657 
    658 def test_archive(options, revision):
    659     print('Testing revision {}...'.format(revision))
    660     command = []
    661     if 'mac' in options.platform:
    662         command = ['./run-safari']
    663     elif 'ios' in options.platform:
    664         command = ['./run-safari', '--simulator']
    665     else:
    666         print('Default test behavior for this platform is not implemented...'.format(options.platform))
    667 
    668     if command:
    669         subprocess.call(command)
    670     return prompt_did_reproduce()
    671    
    672 
    673 def minified_platforms():
    674     # FIXME: query this dynamically from API
    675     return  ['mac-elcapitan', 'mac-sierra']
    676 
    677 
    678 def unminified_platforms():
    679     # FIXME: query this dynamically from API
    680     return ['gtk', 'ios-simulator-10', 'mac-elcapitan', 'mac-sierra', 'win', 'wpe']
    681 
    682 
    683 def is_supported_platform(options):
    684     if options.full:
    685         return options.platform in unminified_platforms()
    686     return options.platform in minified_platforms()
    687 
    688 
    689 def validate_options(options):
    690     if not is_supported_platform(options):
    691         print('Unsupported platform: [{}], exiting...'.format(options.platform))
    692         if options.full:
    693             print('Available Unminified platforms: {}'.format(unminified_platforms()))
    694         else:
    695             print('Available Minified platforms: {}'.format(minified_platforms()))
    696             print('INFO: pass --full to try against full archives')
    697         exit(1)
    698 
    699 
    700 def main(options):
    701     validate_options(options)
    702 
    703     url = get_api_url(options)
    704     r = requests.get(url)
    705     revision_list = get_sorted_revisions(r.json())
    706    
    707     start_index, end_index = get_indices_from_revisions(revision_list, options.start, options.end)
    708     print('Bisecting between {} and {}'.format(revision_list[start_index], revision_list[end_index]))
    709    
    710     # from here forward, use indices instead of revisions
    711     bisect_builds(revision_list, start_index, end_index, options)
    712 
    713 
    714 if __name__ == '__main__':
    715     options = parse_args(sys.argv[1:])
    716     script_path = os.path.abspath(__file__)
    717     script_directory = os.path.dirname(script_path)
    718     os.chdir(script_directory)
    719     webkit_output_dir = tempfile.mkdtemp()
    720     set_webkit_output_dir(webkit_output_dir)
    721     try:
    722         main(options)   
    723     except KeyboardInterrupt:
    724         exit("Aborting.")
    725     finally:
    726         shutil.rmtree(webkit_output_dir, ignore_errors=True)
Note: See TracChangeset for help on using the changeset viewer.