Home | History | Annotate | Download | only in update_payload
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Tracing block data source through a Chrome OS update payload.
      6 
      7 This module is used internally by the main Payload class for tracing block
      8 content through an update payload. This is a useful feature in debugging
      9 payload applying functionality in this package. The interface for invoking the
     10 tracer is as follows:
     11 
     12   tracer = PayloadBlockTracer(payload)
     13   tracer.Run(...)
     14 
     15 """
     16 
     17 from __future__ import print_function
     18 
     19 import common
     20 
     21 
     22 #
     23 # Payload block tracing.
     24 #
     25 class PayloadBlockTracer(object):
     26   """Tracing the origin of block data through update instructions.
     27 
     28   This is a short-lived object whose purpose is to isolate the logic used for
     29   tracing the origin of destination partition blocks.
     30 
     31   """
     32 
     33   def __init__(self, payload):
     34     assert payload.is_init, 'uninitialized update payload'
     35     self.payload = payload
     36 
     37   @staticmethod
     38   def _TraceBlock(block, skip, trace_out_file, operations, base_name):
     39     """Trace the origin of a given block through a sequence of operations.
     40 
     41     This method tries to map the given dest block to the corresponding source
     42     block from which its content originates in the course of an update. It
     43     further tries to trace transitive origins through MOVE operations. It is
     44     rather efficient, doing the actual tracing by means of a single reverse
     45     sweep through the operation sequence. It dumps a log of operations and
     46     source blocks responsible for the data in the given dest block to the
     47     provided output file.
     48 
     49     Args:
     50       block: the block number to trace
     51       skip: number of initial transitive origins to ignore
     52       trace_out_file: a file object to dump the trace to
     53       operations: the sequence of operations
     54       base_name: name of the operation sequence
     55     """
     56     # Traverse operations backwards.
     57     for op, op_name in common.OperationIter(operations, base_name,
     58                                             reverse=True):
     59       total_block_offset = 0
     60       found = False
     61 
     62       # Is the traced block mentioned in the dest extents?
     63       for dst_ex, dst_ex_name in common.ExtentIter(op.dst_extents,
     64                                                    op_name + '.dst_extents'):
     65         if (block >= dst_ex.start_block
     66             and block < dst_ex.start_block + dst_ex.num_blocks):
     67           if skip:
     68             skip -= 1
     69           else:
     70             total_block_offset += block - dst_ex.start_block
     71             trace_out_file.write(
     72                 '%d: %s: found %s (total block offset: %d)\n' %
     73                 (block, dst_ex_name, common.FormatExtent(dst_ex),
     74                  total_block_offset))
     75             found = True
     76             break
     77 
     78         total_block_offset += dst_ex.num_blocks
     79 
     80       if found:
     81         # Don't trace further, unless it's a MOVE.
     82         if op.type != common.OpType.MOVE:
     83           break
     84 
     85         # For MOVE, find corresponding source block and keep tracing.
     86         for src_ex, src_ex_name in common.ExtentIter(op.src_extents,
     87                                                      op_name + '.src_extents'):
     88           if total_block_offset < src_ex.num_blocks:
     89             block = src_ex.start_block + total_block_offset
     90             trace_out_file.write(
     91                 '%s:  mapped to %s (%d)\n' %
     92                 (src_ex_name, common.FormatExtent(src_ex), block))
     93             break
     94 
     95           total_block_offset -= src_ex.num_blocks
     96 
     97   def Run(self, block, skip, trace_out_file, is_kernel):
     98     """Block tracer entry point, invoking the actual search.
     99 
    100     Args:
    101       block: the block number whose origin to trace
    102       skip: the number of first origin mappings to skip
    103       trace_out_file: file object to dump the trace to
    104       is_kernel: trace through kernel (True) or rootfs (False) operations
    105     """
    106     if is_kernel:
    107       operations = self.payload.manifest.kernel_install_operations
    108       base_name = 'kernel_install_operations'
    109     else:
    110       operations = self.payload.manifest.install_operations
    111       base_name = 'install_operations'
    112 
    113     self._TraceBlock(block, skip, trace_out_file, operations, base_name)
    114