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

Side by Side Diff: tools/android/loading/run_sandwich.py

Issue 1690233002: sandwich: Save browser cache as zip archive preserving all timestamps (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@d02
Patch Set: Addresses nits Created 4 years, 10 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
« 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 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
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 Temporary directory containing all the browser cache.
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
146
147
148 def _ZipDirectoryContent(root_directory_path, archive_dest_path):
149 """Zip a directory's content recursively with all the directories'
150 timestamps preserved.
151
152 Args:
153 root_directory_path: The directory's path to archive.
154 archive_dest_path: Archive destination's path.
155 """
156 with zipfile.ZipFile(archive_dest_path, 'w') as zip_output:
157 timestamps = {}
158 for directory_path, dirnames, filenames in os.walk(root_directory_path):
159 for dirname in dirnames:
160 subdirectory_path = os.path.join(directory_path, dirname)
161 subdirectory_relative_path = os.path.relpath(subdirectory_path,
162 root_directory_path)
163 subdirectory_stats = os.stat(subdirectory_path)
164 timestamps[subdirectory_relative_path] = {
165 'atime': subdirectory_stats.st_atime,
166 'mtime': subdirectory_stats.st_mtime}
167 for filename in filenames:
168 file_path = os.path.join(directory_path, filename)
169 file_archive_name = os.path.join('content',
170 os.path.relpath(file_path, root_directory_path))
171 file_stats = os.stat(file_path)
172 timestamps[file_archive_name[8:]] = {
173 'atime': file_stats.st_atime,
174 'mtime': file_stats.st_mtime}
175 zip_output.write(file_path, arcname=file_archive_name)
176 zip_output.writestr('timestamps.json',
177 json.dumps(timestamps, indent=2))
178
179
180 def _UnzipDirectoryContent(archive_path, directory_dest_path):
181 """Unzip a directory's content recursively with all the directories'
182 timestamps preserved.
183
184 Args:
185 archive_path: Archive's path to unzip.
186 directory_dest_path: Directory destination path.
187 """
188 if not os.path.exists(directory_dest_path):
189 os.makedirs(directory_dest_path)
190
191 with zipfile.ZipFile(archive_path) as zip_input:
192 timestamps = None
193 for file_archive_name in zip_input.namelist():
194 if file_archive_name == 'timestamps.json':
195 timestamps = json.loads(zip_input.read(file_archive_name))
196 elif file_archive_name.startswith('content/'):
197 file_relative_path = file_archive_name[8:]
198 file_output_path = os.path.join(directory_dest_path, file_relative_path)
199 file_parent_directory_path = os.path.dirname(file_output_path)
200 if not os.path.exists(file_parent_directory_path):
201 os.makedirs(file_parent_directory_path)
202 with open(file_output_path, 'w') as f:
203 f.write(zip_input.read(file_archive_name))
204
205 assert timestamps
206 for relative_path, stats in timestamps.iteritems():
207 output_path = os.path.join(directory_dest_path, relative_path)
208 if not os.path.exists(output_path):
209 os.makedirs(output_path)
210 os.utime(output_path, (stats['atime'], stats['mtime']))
147 211
148 212
149 def main(): 213 def main():
150 logging.basicConfig(level=logging.INFO) 214 logging.basicConfig(level=logging.INFO)
151 devil_chromium.Initialize() 215 devil_chromium.Initialize()
152 216
153 parser = argparse.ArgumentParser() 217 parser = argparse.ArgumentParser()
154 parser.add_argument('--job', required=True, 218 parser.add_argument('--job', required=True,
155 help='JSON file with job description.') 219 help='JSON file with job description.')
156 parser.add_argument('--output', required=True, 220 parser.add_argument('--output', required=True,
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
196 pages_loaded += 1 260 pages_loaded += 1
197 _SaveChromeTrace(tracing_track.ToJsonDict(), args.output, 261 _SaveChromeTrace(tracing_track.ToJsonDict(), args.output,
198 str(pages_loaded)) 262 str(pages_loaded))
199 263
200 if args.save_cache: 264 if args.save_cache:
201 # Move Chrome to background to allow it to flush the index. 265 # Move Chrome to background to allow it to flush the index.
202 device.adb.Shell('am start com.google.android.launcher') 266 device.adb.Shell('am start com.google.android.launcher')
203 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) 267 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS)
204 device.KillAll(_CHROME_PACKAGE, quiet=True) 268 device.KillAll(_CHROME_PACKAGE, quiet=True)
205 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) 269 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS)
206 _SaveBrowserCache(device, args.output) 270
271 cache_directory_path = _PullBrowserCache(device)
272 _ZipDirectoryContent(cache_directory_path,
273 os.path.join(args.output, 'cache.zip'))
274 shutil.rmtree(cache_directory_path)
207 275
208 276
209 if __name__ == '__main__': 277 if __name__ == '__main__':
210 sys.exit(main()) 278 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