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

Side by Side Diff: build/android/devil/android/device_utils.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 | « no previous file | build/android/incremental_install/installer.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Provides a variety of device interactions based on adb. 5 """Provides a variety of device interactions based on adb.
6 6
7 Eventually, this will be based on adb_wrapper. 7 Eventually, this will be based on adb_wrapper.
8 """ 8 """
9 # pylint: disable=unused-argument 9 # pylint: disable=unused-argument
10 10
11 import collections 11 import collections
12 import itertools 12 import itertools
13 import json
13 import logging 14 import logging
14 import multiprocessing 15 import multiprocessing
15 import os 16 import os
16 import posixpath 17 import posixpath
17 import re 18 import re
18 import shutil 19 import shutil
19 import tempfile 20 import tempfile
20 import time 21 import time
21 import zipfile 22 import zipfile
22 23
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 _MAX_ADB_OUTPUT_LENGTH = 32768 157 _MAX_ADB_OUTPUT_LENGTH = 32768
157 _LAUNCHER_FOCUSED_RE = re.compile( 158 _LAUNCHER_FOCUSED_RE = re.compile(
158 r'\s*mCurrentFocus.*(Launcher|launcher).*') 159 r'\s*mCurrentFocus.*(Launcher|launcher).*')
159 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 160 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
160 161
161 LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop') 162 LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop')
162 163
163 # Property in /data/local.prop that controls Java assertions. 164 # Property in /data/local.prop that controls Java assertions.
164 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' 165 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
165 166
166 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, 167 def __init__(self, device, enable_device_files_cache=False,
168 default_timeout=_DEFAULT_TIMEOUT,
167 default_retries=_DEFAULT_RETRIES): 169 default_retries=_DEFAULT_RETRIES):
168 """DeviceUtils constructor. 170 """DeviceUtils constructor.
169 171
170 Args: 172 Args:
171 device: Either a device serial, an existing AdbWrapper instance, or an 173 device: Either a device serial, an existing AdbWrapper instance, or an
172 an existing AndroidCommands instance. 174 an existing AndroidCommands instance.
175 enable_device_files_cache: For PushChangedFiles(), cache checksums of
176 pushed files rather than recomputing them on a subsequent call.
173 default_timeout: An integer containing the default number of seconds to 177 default_timeout: An integer containing the default number of seconds to
174 wait for an operation to complete if no explicit value 178 wait for an operation to complete if no explicit value is provided.
175 is provided.
176 default_retries: An integer containing the default number or times an 179 default_retries: An integer containing the default number or times an
177 operation should be retried on failure if no explicit 180 operation should be retried on failure if no explicit value is provided.
178 value is provided.
179 """ 181 """
180 self.adb = None 182 self.adb = None
181 if isinstance(device, basestring): 183 if isinstance(device, basestring):
182 self.adb = adb_wrapper.AdbWrapper(device) 184 self.adb = adb_wrapper.AdbWrapper(device)
183 elif isinstance(device, adb_wrapper.AdbWrapper): 185 elif isinstance(device, adb_wrapper.AdbWrapper):
184 self.adb = device 186 self.adb = device
185 else: 187 else:
186 raise ValueError('Unsupported device value: %r' % device) 188 raise ValueError('Unsupported device value: %r' % device)
187 self._commands_installed = None 189 self._commands_installed = None
188 self._default_timeout = default_timeout 190 self._default_timeout = default_timeout
189 self._default_retries = default_retries 191 self._default_retries = default_retries
192 self._enable_device_files_cache = enable_device_files_cache
190 self._cache = {} 193 self._cache = {}
191 self._client_caches = {} 194 self._client_caches = {}
192 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) 195 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
193 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) 196 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
194 197
195 self._ClearCache() 198 self._ClearCache()
196 199
197 def __eq__(self, other): 200 def __eq__(self, other):
198 """Checks whether |other| refers to the same device as |self|. 201 """Checks whether |other| refers to the same device as |self|.
199 202
(...skipping 861 matching lines...) Expand 10 before | Expand all | Expand 10 after
1061 1064
1062 Raises: 1065 Raises:
1063 CommandFailedError on failure. 1066 CommandFailedError on failure.
1064 CommandTimeoutError on timeout. 1067 CommandTimeoutError on timeout.
1065 DeviceUnreachableError on missing device. 1068 DeviceUnreachableError on missing device.
1066 """ 1069 """
1067 1070
1068 all_changed_files = [] 1071 all_changed_files = []
1069 all_stale_files = [] 1072 all_stale_files = []
1070 missing_dirs = [] 1073 missing_dirs = []
1074 cache_commit_funcs = []
1071 for h, d in host_device_tuples: 1075 for h, d in host_device_tuples:
1072 changed_files, up_to_date_files, stale_files = ( 1076 changed_files, up_to_date_files, stale_files, cache_commit_func = (
1073 self._GetChangedAndStaleFiles(h, d, delete_device_stale)) 1077 self._GetChangedAndStaleFiles(h, d, delete_device_stale))
1074 all_changed_files += changed_files 1078 all_changed_files += changed_files
1075 all_stale_files += stale_files 1079 all_stale_files += stale_files
1080 cache_commit_funcs.append(cache_commit_func)
1076 if (os.path.isdir(h) and changed_files and not up_to_date_files 1081 if (os.path.isdir(h) and changed_files and not up_to_date_files
1077 and not stale_files): 1082 and not stale_files):
1078 missing_dirs.append(d) 1083 missing_dirs.append(d)
1079 1084
1080 if delete_device_stale and all_stale_files: 1085 if delete_device_stale and all_stale_files:
1081 self.RunShellCommand(['rm', '-f'] + all_stale_files, 1086 self.RunShellCommand(['rm', '-f'] + all_stale_files,
1082 check_return=True) 1087 check_return=True)
1083 1088
1084 if all_changed_files: 1089 if all_changed_files:
1085 if missing_dirs: 1090 if missing_dirs:
1086 self.RunShellCommand(['mkdir', '-p'] + missing_dirs, check_return=True) 1091 self.RunShellCommand(['mkdir', '-p'] + missing_dirs, check_return=True)
1087 self._PushFilesImpl(host_device_tuples, all_changed_files) 1092 self._PushFilesImpl(host_device_tuples, all_changed_files)
1093 for func in cache_commit_funcs:
1094 func()
1088 1095
1089 def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False): 1096 def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False):
1090 """Get files to push and delete 1097 """Get files to push and delete
1091 1098
1092 Args: 1099 Args:
1093 host_path: an absolute path of a file or directory on the host 1100 host_path: an absolute path of a file or directory on the host
1094 device_path: an absolute path of a file or directory on the device 1101 device_path: an absolute path of a file or directory on the device
1095 track_stale: whether to bother looking for stale files (slower) 1102 track_stale: whether to bother looking for stale files (slower)
1096 1103
1097 Returns: 1104 Returns:
1098 a three-element tuple 1105 a three-element tuple
1099 1st element: a list of (host_files_path, device_files_path) tuples to push 1106 1st element: a list of (host_files_path, device_files_path) tuples to push
1100 2nd element: a list of host_files_path that are up-to-date 1107 2nd element: a list of host_files_path that are up-to-date
1101 3rd element: a list of stale files under device_path, or [] when 1108 3rd element: a list of stale files under device_path, or [] when
1102 track_stale == False 1109 track_stale == False
1103 """ 1110 """
1104 try: 1111 try:
1112 # Length calculations below assume no trailing /.
1113 host_path = host_path.rstrip('/')
1114 device_path = device_path.rstrip('/')
1115
1105 specific_device_paths = [device_path] 1116 specific_device_paths = [device_path]
1106 if not track_stale and os.path.isdir(host_path): 1117 ignore_other_files = not track_stale and os.path.isdir(host_path)
1118 if ignore_other_files:
1107 specific_device_paths = [] 1119 specific_device_paths = []
1108 for root, _, filenames in os.walk(host_path): 1120 for root, _, filenames in os.walk(host_path):
1109 relative_dir = root[len(host_path) + 1:] 1121 relative_dir = root[len(host_path) + 1:]
1110 specific_device_paths.extend( 1122 specific_device_paths.extend(
1111 posixpath.join(device_path, relative_dir, f) for f in filenames) 1123 posixpath.join(device_path, relative_dir, f) for f in filenames)
1112 1124
1125 def device_sums_helper():
1126 if self._enable_device_files_cache:
1127 cache_entry = self._cache['device_path_checksums'].get(device_path)
1128 if cache_entry and cache_entry[0] == ignore_other_files:
1129 return dict(cache_entry[1])
1130
1131 sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self)
1132
1133 if self._enable_device_files_cache:
1134 cache_entry = [ignore_other_files, sums]
1135 self._cache['device_path_checksums'][device_path] = cache_entry
1136 return dict(sums)
1137
1113 host_checksums, device_checksums = reraiser_thread.RunAsync(( 1138 host_checksums, device_checksums = reraiser_thread.RunAsync((
1114 lambda: md5sum.CalculateHostMd5Sums([host_path]), 1139 lambda: md5sum.CalculateHostMd5Sums([host_path]),
1115 lambda: md5sum.CalculateDeviceMd5Sums(specific_device_paths, self))) 1140 device_sums_helper))
1116 except EnvironmentError as e: 1141 except EnvironmentError as e:
1117 logging.warning('Error calculating md5: %s', e) 1142 logging.warning('Error calculating md5: %s', e)
1118 return ([(host_path, device_path)], [], []) 1143 return ([(host_path, device_path)], [], [], lambda: 0)
1119 1144
1120 to_push = [] 1145 to_push = []
1121 up_to_date = [] 1146 up_to_date = []
1122 to_delete = [] 1147 to_delete = []
1123 if os.path.isfile(host_path): 1148 if os.path.isfile(host_path):
1124 host_checksum = host_checksums.get(host_path) 1149 host_checksum = host_checksums.get(host_path)
1125 device_checksum = device_checksums.get(device_path) 1150 device_checksum = device_checksums.get(device_path)
1126 if host_checksum == device_checksum: 1151 if host_checksum == device_checksum:
1127 up_to_date.append(host_path) 1152 up_to_date.append(host_path)
1128 else: 1153 else:
1129 to_push.append((host_path, device_path)) 1154 to_push.append((host_path, device_path))
1130 else: 1155 else:
1131 for host_abs_path, host_checksum in host_checksums.iteritems(): 1156 for host_abs_path, host_checksum in host_checksums.iteritems():
1132 device_abs_path = posixpath.join( 1157 device_abs_path = posixpath.join(
1133 device_path, os.path.relpath(host_abs_path, host_path)) 1158 device_path, os.path.relpath(host_abs_path, host_path))
1134 device_checksum = device_checksums.pop(device_abs_path, None) 1159 device_checksum = device_checksums.pop(device_abs_path, None)
1135 if device_checksum == host_checksum: 1160 if device_checksum == host_checksum:
1136 up_to_date.append(host_abs_path) 1161 up_to_date.append(host_abs_path)
1137 else: 1162 else:
1138 to_push.append((host_abs_path, device_abs_path)) 1163 to_push.append((host_abs_path, device_abs_path))
1139 to_delete = device_checksums.keys() 1164 to_delete = device_checksums.keys()
1140 return (to_push, up_to_date, to_delete) 1165
1166 def cache_commit_func():
1167 if not self._enable_device_files_cache:
1168 return
1169 new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val
1170 for path, val in host_checksums.iteritems()}
1171 cache_entry = [ignore_other_files, new_sums]
1172 self._cache['device_path_checksums'][device_path] = cache_entry
1173
1174 return (to_push, up_to_date, to_delete, cache_commit_func)
1141 1175
1142 def _ComputeDeviceChecksumsForApks(self, package_name): 1176 def _ComputeDeviceChecksumsForApks(self, package_name):
1143 ret = self._cache['package_apk_checksums'].get(package_name) 1177 ret = self._cache['package_apk_checksums'].get(package_name)
1144 if ret is None: 1178 if ret is None:
1145 device_paths = self._GetApplicationPathsInternal(package_name) 1179 device_paths = self._GetApplicationPathsInternal(package_name)
1146 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self) 1180 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1147 ret = set(file_to_checksums.values()) 1181 ret = set(file_to_checksums.values())
1148 self._cache['package_apk_checksums'][package_name] = ret 1182 self._cache['package_apk_checksums'][package_name] = ret
1149 return ret 1183 return ret
1150 1184
(...skipping 762 matching lines...) Expand 10 before | Expand all | Expand 10 after
1913 """Clears all caches.""" 1947 """Clears all caches."""
1914 for client in self._client_caches: 1948 for client in self._client_caches:
1915 self._client_caches[client].clear() 1949 self._client_caches[client].clear()
1916 self._cache = { 1950 self._cache = {
1917 # Map of packageId -> list of on-device .apk paths 1951 # Map of packageId -> list of on-device .apk paths
1918 'package_apk_paths': {}, 1952 'package_apk_paths': {},
1919 # Map of packageId -> set of on-device .apk checksums 1953 # Map of packageId -> set of on-device .apk checksums
1920 'package_apk_checksums': {}, 1954 'package_apk_checksums': {},
1921 # Map of property_name -> value 1955 # Map of property_name -> value
1922 'getprop': {}, 1956 'getprop': {},
1957 # Map of device_path -> [ignore_other_files, map of path->checksum]
1958 'device_path_checksums': {},
1923 } 1959 }
1924 1960
1961 def LoadCacheData(self, data):
1962 """Initializes the cache from data created using DumpCacheData."""
1963 obj = json.loads(data)
1964 self._cache['package_apk_paths'] = obj.get('package_apk_paths', {})
1965 package_apk_checksums = obj.get('package_apk_checksums', {})
1966 for k, v in package_apk_checksums.iteritems():
1967 package_apk_checksums[k] = set(v)
1968 self._cache['package_apk_checksums'] = package_apk_checksums
1969 device_path_checksums = obj.get('device_path_checksums', {})
1970 self._cache['device_path_checksums'] = device_path_checksums
1971
1972 def DumpCacheData(self):
1973 """Dumps the current cache state to a string."""
1974 obj = {}
1975 obj['package_apk_paths'] = self._cache['package_apk_paths']
1976 obj['package_apk_checksums'] = self._cache['package_apk_checksums']
1977 # JSON can't handle sets.
1978 for k, v in obj['package_apk_checksums'].iteritems():
1979 obj['package_apk_checksums'][k] = list(v)
1980 obj['device_path_checksums'] = self._cache['device_path_checksums']
1981 return json.dumps(obj, separators=(',', ':'))
1982
1925 @classmethod 1983 @classmethod
1926 def parallel(cls, devices, async=False): 1984 def parallel(cls, devices, async=False):
1927 """Creates a Parallelizer to operate over the provided list of devices. 1985 """Creates a Parallelizer to operate over the provided list of devices.
1928 1986
1929 If |devices| is either |None| or an empty list, the Parallelizer will 1987 If |devices| is either |None| or an empty list, the Parallelizer will
1930 operate over all attached devices that have not been blacklisted. 1988 operate over all attached devices that have not been blacklisted.
1931 1989
1932 Args: 1990 Args:
1933 devices: A list of either DeviceUtils instances or objects from 1991 devices: A list of either DeviceUtils instances or objects from
1934 from which DeviceUtils instances can be constructed. If None, 1992 from which DeviceUtils instances can be constructed. If None,
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1982 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions 2040 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions
1983 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): 2041 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions):
1984 permissions.append('android.permission.READ_EXTERNAL_STORAGE') 2042 permissions.append('android.permission.READ_EXTERNAL_STORAGE')
1985 cmd = ';'.join('pm grant %s %s' %(package, p) for p in permissions) 2043 cmd = ';'.join('pm grant %s %s' %(package, p) for p in permissions)
1986 if cmd: 2044 if cmd:
1987 output = self.RunShellCommand(cmd) 2045 output = self.RunShellCommand(cmd)
1988 if output: 2046 if output:
1989 logging.warning('Possible problem when granting permissions. Blacklist ' 2047 logging.warning('Possible problem when granting permissions. Blacklist '
1990 'may need to be updated.') 2048 'may need to be updated.')
1991 logging.warning(output) 2049 logging.warning(output)
OLDNEW
« no previous file with comments | « no previous file | build/android/incremental_install/installer.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698