Merge "blockimgdiff.py: Clean up stash id computation in BBOTA v3+."

am: 7cfc591596

Change-Id: Ifaf7f387b840d9f3e59bdbf93beab41a245f7ed5
This commit is contained in:
Tao Bao 2017-01-03 23:48:21 +00:00 committed by android-build-merger
commit 74b665fef1
1 changed files with 77 additions and 52 deletions

View File

@ -14,8 +14,6 @@
from __future__ import print_function from __future__ import print_function
from collections import deque, OrderedDict
from hashlib import sha1
import array import array
import common import common
import functools import functools
@ -23,12 +21,14 @@ import heapq
import itertools import itertools
import multiprocessing import multiprocessing
import os import os
import os.path
import re import re
import subprocess import subprocess
import threading import threading
import time
import tempfile import tempfile
from collections import deque, OrderedDict
from hashlib import sha1
from rangelib import RangeSet from rangelib import RangeSet
@ -348,7 +348,7 @@ class BlockImageDiff(object):
This prevents the target size of one command from being too large; and This prevents the target size of one command from being too large; and
might help to avoid fsync errors on some devices.""" might help to avoid fsync errors on some devices."""
assert (style == "new" or style == "zero") assert style == "new" or style == "zero"
blocks_limit = 1024 blocks_limit = 1024
total = 0 total = 0
while target_blocks: while target_blocks:
@ -359,13 +359,23 @@ class BlockImageDiff(object):
return total return total
out = [] out = []
total = 0 total = 0
# In BBOTA v2, 'stashes' records the map from 'stash_raw_id' to 'stash_id'
# (aka 'sid', which is the stash slot id). The stash in a 'stash_id' will
# be freed immediately after its use. So unlike 'stash_raw_id' (which
# uniquely identifies each pair of stashed blocks), the same 'stash_id'
# may be reused during the life cycle of an update (maintained by
# 'free_stash_ids' heap and 'next_stash_id').
#
# In BBOTA v3+, it uses the hash of the stashed blocks as the stash slot
# id. 'stashes' records the map from 'hash' to the ref count. The stash
# will be freed only if the count decrements to zero.
stashes = {} stashes = {}
stashed_blocks = 0 stashed_blocks = 0
max_stashed_blocks = 0 max_stashed_blocks = 0
if self.version == 2:
free_stash_ids = [] free_stash_ids = []
next_stash_id = 0 next_stash_id = 0
@ -375,15 +385,15 @@ class BlockImageDiff(object):
assert not xf.stash_before assert not xf.stash_before
assert not xf.use_stash assert not xf.use_stash
for s, sr in xf.stash_before: for stash_raw_id, sr in xf.stash_before:
assert s not in stashes if self.version == 2:
assert stash_raw_id not in stashes
if free_stash_ids: if free_stash_ids:
sid = heapq.heappop(free_stash_ids) sid = heapq.heappop(free_stash_ids)
else: else:
sid = next_stash_id sid = next_stash_id
next_stash_id += 1 next_stash_id += 1
stashes[s] = sid stashes[stash_raw_id] = sid
if self.version == 2:
stashed_blocks += sr.size() stashed_blocks += sr.size()
out.append("stash %d %s\n" % (sid, sr.to_string_raw())) out.append("stash %d %s\n" % (sid, sr.to_string_raw()))
else: else:
@ -417,14 +427,13 @@ class BlockImageDiff(object):
unstashed_src_ranges = xf.src_ranges unstashed_src_ranges = xf.src_ranges
mapped_stashes = [] mapped_stashes = []
for s, sr in xf.use_stash: for stash_raw_id, sr in xf.use_stash:
# TODO: We don't need 'sid' (nor free_stash_ids) in BBOTA v3+.
sid = stashes.pop(s)
unstashed_src_ranges = unstashed_src_ranges.subtract(sr) unstashed_src_ranges = unstashed_src_ranges.subtract(sr)
sh = self.HashBlocks(self.src, sr) sh = self.HashBlocks(self.src, sr)
sr = xf.src_ranges.map_within(sr) sr = xf.src_ranges.map_within(sr)
mapped_stashes.append(sr) mapped_stashes.append(sr)
if self.version == 2: if self.version == 2:
sid = stashes.pop(stash_raw_id)
src_str.append("%d:%s" % (sid, sr.to_string_raw())) src_str.append("%d:%s" % (sid, sr.to_string_raw()))
# A stash will be used only once. We need to free the stash # A stash will be used only once. We need to free the stash
# immediately after the use, instead of waiting for the automatic # immediately after the use, instead of waiting for the automatic
@ -433,15 +442,15 @@ class BlockImageDiff(object):
# Bug: 23119955 # Bug: 23119955
free_string.append("free %d\n" % (sid,)) free_string.append("free %d\n" % (sid,))
free_size += sr.size() free_size += sr.size()
heapq.heappush(free_stash_ids, sid)
else: else:
assert sh in stashes assert sh in stashes
src_str.append("%s:%s" % (sh, sr.to_string_raw())) src_str.append("%s:%s" % (sh, sr.to_string_raw()))
stashes[sh] -= 1 stashes[sh] -= 1
if stashes[sh] == 0: if stashes[sh] == 0:
free_size += sr.size()
free_string.append("free %s\n" % (sh,)) free_string.append("free %s\n" % (sh,))
free_size += sr.size()
stashes.pop(sh) stashes.pop(sh)
heapq.heappush(free_stash_ids, sid)
if unstashed_src_ranges: if unstashed_src_ranges:
src_str.insert(1, unstashed_src_ranges.to_string_raw()) src_str.insert(1, unstashed_src_ranges.to_string_raw())
@ -594,11 +603,15 @@ class BlockImageDiff(object):
out.insert(0, "%d\n" % (self.version,)) # format version number out.insert(0, "%d\n" % (self.version,)) # format version number
out.insert(1, "%d\n" % (total,)) out.insert(1, "%d\n" % (total,))
if self.version >= 2: if self.version == 2:
# version 2 only: after the total block count, we give the number # v2 only: after the total block count, we give the number of stash slots
# of stash slots needed, and the maximum size needed (in blocks) # needed, and the maximum size needed (in blocks).
out.insert(2, str(next_stash_id) + "\n") out.insert(2, str(next_stash_id) + "\n")
out.insert(3, str(max_stashed_blocks) + "\n") out.insert(3, str(max_stashed_blocks) + "\n")
elif self.version >= 3:
# v3+: the number of stash slots is unused.
out.insert(2, "0\n")
out.insert(3, str(max_stashed_blocks) + "\n")
with open(prefix + ".transfer.list", "wb") as f: with open(prefix + ".transfer.list", "wb") as f:
for i in out: for i in out:
@ -622,15 +635,15 @@ class BlockImageDiff(object):
stash_map = {} stash_map = {}
# Create the map between a stash and its def/use points. For example, for a # Create the map between a stash and its def/use points. For example, for a
# given stash of (idx, sr), stashes[idx] = (sr, def_cmd, use_cmd). # given stash of (raw_id, sr), stashes[raw_id] = (sr, def_cmd, use_cmd).
for xf in self.transfers: for xf in self.transfers:
# Command xf defines (stores) all the stashes in stash_before. # Command xf defines (stores) all the stashes in stash_before.
for idx, sr in xf.stash_before: for stash_raw_id, sr in xf.stash_before:
stash_map[idx] = (sr, xf) stash_map[stash_raw_id] = (sr, xf)
# Record all the stashes command xf uses. # Record all the stashes command xf uses.
for idx, _ in xf.use_stash: for stash_raw_id, _ in xf.use_stash:
stash_map[idx] += (xf,) stash_map[stash_raw_id] += (xf,)
# Compute the maximum blocks available for stash based on /cache size and # Compute the maximum blocks available for stash based on /cache size and
# the threshold. # the threshold.
@ -638,10 +651,12 @@ class BlockImageDiff(object):
stash_threshold = common.OPTIONS.stash_threshold stash_threshold = common.OPTIONS.stash_threshold
max_allowed = cache_size * stash_threshold / self.tgt.blocksize max_allowed = cache_size * stash_threshold / self.tgt.blocksize
# See the comments for 'stashes' in WriteTransfers().
stashes = {} stashes = {}
stashed_blocks = 0 stashed_blocks = 0
new_blocks = 0 new_blocks = 0
if self.version == 2:
free_stash_ids = [] free_stash_ids = []
next_stash_id = 0 next_stash_id = 0
@ -653,18 +668,17 @@ class BlockImageDiff(object):
replaced_cmds = [] replaced_cmds = []
# xf.stash_before generates explicit stash commands. # xf.stash_before generates explicit stash commands.
for idx, sr in xf.stash_before: for stash_raw_id, sr in xf.stash_before:
assert idx not in stashes # Check the post-command stashed_blocks.
stashed_blocks_after = stashed_blocks
if self.version == 2:
assert stash_raw_id not in stashes
if free_stash_ids: if free_stash_ids:
sid = heapq.heappop(free_stash_ids) sid = heapq.heappop(free_stash_ids)
else: else:
sid = next_stash_id sid = next_stash_id
next_stash_id += 1 next_stash_id += 1
stashes[idx] = sid stashes[stash_raw_id] = sid
# Check the post-command stashed_blocks.
stashed_blocks_after = stashed_blocks
if self.version == 2:
stashed_blocks_after += sr.size() stashed_blocks_after += sr.size()
else: else:
sh = self.HashBlocks(self.src, sr) sh = self.HashBlocks(self.src, sr)
@ -677,7 +691,7 @@ class BlockImageDiff(object):
if stashed_blocks_after > max_allowed: if stashed_blocks_after > max_allowed:
# We cannot stash this one for a later command. Find out the command # We cannot stash this one for a later command. Find out the command
# that will use this stash and replace the command with "new". # that will use this stash and replace the command with "new".
use_cmd = stash_map[idx][2] use_cmd = stash_map[stash_raw_id][2]
replaced_cmds.append(use_cmd) replaced_cmds.append(use_cmd)
print("%10d %9s %s" % (sr.size(), "explicit", use_cmd)) print("%10d %9s %s" % (sr.size(), "explicit", use_cmd))
else: else:
@ -696,21 +710,22 @@ class BlockImageDiff(object):
for cmd in replaced_cmds: for cmd in replaced_cmds:
# It no longer uses any commands in "use_stash". Remove the def points # It no longer uses any commands in "use_stash". Remove the def points
# for all those stashes. # for all those stashes.
for idx, sr in cmd.use_stash: for stash_raw_id, sr in cmd.use_stash:
def_cmd = stash_map[idx][1] def_cmd = stash_map[stash_raw_id][1]
assert (idx, sr) in def_cmd.stash_before assert (stash_raw_id, sr) in def_cmd.stash_before
def_cmd.stash_before.remove((idx, sr)) def_cmd.stash_before.remove((stash_raw_id, sr))
# Add up blocks that violates space limit and print total number to # Add up blocks that violates space limit and print total number to
# screen later. # screen later.
new_blocks += cmd.tgt_ranges.size() new_blocks += cmd.tgt_ranges.size()
cmd.ConvertToNew() cmd.ConvertToNew()
# xf.use_stash generates free commands. # xf.use_stash may generate free commands.
for idx, sr in xf.use_stash: for stash_raw_id, sr in xf.use_stash:
sid = stashes.pop(idx)
if self.version == 2: if self.version == 2:
sid = stashes.pop(stash_raw_id)
stashed_blocks -= sr.size() stashed_blocks -= sr.size()
heapq.heappush(free_stash_ids, sid)
else: else:
sh = self.HashBlocks(self.src, sr) sh = self.HashBlocks(self.src, sr)
assert sh in stashes assert sh in stashes
@ -718,7 +733,6 @@ class BlockImageDiff(object):
if stashes[sh] == 0: if stashes[sh] == 0:
stashed_blocks -= sr.size() stashed_blocks -= sr.size()
stashes.pop(sh) stashes.pop(sh)
heapq.heappush(free_stash_ids, sid)
num_of_bytes = new_blocks * self.tgt.blocksize num_of_bytes = new_blocks * self.tgt.blocksize
print(" Total %d blocks (%d bytes) are packed as new blocks due to " print(" Total %d blocks (%d bytes) are packed as new blocks due to "
@ -962,10 +976,21 @@ class BlockImageDiff(object):
lost_source)) lost_source))
def ReverseBackwardEdges(self): def ReverseBackwardEdges(self):
"""Reverse unsatisfying edges and compute pairs of stashed blocks.
For each transfer, make sure it properly stashes the blocks it touches and
will be used by later transfers. It uses pairs of (stash_raw_id, range) to
record the blocks to be stashed. 'stash_raw_id' is an id that uniquely
identifies each pair. Note that for the same range (e.g. RangeSet("1-5")),
it is possible to have multiple pairs with different 'stash_raw_id's. Each
'stash_raw_id' will be consumed by one transfer. In BBOTA v3+, identical
blocks will be written to the same stash slot in WriteTransfers().
"""
print("Reversing backward edges...") print("Reversing backward edges...")
in_order = 0 in_order = 0
out_of_order = 0 out_of_order = 0
stashes = 0 stash_raw_id = 0
stash_size = 0 stash_size = 0
for xf in self.transfers: for xf in self.transfers:
@ -983,9 +1008,9 @@ class BlockImageDiff(object):
overlap = xf.src_ranges.intersect(u.tgt_ranges) overlap = xf.src_ranges.intersect(u.tgt_ranges)
assert overlap assert overlap
u.stash_before.append((stashes, overlap)) u.stash_before.append((stash_raw_id, overlap))
xf.use_stash.append((stashes, overlap)) xf.use_stash.append((stash_raw_id, overlap))
stashes += 1 stash_raw_id += 1
stash_size += overlap.size() stash_size += overlap.size()
# reverse the edge direction; now xf must go after u # reverse the edge direction; now xf must go after u