1*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2014 The Android Open Source Project 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*9e94795aSAndroid Build Coastguard Worker# 7*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 14*9e94795aSAndroid Build Coastguard Worker 15*9e94795aSAndroid Build Coastguard Workerfrom __future__ import print_function 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerimport array 18*9e94795aSAndroid Build Coastguard Workerimport copy 19*9e94795aSAndroid Build Coastguard Workerimport functools 20*9e94795aSAndroid Build Coastguard Workerimport heapq 21*9e94795aSAndroid Build Coastguard Workerimport itertools 22*9e94795aSAndroid Build Coastguard Workerimport logging 23*9e94795aSAndroid Build Coastguard Workerimport multiprocessing 24*9e94795aSAndroid Build Coastguard Workerimport os 25*9e94795aSAndroid Build Coastguard Workerimport os.path 26*9e94795aSAndroid Build Coastguard Workerimport re 27*9e94795aSAndroid Build Coastguard Workerimport sys 28*9e94795aSAndroid Build Coastguard Workerimport threading 29*9e94795aSAndroid Build Coastguard Workerimport zlib 30*9e94795aSAndroid Build Coastguard Workerfrom collections import deque, namedtuple, OrderedDict 31*9e94795aSAndroid Build Coastguard Worker 32*9e94795aSAndroid Build Coastguard Workerimport common 33*9e94795aSAndroid Build Coastguard Workerfrom images import EmptyImage 34*9e94795aSAndroid Build Coastguard Workerfrom rangelib import RangeSet 35*9e94795aSAndroid Build Coastguard Worker 36*9e94795aSAndroid Build Coastguard Worker__all__ = ["BlockImageDiff"] 37*9e94795aSAndroid Build Coastguard Worker 38*9e94795aSAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 39*9e94795aSAndroid Build Coastguard Worker 40*9e94795aSAndroid Build Coastguard Worker# The tuple contains the style and bytes of a bsdiff|imgdiff patch. 41*9e94795aSAndroid Build Coastguard WorkerPatchInfo = namedtuple("PatchInfo", ["imgdiff", "content"]) 42*9e94795aSAndroid Build Coastguard Worker 43*9e94795aSAndroid Build Coastguard Worker 44*9e94795aSAndroid Build Coastguard Workerdef compute_patch(srcfile, tgtfile, imgdiff=False): 45*9e94795aSAndroid Build Coastguard Worker """Calls bsdiff|imgdiff to compute the patch data, returns a PatchInfo.""" 46*9e94795aSAndroid Build Coastguard Worker patchfile = common.MakeTempFile(prefix='patch-') 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard Worker cmd = ['imgdiff', '-z'] if imgdiff else ['bsdiff'] 49*9e94795aSAndroid Build Coastguard Worker cmd.extend([srcfile, tgtfile, patchfile]) 50*9e94795aSAndroid Build Coastguard Worker 51*9e94795aSAndroid Build Coastguard Worker # Don't dump the bsdiff/imgdiff commands, which are not useful for the case 52*9e94795aSAndroid Build Coastguard Worker # here, since they contain temp filenames only. 53*9e94795aSAndroid Build Coastguard Worker proc = common.Run(cmd, verbose=False) 54*9e94795aSAndroid Build Coastguard Worker output, _ = proc.communicate() 55*9e94795aSAndroid Build Coastguard Worker 56*9e94795aSAndroid Build Coastguard Worker if proc.returncode != 0: 57*9e94795aSAndroid Build Coastguard Worker raise ValueError(output) 58*9e94795aSAndroid Build Coastguard Worker 59*9e94795aSAndroid Build Coastguard Worker with open(patchfile, 'rb') as f: 60*9e94795aSAndroid Build Coastguard Worker return PatchInfo(imgdiff, f.read()) 61*9e94795aSAndroid Build Coastguard Worker 62*9e94795aSAndroid Build Coastguard Worker 63*9e94795aSAndroid Build Coastguard Workerclass Transfer(object): 64*9e94795aSAndroid Build Coastguard Worker def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, tgt_sha1, 65*9e94795aSAndroid Build Coastguard Worker src_sha1, style, by_id): 66*9e94795aSAndroid Build Coastguard Worker self.tgt_name = tgt_name 67*9e94795aSAndroid Build Coastguard Worker self.src_name = src_name 68*9e94795aSAndroid Build Coastguard Worker self.tgt_ranges = tgt_ranges 69*9e94795aSAndroid Build Coastguard Worker self.src_ranges = src_ranges 70*9e94795aSAndroid Build Coastguard Worker self.tgt_sha1 = tgt_sha1 71*9e94795aSAndroid Build Coastguard Worker self.src_sha1 = src_sha1 72*9e94795aSAndroid Build Coastguard Worker self.style = style 73*9e94795aSAndroid Build Coastguard Worker 74*9e94795aSAndroid Build Coastguard Worker # We use OrderedDict rather than dict so that the output is repeatable; 75*9e94795aSAndroid Build Coastguard Worker # otherwise it would depend on the hash values of the Transfer objects. 76*9e94795aSAndroid Build Coastguard Worker self.goes_before = OrderedDict() 77*9e94795aSAndroid Build Coastguard Worker self.goes_after = OrderedDict() 78*9e94795aSAndroid Build Coastguard Worker 79*9e94795aSAndroid Build Coastguard Worker self.stash_before = [] 80*9e94795aSAndroid Build Coastguard Worker self.use_stash = [] 81*9e94795aSAndroid Build Coastguard Worker 82*9e94795aSAndroid Build Coastguard Worker self.id = len(by_id) 83*9e94795aSAndroid Build Coastguard Worker by_id.append(self) 84*9e94795aSAndroid Build Coastguard Worker 85*9e94795aSAndroid Build Coastguard Worker self._patch_info = None 86*9e94795aSAndroid Build Coastguard Worker 87*9e94795aSAndroid Build Coastguard Worker @property 88*9e94795aSAndroid Build Coastguard Worker def patch_info(self): 89*9e94795aSAndroid Build Coastguard Worker return self._patch_info 90*9e94795aSAndroid Build Coastguard Worker 91*9e94795aSAndroid Build Coastguard Worker @patch_info.setter 92*9e94795aSAndroid Build Coastguard Worker def patch_info(self, info): 93*9e94795aSAndroid Build Coastguard Worker if info: 94*9e94795aSAndroid Build Coastguard Worker assert self.style == "diff" 95*9e94795aSAndroid Build Coastguard Worker self._patch_info = info 96*9e94795aSAndroid Build Coastguard Worker 97*9e94795aSAndroid Build Coastguard Worker def NetStashChange(self): 98*9e94795aSAndroid Build Coastguard Worker return (sum(sr.size() for (_, sr) in self.stash_before) - 99*9e94795aSAndroid Build Coastguard Worker sum(sr.size() for (_, sr) in self.use_stash)) 100*9e94795aSAndroid Build Coastguard Worker 101*9e94795aSAndroid Build Coastguard Worker def ConvertToNew(self): 102*9e94795aSAndroid Build Coastguard Worker assert self.style != "new" 103*9e94795aSAndroid Build Coastguard Worker self.use_stash = [] 104*9e94795aSAndroid Build Coastguard Worker self.style = "new" 105*9e94795aSAndroid Build Coastguard Worker self.src_ranges = RangeSet() 106*9e94795aSAndroid Build Coastguard Worker self.patch_info = None 107*9e94795aSAndroid Build Coastguard Worker 108*9e94795aSAndroid Build Coastguard Worker def __str__(self): 109*9e94795aSAndroid Build Coastguard Worker return (str(self.id) + ": <" + str(self.src_ranges) + " " + self.style + 110*9e94795aSAndroid Build Coastguard Worker " to " + str(self.tgt_ranges) + ">") 111*9e94795aSAndroid Build Coastguard Worker 112*9e94795aSAndroid Build Coastguard Worker 113*9e94795aSAndroid Build Coastguard Worker@functools.total_ordering 114*9e94795aSAndroid Build Coastguard Workerclass HeapItem(object): 115*9e94795aSAndroid Build Coastguard Worker def __init__(self, item): 116*9e94795aSAndroid Build Coastguard Worker self.item = item 117*9e94795aSAndroid Build Coastguard Worker # Negate the score since python's heap is a min-heap and we want the 118*9e94795aSAndroid Build Coastguard Worker # maximum score. 119*9e94795aSAndroid Build Coastguard Worker self.score = -item.score 120*9e94795aSAndroid Build Coastguard Worker 121*9e94795aSAndroid Build Coastguard Worker def clear(self): 122*9e94795aSAndroid Build Coastguard Worker self.item = None 123*9e94795aSAndroid Build Coastguard Worker 124*9e94795aSAndroid Build Coastguard Worker def __bool__(self): 125*9e94795aSAndroid Build Coastguard Worker return self.item is not None 126*9e94795aSAndroid Build Coastguard Worker 127*9e94795aSAndroid Build Coastguard Worker # Python 2 uses __nonzero__, while Python 3 uses __bool__. 128*9e94795aSAndroid Build Coastguard Worker __nonzero__ = __bool__ 129*9e94795aSAndroid Build Coastguard Worker 130*9e94795aSAndroid Build Coastguard Worker # The rest operations are generated by functools.total_ordering decorator. 131*9e94795aSAndroid Build Coastguard Worker def __eq__(self, other): 132*9e94795aSAndroid Build Coastguard Worker return self.score == other.score 133*9e94795aSAndroid Build Coastguard Worker 134*9e94795aSAndroid Build Coastguard Worker def __le__(self, other): 135*9e94795aSAndroid Build Coastguard Worker return self.score <= other.score 136*9e94795aSAndroid Build Coastguard Worker 137*9e94795aSAndroid Build Coastguard Worker 138*9e94795aSAndroid Build Coastguard Workerclass ImgdiffStats(object): 139*9e94795aSAndroid Build Coastguard Worker """A class that collects imgdiff stats. 140*9e94795aSAndroid Build Coastguard Worker 141*9e94795aSAndroid Build Coastguard Worker It keeps track of the files that will be applied imgdiff while generating 142*9e94795aSAndroid Build Coastguard Worker BlockImageDiff. It also logs the ones that cannot use imgdiff, with specific 143*9e94795aSAndroid Build Coastguard Worker reasons. The stats is only meaningful when imgdiff not being disabled by the 144*9e94795aSAndroid Build Coastguard Worker caller of BlockImageDiff. In addition, only files with supported types 145*9e94795aSAndroid Build Coastguard Worker (BlockImageDiff.FileTypeSupportedByImgdiff()) are allowed to be logged. 146*9e94795aSAndroid Build Coastguard Worker """ 147*9e94795aSAndroid Build Coastguard Worker 148*9e94795aSAndroid Build Coastguard Worker USED_IMGDIFF = "APK files diff'd with imgdiff" 149*9e94795aSAndroid Build Coastguard Worker USED_IMGDIFF_LARGE_APK = "Large APK files split and diff'd with imgdiff" 150*9e94795aSAndroid Build Coastguard Worker 151*9e94795aSAndroid Build Coastguard Worker # Reasons for not applying imgdiff on APKs. 152*9e94795aSAndroid Build Coastguard Worker SKIPPED_NONMONOTONIC = "Not used imgdiff due to having non-monotonic ranges" 153*9e94795aSAndroid Build Coastguard Worker SKIPPED_SHARED_BLOCKS = "Not used imgdiff due to using shared blocks" 154*9e94795aSAndroid Build Coastguard Worker SKIPPED_INCOMPLETE = "Not used imgdiff due to incomplete RangeSet" 155*9e94795aSAndroid Build Coastguard Worker 156*9e94795aSAndroid Build Coastguard Worker # The list of valid reasons, which will also be the dumped order in a report. 157*9e94795aSAndroid Build Coastguard Worker REASONS = ( 158*9e94795aSAndroid Build Coastguard Worker USED_IMGDIFF, 159*9e94795aSAndroid Build Coastguard Worker USED_IMGDIFF_LARGE_APK, 160*9e94795aSAndroid Build Coastguard Worker SKIPPED_NONMONOTONIC, 161*9e94795aSAndroid Build Coastguard Worker SKIPPED_SHARED_BLOCKS, 162*9e94795aSAndroid Build Coastguard Worker SKIPPED_INCOMPLETE, 163*9e94795aSAndroid Build Coastguard Worker ) 164*9e94795aSAndroid Build Coastguard Worker 165*9e94795aSAndroid Build Coastguard Worker def __init__(self): 166*9e94795aSAndroid Build Coastguard Worker self.stats = {} 167*9e94795aSAndroid Build Coastguard Worker 168*9e94795aSAndroid Build Coastguard Worker def Log(self, filename, reason): 169*9e94795aSAndroid Build Coastguard Worker """Logs why imgdiff can or cannot be applied to the given filename. 170*9e94795aSAndroid Build Coastguard Worker 171*9e94795aSAndroid Build Coastguard Worker Args: 172*9e94795aSAndroid Build Coastguard Worker filename: The filename string. 173*9e94795aSAndroid Build Coastguard Worker reason: One of the reason constants listed in REASONS. 174*9e94795aSAndroid Build Coastguard Worker 175*9e94795aSAndroid Build Coastguard Worker Raises: 176*9e94795aSAndroid Build Coastguard Worker AssertionError: On unsupported filetypes or invalid reason. 177*9e94795aSAndroid Build Coastguard Worker """ 178*9e94795aSAndroid Build Coastguard Worker assert BlockImageDiff.FileTypeSupportedByImgdiff(filename) 179*9e94795aSAndroid Build Coastguard Worker assert reason in self.REASONS 180*9e94795aSAndroid Build Coastguard Worker 181*9e94795aSAndroid Build Coastguard Worker if reason not in self.stats: 182*9e94795aSAndroid Build Coastguard Worker self.stats[reason] = set() 183*9e94795aSAndroid Build Coastguard Worker self.stats[reason].add(filename) 184*9e94795aSAndroid Build Coastguard Worker 185*9e94795aSAndroid Build Coastguard Worker def Report(self): 186*9e94795aSAndroid Build Coastguard Worker """Prints a report of the collected imgdiff stats.""" 187*9e94795aSAndroid Build Coastguard Worker 188*9e94795aSAndroid Build Coastguard Worker def print_header(header, separator): 189*9e94795aSAndroid Build Coastguard Worker logger.info(header) 190*9e94795aSAndroid Build Coastguard Worker logger.info('%s\n', separator * len(header)) 191*9e94795aSAndroid Build Coastguard Worker 192*9e94795aSAndroid Build Coastguard Worker print_header(' Imgdiff Stats Report ', '=') 193*9e94795aSAndroid Build Coastguard Worker for key in self.REASONS: 194*9e94795aSAndroid Build Coastguard Worker if key not in self.stats: 195*9e94795aSAndroid Build Coastguard Worker continue 196*9e94795aSAndroid Build Coastguard Worker values = self.stats[key] 197*9e94795aSAndroid Build Coastguard Worker section_header = ' {} (count: {}) '.format(key, len(values)) 198*9e94795aSAndroid Build Coastguard Worker print_header(section_header, '-') 199*9e94795aSAndroid Build Coastguard Worker logger.info(''.join([' {}\n'.format(name) for name in values])) 200*9e94795aSAndroid Build Coastguard Worker 201*9e94795aSAndroid Build Coastguard Worker 202*9e94795aSAndroid Build Coastguard Workerclass BlockImageDiff(object): 203*9e94795aSAndroid Build Coastguard Worker """Generates the diff of two block image objects. 204*9e94795aSAndroid Build Coastguard Worker 205*9e94795aSAndroid Build Coastguard Worker BlockImageDiff works on two image objects. An image object is anything that 206*9e94795aSAndroid Build Coastguard Worker provides the following attributes: 207*9e94795aSAndroid Build Coastguard Worker 208*9e94795aSAndroid Build Coastguard Worker blocksize: the size in bytes of a block, currently must be 4096. 209*9e94795aSAndroid Build Coastguard Worker 210*9e94795aSAndroid Build Coastguard Worker total_blocks: the total size of the partition/image, in blocks. 211*9e94795aSAndroid Build Coastguard Worker 212*9e94795aSAndroid Build Coastguard Worker care_map: a RangeSet containing which blocks (in the range [0, 213*9e94795aSAndroid Build Coastguard Worker total_blocks) we actually care about; i.e. which blocks contain data. 214*9e94795aSAndroid Build Coastguard Worker 215*9e94795aSAndroid Build Coastguard Worker file_map: a dict that partitions the blocks contained in care_map into 216*9e94795aSAndroid Build Coastguard Worker smaller domains that are useful for doing diffs on. (Typically a domain 217*9e94795aSAndroid Build Coastguard Worker is a file, and the key in file_map is the pathname.) 218*9e94795aSAndroid Build Coastguard Worker 219*9e94795aSAndroid Build Coastguard Worker clobbered_blocks: a RangeSet containing which blocks contain data but may 220*9e94795aSAndroid Build Coastguard Worker be altered by the FS. They need to be excluded when verifying the 221*9e94795aSAndroid Build Coastguard Worker partition integrity. 222*9e94795aSAndroid Build Coastguard Worker 223*9e94795aSAndroid Build Coastguard Worker ReadRangeSet(): a function that takes a RangeSet and returns the data 224*9e94795aSAndroid Build Coastguard Worker contained in the image blocks of that RangeSet. The data is returned as 225*9e94795aSAndroid Build Coastguard Worker a list or tuple of strings; concatenating the elements together should 226*9e94795aSAndroid Build Coastguard Worker produce the requested data. Implementations are free to break up the 227*9e94795aSAndroid Build Coastguard Worker data into list/tuple elements in any way that is convenient. 228*9e94795aSAndroid Build Coastguard Worker 229*9e94795aSAndroid Build Coastguard Worker RangeSha1(): a function that returns (as a hex string) the SHA-1 hash of 230*9e94795aSAndroid Build Coastguard Worker all the data in the specified range. 231*9e94795aSAndroid Build Coastguard Worker 232*9e94795aSAndroid Build Coastguard Worker TotalSha1(): a function that returns (as a hex string) the SHA-1 hash of 233*9e94795aSAndroid Build Coastguard Worker all the data in the image (ie, all the blocks in the care_map minus 234*9e94795aSAndroid Build Coastguard Worker clobbered_blocks, or including the clobbered blocks if 235*9e94795aSAndroid Build Coastguard Worker include_clobbered_blocks is True). 236*9e94795aSAndroid Build Coastguard Worker 237*9e94795aSAndroid Build Coastguard Worker When creating a BlockImageDiff, the src image may be None, in which case the 238*9e94795aSAndroid Build Coastguard Worker list of transfers produced will never read from the original image. 239*9e94795aSAndroid Build Coastguard Worker """ 240*9e94795aSAndroid Build Coastguard Worker 241*9e94795aSAndroid Build Coastguard Worker def __init__(self, tgt, src=None, threads=None, version=4, 242*9e94795aSAndroid Build Coastguard Worker disable_imgdiff=False): 243*9e94795aSAndroid Build Coastguard Worker if threads is None: 244*9e94795aSAndroid Build Coastguard Worker threads = multiprocessing.cpu_count() // 2 245*9e94795aSAndroid Build Coastguard Worker if threads == 0: 246*9e94795aSAndroid Build Coastguard Worker threads = 1 247*9e94795aSAndroid Build Coastguard Worker self.threads = threads 248*9e94795aSAndroid Build Coastguard Worker self.version = version 249*9e94795aSAndroid Build Coastguard Worker self.transfers = [] 250*9e94795aSAndroid Build Coastguard Worker self.src_basenames = {} 251*9e94795aSAndroid Build Coastguard Worker self.src_numpatterns = {} 252*9e94795aSAndroid Build Coastguard Worker self._max_stashed_size = 0 253*9e94795aSAndroid Build Coastguard Worker self.touched_src_ranges = RangeSet() 254*9e94795aSAndroid Build Coastguard Worker self.touched_src_sha1 = None 255*9e94795aSAndroid Build Coastguard Worker self.disable_imgdiff = disable_imgdiff 256*9e94795aSAndroid Build Coastguard Worker self.imgdiff_stats = ImgdiffStats() if not disable_imgdiff else None 257*9e94795aSAndroid Build Coastguard Worker 258*9e94795aSAndroid Build Coastguard Worker assert version in (3, 4) 259*9e94795aSAndroid Build Coastguard Worker 260*9e94795aSAndroid Build Coastguard Worker self.tgt = tgt 261*9e94795aSAndroid Build Coastguard Worker if src is None: 262*9e94795aSAndroid Build Coastguard Worker src = EmptyImage() 263*9e94795aSAndroid Build Coastguard Worker self.src = src 264*9e94795aSAndroid Build Coastguard Worker 265*9e94795aSAndroid Build Coastguard Worker # The updater code that installs the patch always uses 4k blocks. 266*9e94795aSAndroid Build Coastguard Worker assert tgt.blocksize == 4096 267*9e94795aSAndroid Build Coastguard Worker assert src.blocksize == 4096 268*9e94795aSAndroid Build Coastguard Worker 269*9e94795aSAndroid Build Coastguard Worker # The range sets in each filemap should comprise a partition of 270*9e94795aSAndroid Build Coastguard Worker # the care map. 271*9e94795aSAndroid Build Coastguard Worker self.AssertPartition(src.care_map, src.file_map.values()) 272*9e94795aSAndroid Build Coastguard Worker self.AssertPartition(tgt.care_map, tgt.file_map.values()) 273*9e94795aSAndroid Build Coastguard Worker 274*9e94795aSAndroid Build Coastguard Worker @property 275*9e94795aSAndroid Build Coastguard Worker def max_stashed_size(self): 276*9e94795aSAndroid Build Coastguard Worker return self._max_stashed_size 277*9e94795aSAndroid Build Coastguard Worker 278*9e94795aSAndroid Build Coastguard Worker @staticmethod 279*9e94795aSAndroid Build Coastguard Worker def FileTypeSupportedByImgdiff(filename): 280*9e94795aSAndroid Build Coastguard Worker """Returns whether the file type is supported by imgdiff.""" 281*9e94795aSAndroid Build Coastguard Worker return filename.lower().endswith(('.apk', '.jar', '.zip')) 282*9e94795aSAndroid Build Coastguard Worker 283*9e94795aSAndroid Build Coastguard Worker def CanUseImgdiff(self, name, tgt_ranges, src_ranges, large_apk=False): 284*9e94795aSAndroid Build Coastguard Worker """Checks whether we can apply imgdiff for the given RangeSets. 285*9e94795aSAndroid Build Coastguard Worker 286*9e94795aSAndroid Build Coastguard Worker For files in ZIP format (e.g., APKs, JARs, etc.) we would like to use 287*9e94795aSAndroid Build Coastguard Worker 'imgdiff -z' if possible. Because it usually produces significantly smaller 288*9e94795aSAndroid Build Coastguard Worker patches than bsdiff. 289*9e94795aSAndroid Build Coastguard Worker 290*9e94795aSAndroid Build Coastguard Worker This is permissible if all of the following conditions hold. 291*9e94795aSAndroid Build Coastguard Worker - The imgdiff hasn't been disabled by the caller (e.g. squashfs); 292*9e94795aSAndroid Build Coastguard Worker - The file type is supported by imgdiff; 293*9e94795aSAndroid Build Coastguard Worker - The source and target blocks are monotonic (i.e. the data is stored with 294*9e94795aSAndroid Build Coastguard Worker blocks in increasing order); 295*9e94795aSAndroid Build Coastguard Worker - Both files don't contain shared blocks; 296*9e94795aSAndroid Build Coastguard Worker - Both files have complete lists of blocks; 297*9e94795aSAndroid Build Coastguard Worker - We haven't removed any blocks from the source set. 298*9e94795aSAndroid Build Coastguard Worker 299*9e94795aSAndroid Build Coastguard Worker If all these conditions are satisfied, concatenating all the blocks in the 300*9e94795aSAndroid Build Coastguard Worker RangeSet in order will produce a valid ZIP file (plus possibly extra zeros 301*9e94795aSAndroid Build Coastguard Worker in the last block). imgdiff is fine with extra zeros at the end of the file. 302*9e94795aSAndroid Build Coastguard Worker 303*9e94795aSAndroid Build Coastguard Worker Args: 304*9e94795aSAndroid Build Coastguard Worker name: The filename to be diff'd. 305*9e94795aSAndroid Build Coastguard Worker tgt_ranges: The target RangeSet. 306*9e94795aSAndroid Build Coastguard Worker src_ranges: The source RangeSet. 307*9e94795aSAndroid Build Coastguard Worker large_apk: Whether this is to split a large APK. 308*9e94795aSAndroid Build Coastguard Worker 309*9e94795aSAndroid Build Coastguard Worker Returns: 310*9e94795aSAndroid Build Coastguard Worker A boolean result. 311*9e94795aSAndroid Build Coastguard Worker """ 312*9e94795aSAndroid Build Coastguard Worker if self.disable_imgdiff or not self.FileTypeSupportedByImgdiff(name): 313*9e94795aSAndroid Build Coastguard Worker return False 314*9e94795aSAndroid Build Coastguard Worker 315*9e94795aSAndroid Build Coastguard Worker if not tgt_ranges.monotonic or not src_ranges.monotonic: 316*9e94795aSAndroid Build Coastguard Worker self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_NONMONOTONIC) 317*9e94795aSAndroid Build Coastguard Worker return False 318*9e94795aSAndroid Build Coastguard Worker 319*9e94795aSAndroid Build Coastguard Worker if (tgt_ranges.extra.get('uses_shared_blocks') or 320*9e94795aSAndroid Build Coastguard Worker src_ranges.extra.get('uses_shared_blocks')): 321*9e94795aSAndroid Build Coastguard Worker self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_SHARED_BLOCKS) 322*9e94795aSAndroid Build Coastguard Worker return False 323*9e94795aSAndroid Build Coastguard Worker 324*9e94795aSAndroid Build Coastguard Worker if tgt_ranges.extra.get('incomplete') or src_ranges.extra.get('incomplete'): 325*9e94795aSAndroid Build Coastguard Worker self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_INCOMPLETE) 326*9e94795aSAndroid Build Coastguard Worker return False 327*9e94795aSAndroid Build Coastguard Worker 328*9e94795aSAndroid Build Coastguard Worker reason = (ImgdiffStats.USED_IMGDIFF_LARGE_APK if large_apk 329*9e94795aSAndroid Build Coastguard Worker else ImgdiffStats.USED_IMGDIFF) 330*9e94795aSAndroid Build Coastguard Worker self.imgdiff_stats.Log(name, reason) 331*9e94795aSAndroid Build Coastguard Worker return True 332*9e94795aSAndroid Build Coastguard Worker 333*9e94795aSAndroid Build Coastguard Worker def Compute(self, prefix): 334*9e94795aSAndroid Build Coastguard Worker # When looking for a source file to use as the diff input for a 335*9e94795aSAndroid Build Coastguard Worker # target file, we try: 336*9e94795aSAndroid Build Coastguard Worker # 1) an exact path match if available, otherwise 337*9e94795aSAndroid Build Coastguard Worker # 2) a exact basename match if available, otherwise 338*9e94795aSAndroid Build Coastguard Worker # 3) a basename match after all runs of digits are replaced by 339*9e94795aSAndroid Build Coastguard Worker # "#" if available, otherwise 340*9e94795aSAndroid Build Coastguard Worker # 4) we have no source for this target. 341*9e94795aSAndroid Build Coastguard Worker self.AbbreviateSourceNames() 342*9e94795aSAndroid Build Coastguard Worker self.FindTransfers() 343*9e94795aSAndroid Build Coastguard Worker 344*9e94795aSAndroid Build Coastguard Worker self.FindSequenceForTransfers() 345*9e94795aSAndroid Build Coastguard Worker 346*9e94795aSAndroid Build Coastguard Worker # Ensure the runtime stash size is under the limit. 347*9e94795aSAndroid Build Coastguard Worker if common.OPTIONS.cache_size is not None: 348*9e94795aSAndroid Build Coastguard Worker stash_limit = (common.OPTIONS.cache_size * 349*9e94795aSAndroid Build Coastguard Worker common.OPTIONS.stash_threshold / self.tgt.blocksize) 350*9e94795aSAndroid Build Coastguard Worker # Ignore the stash limit and calculate the maximum simultaneously stashed 351*9e94795aSAndroid Build Coastguard Worker # blocks needed. 352*9e94795aSAndroid Build Coastguard Worker _, max_stashed_blocks = self.ReviseStashSize(ignore_stash_limit=True) 353*9e94795aSAndroid Build Coastguard Worker 354*9e94795aSAndroid Build Coastguard Worker # We cannot stash more blocks than the stash limit simultaneously. As a 355*9e94795aSAndroid Build Coastguard Worker # result, some 'diff' commands will be converted to new; leading to an 356*9e94795aSAndroid Build Coastguard Worker # unintended large package. To mitigate this issue, we can carefully 357*9e94795aSAndroid Build Coastguard Worker # choose the transfers for conversion. The number '1024' can be further 358*9e94795aSAndroid Build Coastguard Worker # tweaked here to balance the package size and build time. 359*9e94795aSAndroid Build Coastguard Worker if max_stashed_blocks > stash_limit + 1024: 360*9e94795aSAndroid Build Coastguard Worker self.SelectAndConvertDiffTransfersToNew( 361*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks - stash_limit) 362*9e94795aSAndroid Build Coastguard Worker # Regenerate the sequence as the graph has changed. 363*9e94795aSAndroid Build Coastguard Worker self.FindSequenceForTransfers() 364*9e94795aSAndroid Build Coastguard Worker 365*9e94795aSAndroid Build Coastguard Worker # Revise the stash size again to keep the size under limit. 366*9e94795aSAndroid Build Coastguard Worker self.ReviseStashSize() 367*9e94795aSAndroid Build Coastguard Worker 368*9e94795aSAndroid Build Coastguard Worker # Double-check our work. 369*9e94795aSAndroid Build Coastguard Worker self.AssertSequenceGood() 370*9e94795aSAndroid Build Coastguard Worker self.AssertSha1Good() 371*9e94795aSAndroid Build Coastguard Worker 372*9e94795aSAndroid Build Coastguard Worker self.ComputePatches(prefix) 373*9e94795aSAndroid Build Coastguard Worker self.WriteTransfers(prefix) 374*9e94795aSAndroid Build Coastguard Worker 375*9e94795aSAndroid Build Coastguard Worker # Report the imgdiff stats. 376*9e94795aSAndroid Build Coastguard Worker if not self.disable_imgdiff: 377*9e94795aSAndroid Build Coastguard Worker self.imgdiff_stats.Report() 378*9e94795aSAndroid Build Coastguard Worker 379*9e94795aSAndroid Build Coastguard Worker def WriteTransfers(self, prefix): 380*9e94795aSAndroid Build Coastguard Worker def WriteSplitTransfers(out, style, target_blocks): 381*9e94795aSAndroid Build Coastguard Worker """Limit the size of operand in command 'new' and 'zero' to 1024 blocks. 382*9e94795aSAndroid Build Coastguard Worker 383*9e94795aSAndroid Build Coastguard Worker This prevents the target size of one command from being too large; and 384*9e94795aSAndroid Build Coastguard Worker might help to avoid fsync errors on some devices.""" 385*9e94795aSAndroid Build Coastguard Worker 386*9e94795aSAndroid Build Coastguard Worker assert style == "new" or style == "zero" 387*9e94795aSAndroid Build Coastguard Worker blocks_limit = 1024 388*9e94795aSAndroid Build Coastguard Worker total = 0 389*9e94795aSAndroid Build Coastguard Worker while target_blocks: 390*9e94795aSAndroid Build Coastguard Worker blocks_to_write = target_blocks.first(blocks_limit) 391*9e94795aSAndroid Build Coastguard Worker out.append("%s %s\n" % (style, blocks_to_write.to_string_raw())) 392*9e94795aSAndroid Build Coastguard Worker total += blocks_to_write.size() 393*9e94795aSAndroid Build Coastguard Worker target_blocks = target_blocks.subtract(blocks_to_write) 394*9e94795aSAndroid Build Coastguard Worker return total 395*9e94795aSAndroid Build Coastguard Worker 396*9e94795aSAndroid Build Coastguard Worker out = [] 397*9e94795aSAndroid Build Coastguard Worker total = 0 398*9e94795aSAndroid Build Coastguard Worker 399*9e94795aSAndroid Build Coastguard Worker # In BBOTA v3+, it uses the hash of the stashed blocks as the stash slot 400*9e94795aSAndroid Build Coastguard Worker # id. 'stashes' records the map from 'hash' to the ref count. The stash 401*9e94795aSAndroid Build Coastguard Worker # will be freed only if the count decrements to zero. 402*9e94795aSAndroid Build Coastguard Worker stashes = {} 403*9e94795aSAndroid Build Coastguard Worker stashed_blocks = 0 404*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = 0 405*9e94795aSAndroid Build Coastguard Worker 406*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 407*9e94795aSAndroid Build Coastguard Worker 408*9e94795aSAndroid Build Coastguard Worker for _, sr in xf.stash_before: 409*9e94795aSAndroid Build Coastguard Worker sh = self.src.RangeSha1(sr) 410*9e94795aSAndroid Build Coastguard Worker if sh in stashes: 411*9e94795aSAndroid Build Coastguard Worker stashes[sh] += 1 412*9e94795aSAndroid Build Coastguard Worker else: 413*9e94795aSAndroid Build Coastguard Worker stashes[sh] = 1 414*9e94795aSAndroid Build Coastguard Worker stashed_blocks += sr.size() 415*9e94795aSAndroid Build Coastguard Worker self.touched_src_ranges = self.touched_src_ranges.union(sr) 416*9e94795aSAndroid Build Coastguard Worker out.append("stash %s %s\n" % (sh, sr.to_string_raw())) 417*9e94795aSAndroid Build Coastguard Worker 418*9e94795aSAndroid Build Coastguard Worker if stashed_blocks > max_stashed_blocks: 419*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = stashed_blocks 420*9e94795aSAndroid Build Coastguard Worker 421*9e94795aSAndroid Build Coastguard Worker free_string = [] 422*9e94795aSAndroid Build Coastguard Worker free_size = 0 423*9e94795aSAndroid Build Coastguard Worker 424*9e94795aSAndroid Build Coastguard Worker # <# blocks> <src ranges> 425*9e94795aSAndroid Build Coastguard Worker # OR 426*9e94795aSAndroid Build Coastguard Worker # <# blocks> <src ranges> <src locs> <stash refs...> 427*9e94795aSAndroid Build Coastguard Worker # OR 428*9e94795aSAndroid Build Coastguard Worker # <# blocks> - <stash refs...> 429*9e94795aSAndroid Build Coastguard Worker 430*9e94795aSAndroid Build Coastguard Worker size = xf.src_ranges.size() 431*9e94795aSAndroid Build Coastguard Worker src_str_buffer = [str(size)] 432*9e94795aSAndroid Build Coastguard Worker 433*9e94795aSAndroid Build Coastguard Worker unstashed_src_ranges = xf.src_ranges 434*9e94795aSAndroid Build Coastguard Worker mapped_stashes = [] 435*9e94795aSAndroid Build Coastguard Worker for _, sr in xf.use_stash: 436*9e94795aSAndroid Build Coastguard Worker unstashed_src_ranges = unstashed_src_ranges.subtract(sr) 437*9e94795aSAndroid Build Coastguard Worker sh = self.src.RangeSha1(sr) 438*9e94795aSAndroid Build Coastguard Worker sr = xf.src_ranges.map_within(sr) 439*9e94795aSAndroid Build Coastguard Worker mapped_stashes.append(sr) 440*9e94795aSAndroid Build Coastguard Worker assert sh in stashes 441*9e94795aSAndroid Build Coastguard Worker src_str_buffer.append("%s:%s" % (sh, sr.to_string_raw())) 442*9e94795aSAndroid Build Coastguard Worker stashes[sh] -= 1 443*9e94795aSAndroid Build Coastguard Worker if stashes[sh] == 0: 444*9e94795aSAndroid Build Coastguard Worker free_string.append("free %s\n" % (sh,)) 445*9e94795aSAndroid Build Coastguard Worker free_size += sr.size() 446*9e94795aSAndroid Build Coastguard Worker stashes.pop(sh) 447*9e94795aSAndroid Build Coastguard Worker 448*9e94795aSAndroid Build Coastguard Worker if unstashed_src_ranges: 449*9e94795aSAndroid Build Coastguard Worker src_str_buffer.insert(1, unstashed_src_ranges.to_string_raw()) 450*9e94795aSAndroid Build Coastguard Worker if xf.use_stash: 451*9e94795aSAndroid Build Coastguard Worker mapped_unstashed = xf.src_ranges.map_within(unstashed_src_ranges) 452*9e94795aSAndroid Build Coastguard Worker src_str_buffer.insert(2, mapped_unstashed.to_string_raw()) 453*9e94795aSAndroid Build Coastguard Worker mapped_stashes.append(mapped_unstashed) 454*9e94795aSAndroid Build Coastguard Worker self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes) 455*9e94795aSAndroid Build Coastguard Worker else: 456*9e94795aSAndroid Build Coastguard Worker src_str_buffer.insert(1, "-") 457*9e94795aSAndroid Build Coastguard Worker self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes) 458*9e94795aSAndroid Build Coastguard Worker 459*9e94795aSAndroid Build Coastguard Worker src_str = " ".join(src_str_buffer) 460*9e94795aSAndroid Build Coastguard Worker 461*9e94795aSAndroid Build Coastguard Worker # version 3+: 462*9e94795aSAndroid Build Coastguard Worker # zero <rangeset> 463*9e94795aSAndroid Build Coastguard Worker # new <rangeset> 464*9e94795aSAndroid Build Coastguard Worker # erase <rangeset> 465*9e94795aSAndroid Build Coastguard Worker # bsdiff patchstart patchlen srchash tgthash <tgt rangeset> <src_str> 466*9e94795aSAndroid Build Coastguard Worker # imgdiff patchstart patchlen srchash tgthash <tgt rangeset> <src_str> 467*9e94795aSAndroid Build Coastguard Worker # move hash <tgt rangeset> <src_str> 468*9e94795aSAndroid Build Coastguard Worker 469*9e94795aSAndroid Build Coastguard Worker tgt_size = xf.tgt_ranges.size() 470*9e94795aSAndroid Build Coastguard Worker 471*9e94795aSAndroid Build Coastguard Worker if xf.style == "new": 472*9e94795aSAndroid Build Coastguard Worker assert xf.tgt_ranges 473*9e94795aSAndroid Build Coastguard Worker assert tgt_size == WriteSplitTransfers(out, xf.style, xf.tgt_ranges) 474*9e94795aSAndroid Build Coastguard Worker total += tgt_size 475*9e94795aSAndroid Build Coastguard Worker elif xf.style == "move": 476*9e94795aSAndroid Build Coastguard Worker assert xf.tgt_ranges 477*9e94795aSAndroid Build Coastguard Worker assert xf.src_ranges.size() == tgt_size 478*9e94795aSAndroid Build Coastguard Worker if xf.src_ranges != xf.tgt_ranges: 479*9e94795aSAndroid Build Coastguard Worker # take into account automatic stashing of overlapping blocks 480*9e94795aSAndroid Build Coastguard Worker if xf.src_ranges.overlaps(xf.tgt_ranges): 481*9e94795aSAndroid Build Coastguard Worker temp_stash_usage = stashed_blocks + xf.src_ranges.size() 482*9e94795aSAndroid Build Coastguard Worker if temp_stash_usage > max_stashed_blocks: 483*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = temp_stash_usage 484*9e94795aSAndroid Build Coastguard Worker 485*9e94795aSAndroid Build Coastguard Worker self.touched_src_ranges = self.touched_src_ranges.union( 486*9e94795aSAndroid Build Coastguard Worker xf.src_ranges) 487*9e94795aSAndroid Build Coastguard Worker 488*9e94795aSAndroid Build Coastguard Worker out.append("%s %s %s %s\n" % ( 489*9e94795aSAndroid Build Coastguard Worker xf.style, 490*9e94795aSAndroid Build Coastguard Worker xf.tgt_sha1, 491*9e94795aSAndroid Build Coastguard Worker xf.tgt_ranges.to_string_raw(), src_str)) 492*9e94795aSAndroid Build Coastguard Worker total += tgt_size 493*9e94795aSAndroid Build Coastguard Worker elif xf.style in ("bsdiff", "imgdiff"): 494*9e94795aSAndroid Build Coastguard Worker assert xf.tgt_ranges 495*9e94795aSAndroid Build Coastguard Worker assert xf.src_ranges 496*9e94795aSAndroid Build Coastguard Worker # take into account automatic stashing of overlapping blocks 497*9e94795aSAndroid Build Coastguard Worker if xf.src_ranges.overlaps(xf.tgt_ranges): 498*9e94795aSAndroid Build Coastguard Worker temp_stash_usage = stashed_blocks + xf.src_ranges.size() 499*9e94795aSAndroid Build Coastguard Worker if temp_stash_usage > max_stashed_blocks: 500*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = temp_stash_usage 501*9e94795aSAndroid Build Coastguard Worker 502*9e94795aSAndroid Build Coastguard Worker self.touched_src_ranges = self.touched_src_ranges.union(xf.src_ranges) 503*9e94795aSAndroid Build Coastguard Worker 504*9e94795aSAndroid Build Coastguard Worker out.append("%s %d %d %s %s %s %s\n" % ( 505*9e94795aSAndroid Build Coastguard Worker xf.style, 506*9e94795aSAndroid Build Coastguard Worker xf.patch_start, xf.patch_len, 507*9e94795aSAndroid Build Coastguard Worker xf.src_sha1, 508*9e94795aSAndroid Build Coastguard Worker xf.tgt_sha1, 509*9e94795aSAndroid Build Coastguard Worker xf.tgt_ranges.to_string_raw(), src_str)) 510*9e94795aSAndroid Build Coastguard Worker total += tgt_size 511*9e94795aSAndroid Build Coastguard Worker elif xf.style == "zero": 512*9e94795aSAndroid Build Coastguard Worker assert xf.tgt_ranges 513*9e94795aSAndroid Build Coastguard Worker to_zero = xf.tgt_ranges.subtract(xf.src_ranges) 514*9e94795aSAndroid Build Coastguard Worker assert WriteSplitTransfers(out, xf.style, to_zero) == to_zero.size() 515*9e94795aSAndroid Build Coastguard Worker total += to_zero.size() 516*9e94795aSAndroid Build Coastguard Worker else: 517*9e94795aSAndroid Build Coastguard Worker raise ValueError("unknown transfer style '%s'\n" % xf.style) 518*9e94795aSAndroid Build Coastguard Worker 519*9e94795aSAndroid Build Coastguard Worker if free_string: 520*9e94795aSAndroid Build Coastguard Worker out.append("".join(free_string)) 521*9e94795aSAndroid Build Coastguard Worker stashed_blocks -= free_size 522*9e94795aSAndroid Build Coastguard Worker 523*9e94795aSAndroid Build Coastguard Worker if common.OPTIONS.cache_size is not None: 524*9e94795aSAndroid Build Coastguard Worker # Validation check: abort if we're going to need more stash space than 525*9e94795aSAndroid Build Coastguard Worker # the allowed size (cache_size * threshold). There are two purposes 526*9e94795aSAndroid Build Coastguard Worker # of having a threshold here. a) Part of the cache may have been 527*9e94795aSAndroid Build Coastguard Worker # occupied by some recovery logs. b) It will buy us some time to deal 528*9e94795aSAndroid Build Coastguard Worker # with the oversize issue. 529*9e94795aSAndroid Build Coastguard Worker cache_size = common.OPTIONS.cache_size 530*9e94795aSAndroid Build Coastguard Worker stash_threshold = common.OPTIONS.stash_threshold 531*9e94795aSAndroid Build Coastguard Worker max_allowed = cache_size * stash_threshold 532*9e94795aSAndroid Build Coastguard Worker assert max_stashed_blocks * self.tgt.blocksize <= max_allowed, \ 533*9e94795aSAndroid Build Coastguard Worker 'Stash size %d (%d * %d) exceeds the limit %d (%d * %.2f)' % ( 534*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks * self.tgt.blocksize, max_stashed_blocks, 535*9e94795aSAndroid Build Coastguard Worker self.tgt.blocksize, max_allowed, cache_size, 536*9e94795aSAndroid Build Coastguard Worker stash_threshold) 537*9e94795aSAndroid Build Coastguard Worker 538*9e94795aSAndroid Build Coastguard Worker self.touched_src_sha1 = self.src.RangeSha1(self.touched_src_ranges) 539*9e94795aSAndroid Build Coastguard Worker 540*9e94795aSAndroid Build Coastguard Worker # Zero out extended blocks as a workaround for bug 20881595. 541*9e94795aSAndroid Build Coastguard Worker if self.tgt.extended: 542*9e94795aSAndroid Build Coastguard Worker assert (WriteSplitTransfers(out, "zero", self.tgt.extended) == 543*9e94795aSAndroid Build Coastguard Worker self.tgt.extended.size()) 544*9e94795aSAndroid Build Coastguard Worker total += self.tgt.extended.size() 545*9e94795aSAndroid Build Coastguard Worker 546*9e94795aSAndroid Build Coastguard Worker # We erase all the blocks on the partition that a) don't contain useful 547*9e94795aSAndroid Build Coastguard Worker # data in the new image; b) will not be touched by dm-verity. Out of those 548*9e94795aSAndroid Build Coastguard Worker # blocks, we erase the ones that won't be used in this update at the 549*9e94795aSAndroid Build Coastguard Worker # beginning of an update. The rest would be erased at the end. This is to 550*9e94795aSAndroid Build Coastguard Worker # work around the eMMC issue observed on some devices, which may otherwise 551*9e94795aSAndroid Build Coastguard Worker # get starving for clean blocks and thus fail the update. (b/28347095) 552*9e94795aSAndroid Build Coastguard Worker all_tgt = RangeSet(data=(0, self.tgt.total_blocks)) 553*9e94795aSAndroid Build Coastguard Worker all_tgt_minus_extended = all_tgt.subtract(self.tgt.extended) 554*9e94795aSAndroid Build Coastguard Worker new_dontcare = all_tgt_minus_extended.subtract(self.tgt.care_map) 555*9e94795aSAndroid Build Coastguard Worker 556*9e94795aSAndroid Build Coastguard Worker erase_first = new_dontcare.subtract(self.touched_src_ranges) 557*9e94795aSAndroid Build Coastguard Worker if erase_first: 558*9e94795aSAndroid Build Coastguard Worker out.insert(0, "erase %s\n" % (erase_first.to_string_raw(),)) 559*9e94795aSAndroid Build Coastguard Worker 560*9e94795aSAndroid Build Coastguard Worker erase_last = new_dontcare.subtract(erase_first) 561*9e94795aSAndroid Build Coastguard Worker if erase_last: 562*9e94795aSAndroid Build Coastguard Worker out.append("erase %s\n" % (erase_last.to_string_raw(),)) 563*9e94795aSAndroid Build Coastguard Worker 564*9e94795aSAndroid Build Coastguard Worker out.insert(0, "%d\n" % (self.version,)) # format version number 565*9e94795aSAndroid Build Coastguard Worker out.insert(1, "%d\n" % (total,)) 566*9e94795aSAndroid Build Coastguard Worker # v3+: the number of stash slots is unused. 567*9e94795aSAndroid Build Coastguard Worker out.insert(2, "0\n") 568*9e94795aSAndroid Build Coastguard Worker out.insert(3, str(max_stashed_blocks) + "\n") 569*9e94795aSAndroid Build Coastguard Worker 570*9e94795aSAndroid Build Coastguard Worker with open(prefix + ".transfer.list", "w") as f: 571*9e94795aSAndroid Build Coastguard Worker for i in out: 572*9e94795aSAndroid Build Coastguard Worker f.write(i) 573*9e94795aSAndroid Build Coastguard Worker 574*9e94795aSAndroid Build Coastguard Worker self._max_stashed_size = max_stashed_blocks * self.tgt.blocksize 575*9e94795aSAndroid Build Coastguard Worker OPTIONS = common.OPTIONS 576*9e94795aSAndroid Build Coastguard Worker if OPTIONS.cache_size is not None: 577*9e94795aSAndroid Build Coastguard Worker max_allowed = OPTIONS.cache_size * OPTIONS.stash_threshold 578*9e94795aSAndroid Build Coastguard Worker logger.info( 579*9e94795aSAndroid Build Coastguard Worker "max stashed blocks: %d (%d bytes), limit: %d bytes (%.2f%%)\n", 580*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks, self._max_stashed_size, max_allowed, 581*9e94795aSAndroid Build Coastguard Worker self._max_stashed_size * 100.0 / max_allowed) 582*9e94795aSAndroid Build Coastguard Worker else: 583*9e94795aSAndroid Build Coastguard Worker logger.info( 584*9e94795aSAndroid Build Coastguard Worker "max stashed blocks: %d (%d bytes), limit: <unknown>\n", 585*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks, self._max_stashed_size) 586*9e94795aSAndroid Build Coastguard Worker 587*9e94795aSAndroid Build Coastguard Worker def ReviseStashSize(self, ignore_stash_limit=False): 588*9e94795aSAndroid Build Coastguard Worker """ Revises the transfers to keep the stash size within the size limit. 589*9e94795aSAndroid Build Coastguard Worker 590*9e94795aSAndroid Build Coastguard Worker Iterates through the transfer list and calculates the stash size each 591*9e94795aSAndroid Build Coastguard Worker transfer generates. Converts the affected transfers to new if we reach the 592*9e94795aSAndroid Build Coastguard Worker stash limit. 593*9e94795aSAndroid Build Coastguard Worker 594*9e94795aSAndroid Build Coastguard Worker Args: 595*9e94795aSAndroid Build Coastguard Worker ignore_stash_limit: Ignores the stash limit and calculates the max 596*9e94795aSAndroid Build Coastguard Worker simultaneous stashed blocks instead. No change will be made to the 597*9e94795aSAndroid Build Coastguard Worker transfer list with this flag. 598*9e94795aSAndroid Build Coastguard Worker 599*9e94795aSAndroid Build Coastguard Worker Return: 600*9e94795aSAndroid Build Coastguard Worker A tuple of (tgt blocks converted to new, max stashed blocks) 601*9e94795aSAndroid Build Coastguard Worker """ 602*9e94795aSAndroid Build Coastguard Worker logger.info("Revising stash size...") 603*9e94795aSAndroid Build Coastguard Worker stash_map = {} 604*9e94795aSAndroid Build Coastguard Worker 605*9e94795aSAndroid Build Coastguard Worker # Create the map between a stash and its def/use points. For example, for a 606*9e94795aSAndroid Build Coastguard Worker # given stash of (raw_id, sr), stash_map[raw_id] = (sr, def_cmd, use_cmd). 607*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 608*9e94795aSAndroid Build Coastguard Worker # Command xf defines (stores) all the stashes in stash_before. 609*9e94795aSAndroid Build Coastguard Worker for stash_raw_id, sr in xf.stash_before: 610*9e94795aSAndroid Build Coastguard Worker stash_map[stash_raw_id] = (sr, xf) 611*9e94795aSAndroid Build Coastguard Worker 612*9e94795aSAndroid Build Coastguard Worker # Record all the stashes command xf uses. 613*9e94795aSAndroid Build Coastguard Worker for stash_raw_id, _ in xf.use_stash: 614*9e94795aSAndroid Build Coastguard Worker stash_map[stash_raw_id] += (xf,) 615*9e94795aSAndroid Build Coastguard Worker 616*9e94795aSAndroid Build Coastguard Worker max_allowed_blocks = None 617*9e94795aSAndroid Build Coastguard Worker if not ignore_stash_limit: 618*9e94795aSAndroid Build Coastguard Worker # Compute the maximum blocks available for stash based on /cache size and 619*9e94795aSAndroid Build Coastguard Worker # the threshold. 620*9e94795aSAndroid Build Coastguard Worker cache_size = common.OPTIONS.cache_size 621*9e94795aSAndroid Build Coastguard Worker stash_threshold = common.OPTIONS.stash_threshold 622*9e94795aSAndroid Build Coastguard Worker max_allowed_blocks = cache_size * stash_threshold / self.tgt.blocksize 623*9e94795aSAndroid Build Coastguard Worker 624*9e94795aSAndroid Build Coastguard Worker # See the comments for 'stashes' in WriteTransfers(). 625*9e94795aSAndroid Build Coastguard Worker stashes = {} 626*9e94795aSAndroid Build Coastguard Worker stashed_blocks = 0 627*9e94795aSAndroid Build Coastguard Worker new_blocks = 0 628*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = 0 629*9e94795aSAndroid Build Coastguard Worker 630*9e94795aSAndroid Build Coastguard Worker # Now go through all the commands. Compute the required stash size on the 631*9e94795aSAndroid Build Coastguard Worker # fly. If a command requires excess stash than available, it deletes the 632*9e94795aSAndroid Build Coastguard Worker # stash by replacing the command that uses the stash with a "new" command 633*9e94795aSAndroid Build Coastguard Worker # instead. 634*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 635*9e94795aSAndroid Build Coastguard Worker replaced_cmds = [] 636*9e94795aSAndroid Build Coastguard Worker 637*9e94795aSAndroid Build Coastguard Worker # xf.stash_before generates explicit stash commands. 638*9e94795aSAndroid Build Coastguard Worker for stash_raw_id, sr in xf.stash_before: 639*9e94795aSAndroid Build Coastguard Worker # Check the post-command stashed_blocks. 640*9e94795aSAndroid Build Coastguard Worker stashed_blocks_after = stashed_blocks 641*9e94795aSAndroid Build Coastguard Worker sh = self.src.RangeSha1(sr) 642*9e94795aSAndroid Build Coastguard Worker if sh not in stashes: 643*9e94795aSAndroid Build Coastguard Worker stashed_blocks_after += sr.size() 644*9e94795aSAndroid Build Coastguard Worker 645*9e94795aSAndroid Build Coastguard Worker if max_allowed_blocks and stashed_blocks_after > max_allowed_blocks: 646*9e94795aSAndroid Build Coastguard Worker # We cannot stash this one for a later command. Find out the command 647*9e94795aSAndroid Build Coastguard Worker # that will use this stash and replace the command with "new". 648*9e94795aSAndroid Build Coastguard Worker use_cmd = stash_map[stash_raw_id][2] 649*9e94795aSAndroid Build Coastguard Worker replaced_cmds.append(use_cmd) 650*9e94795aSAndroid Build Coastguard Worker logger.info("%10d %9s %s", sr.size(), "explicit", use_cmd) 651*9e94795aSAndroid Build Coastguard Worker else: 652*9e94795aSAndroid Build Coastguard Worker # Update the stashes map. 653*9e94795aSAndroid Build Coastguard Worker if sh in stashes: 654*9e94795aSAndroid Build Coastguard Worker stashes[sh] += 1 655*9e94795aSAndroid Build Coastguard Worker else: 656*9e94795aSAndroid Build Coastguard Worker stashes[sh] = 1 657*9e94795aSAndroid Build Coastguard Worker stashed_blocks = stashed_blocks_after 658*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = max(max_stashed_blocks, stashed_blocks) 659*9e94795aSAndroid Build Coastguard Worker 660*9e94795aSAndroid Build Coastguard Worker # "move" and "diff" may introduce implicit stashes in BBOTA v3. Prior to 661*9e94795aSAndroid Build Coastguard Worker # ComputePatches(), they both have the style of "diff". 662*9e94795aSAndroid Build Coastguard Worker if xf.style == "diff": 663*9e94795aSAndroid Build Coastguard Worker assert xf.tgt_ranges and xf.src_ranges 664*9e94795aSAndroid Build Coastguard Worker if xf.src_ranges.overlaps(xf.tgt_ranges): 665*9e94795aSAndroid Build Coastguard Worker if (max_allowed_blocks and 666*9e94795aSAndroid Build Coastguard Worker stashed_blocks + xf.src_ranges.size() > max_allowed_blocks): 667*9e94795aSAndroid Build Coastguard Worker replaced_cmds.append(xf) 668*9e94795aSAndroid Build Coastguard Worker logger.info("%10d %9s %s", xf.src_ranges.size(), "implicit", xf) 669*9e94795aSAndroid Build Coastguard Worker else: 670*9e94795aSAndroid Build Coastguard Worker # The whole source ranges will be stashed for implicit stashes. 671*9e94795aSAndroid Build Coastguard Worker max_stashed_blocks = max(max_stashed_blocks, 672*9e94795aSAndroid Build Coastguard Worker stashed_blocks + xf.src_ranges.size()) 673*9e94795aSAndroid Build Coastguard Worker 674*9e94795aSAndroid Build Coastguard Worker # Replace the commands in replaced_cmds with "new"s. 675*9e94795aSAndroid Build Coastguard Worker for cmd in replaced_cmds: 676*9e94795aSAndroid Build Coastguard Worker # It no longer uses any commands in "use_stash". Remove the def points 677*9e94795aSAndroid Build Coastguard Worker # for all those stashes. 678*9e94795aSAndroid Build Coastguard Worker for stash_raw_id, sr in cmd.use_stash: 679*9e94795aSAndroid Build Coastguard Worker def_cmd = stash_map[stash_raw_id][1] 680*9e94795aSAndroid Build Coastguard Worker assert (stash_raw_id, sr) in def_cmd.stash_before 681*9e94795aSAndroid Build Coastguard Worker def_cmd.stash_before.remove((stash_raw_id, sr)) 682*9e94795aSAndroid Build Coastguard Worker 683*9e94795aSAndroid Build Coastguard Worker # Add up blocks that violates space limit and print total number to 684*9e94795aSAndroid Build Coastguard Worker # screen later. 685*9e94795aSAndroid Build Coastguard Worker new_blocks += cmd.tgt_ranges.size() 686*9e94795aSAndroid Build Coastguard Worker cmd.ConvertToNew() 687*9e94795aSAndroid Build Coastguard Worker 688*9e94795aSAndroid Build Coastguard Worker # xf.use_stash may generate free commands. 689*9e94795aSAndroid Build Coastguard Worker for _, sr in xf.use_stash: 690*9e94795aSAndroid Build Coastguard Worker sh = self.src.RangeSha1(sr) 691*9e94795aSAndroid Build Coastguard Worker assert sh in stashes 692*9e94795aSAndroid Build Coastguard Worker stashes[sh] -= 1 693*9e94795aSAndroid Build Coastguard Worker if stashes[sh] == 0: 694*9e94795aSAndroid Build Coastguard Worker stashed_blocks -= sr.size() 695*9e94795aSAndroid Build Coastguard Worker stashes.pop(sh) 696*9e94795aSAndroid Build Coastguard Worker 697*9e94795aSAndroid Build Coastguard Worker num_of_bytes = new_blocks * self.tgt.blocksize 698*9e94795aSAndroid Build Coastguard Worker logger.info( 699*9e94795aSAndroid Build Coastguard Worker " Total %d blocks (%d bytes) are packed as new blocks due to " 700*9e94795aSAndroid Build Coastguard Worker "insufficient cache size. Maximum blocks stashed simultaneously: %d", 701*9e94795aSAndroid Build Coastguard Worker new_blocks, num_of_bytes, max_stashed_blocks) 702*9e94795aSAndroid Build Coastguard Worker return new_blocks, max_stashed_blocks 703*9e94795aSAndroid Build Coastguard Worker 704*9e94795aSAndroid Build Coastguard Worker def ComputePatches(self, prefix): 705*9e94795aSAndroid Build Coastguard Worker logger.info("Reticulating splines...") 706*9e94795aSAndroid Build Coastguard Worker diff_queue = [] 707*9e94795aSAndroid Build Coastguard Worker patch_num = 0 708*9e94795aSAndroid Build Coastguard Worker with open(prefix + ".new.dat", "wb") as new_f: 709*9e94795aSAndroid Build Coastguard Worker for index, xf in enumerate(self.transfers): 710*9e94795aSAndroid Build Coastguard Worker if xf.style == "zero": 711*9e94795aSAndroid Build Coastguard Worker tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize 712*9e94795aSAndroid Build Coastguard Worker logger.info( 713*9e94795aSAndroid Build Coastguard Worker "%10d %10d (%6.2f%%) %7s %s %s", tgt_size, tgt_size, 100.0, 714*9e94795aSAndroid Build Coastguard Worker xf.style, xf.tgt_name, str(xf.tgt_ranges)) 715*9e94795aSAndroid Build Coastguard Worker 716*9e94795aSAndroid Build Coastguard Worker elif xf.style == "new": 717*9e94795aSAndroid Build Coastguard Worker self.tgt.WriteRangeDataToFd(xf.tgt_ranges, new_f) 718*9e94795aSAndroid Build Coastguard Worker tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize 719*9e94795aSAndroid Build Coastguard Worker logger.info( 720*9e94795aSAndroid Build Coastguard Worker "%10d %10d (%6.2f%%) %7s %s %s", tgt_size, tgt_size, 100.0, 721*9e94795aSAndroid Build Coastguard Worker xf.style, xf.tgt_name, str(xf.tgt_ranges)) 722*9e94795aSAndroid Build Coastguard Worker 723*9e94795aSAndroid Build Coastguard Worker elif xf.style == "diff": 724*9e94795aSAndroid Build Coastguard Worker # We can't compare src and tgt directly because they may have 725*9e94795aSAndroid Build Coastguard Worker # the same content but be broken up into blocks differently, eg: 726*9e94795aSAndroid Build Coastguard Worker # 727*9e94795aSAndroid Build Coastguard Worker # ["he", "llo"] vs ["h", "ello"] 728*9e94795aSAndroid Build Coastguard Worker # 729*9e94795aSAndroid Build Coastguard Worker # We want those to compare equal, ideally without having to 730*9e94795aSAndroid Build Coastguard Worker # actually concatenate the strings (these may be tens of 731*9e94795aSAndroid Build Coastguard Worker # megabytes). 732*9e94795aSAndroid Build Coastguard Worker if xf.src_sha1 == xf.tgt_sha1: 733*9e94795aSAndroid Build Coastguard Worker # These are identical; we don't need to generate a patch, 734*9e94795aSAndroid Build Coastguard Worker # just issue copy commands on the device. 735*9e94795aSAndroid Build Coastguard Worker xf.style = "move" 736*9e94795aSAndroid Build Coastguard Worker xf.patch_info = None 737*9e94795aSAndroid Build Coastguard Worker tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize 738*9e94795aSAndroid Build Coastguard Worker if xf.src_ranges != xf.tgt_ranges: 739*9e94795aSAndroid Build Coastguard Worker logger.info( 740*9e94795aSAndroid Build Coastguard Worker "%10d %10d (%6.2f%%) %7s %s %s (from %s)", tgt_size, tgt_size, 741*9e94795aSAndroid Build Coastguard Worker 100.0, xf.style, 742*9e94795aSAndroid Build Coastguard Worker xf.tgt_name if xf.tgt_name == xf.src_name else ( 743*9e94795aSAndroid Build Coastguard Worker xf.tgt_name + " (from " + xf.src_name + ")"), 744*9e94795aSAndroid Build Coastguard Worker str(xf.tgt_ranges), str(xf.src_ranges)) 745*9e94795aSAndroid Build Coastguard Worker else: 746*9e94795aSAndroid Build Coastguard Worker if xf.patch_info: 747*9e94795aSAndroid Build Coastguard Worker # We have already generated the patch (e.g. during split of large 748*9e94795aSAndroid Build Coastguard Worker # APKs or reduction of stash size) 749*9e94795aSAndroid Build Coastguard Worker imgdiff = xf.patch_info.imgdiff 750*9e94795aSAndroid Build Coastguard Worker else: 751*9e94795aSAndroid Build Coastguard Worker imgdiff = self.CanUseImgdiff( 752*9e94795aSAndroid Build Coastguard Worker xf.tgt_name, xf.tgt_ranges, xf.src_ranges) 753*9e94795aSAndroid Build Coastguard Worker xf.style = "imgdiff" if imgdiff else "bsdiff" 754*9e94795aSAndroid Build Coastguard Worker diff_queue.append((index, imgdiff, patch_num)) 755*9e94795aSAndroid Build Coastguard Worker patch_num += 1 756*9e94795aSAndroid Build Coastguard Worker 757*9e94795aSAndroid Build Coastguard Worker else: 758*9e94795aSAndroid Build Coastguard Worker assert False, "unknown style " + xf.style 759*9e94795aSAndroid Build Coastguard Worker 760*9e94795aSAndroid Build Coastguard Worker patches = self.ComputePatchesForInputList(diff_queue, False) 761*9e94795aSAndroid Build Coastguard Worker 762*9e94795aSAndroid Build Coastguard Worker offset = 0 763*9e94795aSAndroid Build Coastguard Worker with open(prefix + ".patch.dat", "wb") as patch_fd: 764*9e94795aSAndroid Build Coastguard Worker for index, patch_info, _ in patches: 765*9e94795aSAndroid Build Coastguard Worker xf = self.transfers[index] 766*9e94795aSAndroid Build Coastguard Worker xf.patch_len = len(patch_info.content) 767*9e94795aSAndroid Build Coastguard Worker xf.patch_start = offset 768*9e94795aSAndroid Build Coastguard Worker offset += xf.patch_len 769*9e94795aSAndroid Build Coastguard Worker patch_fd.write(patch_info.content) 770*9e94795aSAndroid Build Coastguard Worker 771*9e94795aSAndroid Build Coastguard Worker tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize 772*9e94795aSAndroid Build Coastguard Worker logger.info( 773*9e94795aSAndroid Build Coastguard Worker "%10d %10d (%6.2f%%) %7s %s %s %s", xf.patch_len, tgt_size, 774*9e94795aSAndroid Build Coastguard Worker xf.patch_len * 100.0 / tgt_size, xf.style, 775*9e94795aSAndroid Build Coastguard Worker xf.tgt_name if xf.tgt_name == xf.src_name else ( 776*9e94795aSAndroid Build Coastguard Worker xf.tgt_name + " (from " + xf.src_name + ")"), 777*9e94795aSAndroid Build Coastguard Worker xf.tgt_ranges, xf.src_ranges) 778*9e94795aSAndroid Build Coastguard Worker 779*9e94795aSAndroid Build Coastguard Worker def AssertSha1Good(self): 780*9e94795aSAndroid Build Coastguard Worker """Check the SHA-1 of the src & tgt blocks in the transfer list. 781*9e94795aSAndroid Build Coastguard Worker 782*9e94795aSAndroid Build Coastguard Worker Double check the SHA-1 value to avoid the issue in b/71908713, where 783*9e94795aSAndroid Build Coastguard Worker SparseImage.RangeSha1() messed up with the hash calculation in multi-thread 784*9e94795aSAndroid Build Coastguard Worker environment. That specific problem has been fixed by protecting the 785*9e94795aSAndroid Build Coastguard Worker underlying generator function 'SparseImage._GetRangeData()' with lock. 786*9e94795aSAndroid Build Coastguard Worker """ 787*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 788*9e94795aSAndroid Build Coastguard Worker tgt_sha1 = self.tgt.RangeSha1(xf.tgt_ranges) 789*9e94795aSAndroid Build Coastguard Worker assert xf.tgt_sha1 == tgt_sha1 790*9e94795aSAndroid Build Coastguard Worker if xf.style == "diff": 791*9e94795aSAndroid Build Coastguard Worker src_sha1 = self.src.RangeSha1(xf.src_ranges) 792*9e94795aSAndroid Build Coastguard Worker assert xf.src_sha1 == src_sha1 793*9e94795aSAndroid Build Coastguard Worker 794*9e94795aSAndroid Build Coastguard Worker def AssertSequenceGood(self): 795*9e94795aSAndroid Build Coastguard Worker # Simulate the sequences of transfers we will output, and check that: 796*9e94795aSAndroid Build Coastguard Worker # - we never read a block after writing it, and 797*9e94795aSAndroid Build Coastguard Worker # - we write every block we care about exactly once. 798*9e94795aSAndroid Build Coastguard Worker 799*9e94795aSAndroid Build Coastguard Worker # Start with no blocks having been touched yet. 800*9e94795aSAndroid Build Coastguard Worker touched = array.array("B", b"\0" * self.tgt.total_blocks) 801*9e94795aSAndroid Build Coastguard Worker 802*9e94795aSAndroid Build Coastguard Worker # Imagine processing the transfers in order. 803*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 804*9e94795aSAndroid Build Coastguard Worker # Check that the input blocks for this transfer haven't yet been touched. 805*9e94795aSAndroid Build Coastguard Worker 806*9e94795aSAndroid Build Coastguard Worker x = xf.src_ranges 807*9e94795aSAndroid Build Coastguard Worker for _, sr in xf.use_stash: 808*9e94795aSAndroid Build Coastguard Worker x = x.subtract(sr) 809*9e94795aSAndroid Build Coastguard Worker 810*9e94795aSAndroid Build Coastguard Worker for s, e in x: 811*9e94795aSAndroid Build Coastguard Worker # Source image could be larger. Don't check the blocks that are in the 812*9e94795aSAndroid Build Coastguard Worker # source image only. Since they are not in 'touched', and won't ever 813*9e94795aSAndroid Build Coastguard Worker # be touched. 814*9e94795aSAndroid Build Coastguard Worker for i in range(s, min(e, self.tgt.total_blocks)): 815*9e94795aSAndroid Build Coastguard Worker assert touched[i] == 0 816*9e94795aSAndroid Build Coastguard Worker 817*9e94795aSAndroid Build Coastguard Worker # Check that the output blocks for this transfer haven't yet 818*9e94795aSAndroid Build Coastguard Worker # been touched, and touch all the blocks written by this 819*9e94795aSAndroid Build Coastguard Worker # transfer. 820*9e94795aSAndroid Build Coastguard Worker for s, e in xf.tgt_ranges: 821*9e94795aSAndroid Build Coastguard Worker for i in range(s, e): 822*9e94795aSAndroid Build Coastguard Worker assert touched[i] == 0 823*9e94795aSAndroid Build Coastguard Worker touched[i] = 1 824*9e94795aSAndroid Build Coastguard Worker 825*9e94795aSAndroid Build Coastguard Worker # Check that we've written every target block. 826*9e94795aSAndroid Build Coastguard Worker for s, e in self.tgt.care_map: 827*9e94795aSAndroid Build Coastguard Worker for i in range(s, e): 828*9e94795aSAndroid Build Coastguard Worker assert touched[i] == 1 829*9e94795aSAndroid Build Coastguard Worker 830*9e94795aSAndroid Build Coastguard Worker def FindSequenceForTransfers(self): 831*9e94795aSAndroid Build Coastguard Worker """Finds a sequence for the given transfers. 832*9e94795aSAndroid Build Coastguard Worker 833*9e94795aSAndroid Build Coastguard Worker The goal is to minimize the violation of order dependencies between these 834*9e94795aSAndroid Build Coastguard Worker transfers, so that fewer blocks are stashed when applying the update. 835*9e94795aSAndroid Build Coastguard Worker """ 836*9e94795aSAndroid Build Coastguard Worker 837*9e94795aSAndroid Build Coastguard Worker # Clear the existing dependency between transfers 838*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 839*9e94795aSAndroid Build Coastguard Worker xf.goes_before = OrderedDict() 840*9e94795aSAndroid Build Coastguard Worker xf.goes_after = OrderedDict() 841*9e94795aSAndroid Build Coastguard Worker 842*9e94795aSAndroid Build Coastguard Worker xf.stash_before = [] 843*9e94795aSAndroid Build Coastguard Worker xf.use_stash = [] 844*9e94795aSAndroid Build Coastguard Worker 845*9e94795aSAndroid Build Coastguard Worker # Find the ordering dependencies among transfers (this is O(n^2) 846*9e94795aSAndroid Build Coastguard Worker # in the number of transfers). 847*9e94795aSAndroid Build Coastguard Worker self.GenerateDigraph() 848*9e94795aSAndroid Build Coastguard Worker # Find a sequence of transfers that satisfies as many ordering 849*9e94795aSAndroid Build Coastguard Worker # dependencies as possible (heuristically). 850*9e94795aSAndroid Build Coastguard Worker self.FindVertexSequence() 851*9e94795aSAndroid Build Coastguard Worker # Fix up the ordering dependencies that the sequence didn't 852*9e94795aSAndroid Build Coastguard Worker # satisfy. 853*9e94795aSAndroid Build Coastguard Worker self.ReverseBackwardEdges() 854*9e94795aSAndroid Build Coastguard Worker self.ImproveVertexSequence() 855*9e94795aSAndroid Build Coastguard Worker 856*9e94795aSAndroid Build Coastguard Worker def ImproveVertexSequence(self): 857*9e94795aSAndroid Build Coastguard Worker logger.info("Improving vertex order...") 858*9e94795aSAndroid Build Coastguard Worker 859*9e94795aSAndroid Build Coastguard Worker # At this point our digraph is acyclic; we reversed any edges that 860*9e94795aSAndroid Build Coastguard Worker # were backwards in the heuristically-generated sequence. The 861*9e94795aSAndroid Build Coastguard Worker # previously-generated order is still acceptable, but we hope to 862*9e94795aSAndroid Build Coastguard Worker # find a better order that needs less memory for stashed data. 863*9e94795aSAndroid Build Coastguard Worker # Now we do a topological sort to generate a new vertex order, 864*9e94795aSAndroid Build Coastguard Worker # using a greedy algorithm to choose which vertex goes next 865*9e94795aSAndroid Build Coastguard Worker # whenever we have a choice. 866*9e94795aSAndroid Build Coastguard Worker 867*9e94795aSAndroid Build Coastguard Worker # Make a copy of the edge set; this copy will get destroyed by the 868*9e94795aSAndroid Build Coastguard Worker # algorithm. 869*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 870*9e94795aSAndroid Build Coastguard Worker xf.incoming = xf.goes_after.copy() 871*9e94795aSAndroid Build Coastguard Worker xf.outgoing = xf.goes_before.copy() 872*9e94795aSAndroid Build Coastguard Worker 873*9e94795aSAndroid Build Coastguard Worker L = [] # the new vertex order 874*9e94795aSAndroid Build Coastguard Worker 875*9e94795aSAndroid Build Coastguard Worker # S is the set of sources in the remaining graph; we always choose 876*9e94795aSAndroid Build Coastguard Worker # the one that leaves the least amount of stashed data after it's 877*9e94795aSAndroid Build Coastguard Worker # executed. 878*9e94795aSAndroid Build Coastguard Worker S = [(u.NetStashChange(), u.order, u) for u in self.transfers 879*9e94795aSAndroid Build Coastguard Worker if not u.incoming] 880*9e94795aSAndroid Build Coastguard Worker heapq.heapify(S) 881*9e94795aSAndroid Build Coastguard Worker 882*9e94795aSAndroid Build Coastguard Worker while S: 883*9e94795aSAndroid Build Coastguard Worker _, _, xf = heapq.heappop(S) 884*9e94795aSAndroid Build Coastguard Worker L.append(xf) 885*9e94795aSAndroid Build Coastguard Worker for u in xf.outgoing: 886*9e94795aSAndroid Build Coastguard Worker del u.incoming[xf] 887*9e94795aSAndroid Build Coastguard Worker if not u.incoming: 888*9e94795aSAndroid Build Coastguard Worker heapq.heappush(S, (u.NetStashChange(), u.order, u)) 889*9e94795aSAndroid Build Coastguard Worker 890*9e94795aSAndroid Build Coastguard Worker # if this fails then our graph had a cycle. 891*9e94795aSAndroid Build Coastguard Worker assert len(L) == len(self.transfers) 892*9e94795aSAndroid Build Coastguard Worker 893*9e94795aSAndroid Build Coastguard Worker self.transfers = L 894*9e94795aSAndroid Build Coastguard Worker for i, xf in enumerate(L): 895*9e94795aSAndroid Build Coastguard Worker xf.order = i 896*9e94795aSAndroid Build Coastguard Worker 897*9e94795aSAndroid Build Coastguard Worker def ReverseBackwardEdges(self): 898*9e94795aSAndroid Build Coastguard Worker """Reverse unsatisfying edges and compute pairs of stashed blocks. 899*9e94795aSAndroid Build Coastguard Worker 900*9e94795aSAndroid Build Coastguard Worker For each transfer, make sure it properly stashes the blocks it touches and 901*9e94795aSAndroid Build Coastguard Worker will be used by later transfers. It uses pairs of (stash_raw_id, range) to 902*9e94795aSAndroid Build Coastguard Worker record the blocks to be stashed. 'stash_raw_id' is an id that uniquely 903*9e94795aSAndroid Build Coastguard Worker identifies each pair. Note that for the same range (e.g. RangeSet("1-5")), 904*9e94795aSAndroid Build Coastguard Worker it is possible to have multiple pairs with different 'stash_raw_id's. Each 905*9e94795aSAndroid Build Coastguard Worker 'stash_raw_id' will be consumed by one transfer. In BBOTA v3+, identical 906*9e94795aSAndroid Build Coastguard Worker blocks will be written to the same stash slot in WriteTransfers(). 907*9e94795aSAndroid Build Coastguard Worker """ 908*9e94795aSAndroid Build Coastguard Worker 909*9e94795aSAndroid Build Coastguard Worker logger.info("Reversing backward edges...") 910*9e94795aSAndroid Build Coastguard Worker in_order = 0 911*9e94795aSAndroid Build Coastguard Worker out_of_order = 0 912*9e94795aSAndroid Build Coastguard Worker stash_raw_id = 0 913*9e94795aSAndroid Build Coastguard Worker stash_size = 0 914*9e94795aSAndroid Build Coastguard Worker 915*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 916*9e94795aSAndroid Build Coastguard Worker for u in xf.goes_before.copy(): 917*9e94795aSAndroid Build Coastguard Worker # xf should go before u 918*9e94795aSAndroid Build Coastguard Worker if xf.order < u.order: 919*9e94795aSAndroid Build Coastguard Worker # it does, hurray! 920*9e94795aSAndroid Build Coastguard Worker in_order += 1 921*9e94795aSAndroid Build Coastguard Worker else: 922*9e94795aSAndroid Build Coastguard Worker # it doesn't, boo. modify u to stash the blocks that it 923*9e94795aSAndroid Build Coastguard Worker # writes that xf wants to read, and then require u to go 924*9e94795aSAndroid Build Coastguard Worker # before xf. 925*9e94795aSAndroid Build Coastguard Worker out_of_order += 1 926*9e94795aSAndroid Build Coastguard Worker 927*9e94795aSAndroid Build Coastguard Worker overlap = xf.src_ranges.intersect(u.tgt_ranges) 928*9e94795aSAndroid Build Coastguard Worker assert overlap 929*9e94795aSAndroid Build Coastguard Worker 930*9e94795aSAndroid Build Coastguard Worker u.stash_before.append((stash_raw_id, overlap)) 931*9e94795aSAndroid Build Coastguard Worker xf.use_stash.append((stash_raw_id, overlap)) 932*9e94795aSAndroid Build Coastguard Worker stash_raw_id += 1 933*9e94795aSAndroid Build Coastguard Worker stash_size += overlap.size() 934*9e94795aSAndroid Build Coastguard Worker 935*9e94795aSAndroid Build Coastguard Worker # reverse the edge direction; now xf must go after u 936*9e94795aSAndroid Build Coastguard Worker del xf.goes_before[u] 937*9e94795aSAndroid Build Coastguard Worker del u.goes_after[xf] 938*9e94795aSAndroid Build Coastguard Worker xf.goes_after[u] = None # value doesn't matter 939*9e94795aSAndroid Build Coastguard Worker u.goes_before[xf] = None 940*9e94795aSAndroid Build Coastguard Worker 941*9e94795aSAndroid Build Coastguard Worker logger.info( 942*9e94795aSAndroid Build Coastguard Worker " %d/%d dependencies (%.2f%%) were violated; %d source blocks " 943*9e94795aSAndroid Build Coastguard Worker "stashed.", out_of_order, in_order + out_of_order, 944*9e94795aSAndroid Build Coastguard Worker (out_of_order * 100.0 / (in_order + out_of_order)) if ( 945*9e94795aSAndroid Build Coastguard Worker in_order + out_of_order) else 0.0, 946*9e94795aSAndroid Build Coastguard Worker stash_size) 947*9e94795aSAndroid Build Coastguard Worker 948*9e94795aSAndroid Build Coastguard Worker def FindVertexSequence(self): 949*9e94795aSAndroid Build Coastguard Worker logger.info("Finding vertex sequence...") 950*9e94795aSAndroid Build Coastguard Worker 951*9e94795aSAndroid Build Coastguard Worker # This is based on "A Fast & Effective Heuristic for the Feedback 952*9e94795aSAndroid Build Coastguard Worker # Arc Set Problem" by P. Eades, X. Lin, and W.F. Smyth. Think of 953*9e94795aSAndroid Build Coastguard Worker # it as starting with the digraph G and moving all the vertices to 954*9e94795aSAndroid Build Coastguard Worker # be on a horizontal line in some order, trying to minimize the 955*9e94795aSAndroid Build Coastguard Worker # number of edges that end up pointing to the left. Left-pointing 956*9e94795aSAndroid Build Coastguard Worker # edges will get removed to turn the digraph into a DAG. In this 957*9e94795aSAndroid Build Coastguard Worker # case each edge has a weight which is the number of source blocks 958*9e94795aSAndroid Build Coastguard Worker # we'll lose if that edge is removed; we try to minimize the total 959*9e94795aSAndroid Build Coastguard Worker # weight rather than just the number of edges. 960*9e94795aSAndroid Build Coastguard Worker 961*9e94795aSAndroid Build Coastguard Worker # Make a copy of the edge set; this copy will get destroyed by the 962*9e94795aSAndroid Build Coastguard Worker # algorithm. 963*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 964*9e94795aSAndroid Build Coastguard Worker xf.incoming = xf.goes_after.copy() 965*9e94795aSAndroid Build Coastguard Worker xf.outgoing = xf.goes_before.copy() 966*9e94795aSAndroid Build Coastguard Worker xf.score = sum(xf.outgoing.values()) - sum(xf.incoming.values()) 967*9e94795aSAndroid Build Coastguard Worker 968*9e94795aSAndroid Build Coastguard Worker # We use an OrderedDict instead of just a set so that the output 969*9e94795aSAndroid Build Coastguard Worker # is repeatable; otherwise it would depend on the hash values of 970*9e94795aSAndroid Build Coastguard Worker # the transfer objects. 971*9e94795aSAndroid Build Coastguard Worker G = OrderedDict() 972*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 973*9e94795aSAndroid Build Coastguard Worker G[xf] = None 974*9e94795aSAndroid Build Coastguard Worker s1 = deque() # the left side of the sequence, built from left to right 975*9e94795aSAndroid Build Coastguard Worker s2 = deque() # the right side of the sequence, built from right to left 976*9e94795aSAndroid Build Coastguard Worker 977*9e94795aSAndroid Build Coastguard Worker heap = [] 978*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 979*9e94795aSAndroid Build Coastguard Worker xf.heap_item = HeapItem(xf) 980*9e94795aSAndroid Build Coastguard Worker heap.append(xf.heap_item) 981*9e94795aSAndroid Build Coastguard Worker heapq.heapify(heap) 982*9e94795aSAndroid Build Coastguard Worker 983*9e94795aSAndroid Build Coastguard Worker # Use OrderedDict() instead of set() to preserve the insertion order. Need 984*9e94795aSAndroid Build Coastguard Worker # to use 'sinks[key] = None' to add key into the set. sinks will look like 985*9e94795aSAndroid Build Coastguard Worker # { key1: None, key2: None, ... }. 986*9e94795aSAndroid Build Coastguard Worker sinks = OrderedDict.fromkeys(u for u in G if not u.outgoing) 987*9e94795aSAndroid Build Coastguard Worker sources = OrderedDict.fromkeys(u for u in G if not u.incoming) 988*9e94795aSAndroid Build Coastguard Worker 989*9e94795aSAndroid Build Coastguard Worker def adjust_score(iu, delta): 990*9e94795aSAndroid Build Coastguard Worker iu.score += delta 991*9e94795aSAndroid Build Coastguard Worker iu.heap_item.clear() 992*9e94795aSAndroid Build Coastguard Worker iu.heap_item = HeapItem(iu) 993*9e94795aSAndroid Build Coastguard Worker heapq.heappush(heap, iu.heap_item) 994*9e94795aSAndroid Build Coastguard Worker 995*9e94795aSAndroid Build Coastguard Worker while G: 996*9e94795aSAndroid Build Coastguard Worker # Put all sinks at the end of the sequence. 997*9e94795aSAndroid Build Coastguard Worker while sinks: 998*9e94795aSAndroid Build Coastguard Worker new_sinks = OrderedDict() 999*9e94795aSAndroid Build Coastguard Worker for u in sinks: 1000*9e94795aSAndroid Build Coastguard Worker if u not in G: 1001*9e94795aSAndroid Build Coastguard Worker continue 1002*9e94795aSAndroid Build Coastguard Worker s2.appendleft(u) 1003*9e94795aSAndroid Build Coastguard Worker del G[u] 1004*9e94795aSAndroid Build Coastguard Worker for iu in u.incoming: 1005*9e94795aSAndroid Build Coastguard Worker adjust_score(iu, -iu.outgoing.pop(u)) 1006*9e94795aSAndroid Build Coastguard Worker if not iu.outgoing: 1007*9e94795aSAndroid Build Coastguard Worker new_sinks[iu] = None 1008*9e94795aSAndroid Build Coastguard Worker sinks = new_sinks 1009*9e94795aSAndroid Build Coastguard Worker 1010*9e94795aSAndroid Build Coastguard Worker # Put all the sources at the beginning of the sequence. 1011*9e94795aSAndroid Build Coastguard Worker while sources: 1012*9e94795aSAndroid Build Coastguard Worker new_sources = OrderedDict() 1013*9e94795aSAndroid Build Coastguard Worker for u in sources: 1014*9e94795aSAndroid Build Coastguard Worker if u not in G: 1015*9e94795aSAndroid Build Coastguard Worker continue 1016*9e94795aSAndroid Build Coastguard Worker s1.append(u) 1017*9e94795aSAndroid Build Coastguard Worker del G[u] 1018*9e94795aSAndroid Build Coastguard Worker for iu in u.outgoing: 1019*9e94795aSAndroid Build Coastguard Worker adjust_score(iu, +iu.incoming.pop(u)) 1020*9e94795aSAndroid Build Coastguard Worker if not iu.incoming: 1021*9e94795aSAndroid Build Coastguard Worker new_sources[iu] = None 1022*9e94795aSAndroid Build Coastguard Worker sources = new_sources 1023*9e94795aSAndroid Build Coastguard Worker 1024*9e94795aSAndroid Build Coastguard Worker if not G: 1025*9e94795aSAndroid Build Coastguard Worker break 1026*9e94795aSAndroid Build Coastguard Worker 1027*9e94795aSAndroid Build Coastguard Worker # Find the "best" vertex to put next. "Best" is the one that 1028*9e94795aSAndroid Build Coastguard Worker # maximizes the net difference in source blocks saved we get by 1029*9e94795aSAndroid Build Coastguard Worker # pretending it's a source rather than a sink. 1030*9e94795aSAndroid Build Coastguard Worker 1031*9e94795aSAndroid Build Coastguard Worker while True: 1032*9e94795aSAndroid Build Coastguard Worker u = heapq.heappop(heap) 1033*9e94795aSAndroid Build Coastguard Worker if u and u.item in G: 1034*9e94795aSAndroid Build Coastguard Worker u = u.item 1035*9e94795aSAndroid Build Coastguard Worker break 1036*9e94795aSAndroid Build Coastguard Worker 1037*9e94795aSAndroid Build Coastguard Worker s1.append(u) 1038*9e94795aSAndroid Build Coastguard Worker del G[u] 1039*9e94795aSAndroid Build Coastguard Worker for iu in u.outgoing: 1040*9e94795aSAndroid Build Coastguard Worker adjust_score(iu, +iu.incoming.pop(u)) 1041*9e94795aSAndroid Build Coastguard Worker if not iu.incoming: 1042*9e94795aSAndroid Build Coastguard Worker sources[iu] = None 1043*9e94795aSAndroid Build Coastguard Worker 1044*9e94795aSAndroid Build Coastguard Worker for iu in u.incoming: 1045*9e94795aSAndroid Build Coastguard Worker adjust_score(iu, -iu.outgoing.pop(u)) 1046*9e94795aSAndroid Build Coastguard Worker if not iu.outgoing: 1047*9e94795aSAndroid Build Coastguard Worker sinks[iu] = None 1048*9e94795aSAndroid Build Coastguard Worker 1049*9e94795aSAndroid Build Coastguard Worker # Now record the sequence in the 'order' field of each transfer, 1050*9e94795aSAndroid Build Coastguard Worker # and by rearranging self.transfers to be in the chosen sequence. 1051*9e94795aSAndroid Build Coastguard Worker 1052*9e94795aSAndroid Build Coastguard Worker new_transfers = [] 1053*9e94795aSAndroid Build Coastguard Worker for x in itertools.chain(s1, s2): 1054*9e94795aSAndroid Build Coastguard Worker x.order = len(new_transfers) 1055*9e94795aSAndroid Build Coastguard Worker new_transfers.append(x) 1056*9e94795aSAndroid Build Coastguard Worker del x.incoming 1057*9e94795aSAndroid Build Coastguard Worker del x.outgoing 1058*9e94795aSAndroid Build Coastguard Worker 1059*9e94795aSAndroid Build Coastguard Worker self.transfers = new_transfers 1060*9e94795aSAndroid Build Coastguard Worker 1061*9e94795aSAndroid Build Coastguard Worker def GenerateDigraph(self): 1062*9e94795aSAndroid Build Coastguard Worker logger.info("Generating digraph...") 1063*9e94795aSAndroid Build Coastguard Worker 1064*9e94795aSAndroid Build Coastguard Worker # Each item of source_ranges will be: 1065*9e94795aSAndroid Build Coastguard Worker # - None, if that block is not used as a source, 1066*9e94795aSAndroid Build Coastguard Worker # - an ordered set of transfers. 1067*9e94795aSAndroid Build Coastguard Worker source_ranges = [] 1068*9e94795aSAndroid Build Coastguard Worker for b in self.transfers: 1069*9e94795aSAndroid Build Coastguard Worker for s, e in b.src_ranges: 1070*9e94795aSAndroid Build Coastguard Worker if e > len(source_ranges): 1071*9e94795aSAndroid Build Coastguard Worker source_ranges.extend([None] * (e-len(source_ranges))) 1072*9e94795aSAndroid Build Coastguard Worker for i in range(s, e): 1073*9e94795aSAndroid Build Coastguard Worker if source_ranges[i] is None: 1074*9e94795aSAndroid Build Coastguard Worker source_ranges[i] = OrderedDict.fromkeys([b]) 1075*9e94795aSAndroid Build Coastguard Worker else: 1076*9e94795aSAndroid Build Coastguard Worker source_ranges[i][b] = None 1077*9e94795aSAndroid Build Coastguard Worker 1078*9e94795aSAndroid Build Coastguard Worker for a in self.transfers: 1079*9e94795aSAndroid Build Coastguard Worker intersections = OrderedDict() 1080*9e94795aSAndroid Build Coastguard Worker for s, e in a.tgt_ranges: 1081*9e94795aSAndroid Build Coastguard Worker for i in range(s, e): 1082*9e94795aSAndroid Build Coastguard Worker if i >= len(source_ranges): 1083*9e94795aSAndroid Build Coastguard Worker break 1084*9e94795aSAndroid Build Coastguard Worker # Add all the Transfers in source_ranges[i] to the (ordered) set. 1085*9e94795aSAndroid Build Coastguard Worker if source_ranges[i] is not None: 1086*9e94795aSAndroid Build Coastguard Worker for j in source_ranges[i]: 1087*9e94795aSAndroid Build Coastguard Worker intersections[j] = None 1088*9e94795aSAndroid Build Coastguard Worker 1089*9e94795aSAndroid Build Coastguard Worker for b in intersections: 1090*9e94795aSAndroid Build Coastguard Worker if a is b: 1091*9e94795aSAndroid Build Coastguard Worker continue 1092*9e94795aSAndroid Build Coastguard Worker 1093*9e94795aSAndroid Build Coastguard Worker # If the blocks written by A are read by B, then B needs to go before A. 1094*9e94795aSAndroid Build Coastguard Worker i = a.tgt_ranges.intersect(b.src_ranges) 1095*9e94795aSAndroid Build Coastguard Worker if i: 1096*9e94795aSAndroid Build Coastguard Worker if b.src_name == "__ZERO": 1097*9e94795aSAndroid Build Coastguard Worker # the cost of removing source blocks for the __ZERO domain 1098*9e94795aSAndroid Build Coastguard Worker # is (nearly) zero. 1099*9e94795aSAndroid Build Coastguard Worker size = 0 1100*9e94795aSAndroid Build Coastguard Worker else: 1101*9e94795aSAndroid Build Coastguard Worker size = i.size() 1102*9e94795aSAndroid Build Coastguard Worker b.goes_before[a] = size 1103*9e94795aSAndroid Build Coastguard Worker a.goes_after[b] = size 1104*9e94795aSAndroid Build Coastguard Worker 1105*9e94795aSAndroid Build Coastguard Worker def ComputePatchesForInputList(self, diff_queue, compress_target): 1106*9e94795aSAndroid Build Coastguard Worker """Returns a list of patch information for the input list of transfers. 1107*9e94795aSAndroid Build Coastguard Worker 1108*9e94795aSAndroid Build Coastguard Worker Args: 1109*9e94795aSAndroid Build Coastguard Worker diff_queue: a list of transfers with style 'diff' 1110*9e94795aSAndroid Build Coastguard Worker compress_target: If True, compresses the target ranges of each 1111*9e94795aSAndroid Build Coastguard Worker transfers; and save the size. 1112*9e94795aSAndroid Build Coastguard Worker 1113*9e94795aSAndroid Build Coastguard Worker Returns: 1114*9e94795aSAndroid Build Coastguard Worker A list of (transfer order, patch_info, compressed_size) tuples. 1115*9e94795aSAndroid Build Coastguard Worker """ 1116*9e94795aSAndroid Build Coastguard Worker 1117*9e94795aSAndroid Build Coastguard Worker if not diff_queue: 1118*9e94795aSAndroid Build Coastguard Worker return [] 1119*9e94795aSAndroid Build Coastguard Worker 1120*9e94795aSAndroid Build Coastguard Worker if self.threads > 1: 1121*9e94795aSAndroid Build Coastguard Worker logger.info("Computing patches (using %d threads)...", self.threads) 1122*9e94795aSAndroid Build Coastguard Worker else: 1123*9e94795aSAndroid Build Coastguard Worker logger.info("Computing patches...") 1124*9e94795aSAndroid Build Coastguard Worker 1125*9e94795aSAndroid Build Coastguard Worker diff_total = len(diff_queue) 1126*9e94795aSAndroid Build Coastguard Worker patches = [None] * diff_total 1127*9e94795aSAndroid Build Coastguard Worker error_messages = [] 1128*9e94795aSAndroid Build Coastguard Worker 1129*9e94795aSAndroid Build Coastguard Worker # Using multiprocessing doesn't give additional benefits, due to the 1130*9e94795aSAndroid Build Coastguard Worker # pattern of the code. The diffing work is done by subprocess.call, which 1131*9e94795aSAndroid Build Coastguard Worker # already runs in a separate process (not affected much by the GIL - 1132*9e94795aSAndroid Build Coastguard Worker # Global Interpreter Lock). Using multiprocess also requires either a) 1133*9e94795aSAndroid Build Coastguard Worker # writing the diff input files in the main process before forking, or b) 1134*9e94795aSAndroid Build Coastguard Worker # reopening the image file (SparseImage) in the worker processes. Doing 1135*9e94795aSAndroid Build Coastguard Worker # neither of them further improves the performance. 1136*9e94795aSAndroid Build Coastguard Worker lock = threading.Lock() 1137*9e94795aSAndroid Build Coastguard Worker 1138*9e94795aSAndroid Build Coastguard Worker def diff_worker(): 1139*9e94795aSAndroid Build Coastguard Worker while True: 1140*9e94795aSAndroid Build Coastguard Worker with lock: 1141*9e94795aSAndroid Build Coastguard Worker if not diff_queue: 1142*9e94795aSAndroid Build Coastguard Worker return 1143*9e94795aSAndroid Build Coastguard Worker xf_index, imgdiff, patch_index = diff_queue.pop() 1144*9e94795aSAndroid Build Coastguard Worker xf = self.transfers[xf_index] 1145*9e94795aSAndroid Build Coastguard Worker 1146*9e94795aSAndroid Build Coastguard Worker message = [] 1147*9e94795aSAndroid Build Coastguard Worker compressed_size = None 1148*9e94795aSAndroid Build Coastguard Worker 1149*9e94795aSAndroid Build Coastguard Worker patch_info = xf.patch_info 1150*9e94795aSAndroid Build Coastguard Worker if not patch_info: 1151*9e94795aSAndroid Build Coastguard Worker src_file = common.MakeTempFile(prefix="src-") 1152*9e94795aSAndroid Build Coastguard Worker with open(src_file, "wb") as fd: 1153*9e94795aSAndroid Build Coastguard Worker self.src.WriteRangeDataToFd(xf.src_ranges, fd) 1154*9e94795aSAndroid Build Coastguard Worker 1155*9e94795aSAndroid Build Coastguard Worker tgt_file = common.MakeTempFile(prefix="tgt-") 1156*9e94795aSAndroid Build Coastguard Worker with open(tgt_file, "wb") as fd: 1157*9e94795aSAndroid Build Coastguard Worker self.tgt.WriteRangeDataToFd(xf.tgt_ranges, fd) 1158*9e94795aSAndroid Build Coastguard Worker 1159*9e94795aSAndroid Build Coastguard Worker try: 1160*9e94795aSAndroid Build Coastguard Worker patch_info = compute_patch(src_file, tgt_file, imgdiff) 1161*9e94795aSAndroid Build Coastguard Worker except ValueError as e: 1162*9e94795aSAndroid Build Coastguard Worker message.append( 1163*9e94795aSAndroid Build Coastguard Worker "Failed to generate %s for %s: tgt=%s, src=%s:\n%s" % ( 1164*9e94795aSAndroid Build Coastguard Worker "imgdiff" if imgdiff else "bsdiff", 1165*9e94795aSAndroid Build Coastguard Worker xf.tgt_name if xf.tgt_name == xf.src_name else 1166*9e94795aSAndroid Build Coastguard Worker xf.tgt_name + " (from " + xf.src_name + ")", 1167*9e94795aSAndroid Build Coastguard Worker xf.tgt_ranges, xf.src_ranges, e.message)) 1168*9e94795aSAndroid Build Coastguard Worker 1169*9e94795aSAndroid Build Coastguard Worker if compress_target: 1170*9e94795aSAndroid Build Coastguard Worker tgt_data = self.tgt.ReadRangeSet(xf.tgt_ranges) 1171*9e94795aSAndroid Build Coastguard Worker try: 1172*9e94795aSAndroid Build Coastguard Worker # Compresses with the default level 1173*9e94795aSAndroid Build Coastguard Worker compress_obj = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS) 1174*9e94795aSAndroid Build Coastguard Worker compressed_data = (compress_obj.compress(b"".join(tgt_data)) 1175*9e94795aSAndroid Build Coastguard Worker + compress_obj.flush()) 1176*9e94795aSAndroid Build Coastguard Worker compressed_size = len(compressed_data) 1177*9e94795aSAndroid Build Coastguard Worker except zlib.error as e: 1178*9e94795aSAndroid Build Coastguard Worker message.append( 1179*9e94795aSAndroid Build Coastguard Worker "Failed to compress the data in target range {} for {}:\n" 1180*9e94795aSAndroid Build Coastguard Worker "{}".format(xf.tgt_ranges, xf.tgt_name, e.message)) 1181*9e94795aSAndroid Build Coastguard Worker 1182*9e94795aSAndroid Build Coastguard Worker if message: 1183*9e94795aSAndroid Build Coastguard Worker with lock: 1184*9e94795aSAndroid Build Coastguard Worker error_messages.extend(message) 1185*9e94795aSAndroid Build Coastguard Worker 1186*9e94795aSAndroid Build Coastguard Worker with lock: 1187*9e94795aSAndroid Build Coastguard Worker patches[patch_index] = (xf_index, patch_info, compressed_size) 1188*9e94795aSAndroid Build Coastguard Worker 1189*9e94795aSAndroid Build Coastguard Worker threads = [threading.Thread(target=diff_worker) 1190*9e94795aSAndroid Build Coastguard Worker for _ in range(self.threads)] 1191*9e94795aSAndroid Build Coastguard Worker for th in threads: 1192*9e94795aSAndroid Build Coastguard Worker th.start() 1193*9e94795aSAndroid Build Coastguard Worker while threads: 1194*9e94795aSAndroid Build Coastguard Worker threads.pop().join() 1195*9e94795aSAndroid Build Coastguard Worker 1196*9e94795aSAndroid Build Coastguard Worker if error_messages: 1197*9e94795aSAndroid Build Coastguard Worker logger.error('ERROR:') 1198*9e94795aSAndroid Build Coastguard Worker logger.error('\n'.join(error_messages)) 1199*9e94795aSAndroid Build Coastguard Worker logger.error('\n\n\n') 1200*9e94795aSAndroid Build Coastguard Worker sys.exit(1) 1201*9e94795aSAndroid Build Coastguard Worker 1202*9e94795aSAndroid Build Coastguard Worker return patches 1203*9e94795aSAndroid Build Coastguard Worker 1204*9e94795aSAndroid Build Coastguard Worker def SelectAndConvertDiffTransfersToNew(self, violated_stash_blocks): 1205*9e94795aSAndroid Build Coastguard Worker """Converts the diff transfers to reduce the max simultaneous stash. 1206*9e94795aSAndroid Build Coastguard Worker 1207*9e94795aSAndroid Build Coastguard Worker Since the 'new' data is compressed with deflate, we can select the 'diff' 1208*9e94795aSAndroid Build Coastguard Worker transfers for conversion by comparing its patch size with the size of the 1209*9e94795aSAndroid Build Coastguard Worker compressed data. Ideally, we want to convert the transfers with a small 1210*9e94795aSAndroid Build Coastguard Worker size increase, but using a large number of stashed blocks. 1211*9e94795aSAndroid Build Coastguard Worker """ 1212*9e94795aSAndroid Build Coastguard Worker TransferSizeScore = namedtuple("TransferSizeScore", 1213*9e94795aSAndroid Build Coastguard Worker "xf, used_stash_blocks, score") 1214*9e94795aSAndroid Build Coastguard Worker 1215*9e94795aSAndroid Build Coastguard Worker logger.info("Selecting diff commands to convert to new.") 1216*9e94795aSAndroid Build Coastguard Worker diff_queue = [] 1217*9e94795aSAndroid Build Coastguard Worker for xf in self.transfers: 1218*9e94795aSAndroid Build Coastguard Worker if xf.style == "diff" and xf.src_sha1 != xf.tgt_sha1: 1219*9e94795aSAndroid Build Coastguard Worker use_imgdiff = self.CanUseImgdiff(xf.tgt_name, xf.tgt_ranges, 1220*9e94795aSAndroid Build Coastguard Worker xf.src_ranges) 1221*9e94795aSAndroid Build Coastguard Worker diff_queue.append((xf.order, use_imgdiff, len(diff_queue))) 1222*9e94795aSAndroid Build Coastguard Worker 1223*9e94795aSAndroid Build Coastguard Worker # Remove the 'move' transfers, and compute the patch & compressed size 1224*9e94795aSAndroid Build Coastguard Worker # for the remaining. 1225*9e94795aSAndroid Build Coastguard Worker result = self.ComputePatchesForInputList(diff_queue, True) 1226*9e94795aSAndroid Build Coastguard Worker 1227*9e94795aSAndroid Build Coastguard Worker conversion_candidates = [] 1228*9e94795aSAndroid Build Coastguard Worker for xf_index, patch_info, compressed_size in result: 1229*9e94795aSAndroid Build Coastguard Worker xf = self.transfers[xf_index] 1230*9e94795aSAndroid Build Coastguard Worker if not xf.patch_info: 1231*9e94795aSAndroid Build Coastguard Worker xf.patch_info = patch_info 1232*9e94795aSAndroid Build Coastguard Worker 1233*9e94795aSAndroid Build Coastguard Worker size_ratio = len(xf.patch_info.content) * 100.0 / compressed_size 1234*9e94795aSAndroid Build Coastguard Worker diff_style = "imgdiff" if xf.patch_info.imgdiff else "bsdiff" 1235*9e94795aSAndroid Build Coastguard Worker logger.info("%s, target size: %d blocks, style: %s, patch size: %d," 1236*9e94795aSAndroid Build Coastguard Worker " compression_size: %d, ratio %.2f%%", xf.tgt_name, 1237*9e94795aSAndroid Build Coastguard Worker xf.tgt_ranges.size(), diff_style, 1238*9e94795aSAndroid Build Coastguard Worker len(xf.patch_info.content), compressed_size, size_ratio) 1239*9e94795aSAndroid Build Coastguard Worker 1240*9e94795aSAndroid Build Coastguard Worker used_stash_blocks = sum(sr.size() for _, sr in xf.use_stash) 1241*9e94795aSAndroid Build Coastguard Worker # Convert the transfer to new if the compressed size is smaller or equal. 1242*9e94795aSAndroid Build Coastguard Worker # We don't need to maintain the stash_before lists here because the 1243*9e94795aSAndroid Build Coastguard Worker # graph will be regenerated later. 1244*9e94795aSAndroid Build Coastguard Worker if len(xf.patch_info.content) >= compressed_size: 1245*9e94795aSAndroid Build Coastguard Worker # Add the transfer to the candidate list with negative score. And it 1246*9e94795aSAndroid Build Coastguard Worker # will be converted later. 1247*9e94795aSAndroid Build Coastguard Worker conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks, 1248*9e94795aSAndroid Build Coastguard Worker -1)) 1249*9e94795aSAndroid Build Coastguard Worker elif used_stash_blocks > 0: 1250*9e94795aSAndroid Build Coastguard Worker # This heuristic represents the size increase in the final package to 1251*9e94795aSAndroid Build Coastguard Worker # remove per unit of stashed data. 1252*9e94795aSAndroid Build Coastguard Worker score = ((compressed_size - len(xf.patch_info.content)) * 100.0 1253*9e94795aSAndroid Build Coastguard Worker / used_stash_blocks) 1254*9e94795aSAndroid Build Coastguard Worker conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks, 1255*9e94795aSAndroid Build Coastguard Worker score)) 1256*9e94795aSAndroid Build Coastguard Worker # Transfers with lower score (i.e. less expensive to convert) will be 1257*9e94795aSAndroid Build Coastguard Worker # converted first. 1258*9e94795aSAndroid Build Coastguard Worker conversion_candidates.sort(key=lambda x: x.score) 1259*9e94795aSAndroid Build Coastguard Worker 1260*9e94795aSAndroid Build Coastguard Worker # TODO(xunchang), improve the logic to find the transfers to convert, e.g. 1261*9e94795aSAndroid Build Coastguard Worker # convert the ones that contribute to the max stash, run ReviseStashSize 1262*9e94795aSAndroid Build Coastguard Worker # multiple times etc. 1263*9e94795aSAndroid Build Coastguard Worker removed_stashed_blocks = 0 1264*9e94795aSAndroid Build Coastguard Worker for xf, used_stash_blocks, _ in conversion_candidates: 1265*9e94795aSAndroid Build Coastguard Worker logger.info("Converting %s to new", xf.tgt_name) 1266*9e94795aSAndroid Build Coastguard Worker xf.ConvertToNew() 1267*9e94795aSAndroid Build Coastguard Worker removed_stashed_blocks += used_stash_blocks 1268*9e94795aSAndroid Build Coastguard Worker # Experiments show that we will get a smaller package size if we remove 1269*9e94795aSAndroid Build Coastguard Worker # slightly more stashed blocks than the violated stash blocks. 1270*9e94795aSAndroid Build Coastguard Worker if removed_stashed_blocks >= violated_stash_blocks: 1271*9e94795aSAndroid Build Coastguard Worker break 1272*9e94795aSAndroid Build Coastguard Worker 1273*9e94795aSAndroid Build Coastguard Worker logger.info("Removed %d stashed blocks", removed_stashed_blocks) 1274*9e94795aSAndroid Build Coastguard Worker 1275*9e94795aSAndroid Build Coastguard Worker def FindTransfers(self): 1276*9e94795aSAndroid Build Coastguard Worker """Parse the file_map to generate all the transfers.""" 1277*9e94795aSAndroid Build Coastguard Worker 1278*9e94795aSAndroid Build Coastguard Worker def AddSplitTransfersWithFixedSizeChunks(tgt_name, src_name, tgt_ranges, 1279*9e94795aSAndroid Build Coastguard Worker src_ranges, style, by_id): 1280*9e94795aSAndroid Build Coastguard Worker """Add one or multiple Transfer()s by splitting large files. 1281*9e94795aSAndroid Build Coastguard Worker 1282*9e94795aSAndroid Build Coastguard Worker For BBOTA v3, we need to stash source blocks for resumable feature. 1283*9e94795aSAndroid Build Coastguard Worker However, with the growth of file size and the shrink of the cache 1284*9e94795aSAndroid Build Coastguard Worker partition source blocks are too large to be stashed. If a file occupies 1285*9e94795aSAndroid Build Coastguard Worker too many blocks, we split it into smaller pieces by getting multiple 1286*9e94795aSAndroid Build Coastguard Worker Transfer()s. 1287*9e94795aSAndroid Build Coastguard Worker 1288*9e94795aSAndroid Build Coastguard Worker The downside is that after splitting, we may increase the package size 1289*9e94795aSAndroid Build Coastguard Worker since the split pieces don't align well. According to our experiments, 1290*9e94795aSAndroid Build Coastguard Worker 1/8 of the cache size as the per-piece limit appears to be optimal. 1291*9e94795aSAndroid Build Coastguard Worker Compared to the fixed 1024-block limit, it reduces the overall package 1292*9e94795aSAndroid Build Coastguard Worker size by 30% for volantis, and 20% for angler and bullhead.""" 1293*9e94795aSAndroid Build Coastguard Worker 1294*9e94795aSAndroid Build Coastguard Worker pieces = 0 1295*9e94795aSAndroid Build Coastguard Worker while (tgt_ranges.size() > max_blocks_per_transfer and 1296*9e94795aSAndroid Build Coastguard Worker src_ranges.size() > max_blocks_per_transfer): 1297*9e94795aSAndroid Build Coastguard Worker tgt_split_name = "%s-%d" % (tgt_name, pieces) 1298*9e94795aSAndroid Build Coastguard Worker src_split_name = "%s-%d" % (src_name, pieces) 1299*9e94795aSAndroid Build Coastguard Worker tgt_first = tgt_ranges.first(max_blocks_per_transfer) 1300*9e94795aSAndroid Build Coastguard Worker src_first = src_ranges.first(max_blocks_per_transfer) 1301*9e94795aSAndroid Build Coastguard Worker 1302*9e94795aSAndroid Build Coastguard Worker Transfer(tgt_split_name, src_split_name, tgt_first, src_first, 1303*9e94795aSAndroid Build Coastguard Worker self.tgt.RangeSha1(tgt_first), self.src.RangeSha1(src_first), 1304*9e94795aSAndroid Build Coastguard Worker style, by_id) 1305*9e94795aSAndroid Build Coastguard Worker 1306*9e94795aSAndroid Build Coastguard Worker tgt_ranges = tgt_ranges.subtract(tgt_first) 1307*9e94795aSAndroid Build Coastguard Worker src_ranges = src_ranges.subtract(src_first) 1308*9e94795aSAndroid Build Coastguard Worker pieces += 1 1309*9e94795aSAndroid Build Coastguard Worker 1310*9e94795aSAndroid Build Coastguard Worker # Handle remaining blocks. 1311*9e94795aSAndroid Build Coastguard Worker if tgt_ranges.size() or src_ranges.size(): 1312*9e94795aSAndroid Build Coastguard Worker # Must be both non-empty. 1313*9e94795aSAndroid Build Coastguard Worker assert tgt_ranges.size() and src_ranges.size() 1314*9e94795aSAndroid Build Coastguard Worker tgt_split_name = "%s-%d" % (tgt_name, pieces) 1315*9e94795aSAndroid Build Coastguard Worker src_split_name = "%s-%d" % (src_name, pieces) 1316*9e94795aSAndroid Build Coastguard Worker Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges, 1317*9e94795aSAndroid Build Coastguard Worker self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges), 1318*9e94795aSAndroid Build Coastguard Worker style, by_id) 1319*9e94795aSAndroid Build Coastguard Worker 1320*9e94795aSAndroid Build Coastguard Worker def AddSplitTransfers(tgt_name, src_name, tgt_ranges, src_ranges, style, 1321*9e94795aSAndroid Build Coastguard Worker by_id): 1322*9e94795aSAndroid Build Coastguard Worker """Find all the zip files and split the others with a fixed chunk size. 1323*9e94795aSAndroid Build Coastguard Worker 1324*9e94795aSAndroid Build Coastguard Worker This function will construct a list of zip archives, which will later be 1325*9e94795aSAndroid Build Coastguard Worker split by imgdiff to reduce the final patch size. For the other files, 1326*9e94795aSAndroid Build Coastguard Worker we will plainly split them based on a fixed chunk size with the potential 1327*9e94795aSAndroid Build Coastguard Worker patch size penalty. 1328*9e94795aSAndroid Build Coastguard Worker """ 1329*9e94795aSAndroid Build Coastguard Worker 1330*9e94795aSAndroid Build Coastguard Worker assert style == "diff" 1331*9e94795aSAndroid Build Coastguard Worker 1332*9e94795aSAndroid Build Coastguard Worker # Change nothing for small files. 1333*9e94795aSAndroid Build Coastguard Worker if (tgt_ranges.size() <= max_blocks_per_transfer and 1334*9e94795aSAndroid Build Coastguard Worker src_ranges.size() <= max_blocks_per_transfer): 1335*9e94795aSAndroid Build Coastguard Worker Transfer(tgt_name, src_name, tgt_ranges, src_ranges, 1336*9e94795aSAndroid Build Coastguard Worker self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges), 1337*9e94795aSAndroid Build Coastguard Worker style, by_id) 1338*9e94795aSAndroid Build Coastguard Worker return 1339*9e94795aSAndroid Build Coastguard Worker 1340*9e94795aSAndroid Build Coastguard Worker # Split large APKs with imgdiff, if possible. We're intentionally checking 1341*9e94795aSAndroid Build Coastguard Worker # file types one more time (CanUseImgdiff() checks that as well), before 1342*9e94795aSAndroid Build Coastguard Worker # calling the costly RangeSha1()s. 1343*9e94795aSAndroid Build Coastguard Worker if (self.FileTypeSupportedByImgdiff(tgt_name) and 1344*9e94795aSAndroid Build Coastguard Worker self.tgt.RangeSha1(tgt_ranges) != self.src.RangeSha1(src_ranges)): 1345*9e94795aSAndroid Build Coastguard Worker if self.CanUseImgdiff(tgt_name, tgt_ranges, src_ranges, True): 1346*9e94795aSAndroid Build Coastguard Worker large_apks.append((tgt_name, src_name, tgt_ranges, src_ranges)) 1347*9e94795aSAndroid Build Coastguard Worker return 1348*9e94795aSAndroid Build Coastguard Worker 1349*9e94795aSAndroid Build Coastguard Worker AddSplitTransfersWithFixedSizeChunks(tgt_name, src_name, tgt_ranges, 1350*9e94795aSAndroid Build Coastguard Worker src_ranges, style, by_id) 1351*9e94795aSAndroid Build Coastguard Worker 1352*9e94795aSAndroid Build Coastguard Worker def AddTransfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id, 1353*9e94795aSAndroid Build Coastguard Worker split=False): 1354*9e94795aSAndroid Build Coastguard Worker """Wrapper function for adding a Transfer().""" 1355*9e94795aSAndroid Build Coastguard Worker 1356*9e94795aSAndroid Build Coastguard Worker # We specialize diff transfers only (which covers bsdiff/imgdiff/move); 1357*9e94795aSAndroid Build Coastguard Worker # otherwise add the Transfer() as is. 1358*9e94795aSAndroid Build Coastguard Worker if style != "diff" or not split: 1359*9e94795aSAndroid Build Coastguard Worker Transfer(tgt_name, src_name, tgt_ranges, src_ranges, 1360*9e94795aSAndroid Build Coastguard Worker self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges), 1361*9e94795aSAndroid Build Coastguard Worker style, by_id) 1362*9e94795aSAndroid Build Coastguard Worker return 1363*9e94795aSAndroid Build Coastguard Worker 1364*9e94795aSAndroid Build Coastguard Worker # Handle .odex files specially to analyze the block-wise difference. If 1365*9e94795aSAndroid Build Coastguard Worker # most of the blocks are identical with only few changes (e.g. header), 1366*9e94795aSAndroid Build Coastguard Worker # we will patch the changed blocks only. This avoids stashing unchanged 1367*9e94795aSAndroid Build Coastguard Worker # blocks while patching. We limit the analysis to files without size 1368*9e94795aSAndroid Build Coastguard Worker # changes only. This is to avoid sacrificing the OTA generation cost too 1369*9e94795aSAndroid Build Coastguard Worker # much. 1370*9e94795aSAndroid Build Coastguard Worker if (tgt_name.split(".")[-1].lower() == 'odex' and 1371*9e94795aSAndroid Build Coastguard Worker tgt_ranges.size() == src_ranges.size()): 1372*9e94795aSAndroid Build Coastguard Worker 1373*9e94795aSAndroid Build Coastguard Worker # 0.5 threshold can be further tuned. The tradeoff is: if only very 1374*9e94795aSAndroid Build Coastguard Worker # few blocks remain identical, we lose the opportunity to use imgdiff 1375*9e94795aSAndroid Build Coastguard Worker # that may have better compression ratio than bsdiff. 1376*9e94795aSAndroid Build Coastguard Worker crop_threshold = 0.5 1377*9e94795aSAndroid Build Coastguard Worker 1378*9e94795aSAndroid Build Coastguard Worker tgt_skipped = RangeSet() 1379*9e94795aSAndroid Build Coastguard Worker src_skipped = RangeSet() 1380*9e94795aSAndroid Build Coastguard Worker tgt_size = tgt_ranges.size() 1381*9e94795aSAndroid Build Coastguard Worker tgt_changed = 0 1382*9e94795aSAndroid Build Coastguard Worker for src_block, tgt_block in zip(src_ranges.next_item(), 1383*9e94795aSAndroid Build Coastguard Worker tgt_ranges.next_item()): 1384*9e94795aSAndroid Build Coastguard Worker src_rs = RangeSet(str(src_block)) 1385*9e94795aSAndroid Build Coastguard Worker tgt_rs = RangeSet(str(tgt_block)) 1386*9e94795aSAndroid Build Coastguard Worker if self.src.ReadRangeSet(src_rs) == self.tgt.ReadRangeSet(tgt_rs): 1387*9e94795aSAndroid Build Coastguard Worker tgt_skipped = tgt_skipped.union(tgt_rs) 1388*9e94795aSAndroid Build Coastguard Worker src_skipped = src_skipped.union(src_rs) 1389*9e94795aSAndroid Build Coastguard Worker else: 1390*9e94795aSAndroid Build Coastguard Worker tgt_changed += tgt_rs.size() 1391*9e94795aSAndroid Build Coastguard Worker 1392*9e94795aSAndroid Build Coastguard Worker # Terminate early if no clear sign of benefits. 1393*9e94795aSAndroid Build Coastguard Worker if tgt_changed > tgt_size * crop_threshold: 1394*9e94795aSAndroid Build Coastguard Worker break 1395*9e94795aSAndroid Build Coastguard Worker 1396*9e94795aSAndroid Build Coastguard Worker if tgt_changed < tgt_size * crop_threshold: 1397*9e94795aSAndroid Build Coastguard Worker assert tgt_changed + tgt_skipped.size() == tgt_size 1398*9e94795aSAndroid Build Coastguard Worker logger.info( 1399*9e94795aSAndroid Build Coastguard Worker '%10d %10d (%6.2f%%) %s', tgt_skipped.size(), tgt_size, 1400*9e94795aSAndroid Build Coastguard Worker tgt_skipped.size() * 100.0 / tgt_size, tgt_name) 1401*9e94795aSAndroid Build Coastguard Worker AddSplitTransfers( 1402*9e94795aSAndroid Build Coastguard Worker "%s-skipped" % (tgt_name,), 1403*9e94795aSAndroid Build Coastguard Worker "%s-skipped" % (src_name,), 1404*9e94795aSAndroid Build Coastguard Worker tgt_skipped, src_skipped, style, by_id) 1405*9e94795aSAndroid Build Coastguard Worker 1406*9e94795aSAndroid Build Coastguard Worker # Intentionally change the file extension to avoid being imgdiff'd as 1407*9e94795aSAndroid Build Coastguard Worker # the files are no longer in their original format. 1408*9e94795aSAndroid Build Coastguard Worker tgt_name = "%s-cropped" % (tgt_name,) 1409*9e94795aSAndroid Build Coastguard Worker src_name = "%s-cropped" % (src_name,) 1410*9e94795aSAndroid Build Coastguard Worker tgt_ranges = tgt_ranges.subtract(tgt_skipped) 1411*9e94795aSAndroid Build Coastguard Worker src_ranges = src_ranges.subtract(src_skipped) 1412*9e94795aSAndroid Build Coastguard Worker 1413*9e94795aSAndroid Build Coastguard Worker # Possibly having no changed blocks. 1414*9e94795aSAndroid Build Coastguard Worker if not tgt_ranges: 1415*9e94795aSAndroid Build Coastguard Worker return 1416*9e94795aSAndroid Build Coastguard Worker 1417*9e94795aSAndroid Build Coastguard Worker # Add the transfer(s). 1418*9e94795aSAndroid Build Coastguard Worker AddSplitTransfers( 1419*9e94795aSAndroid Build Coastguard Worker tgt_name, src_name, tgt_ranges, src_ranges, style, by_id) 1420*9e94795aSAndroid Build Coastguard Worker 1421*9e94795aSAndroid Build Coastguard Worker def ParseAndValidateSplitInfo(patch_size, tgt_ranges, src_ranges, 1422*9e94795aSAndroid Build Coastguard Worker split_info): 1423*9e94795aSAndroid Build Coastguard Worker """Parse the split_info and return a list of info tuples. 1424*9e94795aSAndroid Build Coastguard Worker 1425*9e94795aSAndroid Build Coastguard Worker Args: 1426*9e94795aSAndroid Build Coastguard Worker patch_size: total size of the patch file. 1427*9e94795aSAndroid Build Coastguard Worker tgt_ranges: Ranges of the target file within the original image. 1428*9e94795aSAndroid Build Coastguard Worker src_ranges: Ranges of the source file within the original image. 1429*9e94795aSAndroid Build Coastguard Worker split_info format: 1430*9e94795aSAndroid Build Coastguard Worker imgdiff version# 1431*9e94795aSAndroid Build Coastguard Worker count of pieces 1432*9e94795aSAndroid Build Coastguard Worker <patch_size_1> <tgt_size_1> <src_ranges_1> 1433*9e94795aSAndroid Build Coastguard Worker ... 1434*9e94795aSAndroid Build Coastguard Worker <patch_size_n> <tgt_size_n> <src_ranges_n> 1435*9e94795aSAndroid Build Coastguard Worker 1436*9e94795aSAndroid Build Coastguard Worker Returns: 1437*9e94795aSAndroid Build Coastguard Worker [patch_start, patch_len, split_tgt_ranges, split_src_ranges] 1438*9e94795aSAndroid Build Coastguard Worker """ 1439*9e94795aSAndroid Build Coastguard Worker 1440*9e94795aSAndroid Build Coastguard Worker version = int(split_info[0]) 1441*9e94795aSAndroid Build Coastguard Worker assert version == 2 1442*9e94795aSAndroid Build Coastguard Worker count = int(split_info[1]) 1443*9e94795aSAndroid Build Coastguard Worker assert len(split_info) - 2 == count 1444*9e94795aSAndroid Build Coastguard Worker 1445*9e94795aSAndroid Build Coastguard Worker split_info_list = [] 1446*9e94795aSAndroid Build Coastguard Worker patch_start = 0 1447*9e94795aSAndroid Build Coastguard Worker tgt_remain = copy.deepcopy(tgt_ranges) 1448*9e94795aSAndroid Build Coastguard Worker # each line has the format <patch_size>, <tgt_size>, <src_ranges> 1449*9e94795aSAndroid Build Coastguard Worker for line in split_info[2:]: 1450*9e94795aSAndroid Build Coastguard Worker info = line.split() 1451*9e94795aSAndroid Build Coastguard Worker assert len(info) == 3 1452*9e94795aSAndroid Build Coastguard Worker patch_length = int(info[0]) 1453*9e94795aSAndroid Build Coastguard Worker 1454*9e94795aSAndroid Build Coastguard Worker split_tgt_size = int(info[1]) 1455*9e94795aSAndroid Build Coastguard Worker assert split_tgt_size % 4096 == 0 1456*9e94795aSAndroid Build Coastguard Worker assert split_tgt_size // 4096 <= tgt_remain.size() 1457*9e94795aSAndroid Build Coastguard Worker split_tgt_ranges = tgt_remain.first(split_tgt_size // 4096) 1458*9e94795aSAndroid Build Coastguard Worker tgt_remain = tgt_remain.subtract(split_tgt_ranges) 1459*9e94795aSAndroid Build Coastguard Worker 1460*9e94795aSAndroid Build Coastguard Worker # Find the split_src_ranges within the image file from its relative 1461*9e94795aSAndroid Build Coastguard Worker # position in file. 1462*9e94795aSAndroid Build Coastguard Worker split_src_indices = RangeSet.parse_raw(info[2]) 1463*9e94795aSAndroid Build Coastguard Worker split_src_ranges = RangeSet() 1464*9e94795aSAndroid Build Coastguard Worker for r in split_src_indices: 1465*9e94795aSAndroid Build Coastguard Worker curr_range = src_ranges.first(r[1]).subtract(src_ranges.first(r[0])) 1466*9e94795aSAndroid Build Coastguard Worker assert not split_src_ranges.overlaps(curr_range) 1467*9e94795aSAndroid Build Coastguard Worker split_src_ranges = split_src_ranges.union(curr_range) 1468*9e94795aSAndroid Build Coastguard Worker 1469*9e94795aSAndroid Build Coastguard Worker split_info_list.append((patch_start, patch_length, 1470*9e94795aSAndroid Build Coastguard Worker split_tgt_ranges, split_src_ranges)) 1471*9e94795aSAndroid Build Coastguard Worker patch_start += patch_length 1472*9e94795aSAndroid Build Coastguard Worker 1473*9e94795aSAndroid Build Coastguard Worker # Check that the sizes of all the split pieces add up to the final file 1474*9e94795aSAndroid Build Coastguard Worker # size for patch and target. 1475*9e94795aSAndroid Build Coastguard Worker assert tgt_remain.size() == 0 1476*9e94795aSAndroid Build Coastguard Worker assert patch_start == patch_size 1477*9e94795aSAndroid Build Coastguard Worker return split_info_list 1478*9e94795aSAndroid Build Coastguard Worker 1479*9e94795aSAndroid Build Coastguard Worker def SplitLargeApks(): 1480*9e94795aSAndroid Build Coastguard Worker """Split the large apks files. 1481*9e94795aSAndroid Build Coastguard Worker 1482*9e94795aSAndroid Build Coastguard Worker Example: Chrome.apk will be split into 1483*9e94795aSAndroid Build Coastguard Worker src-0: Chrome.apk-0, tgt-0: Chrome.apk-0 1484*9e94795aSAndroid Build Coastguard Worker src-1: Chrome.apk-1, tgt-1: Chrome.apk-1 1485*9e94795aSAndroid Build Coastguard Worker ... 1486*9e94795aSAndroid Build Coastguard Worker 1487*9e94795aSAndroid Build Coastguard Worker After the split, the target pieces are continuous and block aligned; and 1488*9e94795aSAndroid Build Coastguard Worker the source pieces are mutually exclusive. During the split, we also 1489*9e94795aSAndroid Build Coastguard Worker generate and save the image patch between src-X & tgt-X. This patch will 1490*9e94795aSAndroid Build Coastguard Worker be valid because the block ranges of src-X & tgt-X will always stay the 1491*9e94795aSAndroid Build Coastguard Worker same afterwards; but there's a chance we don't use the patch if we 1492*9e94795aSAndroid Build Coastguard Worker convert the "diff" command into "new" or "move" later. 1493*9e94795aSAndroid Build Coastguard Worker """ 1494*9e94795aSAndroid Build Coastguard Worker 1495*9e94795aSAndroid Build Coastguard Worker while True: 1496*9e94795aSAndroid Build Coastguard Worker with transfer_lock: 1497*9e94795aSAndroid Build Coastguard Worker if not large_apks: 1498*9e94795aSAndroid Build Coastguard Worker return 1499*9e94795aSAndroid Build Coastguard Worker tgt_name, src_name, tgt_ranges, src_ranges = large_apks.pop(0) 1500*9e94795aSAndroid Build Coastguard Worker 1501*9e94795aSAndroid Build Coastguard Worker src_file = common.MakeTempFile(prefix="src-") 1502*9e94795aSAndroid Build Coastguard Worker tgt_file = common.MakeTempFile(prefix="tgt-") 1503*9e94795aSAndroid Build Coastguard Worker with open(src_file, "wb") as src_fd: 1504*9e94795aSAndroid Build Coastguard Worker self.src.WriteRangeDataToFd(src_ranges, src_fd) 1505*9e94795aSAndroid Build Coastguard Worker with open(tgt_file, "wb") as tgt_fd: 1506*9e94795aSAndroid Build Coastguard Worker self.tgt.WriteRangeDataToFd(tgt_ranges, tgt_fd) 1507*9e94795aSAndroid Build Coastguard Worker 1508*9e94795aSAndroid Build Coastguard Worker patch_file = common.MakeTempFile(prefix="patch-") 1509*9e94795aSAndroid Build Coastguard Worker patch_info_file = common.MakeTempFile(prefix="split_info-") 1510*9e94795aSAndroid Build Coastguard Worker cmd = ["imgdiff", "-z", 1511*9e94795aSAndroid Build Coastguard Worker "--block-limit={}".format(max_blocks_per_transfer), 1512*9e94795aSAndroid Build Coastguard Worker "--split-info=" + patch_info_file, 1513*9e94795aSAndroid Build Coastguard Worker src_file, tgt_file, patch_file] 1514*9e94795aSAndroid Build Coastguard Worker proc = common.Run(cmd) 1515*9e94795aSAndroid Build Coastguard Worker imgdiff_output, _ = proc.communicate() 1516*9e94795aSAndroid Build Coastguard Worker assert proc.returncode == 0, \ 1517*9e94795aSAndroid Build Coastguard Worker "Failed to create imgdiff patch between {} and {}:\n{}".format( 1518*9e94795aSAndroid Build Coastguard Worker src_name, tgt_name, imgdiff_output) 1519*9e94795aSAndroid Build Coastguard Worker 1520*9e94795aSAndroid Build Coastguard Worker with open(patch_info_file) as patch_info: 1521*9e94795aSAndroid Build Coastguard Worker lines = patch_info.readlines() 1522*9e94795aSAndroid Build Coastguard Worker 1523*9e94795aSAndroid Build Coastguard Worker patch_size_total = os.path.getsize(patch_file) 1524*9e94795aSAndroid Build Coastguard Worker split_info_list = ParseAndValidateSplitInfo(patch_size_total, 1525*9e94795aSAndroid Build Coastguard Worker tgt_ranges, src_ranges, 1526*9e94795aSAndroid Build Coastguard Worker lines) 1527*9e94795aSAndroid Build Coastguard Worker for index, (patch_start, patch_length, split_tgt_ranges, 1528*9e94795aSAndroid Build Coastguard Worker split_src_ranges) in enumerate(split_info_list): 1529*9e94795aSAndroid Build Coastguard Worker with open(patch_file, 'rb') as f: 1530*9e94795aSAndroid Build Coastguard Worker f.seek(patch_start) 1531*9e94795aSAndroid Build Coastguard Worker patch_content = f.read(patch_length) 1532*9e94795aSAndroid Build Coastguard Worker 1533*9e94795aSAndroid Build Coastguard Worker split_src_name = "{}-{}".format(src_name, index) 1534*9e94795aSAndroid Build Coastguard Worker split_tgt_name = "{}-{}".format(tgt_name, index) 1535*9e94795aSAndroid Build Coastguard Worker split_large_apks.append((split_tgt_name, 1536*9e94795aSAndroid Build Coastguard Worker split_src_name, 1537*9e94795aSAndroid Build Coastguard Worker split_tgt_ranges, 1538*9e94795aSAndroid Build Coastguard Worker split_src_ranges, 1539*9e94795aSAndroid Build Coastguard Worker patch_content)) 1540*9e94795aSAndroid Build Coastguard Worker 1541*9e94795aSAndroid Build Coastguard Worker logger.info("Finding transfers...") 1542*9e94795aSAndroid Build Coastguard Worker 1543*9e94795aSAndroid Build Coastguard Worker large_apks = [] 1544*9e94795aSAndroid Build Coastguard Worker split_large_apks = [] 1545*9e94795aSAndroid Build Coastguard Worker cache_size = common.OPTIONS.cache_size 1546*9e94795aSAndroid Build Coastguard Worker split_threshold = 0.125 1547*9e94795aSAndroid Build Coastguard Worker assert cache_size is not None 1548*9e94795aSAndroid Build Coastguard Worker max_blocks_per_transfer = int(cache_size * split_threshold / 1549*9e94795aSAndroid Build Coastguard Worker self.tgt.blocksize) 1550*9e94795aSAndroid Build Coastguard Worker empty = RangeSet() 1551*9e94795aSAndroid Build Coastguard Worker for tgt_fn, tgt_ranges in sorted(self.tgt.file_map.items()): 1552*9e94795aSAndroid Build Coastguard Worker if tgt_fn == "__ZERO": 1553*9e94795aSAndroid Build Coastguard Worker # the special "__ZERO" domain is all the blocks not contained 1554*9e94795aSAndroid Build Coastguard Worker # in any file and that are filled with zeros. We have a 1555*9e94795aSAndroid Build Coastguard Worker # special transfer style for zero blocks. 1556*9e94795aSAndroid Build Coastguard Worker src_ranges = self.src.file_map.get("__ZERO", empty) 1557*9e94795aSAndroid Build Coastguard Worker AddTransfer(tgt_fn, "__ZERO", tgt_ranges, src_ranges, 1558*9e94795aSAndroid Build Coastguard Worker "zero", self.transfers) 1559*9e94795aSAndroid Build Coastguard Worker continue 1560*9e94795aSAndroid Build Coastguard Worker 1561*9e94795aSAndroid Build Coastguard Worker elif tgt_fn == "__COPY": 1562*9e94795aSAndroid Build Coastguard Worker # "__COPY" domain includes all the blocks not contained in any 1563*9e94795aSAndroid Build Coastguard Worker # file and that need to be copied unconditionally to the target. 1564*9e94795aSAndroid Build Coastguard Worker AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers) 1565*9e94795aSAndroid Build Coastguard Worker continue 1566*9e94795aSAndroid Build Coastguard Worker 1567*9e94795aSAndroid Build Coastguard Worker elif tgt_fn == "__HASHTREE": 1568*9e94795aSAndroid Build Coastguard Worker continue 1569*9e94795aSAndroid Build Coastguard Worker 1570*9e94795aSAndroid Build Coastguard Worker elif tgt_fn in self.src.file_map: 1571*9e94795aSAndroid Build Coastguard Worker # Look for an exact pathname match in the source. 1572*9e94795aSAndroid Build Coastguard Worker AddTransfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn], 1573*9e94795aSAndroid Build Coastguard Worker "diff", self.transfers, True) 1574*9e94795aSAndroid Build Coastguard Worker continue 1575*9e94795aSAndroid Build Coastguard Worker 1576*9e94795aSAndroid Build Coastguard Worker b = os.path.basename(tgt_fn) 1577*9e94795aSAndroid Build Coastguard Worker if b in self.src_basenames: 1578*9e94795aSAndroid Build Coastguard Worker # Look for an exact basename match in the source. 1579*9e94795aSAndroid Build Coastguard Worker src_fn = self.src_basenames[b] 1580*9e94795aSAndroid Build Coastguard Worker AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn], 1581*9e94795aSAndroid Build Coastguard Worker "diff", self.transfers, True) 1582*9e94795aSAndroid Build Coastguard Worker continue 1583*9e94795aSAndroid Build Coastguard Worker 1584*9e94795aSAndroid Build Coastguard Worker b = re.sub("[0-9]+", "#", b) 1585*9e94795aSAndroid Build Coastguard Worker if b in self.src_numpatterns: 1586*9e94795aSAndroid Build Coastguard Worker # Look for a 'number pattern' match (a basename match after 1587*9e94795aSAndroid Build Coastguard Worker # all runs of digits are replaced by "#"). (This is useful 1588*9e94795aSAndroid Build Coastguard Worker # for .so files that contain version numbers in the filename 1589*9e94795aSAndroid Build Coastguard Worker # that get bumped.) 1590*9e94795aSAndroid Build Coastguard Worker src_fn = self.src_numpatterns[b] 1591*9e94795aSAndroid Build Coastguard Worker AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn], 1592*9e94795aSAndroid Build Coastguard Worker "diff", self.transfers, True) 1593*9e94795aSAndroid Build Coastguard Worker continue 1594*9e94795aSAndroid Build Coastguard Worker 1595*9e94795aSAndroid Build Coastguard Worker AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers) 1596*9e94795aSAndroid Build Coastguard Worker 1597*9e94795aSAndroid Build Coastguard Worker transfer_lock = threading.Lock() 1598*9e94795aSAndroid Build Coastguard Worker threads = [threading.Thread(target=SplitLargeApks) 1599*9e94795aSAndroid Build Coastguard Worker for _ in range(self.threads)] 1600*9e94795aSAndroid Build Coastguard Worker for th in threads: 1601*9e94795aSAndroid Build Coastguard Worker th.start() 1602*9e94795aSAndroid Build Coastguard Worker while threads: 1603*9e94795aSAndroid Build Coastguard Worker threads.pop().join() 1604*9e94795aSAndroid Build Coastguard Worker 1605*9e94795aSAndroid Build Coastguard Worker # Sort the split transfers for large apks to generate a determinate package. 1606*9e94795aSAndroid Build Coastguard Worker split_large_apks.sort() 1607*9e94795aSAndroid Build Coastguard Worker for (tgt_name, src_name, tgt_ranges, src_ranges, 1608*9e94795aSAndroid Build Coastguard Worker patch) in split_large_apks: 1609*9e94795aSAndroid Build Coastguard Worker transfer_split = Transfer(tgt_name, src_name, tgt_ranges, src_ranges, 1610*9e94795aSAndroid Build Coastguard Worker self.tgt.RangeSha1(tgt_ranges), 1611*9e94795aSAndroid Build Coastguard Worker self.src.RangeSha1(src_ranges), 1612*9e94795aSAndroid Build Coastguard Worker "diff", self.transfers) 1613*9e94795aSAndroid Build Coastguard Worker transfer_split.patch_info = PatchInfo(True, patch) 1614*9e94795aSAndroid Build Coastguard Worker 1615*9e94795aSAndroid Build Coastguard Worker def AbbreviateSourceNames(self): 1616*9e94795aSAndroid Build Coastguard Worker for k in self.src.file_map.keys(): 1617*9e94795aSAndroid Build Coastguard Worker b = os.path.basename(k) 1618*9e94795aSAndroid Build Coastguard Worker self.src_basenames[b] = k 1619*9e94795aSAndroid Build Coastguard Worker b = re.sub("[0-9]+", "#", b) 1620*9e94795aSAndroid Build Coastguard Worker self.src_numpatterns[b] = k 1621*9e94795aSAndroid Build Coastguard Worker 1622*9e94795aSAndroid Build Coastguard Worker @staticmethod 1623*9e94795aSAndroid Build Coastguard Worker def AssertPartition(total, seq): 1624*9e94795aSAndroid Build Coastguard Worker """Assert that all the RangeSets in 'seq' form a partition of the 1625*9e94795aSAndroid Build Coastguard Worker 'total' RangeSet (ie, they are nonintersecting and their union 1626*9e94795aSAndroid Build Coastguard Worker equals 'total').""" 1627*9e94795aSAndroid Build Coastguard Worker 1628*9e94795aSAndroid Build Coastguard Worker so_far = RangeSet() 1629*9e94795aSAndroid Build Coastguard Worker for i in seq: 1630*9e94795aSAndroid Build Coastguard Worker assert not so_far.overlaps(i) 1631*9e94795aSAndroid Build Coastguard Worker so_far = so_far.union(i) 1632*9e94795aSAndroid Build Coastguard Worker assert so_far == total 1633