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

Side by Side Diff: tools/skp/webpages_playback.py

Issue 655313003: Add recreate_skps script (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Fix cleanup Created 6 years, 2 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
« tools/skp/recreate_skps.py ('K') | « tools/skp/recreate_skps.py ('k') | 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
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Archives or replays webpages and creates SKPs in a Google Storage location.
7
8 To archive webpages and store SKP files (archives should be rarely updated):
9
10 cd ../buildbot/slave/skia_slave_scripts
11 python webpages_playback.py --dest_gsbase=gs://rmistry --record \
12 --page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
13 --browser_executable=/tmp/chromium/out/Release/chrome
14
15
16 To replay archived webpages and re-generate SKP files (should be run whenever
17 SkPicture.PICTURE_VERSION changes):
18
19 cd ../buildbot/slave/skia_slave_scripts
20 python webpages_playback.py --dest_gsbase=gs://rmistry \
21 --page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
22 --browser_executable=/tmp/chromium/out/Release/chrome
23
24
25 Specify the --page_sets flag (default value is 'all') to pick a list of which
26 webpages should be archived and/or replayed. Eg:
27
28 --page_sets=page_sets/skia_yahooanswers_desktop.json,\
29 page_sets/skia_wikipedia_galaxynexus.json
30
31 The --browser_executable flag should point to the browser binary you want to use
32 to capture archives and/or capture SKP files. Majority of the time it should be
33 a newly built chrome binary.
34
35 The --upload_to_gs flag controls whether generated artifacts will be uploaded
36 to Google Storage (default value is False if not specified).
37
38 The --non-interactive flag controls whether the script will prompt the user
39 (default value is False if not specified).
40
41 The --skia_tools flag if specified will allow this script to run
42 debugger, render_pictures, and render_pdfs on the captured
43 SKP(s). The tools are run after all SKPs are succesfully captured to make sure
44 they can be added to the buildbots with no breakages.
45 To preview the captured SKP before proceeding to the next page_set specify both
46 --skia_tools and --view_debugger_output.
47 """
48
49 import glob
50 import optparse
51 import os
52 import posixpath
53 import shutil
54 import subprocess
55 import sys
56 import tempfile
57 import time
58 import traceback
59
60 sys.path.insert(0, os.getcwd())
61
62 from common.py.utils import gs_utils
63 from common.py.utils import shell_utils
64
65 ROOT_PLAYBACK_DIR_NAME = 'playback'
66 SKPICTURES_DIR_NAME = 'skps'
67
68
69 # Local archive and SKP directories.
70 LOCAL_PLAYBACK_ROOT_DIR = os.path.join(
71 tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME)
72 LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
73 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
74 TMP_SKP_DIR = tempfile.mkdtemp()
75
76 # Stdout that signifies that a recording has failed.
77 RECORD_FAILURE_MSG = 'The recording has not been updated for these pages.'
78
79 # Name of the SKP benchmark
80 SKP_BENCHMARK = 'skpicture_printer'
81
82 # The max base name length of Skp files.
83 MAX_SKP_BASE_NAME_LEN = 31
84
85 # Dictionary of device to platform prefixes for SKP files.
86 DEVICE_TO_PLATFORM_PREFIX = {
87 'desktop': 'desk',
88 'galaxynexus': 'mobi',
89 'nexus10': 'tabl'
90 }
91
92 # How many times the record_wpr binary should be retried.
93 RETRY_RECORD_WPR_COUNT = 5
94 # How many times the run_measurement binary should be retried.
95 RETRY_RUN_MEASUREMENT_COUNT = 5
rmistry 2014/10/17 12:44:50 I suspect this number is going to gradually rise :
borenet 2014/10/17 13:21:43 That's fine, as long as it stays low on my machine
96
97 X11_DISPLAY = os.getenv('DISPLAY', ':0')
98
99 GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
100 GS_FINE_GRAINED_ACL_LIST = [
101 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
102 gs_utils.GSUtils.Permission.READ),
103 ]
104
105
106 class SkPicturePlayback(object):
107 """Class that archives or replays webpages and creates SKPs."""
108
109 def __init__(self, parse_options):
110 """Constructs a SkPicturePlayback BuildStep instance."""
111 assert parse_options.browser_executable, 'Must specify --browser_executable'
112 self._browser_executable = parse_options.browser_executable
113
114 self._all_page_sets_specified = parse_options.page_sets == 'all'
115 self._page_sets = self._ParsePageSets(parse_options.page_sets)
116
117 self._dest_gsbase = parse_options.dest_gsbase
118 self._record = parse_options.record
119 self._skia_tools = parse_options.skia_tools
120 self._non_interactive = parse_options.non_interactive
121 self._upload_to_gs = parse_options.upload_to_gs
122 self._alternate_upload_dir = parse_options.alternate_upload_dir
123 self._skip_all_gs_access = parse_options.skip_all_gs_access
124 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
125 'tools', 'perf')
126
127 self._local_skp_dir = os.path.join(
128 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
129 self._local_record_webpages_archive_dir = os.path.join(
130 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
131
132 # List of SKP files generated by this script.
133 self._skp_files = []
134
135 def _ParsePageSets(self, page_sets):
136 if not page_sets:
137 raise ValueError('Must specify at least one page_set!')
138 elif self._all_page_sets_specified:
139 # Get everything from the page_sets directory.
140 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
141 'page_sets')
142 ps = [os.path.join(page_sets_dir, page_set)
143 for page_set in os.listdir(page_sets_dir)
144 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
145 page_set.endswith('.py')]
146 elif '*' in page_sets:
147 # Explode and return the glob.
148 ps = glob.glob(page_sets)
149 else:
150 ps = page_sets.split(',')
151 ps.sort()
152 return ps
153
154 def Run(self):
155 """Run the SkPicturePlayback BuildStep."""
156
157 # TODO(borenet): Ensure the right .boto file is used by gsutil.
rmistry 2014/10/17 12:44:50 This todo has been resolved?
borenet 2014/10/17 13:21:43 I guess? I removed the block of code which did th
158
159 # Delete any left over data files in the data directory.
160 for archive_file in glob.glob(
161 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
162 os.remove(archive_file)
163
164 # Delete the local root directory if it already exists.
165 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
166 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
167
168 # Create the required local storage directories.
169 self._CreateLocalStorageDirs()
170
171 # Start the timer.
172 start_time = time.time()
173
174 # Loop through all page_sets.
175 for page_set in self._page_sets:
176
177 page_set_basename = os.path.basename(page_set).split('.')[0] + '.json'
178 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
179
180 if self._record:
181 # Create an archive of the specified webpages if '--record=True' is
182 # specified.
183 record_wpr_cmd = (
184 'DISPLAY=%s' % X11_DISPLAY,
185 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
186 '--extra-browser-args=--disable-setuid-sandbox',
187 '--browser=exact',
188 '--browser-executable=%s' % self._browser_executable,
189 page_set
190 )
191 for _ in range(RETRY_RECORD_WPR_COUNT):
192 output = shell_utils.run(' '.join(record_wpr_cmd), shell=True)
193 if RECORD_FAILURE_MSG in output:
194 print output
195 else:
196 # Break out of the retry loop since there were no errors.
197 break
198 else:
199 # If we get here then record_wpr did not succeed and thus did not
200 # break out of the loop.
201 raise Exception('record_wpr failed for page_set: %s' % page_set)
202
203 else:
204 if not self._skip_all_gs_access:
205 # Get the webpages archive so that it can be replayed.
206 self._DownloadWebpagesArchive(wpr_data_file, page_set_basename)
207
208 page_set_name = os.path.basename(page_set).split('.')[0]
209 run_measurement_cmd = (
210 'DISPLAY=%s' % X11_DISPLAY,
211 'timeout', '300',
212 os.path.join(self._telemetry_binaries_dir, 'run_measurement'),
213 '--extra-browser-args=--disable-setuid-sandbox',
214 '--browser=exact',
215 '--browser-executable=%s' % self._browser_executable,
216 SKP_BENCHMARK,
217 page_set_name,
218 '-o',
219 '/tmp/test.skp',
220 '--skp-outdir=%s' % TMP_SKP_DIR
221 )
222 page_set_dst = os.path.join(self._telemetry_binaries_dir, 'page_sets',
223 os.path.basename(page_set))
224 wpr_dst = os.path.join(self._telemetry_binaries_dir, 'page_sets', 'data',
225 wpr_data_file)
226 json_dst = os.path.join(self._telemetry_binaries_dir, 'page_sets', 'data',
227 page_set_basename)
228 copied_page_set = False
229 if not os.path.exists(page_set_dst):
230 print 'Copying %s to %s' % (page_set, page_set_dst)
231 shutil.copyfile(page_set, page_set_dst)
232 wpr_src = os.path.join(os.path.dirname(page_set), 'data',
233 wpr_data_file)
234 print 'Copying %s to %s' % (wpr_src, wpr_dst)
235 shutil.copyfile(wpr_src, wpr_dst)
236 json_src = os.path.join(os.path.dirname(page_set), 'data',
237 page_set_basename)
238 print 'Copying %s to %s' % (json_src, json_dst)
239 shutil.copyfile(json_src, json_dst)
240 copied_page_set = True
241
242 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
243 try:
244 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
245 shell_utils.run(' '.join(run_measurement_cmd), shell=True)
246 except shell_utils.CommandFailedException:
247 # skpicture_printer sometimes fails with AssertionError but the
248 # captured SKP is still valid. This is a known issue.
249 pass
250
251 if self._record:
252 # Move over the created archive into the local webpages archive
253 # directory.
254 shutil.move(
255 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
256 self._local_record_webpages_archive_dir)
257 shutil.move(
258 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
259 page_set_basename),
260 self._local_record_webpages_archive_dir)
261
262 # Rename generated SKP files into more descriptive names.
263 try:
264 self._RenameSkpFiles(page_set)
265 # Break out of the retry loop since there were no errors.
266 break
267 except Exception:
268 # There was a failure continue with the loop.
269 traceback.print_exc()
270 print '\n\n=======Retrying %s=======\n\n' % page_set
271 time.sleep(10)
272 else:
273 if copied_page_set:
274 os.remove(page_set_dst)
275 os.remove(wpr_dst)
276 os.remove(json_dst)
277 # If we get here then run_measurement did not succeed and thus did not
278 # break out of the loop.
279 raise Exception('run_measurement failed for page_set: %s' % page_set)
280
281 if copied_page_set:
282 os.remove(page_set_dst)
283 os.remove(wpr_dst)
284 os.remove(json_dst)
285
286 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
287 time.time() - start_time)
288
289 if self._skia_tools:
290 render_pictures_cmd = [
291 os.path.join(self._skia_tools, 'render_pictures'),
292 '-r', self._local_skp_dir
293 ]
294 render_pdfs_cmd = [
295 os.path.join(self._skia_tools, 'render_pdfs'),
296 self._local_skp_dir
297 ]
298
299 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
300 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
301 proc = subprocess.Popen(tools_cmd)
302 (code, output) = shell_utils.log_process_after_completion(proc,
303 echo=False)
304 if code != 0:
305 raise Exception('%s failed!' % ' '.join(tools_cmd))
306
307 if not self._non_interactive:
308 print '\n\n=======Running debugger======='
309 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
310 os.path.join(self._local_skp_dir, '*')))
311
312 print '\n\n'
313
314 if not self._skip_all_gs_access and self._upload_to_gs:
315 print '\n\n=======Uploading to Google Storage=======\n\n'
316 # Copy the directory structure in the root directory into Google Storage.
317 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
318 if self._alternate_upload_dir:
319 dest_dir_name = self._alternate_upload_dir
320
321 gs_bucket = self._dest_gsbase.lstrip(gs_utils.GS_PREFIX)
322 gs_utils.GSUtils().upload_dir_contents(
323 LOCAL_PLAYBACK_ROOT_DIR, gs_bucket, dest_dir_name,
324 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
325 predefined_acl=GS_PREDEFINED_ACL,
326 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
327
328 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
329 posixpath.join(self._dest_gsbase, dest_dir_name, SKPICTURES_DIR_NAME))
330 else:
331 print '\n\n=======Not Uploading to Google Storage=======\n\n'
332 print 'Generated resources are available in %s\n\n' % (
333 LOCAL_PLAYBACK_ROOT_DIR)
334
335 return 0
336
337 def _RenameSkpFiles(self, page_set):
338 """Rename generated SKP files into more descriptive names.
339
340 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
341 .skp in there to be this page_set's representative .skp.
342 """
343 # Here's where we're assuming there's one page per pageset.
344 # If there were more than one, we'd overwrite filename below.
345
346 # /path/to/skia_yahooanswers_desktop.json -> skia_yahooanswers_desktop.json
347 _, ps_filename = os.path.split(page_set)
348 # skia_yahooanswers_desktop.json -> skia_yahooanswers_desktop
349 ps_basename, _ = os.path.splitext(ps_filename)
350 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
351 _, page_name, device = ps_basename.split('_')
352
353 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
354 filename = basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
355
356 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
357 assert len(subdirs) == 1
358 for site in subdirs:
359 # We choose the largest .skp as the most likely to be interesting.
360 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
361 key=lambda path: os.stat(path).st_size)
362 dest = os.path.join(self._local_skp_dir, filename)
363 print 'Moving', largest_skp, 'to', dest
364 shutil.move(largest_skp, dest)
365 self._skp_files.append(filename)
366 shutil.rmtree(site)
367
368 def _CreateLocalStorageDirs(self):
369 """Creates required local storage directories for this script."""
370 for d in (self._local_record_webpages_archive_dir,
371 self._local_skp_dir):
372 if os.path.exists(d):
373 shutil.rmtree(d)
374 os.makedirs(d)
375
376 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_basename):
377 """Downloads the webpages archive and its required page set from GS."""
378 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
379 wpr_data_file)
380 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
381 'webpages_archive',
382 page_set_basename)
383 gs = gs_utils.GSUtils()
384 gs_bucket = self._dest_gsbase.lstrip(gs_utils.GS_PREFIX)
385 if (gs.does_storage_object_exist(gs_bucket, wpr_source) and
386 gs.does_storage_object_exist(gs_bucket, page_set_source)):
387 gs.download_file(gs_bucket, wpr_source,
388 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
389 wpr_data_file))
390 gs.download_file(gs_bucket, page_set_source,
391 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
392 page_set_basename))
393 else:
394 raise Exception('%s and %s do not exist in Google Storage!' % (
395 wpr_source, page_set_source))
396
397
398 if '__main__' == __name__:
399 option_parser = optparse.OptionParser()
400 option_parser.add_option(
401 '', '--page_sets',
402 help='Specifies the page sets to use to archive. Supports globs.',
403 default='all')
404 option_parser.add_option(
405 '', '--skip_all_gs_access', action='store_true',
406 help='All Google Storage interactions will be skipped if this flag is '
407 'specified. This is useful for cases where the user does not have '
408 'the required .boto file but would like to generate webpage '
409 'archives and SKPs from the Skia page sets.',
410 default=False)
411 option_parser.add_option(
412 '', '--record', action='store_true',
413 help='Specifies whether a new website archive should be created.',
414 default=False)
415 option_parser.add_option(
416 '', '--dest_gsbase',
417 help='gs:// bucket_name, the bucket to upload the file to.',
418 default='gs://chromium-skia-gm')
419 option_parser.add_option(
420 '', '--skia_tools',
421 help=('Path to compiled Skia executable tools. '
422 'render_pictures/render_pdfs is run on the set '
423 'after all SKPs are captured. If the script is run without '
424 '--non-interactive then the debugger is also run at the end. Debug '
425 'builds are recommended because they seem to catch more failures '
426 'than Release builds.'),
427 default=None)
428 option_parser.add_option(
429 '', '--upload_to_gs', action='store_true',
430 help='Does not upload to Google Storage if this is False.',
431 default=False)
432 option_parser.add_option(
433 '', '--alternate_upload_dir',
434 help='Uploads to a different directory in Google Storage if this flag is '
435 'specified',
436 default=None)
437 option_parser.add_option(
438 '', '--output_dir',
439 help='Directory where SKPs and webpage archives will be outputted to.',
440 default=tempfile.gettempdir())
441 option_parser.add_option(
442 '', '--browser_executable',
443 help='The exact browser executable to run.',
444 default=None)
445 option_parser.add_option(
446 '', '--chrome_src_path',
447 help='Path to the chromium src directory.',
448 default=None)
449 option_parser.add_option(
450 '', '--non-interactive', action='store_true',
451 help='Runs the script without any prompts. If this flag is specified and '
452 '--skia_tools is specified then the debugger is not run.',
453 default=False)
454 options, unused_args = option_parser.parse_args()
455
456 playback = SkPicturePlayback(options)
457 sys.exit(playback.Run())
OLDNEW
« tools/skp/recreate_skps.py ('K') | « tools/skp/recreate_skps.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698