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

Side by Side Diff: build/android/incremental_install/installer.py

Issue 1375043002: Speed up incremental_install by caching device checksums between runs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@device-utils-brace-fix
Patch Set: fix cache_commit_func Created 5 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
« no previous file with comments | « build/android/devil/android/device_utils.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
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2015 The Chromium Authors. All rights reserved. 3 # Copyright 2015 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Install *_incremental.apk targets as well as their dependent files.""" 7 """Install *_incremental.apk targets as well as their dependent files."""
8 8
9 import argparse 9 import argparse
10 import glob 10 import glob
(...skipping 16 matching lines...) Expand all
27 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'gyp')) 27 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, 'gyp'))
28 from util import build_utils 28 from util import build_utils
29 29
30 30
31 def _TransformDexPaths(paths): 31 def _TransformDexPaths(paths):
32 """Given paths like ["/a/b/c", "/a/c/d"], returns ["b.c", "c.d"].""" 32 """Given paths like ["/a/b/c", "/a/c/d"], returns ["b.c", "c.d"]."""
33 prefix_len = len(os.path.commonprefix(paths)) 33 prefix_len = len(os.path.commonprefix(paths))
34 return [p[prefix_len:].replace(os.sep, '.') for p in paths] 34 return [p[prefix_len:].replace(os.sep, '.') for p in paths]
35 35
36 36
37 def _Execute(concurrently, *funcs):
38 """Calls all functions in |funcs| concurrently or in sequence."""
39 timer = time_profile.TimeProfile()
40 if concurrently:
41 reraiser_thread.RunAsync(funcs)
42 else:
43 for f in funcs:
44 f()
45 timer.Stop(log=False)
46 return timer
47
48
37 def main(): 49 def main():
38 parser = argparse.ArgumentParser() 50 parser = argparse.ArgumentParser()
39 parser.add_argument('apk_path', 51 parser.add_argument('apk_path',
40 help='The path to the APK to install.') 52 help='The path to the APK to install.')
41 parser.add_argument('--split', 53 parser.add_argument('--split',
42 action='append', 54 action='append',
43 dest='splits', 55 dest='splits',
44 help='A glob matching the apk splits. ' 56 help='A glob matching the apk splits. '
45 'Can be specified multiple times.') 57 'Can be specified multiple times.')
46 parser.add_argument('--lib-dir', 58 parser.add_argument('--lib-dir',
47 help='Path to native libraries directory.') 59 help='Path to native libraries directory.')
48 parser.add_argument('--dex-files', 60 parser.add_argument('--dex-files',
49 help='List of dex files to push.', 61 help='List of dex files to push.',
50 action='append', 62 action='append',
51 default=[]) 63 default=[])
52 parser.add_argument('-d', '--device', dest='device', 64 parser.add_argument('-d', '--device', dest='device',
53 help='Target device for apk to install on.') 65 help='Target device for apk to install on.')
54 parser.add_argument('--uninstall', 66 parser.add_argument('--uninstall',
55 action='store_true', 67 action='store_true',
56 default=False, 68 default=False,
57 help='Remove the app and all side-loaded files.') 69 help='Remove the app and all side-loaded files.')
58 parser.add_argument('--output-directory', 70 parser.add_argument('--output-directory',
59 help='Path to the root build directory.') 71 help='Path to the root build directory.')
60 parser.add_argument('--no-threading', 72 parser.add_argument('--no-threading',
61 action='store_true', 73 action='store_false',
62 default=False, 74 default=True,
75 dest='threading',
63 help='Do not install and push concurrently') 76 help='Do not install and push concurrently')
64 parser.add_argument('-v', 77 parser.add_argument('-v',
65 '--verbose', 78 '--verbose',
66 dest='verbose_count', 79 dest='verbose_count',
67 default=0, 80 default=0,
68 action='count', 81 action='count',
69 help='Verbose level (multiple times for more)') 82 help='Verbose level (multiple times for more)')
70 83
71 args = parser.parse_args() 84 args = parser.parse_args()
72 85
73 run_tests_helper.SetLogLevel(args.verbose_count) 86 run_tests_helper.SetLogLevel(args.verbose_count)
74 constants.SetBuildType('Debug') 87 constants.SetBuildType('Debug')
75 if args.output_directory: 88 if args.output_directory:
76 constants.SetOutputDirectory(args.output_directory) 89 constants.SetOutputDirectory(args.output_directory)
77 90
78 main_timer = time_profile.TimeProfile() 91 main_timer = time_profile.TimeProfile()
79 install_timer = time_profile.TimeProfile() 92 install_timer = time_profile.TimeProfile()
80 push_native_timer = time_profile.TimeProfile() 93 push_native_timer = time_profile.TimeProfile()
81 push_dex_timer = time_profile.TimeProfile() 94 push_dex_timer = time_profile.TimeProfile()
82 95
83 if args.device: 96 if args.device:
84 # Retries are annoying when commands fail for legitimate reasons. Might want 97 # Retries are annoying when commands fail for legitimate reasons. Might want
85 # to enable them if this is ever used on bots though. 98 # to enable them if this is ever used on bots though.
86 device = device_utils.DeviceUtils(args.device, default_retries=0) 99 device = device_utils.DeviceUtils(
100 args.device, default_retries=0, enable_device_files_cache=True)
87 else: 101 else:
88 devices = device_utils.DeviceUtils.HealthyDevices(default_retries=0) 102 devices = device_utils.DeviceUtils.HealthyDevices(
103 default_retries=0, enable_device_files_cache=True)
89 if not devices: 104 if not devices:
90 raise device_errors.NoDevicesError() 105 raise device_errors.NoDevicesError()
91 elif len(devices) == 1: 106 elif len(devices) == 1:
92 device = devices[0] 107 device = devices[0]
93 else: 108 else:
94 all_devices = device_utils.DeviceUtils.parallel(devices) 109 all_devices = device_utils.DeviceUtils.parallel(devices)
95 msg = ('More than one device available.\n' 110 msg = ('More than one device available.\n'
96 'Use --device=SERIAL to select a device.\n' 111 'Use --device=SERIAL to select a device.\n'
97 'Available devices:\n') 112 'Available devices:\n')
98 descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None) 113 descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None)
99 for d, desc in zip(devices, descriptions): 114 for d, desc in zip(devices, descriptions):
100 msg += ' %s (%s)\n' % (d, desc) 115 msg += ' %s (%s)\n' % (d, desc)
101 raise Exception(msg) 116 raise Exception(msg)
102 117
103 apk_help = apk_helper.ApkHelper(args.apk_path) 118 apk_help = apk_helper.ApkHelper(args.apk_path)
104 apk_package = apk_help.GetPackageName() 119 apk_package = apk_help.GetPackageName()
105 device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package 120 device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package
106 121
107 if args.uninstall: 122 if args.uninstall:
108 device.Uninstall(apk_package) 123 device.Uninstall(apk_package)
109 device.RunShellCommand(['rm', '-rf', device_incremental_dir], 124 device.RunShellCommand(['rm', '-rf', device_incremental_dir],
110 check_return=True) 125 check_return=True)
111 logging.info('Uninstall took %s seconds.', main_timer.GetDelta()) 126 logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
112 return 127 return
113 128
114 if device.build_version_sdk >= version_codes.MARSHMALLOW:
115 if apk_help.HasIsolatedProcesses():
116 raise Exception('Cannot use perform incremental installs on Android M+ '
117 'without first disabling isolated processes. Use GN arg: '
118 'disable_incremental_isolated_processes=true to do so.')
119
120 # Install .apk(s) if any of them have changed. 129 # Install .apk(s) if any of them have changed.
121 def do_install(): 130 def do_install():
122 install_timer.Start() 131 install_timer.Start()
123 if args.splits: 132 if args.splits:
124 splits = [] 133 splits = []
125 for split_glob in args.splits: 134 for split_glob in args.splits:
126 splits.extend((f for f in glob.glob(split_glob))) 135 splits.extend((f for f in glob.glob(split_glob)))
127 device.InstallSplitApk(args.apk_path, splits, reinstall=True, 136 device.InstallSplitApk(args.apk_path, splits, reinstall=True,
128 allow_cached_props=True, permissions=()) 137 allow_cached_props=True, permissions=())
129 else: 138 else:
(...skipping 16 matching lines...) Expand all
146 with build_utils.TempDir() as temp_dir: 155 with build_utils.TempDir() as temp_dir:
147 device_dex_dir = posixpath.join(device_incremental_dir, 'dex') 156 device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
148 # Ensure no two files have the same name. 157 # Ensure no two files have the same name.
149 transformed_names = _TransformDexPaths(args.dex_files) 158 transformed_names = _TransformDexPaths(args.dex_files)
150 for src_path, dest_name in zip(args.dex_files, transformed_names): 159 for src_path, dest_name in zip(args.dex_files, transformed_names):
151 shutil.copyfile(src_path, os.path.join(temp_dir, dest_name)) 160 shutil.copyfile(src_path, os.path.join(temp_dir, dest_name))
152 device.PushChangedFiles([(temp_dir, device_dex_dir)], 161 device.PushChangedFiles([(temp_dir, device_dex_dir)],
153 delete_device_stale=True) 162 delete_device_stale=True)
154 push_dex_timer.Stop(log=False) 163 push_dex_timer.Stop(log=False)
155 164
165 def check_sdk_version():
166 if device.build_version_sdk >= version_codes.MARSHMALLOW:
167 if apk_help.HasIsolatedProcesses():
168 raise Exception('Cannot use perform incremental installs on Android M+ '
169 'without first disabling isolated processes.\n'
170 'To do so, use GN arg:\n'
171 ' disable_incremental_isolated_processes=true')
172
173 cache_path = '%s/files-cache.json' % device_incremental_dir
174 def restore_cache():
175 # Delete the cached file so that any exceptions cause the next attempt
176 # to re-compute md5s.
177 cmd = 'P=%s;cat $P 2>/dev/null && rm $P' % cache_path
178 lines = device.RunShellCommand(cmd, check_return=False, large_output=True)
179 if lines:
180 device.LoadCacheData(lines[0])
181 else:
182 logging.info('Device cache not found: %s', cache_path)
183
184 def save_cache():
185 cache_data = device.DumpCacheData()
186 device.WriteFile(cache_path, cache_data)
187
156 # Create 2 lock files: 188 # Create 2 lock files:
157 # * install.lock tells the app to pause on start-up (until we release it). 189 # * install.lock tells the app to pause on start-up (until we release it).
158 # * firstrun.lock is used by the app to pause all secondary processes until 190 # * firstrun.lock is used by the app to pause all secondary processes until
159 # the primary process finishes loading the .dex / .so files. 191 # the primary process finishes loading the .dex / .so files.
160 def create_lock_files(): 192 def create_lock_files():
161 # Creates or zeros out lock files. 193 # Creates or zeros out lock files.
162 cmd = ('D="%s";' 194 cmd = ('D="%s";'
163 'mkdir -p $D &&' 195 'mkdir -p $D &&'
164 'echo -n >$D/install.lock 2>$D/firstrun.lock') 196 'echo -n >$D/install.lock 2>$D/firstrun.lock')
165 device.RunShellCommand(cmd % device_incremental_dir, check_return=True) 197 device.RunShellCommand(cmd % device_incremental_dir, check_return=True)
166 198
167 # The firstrun.lock is released by the app itself. 199 # The firstrun.lock is released by the app itself.
168 def release_installer_lock(): 200 def release_installer_lock():
169 device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir, 201 device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir,
170 check_return=True) 202 check_return=True)
171 203
172 create_lock_files()
173 # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't 204 # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
174 # been designed for multi-threading. Enabling only because this is a 205 # been designed for multi-threading. Enabling only because this is a
175 # developer-only tool. 206 # developer-only tool.
176 if args.no_threading: 207 setup_timer = _Execute(
177 do_install() 208 args.threading, create_lock_files, restore_cache, check_sdk_version)
178 do_push_files() 209
179 else: 210 _Execute(args.threading, do_install, do_push_files)
180 reraiser_thread.RunAsync((do_install, do_push_files)) 211
181 release_installer_lock() 212 finalize_timer = _Execute(args.threading, release_installer_lock, save_cache)
182 logging.info('Took %s seconds (install=%s, libs=%s, dex=%s)', 213
183 main_timer.GetDelta(), install_timer.GetDelta(), 214 logging.info(
184 push_native_timer.GetDelta(), push_dex_timer.GetDelta()) 215 'Took %s seconds (setup=%s, install=%s, libs=%s, dex=%s, finalize=%s)',
216 main_timer.GetDelta(), setup_timer.GetDelta(), install_timer.GetDelta(),
217 push_native_timer.GetDelta(), push_dex_timer.GetDelta(),
218 finalize_timer.GetDelta())
185 219
186 220
187 if __name__ == '__main__': 221 if __name__ == '__main__':
188 sys.exit(main()) 222 sys.exit(main())
189 223
OLDNEW
« no previous file with comments | « build/android/devil/android/device_utils.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698