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

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

Issue 1707793002: sandwich: Refactor to use more existing code. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Integrates options layer slightly more 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
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
(...skipping 17 matching lines...) Expand all
28 28
29 sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil')) 29 sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil'))
30 from devil.android import device_utils 30 from devil.android import device_utils
31 31
32 sys.path.append(os.path.join(_SRC_DIR, 'build', 'android')) 32 sys.path.append(os.path.join(_SRC_DIR, 'build', 'android'))
33 from pylib import constants 33 from pylib import constants
34 import devil_chromium 34 import devil_chromium
35 35
36 import device_setup 36 import device_setup
37 import devtools_monitor 37 import devtools_monitor
38 import json 38 import options
39 import page_track 39 import page_track
40 import pull_sandwich_metrics 40 import pull_sandwich_metrics
41 import trace_recorder
41 import tracing 42 import tracing
42 43
43 44
45 # Use options layer to access contants.
Benoit L 2016/02/22 10:26:13 nit: s/contants/constants/
gabadie 2016/02/22 11:05:06 Done.
46 OPTIONS = options.OPTIONS
47
44 _JOB_SEARCH_PATH = 'sandwich_jobs' 48 _JOB_SEARCH_PATH = 'sandwich_jobs'
45 49
46 # Directory name under --output to save the cache from the device. 50 # Directory name under --output to save the cache from the device.
47 _CACHE_DIRECTORY_NAME = 'cache' 51 _CACHE_DIRECTORY_NAME = 'cache'
48 52
49 # Name of cache subdirectory on the device where the cache index is stored. 53 # Name of cache subdirectory on the device where the cache index is stored.
50 _INDEX_DIRECTORY_NAME = 'index-dir' 54 _INDEX_DIRECTORY_NAME = 'index-dir'
51 55
52 # Name of the file containing the cache index. This file is stored on the device 56 # Name of the file containing the cache index. This file is stored on the device
53 # in the cache directory under _INDEX_DIRECTORY_NAME. 57 # in the cache directory under _INDEX_DIRECTORY_NAME.
54 _REAL_INDEX_FILE_NAME = 'the-real-index' 58 _REAL_INDEX_FILE_NAME = 'the-real-index'
55 59
56 # Name of the chrome package.
57 _CHROME_PACKAGE = (
58 constants.PACKAGE_INFO[device_setup.DEFAULT_CHROME_PACKAGE].package)
59
60 # An estimate of time to wait for the device to become idle after expensive 60 # An estimate of time to wait for the device to become idle after expensive
61 # operations, such as opening the launcher activity. 61 # operations, such as opening the launcher activity.
62 _TIME_TO_DEVICE_IDLE_SECONDS = 2 62 _TIME_TO_DEVICE_IDLE_SECONDS = 2
63 63
64 # Cache directory's path on the device. 64
65 _REMOTE_CACHE_DIRECTORY = '/data/data/' + _CHROME_PACKAGE + '/cache/Cache' 65 def _RemoteCacheDirectory():
66 return '/data/data/{}/cache/Cache'.format(OPTIONS.chrome_package_name)
66 67
67 68
68 def _ReadUrlsFromJobDescription(job_name): 69 def _ReadUrlsFromJobDescription(job_name):
69 """Retrieves the list of URLs associated with the job name.""" 70 """Retrieves the list of URLs associated with the job name."""
70 try: 71 try:
71 # Extra sugar: attempt to load from a relative path. 72 # Extra sugar: attempt to load from a relative path.
72 json_file_name = os.path.join(os.path.dirname(__file__), _JOB_SEARCH_PATH, 73 json_file_name = os.path.join(os.path.dirname(__file__), _JOB_SEARCH_PATH,
73 job_name) 74 job_name)
74 with open(json_file_name) as f: 75 with open(json_file_name) as f:
75 json_data = json.load(f) 76 json_data = json.load(f)
76 except IOError: 77 except IOError:
77 # Attempt to read by regular file name. 78 # Attempt to read by regular file name.
78 with open(job_name) as f: 79 with open(job_name) as f:
79 json_data = json.load(f) 80 json_data = json.load(f)
80 81
81 key = 'urls' 82 key = 'urls'
82 if json_data and key in json_data: 83 if json_data and key in json_data:
83 url_list = json_data[key] 84 url_list = json_data[key]
84 if isinstance(url_list, list) and len(url_list) > 0: 85 if isinstance(url_list, list) and len(url_list) > 0:
85 return url_list 86 return url_list
86 raise Exception('Job description does not define a list named "urls"') 87 raise Exception('Job description does not define a list named "urls"')
87 88
88 89
89 def _SaveChromeTrace(events, target_directory):
90 """Saves the trace events, ignores IO errors.
91
92 Args:
93 events: a dict as returned by TracingTrack.ToJsonDict()
94 target_directory: Directory path where trace is created.
95 """
96 filename = os.path.join(target_directory, 'trace.json')
97 try:
98 os.makedirs(target_directory)
99 with open(filename, 'w') as f:
100 json.dump({'traceEvents': events['events'], 'metadata': {}}, f, indent=2)
101 except IOError:
102 logging.warning('Could not save a trace: %s' % filename)
103 # Swallow the exception.
104
105
106 def _UpdateTimestampFromAdbStat(filename, stat): 90 def _UpdateTimestampFromAdbStat(filename, stat):
107 os.utime(filename, (stat.st_time, stat.st_time)) 91 os.utime(filename, (stat.st_time, stat.st_time))
108 92
109 93
110 def _AdbShell(adb, cmd): 94 def _AdbShell(adb, cmd):
111 adb.Shell(subprocess.list2cmdline(cmd)) 95 adb.Shell(subprocess.list2cmdline(cmd))
112 96
113 97
114 def _AdbUtime(adb, filename, timestamp): 98 def _AdbUtime(adb, filename, timestamp):
115 """Adb equivalent of os.utime(filename, (timestamp, timestamp)) 99 """Adb equivalent of os.utime(filename, (timestamp, timestamp))
116 """ 100 """
117 touch_stamp = datetime.fromtimestamp(timestamp).strftime('%Y%m%d.%H%M%S') 101 touch_stamp = datetime.fromtimestamp(timestamp).strftime('%Y%m%d.%H%M%S')
118 _AdbShell(adb, ['touch', '-t', touch_stamp, filename]) 102 _AdbShell(adb, ['touch', '-t', touch_stamp, filename])
119 103
120 104
121 def _PullBrowserCache(device): 105 def _PullBrowserCache(device):
122 """Pulls the browser cache from the device and saves it locally. 106 """Pulls the browser cache from the device and saves it locally.
123 107
124 Cache is saved with the same file structure as on the device. Timestamps are 108 Cache is saved with the same file structure as on the device. Timestamps are
125 important to preserve because indexing and eviction depends on them. 109 important to preserve because indexing and eviction depends on them.
126 110
127 Returns: 111 Returns:
128 Temporary directory containing all the browser cache. 112 Temporary directory containing all the browser cache.
129 """ 113 """
130 save_target = tempfile.mkdtemp(suffix='.cache') 114 save_target = tempfile.mkdtemp(suffix='.cache')
131 for filename, stat in device.adb.Ls(_REMOTE_CACHE_DIRECTORY): 115 for filename, stat in device.adb.Ls(_RemoteCacheDirectory()):
132 if filename == '..': 116 if filename == '..':
133 continue 117 continue
134 if filename == '.': 118 if filename == '.':
135 cache_directory_stat = stat 119 cache_directory_stat = stat
136 continue 120 continue
137 original_file = os.path.join(_REMOTE_CACHE_DIRECTORY, filename) 121 original_file = os.path.join(_RemoteCacheDirectory(), filename)
138 saved_file = os.path.join(save_target, filename) 122 saved_file = os.path.join(save_target, filename)
139 device.adb.Pull(original_file, saved_file) 123 device.adb.Pull(original_file, saved_file)
140 _UpdateTimestampFromAdbStat(saved_file, stat) 124 _UpdateTimestampFromAdbStat(saved_file, stat)
141 if filename == _INDEX_DIRECTORY_NAME: 125 if filename == _INDEX_DIRECTORY_NAME:
142 # The directory containing the index was pulled recursively, update the 126 # The directory containing the index was pulled recursively, update the
143 # timestamps for known files. They are ignored by cache backend, but may 127 # timestamps for known files. They are ignored by cache backend, but may
144 # be useful for debugging. 128 # be useful for debugging.
145 index_dir_stat = stat 129 index_dir_stat = stat
146 saved_index_dir = os.path.join(save_target, _INDEX_DIRECTORY_NAME) 130 saved_index_dir = os.path.join(save_target, _INDEX_DIRECTORY_NAME)
147 saved_index_file = os.path.join(saved_index_dir, _REAL_INDEX_FILE_NAME) 131 saved_index_file = os.path.join(saved_index_dir, _REAL_INDEX_FILE_NAME)
(...skipping 11 matching lines...) Expand all
159 143
160 144
161 def _PushBrowserCache(device, local_cache_path): 145 def _PushBrowserCache(device, local_cache_path):
162 """Pushes the browser cache saved locally to the device. 146 """Pushes the browser cache saved locally to the device.
163 147
164 Args: 148 Args:
165 device: Android device. 149 device: Android device.
166 local_cache_path: The directory's path containing the cache locally. 150 local_cache_path: The directory's path containing the cache locally.
167 """ 151 """
168 # Clear previous cache. 152 # Clear previous cache.
169 _AdbShell(device.adb, ['rm', '-rf', _REMOTE_CACHE_DIRECTORY]) 153 _AdbShell(device.adb, ['rm', '-rf', _RemoteCacheDirectory()])
170 _AdbShell(device.adb, ['mkdir', _REMOTE_CACHE_DIRECTORY]) 154 _AdbShell(device.adb, ['mkdir', _RemoteCacheDirectory()])
171 155
172 # Push cache content. 156 # Push cache content.
173 device.adb.Push(local_cache_path, _REMOTE_CACHE_DIRECTORY) 157 device.adb.Push(local_cache_path, _RemoteCacheDirectory())
174 158
175 # Walk through the local cache to update mtime on the device. 159 # Walk through the local cache to update mtime on the device.
176 def MirrorMtime(local_path): 160 def MirrorMtime(local_path):
177 cache_relative_path = os.path.relpath(local_path, start=local_cache_path) 161 cache_relative_path = os.path.relpath(local_path, start=local_cache_path)
178 remote_path = os.path.join(_REMOTE_CACHE_DIRECTORY, cache_relative_path) 162 remote_path = os.path.join(_RemoteCacheDirectory(), cache_relative_path)
179 _AdbUtime(device.adb, remote_path, os.stat(local_path).st_mtime) 163 _AdbUtime(device.adb, remote_path, os.stat(local_path).st_mtime)
180 164
181 for local_directory_path, dirnames, filenames in os.walk( 165 for local_directory_path, dirnames, filenames in os.walk(
182 local_cache_path, topdown=False): 166 local_cache_path, topdown=False):
183 for filename in filenames: 167 for filename in filenames:
184 MirrorMtime(os.path.join(local_directory_path, filename)) 168 MirrorMtime(os.path.join(local_directory_path, filename))
185 for dirname in dirnames: 169 for dirname in dirnames:
186 MirrorMtime(os.path.join(local_directory_path, dirname)) 170 MirrorMtime(os.path.join(local_directory_path, dirname))
187 MirrorMtime(local_cache_path) 171 MirrorMtime(local_cache_path)
188 172
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
271 int(dirname) 255 int(dirname)
272 except ValueError: 256 except ValueError:
273 continue 257 continue
274 shutil.rmtree(directory_path) 258 shutil.rmtree(directory_path)
275 259
276 260
277 def main(): 261 def main():
278 logging.basicConfig(level=logging.INFO) 262 logging.basicConfig(level=logging.INFO)
279 devil_chromium.Initialize() 263 devil_chromium.Initialize()
280 264
265 # Don't give the argument yet. All we are interested in for now is accessing
266 # the default values of OPTIONS.
267 OPTIONS.ParseArgs([])
268
281 parser = argparse.ArgumentParser() 269 parser = argparse.ArgumentParser()
282 parser.add_argument('--job', required=True, 270 parser.add_argument('--job', required=True,
283 help='JSON file with job description.') 271 help='JSON file with job description.')
284 parser.add_argument('--output', required=True, 272 parser.add_argument('--output', required=True,
285 help='Name of output directory to create.') 273 help='Name of output directory to create.')
286 parser.add_argument('--repeat', default=1, type=int, 274 parser.add_argument('--repeat', default=1, type=int,
287 help='How many times to run the job') 275 help='How many times to run the job')
288 parser.add_argument('--cache-op', 276 parser.add_argument('--cache-op',
289 choices=['clear', 'save', 'push', 'reload'], 277 choices=['clear', 'save', 'push', 'reload'],
290 default='clear', 278 default='clear',
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 assert os.path.isfile(local_cache_archive_path) 312 assert os.path.isfile(local_cache_archive_path)
325 local_cache_directory_path = tempfile.mkdtemp(suffix='.cache') 313 local_cache_directory_path = tempfile.mkdtemp(suffix='.cache')
326 _UnzipDirectoryContent(local_cache_archive_path, local_cache_directory_path) 314 _UnzipDirectoryContent(local_cache_archive_path, local_cache_directory_path)
327 315
328 with device_setup.WprHost(device, args.wpr_archive, args.wpr_record, 316 with device_setup.WprHost(device, args.wpr_archive, args.wpr_record,
329 args.disable_wpr_script_injection) as additional_flags: 317 args.disable_wpr_script_injection) as additional_flags:
330 def _RunNavigation(url, clear_cache, trace_id): 318 def _RunNavigation(url, clear_cache, trace_id):
331 with device_setup.DeviceConnection( 319 with device_setup.DeviceConnection(
332 device=device, 320 device=device,
333 additional_flags=additional_flags) as connection: 321 additional_flags=additional_flags) as connection:
334 if clear_cache: 322 loading_trace = trace_recorder.MonitorUrl(
335 connection.ClearCache() 323 connection, url,
336 page_track.PageTrack(connection) 324 clear_cache=clear_cache,
337 tracing_track = tracing.TracingTrack(connection,
338 categories=pull_sandwich_metrics.CATEGORIES) 325 categories=pull_sandwich_metrics.CATEGORIES)
339 connection.SetUpMonitoring()
340 connection.SendAndIgnoreResponse('Page.navigate', {'url': url})
341 connection.StartMonitoring()
342 if trace_id != None: 326 if trace_id != None:
343 trace_target_directory = os.path.join(args.output, str(trace_id)) 327 loading_trace_path = os.path.join(
344 _SaveChromeTrace(tracing_track.ToJsonDict(), trace_target_directory) 328 args.output, str(trace_id), 'trace.json')
329 os.makedirs(os.path.dirname(loading_trace_path))
330 loading_trace.SaveToJsonFile(loading_trace_path)
345 331
346 for _ in xrange(args.repeat): 332 for _ in xrange(args.repeat):
347 for url in job_urls: 333 for url in job_urls:
348 clear_cache = False 334 clear_cache = False
349 if args.cache_op == 'clear': 335 if args.cache_op == 'clear':
350 clear_cache = True 336 clear_cache = True
351 elif args.cache_op == 'push': 337 elif args.cache_op == 'push':
352 device.KillAll(_CHROME_PACKAGE, quiet=True) 338 device.KillAll(OPTIONS.chrome_package_name, quiet=True)
353 _PushBrowserCache(device, local_cache_directory_path) 339 _PushBrowserCache(device, local_cache_directory_path)
354 elif args.cache_op == 'reload': 340 elif args.cache_op == 'reload':
355 _RunNavigation(url, clear_cache=True, trace_id=None) 341 _RunNavigation(url, clear_cache=True, trace_id=None)
356 elif args.cache_op == 'save': 342 elif args.cache_op == 'save':
357 clear_cache = not run_infos['urls'] 343 clear_cache = not run_infos['urls']
358 _RunNavigation(url, clear_cache=clear_cache, 344 _RunNavigation(url, clear_cache=clear_cache,
359 trace_id=len(run_infos['urls'])) 345 trace_id=len(run_infos['urls']))
360 run_infos['urls'].append(url) 346 run_infos['urls'].append(url)
361 347
362 if local_cache_directory_path: 348 if local_cache_directory_path:
363 shutil.rmtree(local_cache_directory_path) 349 shutil.rmtree(local_cache_directory_path)
364 350
365 if args.cache_op == 'save': 351 if args.cache_op == 'save':
366 # Move Chrome to background to allow it to flush the index. 352 # Move Chrome to background to allow it to flush the index.
367 device.adb.Shell('am start com.google.android.launcher') 353 device.adb.Shell('am start com.google.android.launcher')
368 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) 354 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS)
369 device.KillAll(_CHROME_PACKAGE, quiet=True) 355 device.KillAll(OPTIONS.chrome_package_name, quiet=True)
370 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS) 356 time.sleep(_TIME_TO_DEVICE_IDLE_SECONDS)
371 357
372 cache_directory_path = _PullBrowserCache(device) 358 cache_directory_path = _PullBrowserCache(device)
373 _ZipDirectoryContent(cache_directory_path, local_cache_archive_path) 359 _ZipDirectoryContent(cache_directory_path, local_cache_archive_path)
374 shutil.rmtree(cache_directory_path) 360 shutil.rmtree(cache_directory_path)
375 361
376 with open(os.path.join(args.output, 'run_infos.json'), 'w') as file_output: 362 with open(os.path.join(args.output, 'run_infos.json'), 'w') as file_output:
377 json.dump(run_infos, file_output, indent=2) 363 json.dump(run_infos, file_output, indent=2)
378 364
379 365
380 if __name__ == '__main__': 366 if __name__ == '__main__':
381 sys.exit(main()) 367 sys.exit(main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698