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 7This module is used internally by the main Payload class for tracing block 8content through an update payload. This is a useful feature in debugging 9payload applying functionality in this package. The interface for invoking the 10tracer is as follows: 11 12 tracer = PayloadBlockTracer(payload) 13 tracer.Run(...) 14 15""" 16 17from __future__ import print_function 18 19import common 20 21 22# 23# Payload block tracing. 24# 25class 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