Index: slave/skia_slave_scripts/webpages_playback.py |
diff --git a/slave/skia_slave_scripts/webpages_playback.py b/slave/skia_slave_scripts/webpages_playback.py |
deleted file mode 100644 |
index 46963bb7651192ffe6f01105d7389451aa59149b..0000000000000000000000000000000000000000 |
--- a/slave/skia_slave_scripts/webpages_playback.py |
+++ /dev/null |
@@ -1,492 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""Archives or replays webpages and creates SKPs in a Google Storage location. |
- |
-To archive webpages and store SKP files (archives should be rarely updated): |
- |
-cd ../buildbot/slave/skia_slave_scripts |
-python webpages_playback.py --dest_gsbase=gs://rmistry --record \ |
---page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \ |
---browser_executable=/tmp/chromium/out/Release/chrome |
- |
- |
-To replay archived webpages and re-generate SKP files (should be run whenever |
-SkPicture.PICTURE_VERSION changes): |
- |
-cd ../buildbot/slave/skia_slave_scripts |
-python webpages_playback.py --dest_gsbase=gs://rmistry \ |
---page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \ |
---browser_executable=/tmp/chromium/out/Release/chrome |
- |
- |
-Specify the --page_sets flag (default value is 'all') to pick a list of which |
-webpages should be archived and/or replayed. Eg: |
- |
---page_sets=page_sets/skia_yahooanswers_desktop.json,\ |
-page_sets/skia_wikipedia_galaxynexus.json |
- |
-The --browser_executable flag should point to the browser binary you want to use |
-to capture archives and/or capture SKP files. Majority of the time it should be |
-a newly built chrome binary. |
- |
-The --upload_to_gs flag controls whether generated artifacts will be uploaded |
-to Google Storage (default value is False if not specified). |
- |
-The --non-interactive flag controls whether the script will prompt the user |
-(default value is False if not specified). |
- |
-The --skia_tools flag if specified will allow this script to run |
-debugger, render_pictures, and render_pdfs on the captured |
-SKP(s). The tools are run after all SKPs are succesfully captured to make sure |
-they can be added to the buildbots with no breakages. |
-To preview the captured SKP before proceeding to the next page_set specify both |
---skia_tools and --view_debugger_output. |
-""" |
- |
-import glob |
-import optparse |
-import os |
-import posixpath |
-import shutil |
-import subprocess |
-import sys |
-import tempfile |
-import time |
-import traceback |
- |
- |
-# Set the PYTHONPATH for this script to include chromium_buildbot scripts, |
-# and site_config. |
-BUILDBOT_PATH = os.path.realpath(os.path.join( |
- os.path.dirname(os.path.abspath(__file__)), os.pardir, os.pardir)) |
-sys.path.append(os.path.join(BUILDBOT_PATH, 'common')) |
-sys.path.append(os.path.join(BUILDBOT_PATH, 'third_party', 'chromium_buildbot', |
- 'scripts')) |
-sys.path.append(os.path.join(BUILDBOT_PATH, 'third_party', 'chromium_buildbot', |
- 'site_config')) |
- |
-from utils import file_utils |
-from utils import gs_utils |
-from utils import old_gs_utils |
-from py.utils import misc |
-from py.utils import shell_utils |
- |
-from slave import slave_utils |
- |
-from playback_dirs import ROOT_PLAYBACK_DIR_NAME |
-from playback_dirs import SKPICTURES_DIR_NAME |
- |
- |
-# Local archive and SKP directories. |
-LOCAL_PLAYBACK_ROOT_DIR = os.path.join( |
- tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME) |
-LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join( |
- os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data') |
-TMP_SKP_DIR = tempfile.mkdtemp() |
- |
-# Location of Telemetry binaries (record_wpr, run_measurement). |
-TELEMETRY_BINARIES_DIR = os.path.join(misc.BUILDBOT_PATH, 'third_party', |
- 'chromium_trunk', 'src', 'tools', 'perf') |
- |
-# Location of the credentials.json file and the string that represents missing |
-# passwords. |
-CREDENTIALS_FILE_PATH = os.path.join( |
- os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data', |
- 'credentials.json' |
-) |
- |
-# Stdout that signifies that a recording has failed. |
-RECORD_FAILURE_MSG = 'The recording has not been updated for these pages.' |
- |
-# Name of the SKP benchmark |
-SKP_BENCHMARK = 'skpicture_printer' |
- |
-# The max base name length of Skp files. |
-MAX_SKP_BASE_NAME_LEN = 31 |
- |
-# Dictionary of device to platform prefixes for SKP files. |
-DEVICE_TO_PLATFORM_PREFIX = { |
- 'desktop': 'desk', |
- 'galaxynexus': 'mobi', |
- 'nexus10': 'tabl' |
-} |
- |
-# How many times the record_wpr binary should be retried. |
-RETRY_RECORD_WPR_COUNT = 5 |
-# How many times the run_measurement binary should be retried. |
-RETRY_RUN_MEASUREMENT_COUNT = 30 |
- |
-# Location of the credentials.json file in Google Storage. |
-CREDENTIALS_GS_LOCATION = ( |
- 'gs://chromium-skia-gm/playback/credentials/credentials.json') |
- |
-X11_DISPLAY = os.getenv('DISPLAY', ':0') |
- |
- |
-class SkPicturePlayback(object): |
- """Class that archives or replays webpages and creates SKPs.""" |
- |
- def __init__(self, parse_options): |
- """Constructs a SkPicturePlayback BuildStep instance.""" |
- assert parse_options.browser_executable, 'Must specify --browser_executable' |
- self._browser_executable = parse_options.browser_executable |
- |
- self._all_page_sets_specified = parse_options.page_sets == 'all' |
- self._page_sets = self._ParsePageSets(parse_options.page_sets) |
- |
- self._dest_gsbase = parse_options.dest_gsbase |
- self._record = parse_options.record |
- self._skia_tools = parse_options.skia_tools |
- self._non_interactive = parse_options.non_interactive |
- self._upload_to_gs = parse_options.upload_to_gs |
- self._alternate_upload_dir = parse_options.alternate_upload_dir |
- self._skip_all_gs_access = parse_options.skip_all_gs_access |
- |
- self._local_skp_dir = os.path.join( |
- parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME) |
- self._local_record_webpages_archive_dir = os.path.join( |
- parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive') |
- |
- # List of SKP files generated by this script. |
- self._skp_files = [] |
- |
- def _ParsePageSets(self, page_sets): |
- if not page_sets: |
- raise ValueError('Must specify atleast one page_set!') |
- elif self._all_page_sets_specified: |
- # Get everything from the page_sets directory. |
- page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), |
- 'page_sets') |
- return [os.path.join(page_sets_dir, page_set) |
- for page_set in os.listdir(page_sets_dir) |
- if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and |
- page_set.endswith('.py')] |
- elif '*' in page_sets: |
- # Explode and return the glob. |
- return glob.glob(page_sets) |
- else: |
- return page_sets.split(',') |
- |
- def Run(self): |
- """Run the SkPicturePlayback BuildStep.""" |
- |
- # Ensure the right .boto file is used by gsutil. |
- if not self._skip_all_gs_access and old_gs_utils.read_timestamp_file( |
- timestamp_file_name=old_gs_utils.TIMESTAMP_COMPLETED_FILENAME, |
- gs_base=self._dest_gsbase, |
- gs_relative_dir=posixpath.join(ROOT_PLAYBACK_DIR_NAME, |
- SKPICTURES_DIR_NAME)) == "0": |
- raise Exception( |
- 'Missing .boto file or .boto does not have the right credentials.' |
- 'Please see https://docs.google.com/a/google.com/document/d/1ZzHP6M5q' |
- 'ACA9nJnLqOZr2Hl0rjYqE4yQsQWAfVjKCzs/edit ' |
- '(may have to request access). The .boto file will need to be placed ' |
- 'under third_party/chromium_buildbot/site_config/') |
- |
- # Download the credentials file if it was not previously downloaded. |
- if self._skip_all_gs_access: |
- print """\n\nPlease create a %s file that contains: |
- { |
- "google": { |
- "username": "google_testing_account_username", |
- "password": "google_testing_account_password" |
- }, |
- "facebook": { |
- "username": "facebook_testing_account_username", |
- "password": "facebook_testing_account_password" |
- } |
- }\n\n""" % CREDENTIALS_FILE_PATH |
- raw_input("Please press a key when you are ready to proceed...") |
- elif not os.path.isfile(CREDENTIALS_FILE_PATH): |
- # Download the credentials.json file from Google Storage. |
- slave_utils.GSUtilDownloadFile( |
- src=CREDENTIALS_GS_LOCATION, dst=CREDENTIALS_FILE_PATH) |
- |
- # Delete any left over data files in the data directory. |
- for archive_file in glob.glob( |
- os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')): |
- os.remove(archive_file) |
- |
- # Delete the local root directory if it already exists. |
- if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR): |
- shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR) |
- |
- # Create the required local storage directories. |
- self._CreateLocalStorageDirs() |
- |
- # Start the timer. |
- start_time = time.time() |
- |
- # Loop through all page_sets. |
- for page_set in self._page_sets: |
- |
- page_set_basename = os.path.basename(page_set).split('.')[0] + '.json' |
- wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr' |
- |
- if self._record: |
- # Create an archive of the specified webpages if '--record=True' is |
- # specified. |
- record_wpr_cmd = ( |
- 'DISPLAY=%s' % X11_DISPLAY, |
- os.path.join(TELEMETRY_BINARIES_DIR, 'record_wpr'), |
- '--extra-browser-args=--disable-setuid-sandbox', |
- '--browser=exact', |
- '--browser-executable=%s' % self._browser_executable, |
- page_set |
- ) |
- for _ in range(RETRY_RECORD_WPR_COUNT): |
- output = shell_utils.run(' '.join(record_wpr_cmd), shell=True) |
- if RECORD_FAILURE_MSG in output: |
- print output |
- else: |
- # Break out of the retry loop since there were no errors. |
- break |
- else: |
- # If we get here then record_wpr did not succeed and thus did not |
- # break out of the loop. |
- raise Exception('record_wpr failed for page_set: %s' % page_set) |
- |
- else: |
- if not self._skip_all_gs_access: |
- # Get the webpages archive so that it can be replayed. |
- self._DownloadWebpagesArchive(wpr_data_file, page_set_basename) |
- |
- run_measurement_cmd = ( |
- 'DISPLAY=%s' % X11_DISPLAY, |
- 'timeout', '300', |
- os.path.join(TELEMETRY_BINARIES_DIR, 'run_measurement'), |
- '--extra-browser-args=--disable-setuid-sandbox', |
- '--browser=exact', |
- '--browser-executable=%s' % self._browser_executable, |
- SKP_BENCHMARK, |
- page_set, |
- '-o', |
- '/tmp/test.skp', |
- '--skp-outdir=%s' % TMP_SKP_DIR |
- ) |
- for _ in range(RETRY_RUN_MEASUREMENT_COUNT): |
- try: |
- print '\n\n=======Capturing SKP of %s=======\n\n' % page_set |
- shell_utils.run(' '.join(run_measurement_cmd), shell=True) |
- except shell_utils.CommandFailedException: |
- # skpicture_printer sometimes fails with AssertionError but the |
- # captured SKP is still valid. This is a known issue. |
- pass |
- |
- if self._record: |
- # Move over the created archive into the local webpages archive |
- # directory. |
- shutil.move( |
- os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file), |
- self._local_record_webpages_archive_dir) |
- shutil.move( |
- os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, |
- page_set_basename), |
- self._local_record_webpages_archive_dir) |
- |
- # Rename generated SKP files into more descriptive names. |
- try: |
- self._RenameSkpFiles(page_set) |
- # Break out of the retry loop since there were no errors. |
- break |
- except Exception: |
- # There was a failure continue with the loop. |
- traceback.print_exc() |
- print '\n\n=======Retrying %s=======\n\n' % page_set |
- time.sleep(10) |
- else: |
- # If we get here then run_measurement did not succeed and thus did not |
- # break out of the loop. |
- raise Exception('run_measurement failed for page_set: %s' % page_set) |
- |
- print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % ( |
- time.time() - start_time) |
- |
- if self._skia_tools: |
- render_pictures_cmd = [ |
- os.path.join(self._skia_tools, 'render_pictures'), |
- '-r', self._local_skp_dir |
- ] |
- render_pdfs_cmd = [ |
- os.path.join(self._skia_tools, 'render_pdfs'), |
- self._local_skp_dir |
- ] |
- |
- for tools_cmd in (render_pictures_cmd, render_pdfs_cmd): |
- print '\n\n=======Running %s=======' % ' '.join(tools_cmd) |
- proc = subprocess.Popen(tools_cmd) |
- (code, output) = shell_utils.log_process_after_completion(proc, |
- echo=False) |
- if code != 0: |
- raise Exception('%s failed!' % ' '.join(tools_cmd)) |
- |
- if not self._non_interactive: |
- print '\n\n=======Running debugger=======' |
- os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'), |
- os.path.join(self._local_skp_dir, '*'))) |
- |
- print '\n\n' |
- |
- if not self._skip_all_gs_access and self._upload_to_gs: |
- print '\n\n=======Uploading to Google Storage=======\n\n' |
- # Copy the directory structure in the root directory into Google Storage. |
- dest_dir_name = ROOT_PLAYBACK_DIR_NAME |
- if self._alternate_upload_dir: |
- dest_dir_name = self._alternate_upload_dir |
- gs_status = slave_utils.GSUtilCopyDir( |
- src_dir=LOCAL_PLAYBACK_ROOT_DIR, |
- gs_base=self._dest_gsbase, |
- dest_dir=dest_dir_name, |
- gs_acl=gs_utils.GSUtils.PLAYBACK_CANNED_ACL) |
- if gs_status != 0: |
- raise Exception( |
- 'ERROR: GSUtilCopyDir error %d. "%s" -> "%s/%s"' % ( |
- gs_status, LOCAL_PLAYBACK_ROOT_DIR, self._dest_gsbase, |
- ROOT_PLAYBACK_DIR_NAME)) |
- self._SetGoogleReadACLs( |
- posixpath.join(self._dest_gsbase, dest_dir_name, SKPICTURES_DIR_NAME)) |
- |
- # Add a timestamp file to the SKP directory in Google Storage so we can |
- # use directory level rsync like functionality. |
- old_gs_utils.write_timestamp_file( |
- timestamp_file_name=old_gs_utils.TIMESTAMP_COMPLETED_FILENAME, |
- timestamp_value=time.time(), |
- gs_base=self._dest_gsbase, |
- gs_relative_dir=posixpath.join(dest_dir_name, SKPICTURES_DIR_NAME), |
- gs_acl=gs_utils.GSUtils.PLAYBACK_CANNED_ACL, |
- local_dir=LOCAL_PLAYBACK_ROOT_DIR) |
- |
- print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % ( |
- posixpath.join(self._dest_gsbase, dest_dir_name, SKPICTURES_DIR_NAME)) |
- else: |
- print '\n\n=======Not Uploading to Google Storage=======\n\n' |
- print 'Generated resources are available in %s\n\n' % ( |
- LOCAL_PLAYBACK_ROOT_DIR) |
- |
- return 0 |
- |
- def _SetGoogleReadACLs(self, gs_dir): |
- """Sets the ACLs of all objects in the directory to google read-only. |
- |
- This method assumes that there is a gsutil in the system PATH that is recent |
- enough to run the 'acl ch' command. The gsutil in chromium_buildbot is old |
- and cannot run this command. |
- """ |
- update_acls_cmd = ['gsutil', 'acl', 'ch', '-g', 'google.com:READ', |
- posixpath.join(gs_dir, '*')] |
- shell_utils.run(update_acls_cmd) |
- |
- def _RenameSkpFiles(self, page_set): |
- """Rename generated SKP files into more descriptive names. |
- |
- Look into the subdirectory of TMP_SKP_DIR and find the most interesting |
- .skp in there to be this page_set's representative .skp. |
- """ |
- # Here's where we're assuming there's one page per pageset. |
- # If there were more than one, we'd overwrite filename below. |
- |
- # /path/to/skia_yahooanswers_desktop.json -> skia_yahooanswers_desktop.json |
- _, ps_filename = os.path.split(page_set) |
- # skia_yahooanswers_desktop.json -> skia_yahooanswers_desktop |
- ps_basename, _ = os.path.splitext(ps_filename) |
- # skia_yahooanswers_desktop -> skia, yahooanswers, desktop |
- _, page_name, device = ps_basename.split('_') |
- |
- basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name) |
- filename = basename[:MAX_SKP_BASE_NAME_LEN] + '.skp' |
- |
- subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*')) |
- assert len(subdirs) == 1 |
- for site in subdirs: |
- # We choose the largest .skp as the most likely to be interesting. |
- largest_skp = max(glob.glob(os.path.join(site, '*.skp')), |
- key=lambda path: os.stat(path).st_size) |
- dest = os.path.join(self._local_skp_dir, filename) |
- print 'Moving', largest_skp, 'to', dest |
- shutil.move(largest_skp, dest) |
- self._skp_files.append(filename) |
- shutil.rmtree(site) |
- |
- def _CreateLocalStorageDirs(self): |
- """Creates required local storage directories for this script.""" |
- file_utils.create_clean_local_dir(self._local_record_webpages_archive_dir) |
- file_utils.create_clean_local_dir(self._local_skp_dir) |
- |
- def _DownloadWebpagesArchive(self, wpr_data_file, page_set_basename): |
- """Downloads the webpages archive and its required page set from GS.""" |
- wpr_source = posixpath.join( |
- self._dest_gsbase, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive', |
- wpr_data_file) |
- page_set_source = posixpath.join( |
- self._dest_gsbase, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive', |
- page_set_basename) |
- if (old_gs_utils.does_storage_object_exist(wpr_source) and |
- old_gs_utils.does_storage_object_exist(page_set_source)): |
- slave_utils.GSUtilDownloadFile( |
- src=wpr_source, dst=LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR) |
- slave_utils.GSUtilDownloadFile( |
- src=page_set_source, dst=LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR) |
- else: |
- raise Exception('%s and %s do not exist in Google Storage!' % ( |
- wpr_source, page_set_source)) |
- |
- |
-if '__main__' == __name__: |
- option_parser = optparse.OptionParser() |
- option_parser.add_option( |
- '', '--page_sets', |
- help='Specifies the page sets to use to archive. Supports globs.', |
- default='all') |
- option_parser.add_option( |
- '', '--skip_all_gs_access', action='store_true', |
- help='All Google Storage interactions will be skipped if this flag is ' |
- 'specified. This is useful for cases where the user does not have ' |
- 'the required .boto file but would like to generate webpage ' |
- 'archives and SKPs from the Skia page sets.', |
- default=False) |
- option_parser.add_option( |
- '', '--record', action='store_true', |
- help='Specifies whether a new website archive should be created.', |
- default=False) |
- option_parser.add_option( |
- '', '--dest_gsbase', |
- help='gs:// bucket_name, the bucket to upload the file to.', |
- default=old_gs_utils.DEFAULT_DEST_GSBASE) |
- option_parser.add_option( |
- '', '--skia_tools', |
- help=('Path to compiled Skia executable tools. ' |
- 'render_pictures/render_pdfs is run on the set ' |
- 'after all SKPs are captured. If the script is run without ' |
- '--non-interactive then the debugger is also run at the end. Debug ' |
- 'builds are recommended because they seem to catch more failures ' |
- 'than Release builds.'), |
- default=None) |
- option_parser.add_option( |
- '', '--upload_to_gs', action='store_true', |
- help='Does not upload to Google Storage if this is False.', |
- default=False) |
- option_parser.add_option( |
- '', '--alternate_upload_dir', |
- help='Uploads to a different directory in Google Storage if this flag is ' |
- 'specified', |
- default=None) |
- option_parser.add_option( |
- '', '--output_dir', |
- help='Directory where SKPs and webpage archives will be outputted to.', |
- default=tempfile.gettempdir()) |
- option_parser.add_option( |
- '', '--browser_executable', |
- help='The exact browser executable to run.', |
- default=None) |
- option_parser.add_option( |
- '', '--non-interactive', action='store_true', |
- help='Runs the script without any prompts. If this flag is specified and ' |
- '--skia_tools is specified then the debugger is not run.', |
- default=False) |
- options, unused_args = option_parser.parse_args() |
- |
- playback = SkPicturePlayback(options) |
- sys.exit(playback.Run()) |