Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
| 2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 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 """Instructs Chrome to load series of web pages and reports results. | 6 """Instructs Chrome to load series of web pages and reports results. |
| 7 | 7 |
| 8 When running Chrome is sandwiched between preprocessed disk caches and | 8 When running Chrome is sandwiched between preprocessed disk caches and |
| 9 WepPageReplay serving all connections. | 9 WepPageReplay serving all connections. |
| 10 | 10 |
| 11 TODO(pasko): implement cache preparation and WPR. | 11 TODO(pasko): implement cache preparation and WPR. |
| 12 """ | 12 """ |
| 13 | 13 |
| 14 import argparse | 14 import argparse |
| 15 import json | |
| 15 import logging | 16 import logging |
| 16 import os | 17 import os |
| 18 import shutil | |
| 17 import sys | 19 import sys |
| 20 import tempfile | |
| 18 import time | 21 import time |
| 22 import zipfile | |
| 19 | 23 |
| 20 _SRC_DIR = os.path.abspath(os.path.join( | 24 _SRC_DIR = os.path.abspath(os.path.join( |
| 21 os.path.dirname(__file__), '..', '..', '..')) | 25 os.path.dirname(__file__), '..', '..', '..')) |
| 22 | 26 |
| 23 sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil')) | 27 sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil')) |
| 24 from devil.android import device_utils | 28 from devil.android import device_utils |
| 25 | 29 |
| 26 sys.path.append(os.path.join(_SRC_DIR, 'build', 'android')) | 30 sys.path.append(os.path.join(_SRC_DIR, 'build', 'android')) |
| 27 from pylib import constants | 31 from pylib import constants |
| 28 import devil_chromium | 32 import devil_chromium |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 json.dump({'traceEvents': events['events'], 'metadata': {}}, f, indent=2) | 97 json.dump({'traceEvents': events['events'], 'metadata': {}}, f, indent=2) |
| 94 except IOError: | 98 except IOError: |
| 95 logging.warning('Could not save a trace: %s' % filename) | 99 logging.warning('Could not save a trace: %s' % filename) |
| 96 # Swallow the exception. | 100 # Swallow the exception. |
| 97 | 101 |
| 98 | 102 |
| 99 def _UpdateTimestampFromAdbStat(filename, stat): | 103 def _UpdateTimestampFromAdbStat(filename, stat): |
| 100 os.utime(filename, (stat.st_time, stat.st_time)) | 104 os.utime(filename, (stat.st_time, stat.st_time)) |
| 101 | 105 |
| 102 | 106 |
| 103 def _SaveBrowserCache(device, output_directory): | 107 def _PullBrowserCache(device): |
| 104 """Pulls the browser cache from the device and saves it locally. | 108 """Pulls the browser cache from the device and saves it locally. |
| 105 | 109 |
| 106 Cache is saved with the same file structure as on the device. Timestamps are | 110 Cache is saved with the same file structure as on the device. Timestamps are |
| 107 important to preserve because indexing and eviction depends on them. | 111 important to preserve because indexing and eviction depends on them. |
| 108 | 112 |
| 109 Args: | 113 Returns: |
| 110 output_directory: name of the directory for saving cache. | 114 Oemporary directory containing all the browser cache. |
|
mattcary
2016/02/12 10:46:45
Temporary
Benoit L
2016/02/15 12:49:19
s/Oemporary/Temporary/
gabadie
2016/02/15 13:16:44
Done.
| |
| 111 """ | 115 """ |
| 112 save_target = os.path.join(output_directory, _CACHE_DIRECTORY_NAME) | 116 save_target = tempfile.mkdtemp(suffix='.cache') |
| 113 try: | |
| 114 os.makedirs(save_target) | |
| 115 except IOError: | |
| 116 logging.warning('Could not create directory: %s' % save_target) | |
| 117 raise | |
| 118 | |
| 119 cache_directory = '/data/data/' + _CHROME_PACKAGE + '/cache/Cache' | 117 cache_directory = '/data/data/' + _CHROME_PACKAGE + '/cache/Cache' |
| 120 for filename, stat in device.adb.Ls(cache_directory): | 118 for filename, stat in device.adb.Ls(cache_directory): |
| 121 if filename == '..': | 119 if filename == '..': |
| 122 continue | 120 continue |
| 123 if filename == '.': | 121 if filename == '.': |
| 124 cache_directory_stat = stat | 122 cache_directory_stat = stat |
| 125 continue | 123 continue |
| 126 original_file = os.path.join(cache_directory, filename) | 124 original_file = os.path.join(cache_directory, filename) |
| 127 saved_file = os.path.join(save_target, filename) | 125 saved_file = os.path.join(save_target, filename) |
| 128 device.adb.Pull(original_file, saved_file) | 126 device.adb.Pull(original_file, saved_file) |
| 129 _UpdateTimestampFromAdbStat(saved_file, stat) | 127 _UpdateTimestampFromAdbStat(saved_file, stat) |
| 130 if filename == _INDEX_DIRECTORY_NAME: | 128 if filename == _INDEX_DIRECTORY_NAME: |
| 131 # The directory containing the index was pulled recursively, update the | 129 # The directory containing the index was pulled recursively, update the |
| 132 # timestamps for known files. They are ignored by cache backend, but may | 130 # timestamps for known files. They are ignored by cache backend, but may |
| 133 # be useful for debugging. | 131 # be useful for debugging. |
| 134 index_dir_stat = stat | 132 index_dir_stat = stat |
| 135 saved_index_dir = os.path.join(save_target, _INDEX_DIRECTORY_NAME) | 133 saved_index_dir = os.path.join(save_target, _INDEX_DIRECTORY_NAME) |
| 136 saved_index_file = os.path.join(saved_index_dir, _REAL_INDEX_FILE_NAME) | 134 saved_index_file = os.path.join(saved_index_dir, _REAL_INDEX_FILE_NAME) |
| 137 for sub_file, sub_stat in device.adb.Ls(original_file): | 135 for sub_file, sub_stat in device.adb.Ls(original_file): |
| 138 if sub_file == _REAL_INDEX_FILE_NAME: | 136 if sub_file == _REAL_INDEX_FILE_NAME: |
| 139 _UpdateTimestampFromAdbStat(saved_index_file, sub_stat) | 137 _UpdateTimestampFromAdbStat(saved_index_file, sub_stat) |
| 140 break | 138 break |
| 141 _UpdateTimestampFromAdbStat(saved_index_dir, index_dir_stat) | 139 _UpdateTimestampFromAdbStat(saved_index_dir, index_dir_stat) |
| 142 | 140 |
| 143 # Store the cache directory modification time. It is important to update it | 141 # Store the cache directory modification time. It is important to update it |
| 144 # after all files in it have been written. The timestamp is compared with | 142 # after all files in it have been written. The timestamp is compared with |
| 145 # the contents of the index file when freshness is determined. | 143 # the contents of the index file when freshness is determined. |
| 146 _UpdateTimestampFromAdbStat(save_target, cache_directory_stat) | 144 _UpdateTimestampFromAdbStat(save_target, cache_directory_stat) |
| 145 return save_target | |
| 147 | 146 |
| 147 def _ZipDirectoryContent(root_directory_path, archive_dest_path): | |
|
Benoit L
2016/02/15 12:49:19
nit: 2 blank lines.
gabadie
2016/02/15 13:16:44
Done.
| |
| 148 """Zip a directory's content recursively with all the directories's | |
|
Benoit L
2016/02/15 12:49:19
s/'s/'/
gabadie
2016/02/15 13:16:44
Done.
| |
| 149 timestamp preserved. | |
| 150 | |
| 151 Args: | |
| 152 root_directory_path: The directory's path to archive. | |
| 153 archive_dest_path: Archive destination's path. | |
| 154 """ | |
| 155 with zipfile.ZipFile(archive_dest_path, 'w') as zip_output: | |
| 156 timestamps = {} | |
| 157 for directory_path, dirnames, filenames in os.walk(root_directory_path): | |
| 158 for dirname in dirnames: | |
| 159 subdirectory_path = os.path.join(directory_path, dirname) | |
| 160 subdirectory_relative_path = os.path.relpath(subdirectory_path, | |
| 161 root_directory_path) | |
| 162 subdirectory_stats = os.stat(subdirectory_path) | |
| 163 timestamps[subdirectory_relative_path] = { | |
| 164 'atime': subdirectory_stats.st_atime, | |
| 165 'mtime': subdirectory_stats.st_mtime} | |
| 166 for filename in filenames: | |
| 167 file_path = os.path.join(directory_path, filename) | |
| 168 file_archive_name = os.path.join('content', | |
| 169 os.path.relpath(file_path, root_directory_path)) | |
| 170 file_stats = os.stat(file_path) | |
| 171 timestamps[file_archive_name[8:]] = { | |
| 172 'atime': file_stats.st_atime, | |
| 173 'mtime': file_stats.st_mtime} | |
| 174 zip_output.write(file_path, arcname=file_archive_name) | |
| 175 zip_output.writestr('timestamps.json', | |
| 176 json.dumps(timestamps, indent=2)) | |
| 177 | |
| 178 def _UnzipDirectoryContent(archive_path, directory_dest_path): | |
| 179 """Unzip a directory's content recursively with all the directories's | |
|
Benoit L
2016/02/15 12:49:19
s/'s/'/
gabadie
2016/02/15 13:16:44
Done.
| |
| 180 timestamp preserved. | |
|
Benoit L
2016/02/15 12:49:19
timestamps.
gabadie
2016/02/15 13:16:44
Done.
| |
| 181 | |
| 182 Args: | |
| 183 archive_path: Archive's path to unzip. | |
| 184 directory_dest_path: Directory destination path. | |
| 185 """ | |
| 186 if not os.path.exists(directory_dest_path): | |
| 187 os.makedirs(directory_dest_path) | |
| 188 | |
| 189 with zipfile.ZipFile(archive_path) as zip_input: | |
| 190 timestamps = None | |
| 191 for file_archive_name in zip_input.namelist(): | |
| 192 if file_archive_name == 'timestamps.json': | |
| 193 timestamps = json.loads(zip_input.read(file_archive_name)) | |
| 194 | |
|
Benoit L
2016/02/15 12:49:19
nit: blank line here is weird.
gabadie
2016/02/15 13:16:44
Done.
| |
| 195 elif file_archive_name.startswith('content/'): | |
| 196 file_relative_path = file_archive_name[8:] | |
| 197 file_output_path = os.path.join(directory_dest_path, file_relative_path) | |
| 198 file_parent_directory_path = os.path.dirname(file_output_path) | |
| 199 if not os.path.exists(file_parent_directory_path): | |
| 200 os.makedirs(file_parent_directory_path) | |
| 201 with open(file_output_path, 'w') as f: | |
| 202 f.write(zip_input.read(file_archive_name)) | |
| 203 | |
| 204 assert timestamps | |
| 205 for relative_path, stats in timestamps.iteritems(): | |
| 206 output_path = os.path.join(directory_dest_path, relative_path) | |
| 207 if not os.path.exists(output_path): | |
| 208 os.makedirs(output_path) | |
| 209 os.utime(output_path, (stats['atime'], stats['mtime'])) | |
| 148 | 210 |
| 149 def main(): | 211 def main(): |
| 150 logging.basicConfig(level=logging.INFO) | 212 logging.basicConfig(level=logging.INFO) |
| 151 devil_chromium.Initialize() | 213 devil_chromium.Initialize() |
| 152 | 214 |
| 153 parser = argparse.ArgumentParser() | 215 parser = argparse.ArgumentParser() |
| 154 parser.add_argument('--job', required=True, | 216 parser.add_argument('--job', required=True, |
| 155 help='JSON file with job description.') | 217 help='JSON file with job description.') |
| 156 parser.add_argument('--output', required=True, | 218 parser.add_argument('--output', required=True, |
| 157 help='Name of output directory to create.') | 219 help='Name of output directory to create.') |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 pages_loaded += 1 | 258 pages_loaded += 1 |
| 197 _SaveChromeTrace(tracing_track.ToJsonDict(), args.output, | 259 _SaveChromeTrace(tracing_track.ToJsonDict(), args.output, |
| 198 str(pages_loaded)) | 260 str(pages_loaded)) |
| 199 | 261 |
| 200 if args.save_cache: | 262 if args.save_cache: |
| 201 # Move Chrome to background to allow it to flush the index. | 263 # Move Chrome to background to allow it to flush the index. |
| 202 device.adb.Shell('am start com.google.android.launcher') | 264 device.adb.Shell('am start com.google.android.launcher') |
| 203 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) | 265 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) |
| 204 device.KillAll(_CHROME_PACKAGE, quiet=True) | 266 device.KillAll(_CHROME_PACKAGE, quiet=True) |
| 205 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) | 267 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) |
| 206 _SaveBrowserCache(device, args.output) | 268 |
| 269 cache_directory_path = _PullBrowserCache(device) | |
| 270 _ZipDirectoryContent(cache_directory_path, | |
| 271 os.path.join(args.output, 'cache.zip')) | |
| 272 shutil.rmtree(cache_directory_path) | |
| 207 | 273 |
| 208 | 274 |
| 209 if __name__ == '__main__': | 275 if __name__ == '__main__': |
| 210 sys.exit(main()) | 276 sys.exit(main()) |
| OLD | NEW |