blob: e7a9d2739a54b5d022e86b802e4bca5f48038239 [file] [log] [blame]
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001# 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
17import common
18
19
20#
21# Payload block tracing.
22#
23class PayloadBlockTracer(object):
24 """Tracing the origin of block data through update instructions.
25
26 This is a short-lived object whose purpose is to isolate the logic used for
27 tracing the origin of destination partition blocks.
28
29 """
30
31 def __init__(self, payload):
32 assert payload.is_init, 'uninitialized update payload'
33 self.payload = payload
34
35 @staticmethod
36 def _TraceBlock(block, skip, trace_out_file, operations, base_name):
37 """Trace the origin of a given block through a sequence of operations.
38
39 This method tries to map the given dest block to the corresponding source
40 block from which its content originates in the course of an update. It
41 further tries to trace transitive origins through MOVE operations. It is
42 rather efficient, doing the actual tracing by means of a single reverse
43 sweep through the operation sequence. It dumps a log of operations and
44 source blocks responsible for the data in the given dest block to the
45 provided output file.
46
47 Args:
48 block: the block number to trace
49 skip: number of initial transitive origins to ignore
50 trace_out_file: a file object to dump the trace to
51 operations: the sequence of operations
52 base_name: name of the operation sequence
53
54 """
55 # Traverse operations backwards.
56 for op, op_name in common.OperationIter(operations, base_name,
57 reverse=True):
58 total_block_offset = 0
59 found = False
60
61 # Is the traced block mentioned in the dest extents?
62 for dst_ex, dst_ex_name in common.ExtentIter(op.dst_extents,
63 op_name + '.dst_extents'):
64 if (block >= dst_ex.start_block
65 and block < dst_ex.start_block + dst_ex.num_blocks):
66 if skip:
67 skip -= 1
68 else:
69 total_block_offset += block - dst_ex.start_block
70 trace_out_file.write(
71 '%s: found %s (total block offset: %d)\n' %
72 (dst_ex_name, common.FormatExtent(dst_ex), total_block_offset))
73 found = True
74 break
75
76 total_block_offset += dst_ex.num_blocks
77
78 if found:
79 # Don't trace further, unless it's a MOVE.
80 if op.type != common.OpType.MOVE:
81 break
82
83 # For MOVE, find corresponding source block and keep tracing.
84 for src_ex, src_ex_name in common.ExtentIter(op.src_extents,
85 op_name + '.src_extents'):
86 if total_block_offset < src_ex.num_blocks:
87 block = src_ex.start_block + total_block_offset
88 trace_out_file.write(
89 '%s: mapped to %s (%d)\n' %
90 (src_ex_name, common.FormatExtent(src_ex), block))
91 break
92
93 total_block_offset -= src_ex.num_blocks
94
95 def Run(self, block, skip, trace_out_file, is_kernel):
96 """Block tracer entry point, invoking the actual search.
97
98 Args:
99 block: the block number whose origin to trace
100 skip: the number of first origin mappings to skip
101 trace_out_file: file object to dump the trace to
102 is_kernel: trace through kernel (True) or rootfs (False) operations
103
104 """
105 if is_kernel:
106 self._TraceBlock(block, skip, trace_out_file,
107 self.payload.manifest.kernel_install_operations,
108 'kernel_install_operations')
109 else:
110 self._TraceBlock(block, skip, trace_out_file,
111 self.payload.manifest.install_operations,
112 'install_operations')