1 : // Copyright 2012 Google Inc.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/instrument/transforms/coverage_transform.h"
16 :
17 : #include "syzygy/block_graph/basic_block_assembler.h"
18 : #include "syzygy/block_graph/typed_block.h"
19 : #include "syzygy/common/basic_block_frequency_data.h"
20 : #include "syzygy/core/disassembler_util.h"
21 : #include "syzygy/pe/block_util.h"
22 : #include "syzygy/pe/pe_utils.h"
23 :
24 : namespace instrument {
25 : namespace transforms {
26 :
27 : namespace {
28 :
29 : using common::BasicBlockFrequencyData;
30 : using core::eax;
31 : using block_graph::BasicBlock;
32 : using block_graph::BasicBlockAssembler;
33 : using block_graph::BasicBlockReference;
34 : using block_graph::BlockGraph;
35 : using block_graph::Displacement;
36 : using block_graph::Immediate;
37 : using block_graph::Operand;
38 :
39 : typedef block_graph::TypedBlock<BasicBlockFrequencyData> CoverageDataBlock;
40 :
41 : bool AddCoverageDataSection(BlockGraph* block_graph,
42 E : BlockGraph::Block** coverage_data_block) {
43 E : DCHECK(block_graph != NULL);
44 E : DCHECK(coverage_data_block != NULL);
45 :
46 : BlockGraph::Section* coverage_section = block_graph->FindSection(
47 E : common::kBasicBlockFrequencySectionName);
48 E : if (coverage_section != NULL) {
49 E : LOG(ERROR) << "Block-graph already contains a code coverage data section ("
50 : << common::kBasicBlockFrequencySectionName << ").";
51 E : return false;
52 : }
53 :
54 : coverage_section = block_graph->AddSection(
55 : common::kBasicBlockFrequencySectionName,
56 E : common::kBasicBlockFrequencySectionCharacteristics);
57 E : DCHECK(coverage_section != NULL);
58 :
59 : BlockGraph::Block* block =
60 : block_graph->AddBlock(BlockGraph::DATA_BLOCK,
61 : sizeof(BasicBlockFrequencyData),
62 E : "Coverage data");
63 E : DCHECK(block != NULL);
64 E : block->set_section(coverage_section->id());
65 :
66 E : BasicBlockFrequencyData coverage_data = {};
67 E : coverage_data.agent_id = common::kBasicBlockCoverageAgentId;
68 E : coverage_data.version = common::kBasicBlockFrequencyDataVersion;
69 E : coverage_data.tls_index = TLS_OUT_OF_INDEXES;
70 :
71 E : block->CopyData(sizeof(coverage_data), &coverage_data);
72 E : *coverage_data_block = block;
73 :
74 E : return true;
75 E : }
76 :
77 : } // namespace
78 :
79 : const char CoverageInstrumentationTransform::kTransformName[] =
80 : "CoverageInstrumentationTransform";
81 :
82 : CoverageInstrumentationTransform::CoverageInstrumentationTransform()
83 E : : coverage_data_block_(NULL) {
84 E : }
85 :
86 : bool CoverageInstrumentationTransform::TransformBasicBlockSubGraph(
87 : BlockGraph* block_graph,
88 E : BasicBlockSubGraph* basic_block_subgraph) {
89 E : DCHECK(block_graph != NULL);
90 E : DCHECK(basic_block_subgraph != NULL);
91 :
92 : // Iterate over the basic blocks.
93 : BasicBlockSubGraph::BBCollection::iterator it =
94 E : basic_block_subgraph->basic_blocks().begin();
95 E : for (; it != basic_block_subgraph->basic_blocks().end(); ++it) {
96 E : BasicBlockSubGraph::BasicBlock& bb = it->second;
97 :
98 : // We're only interested in code blocks.
99 E : if (bb.type() != BasicBlock::BASIC_CODE_BLOCK)
100 E : continue;
101 :
102 : // Find the source range associated with this basic-block.
103 : // TODO(chrisha): Make this a utility function on BasicBlock and eventually
104 : // move all of the data into instructions and successors.
105 : const BlockGraph::Block::SourceRanges::RangePair* range_pair =
106 : basic_block_subgraph->original_block()->source_ranges().FindRangePair(
107 E : BlockGraph::Block::SourceRanges::SourceRange(bb.offset(), 1));
108 :
109 : // If there's no source data, something has gone terribly wrong. In fact, it
110 : // likely means that we've stacked transforms and new instructions have
111 : // been prepended to this BB. We don't support this yet.
112 E : DCHECK(range_pair != NULL);
113 :
114 : // We prepend each basic code block with the following instructions:
115 : // 0. push eax
116 : // 1. mov eax, dword ptr[basic_block_seen_array]
117 : // 2. mov byte ptr[eax + basic_block_index], 1
118 : // 3. pop eax
119 : BasicBlockAssembler assm(bb.instructions().begin(),
120 E : &bb.instructions());
121 : // Prepend the instrumentation instructions.
122 E : assm.push(eax);
123 : static const BlockGraph::Offset kDstOffset =
124 : offsetof(BasicBlockFrequencyData, frequency_data);
125 E : assm.mov(eax, Operand(Displacement(coverage_data_block_, kDstOffset)));
126 E : assm.mov_b(Operand(eax, Displacement(bb_ranges_.size())), Immediate(1));
127 E : assm.pop(eax);
128 :
129 : // Get the RVA of the BB by translating its offset. We also get its size
130 : // from the source range. This assumes that the BB is an original
131 : // untransformed BB.
132 E : const BlockGraph::Block::DataRange& data_range = range_pair->first;
133 E : const BlockGraph::Block::SourceRange& src_range = range_pair->second;
134 : core::RelativeAddress bb_addr = src_range.start() +
135 E : (bb.offset() - data_range.start());
136 E : size_t bb_size = src_range.size();
137 E : bb_ranges_.push_back(RelativeAddressRange(bb_addr, bb_size));
138 E : }
139 :
140 E : return true;
141 E : }
142 :
143 : bool CoverageInstrumentationTransform::PreBlockGraphIteration(
144 E : BlockGraph* block_graph, BlockGraph::Block* header_block) {
145 E : DCHECK(block_graph != NULL);
146 E : DCHECK(header_block != NULL);
147 :
148 E : if (!AddCoverageDataSection(block_graph, &coverage_data_block_))
149 E : return false;
150 E : DCHECK(coverage_data_block_ != NULL);
151 :
152 E : return true;
153 E : }
154 :
155 : bool CoverageInstrumentationTransform::OnBlock(
156 E : BlockGraph* block_graph, BlockGraph::Block* block) {
157 E : DCHECK(block_graph != NULL);
158 E : DCHECK(block != NULL);
159 :
160 : // We only care about code blocks.
161 E : if (block->type() != BlockGraph::CODE_BLOCK)
162 E : return true;
163 :
164 : // We only care about blocks that are safe for basic block decomposition.
165 E : if (!pe::CodeBlockIsBasicBlockDecomposable(block))
166 E : return true;
167 :
168 : // Apply our basic block transform.
169 : if (!block_graph::ApplyBasicBlockSubGraphTransform(
170 E : this, block_graph, block, NULL)) {
171 i : return false;
172 : }
173 :
174 E : return true;
175 E : }
176 :
177 : bool CoverageInstrumentationTransform::PostBlockGraphIteration(
178 E : BlockGraph* block_graph, BlockGraph::Block* header_block) {
179 E : DCHECK(block_graph != NULL);
180 E : DCHECK(header_block != NULL);
181 :
182 E : if (bb_ranges_.size() == 0) {
183 i : LOG(WARNING) << "Encountered no basic code blocks during instrumentation.";
184 i : return true;
185 : }
186 :
187 : // Set the final basic block count. This is used by the runtime library to
188 : // know how big an array to allocate for the statistics.
189 E : CoverageDataBlock coverage_data;
190 E : DCHECK(coverage_data_block_ != NULL);
191 E : CHECK(coverage_data.Init(0, coverage_data_block_));
192 E : coverage_data->num_basic_blocks = bb_ranges_.size();
193 E : coverage_data->frequency_size = 1U;
194 :
195 : // Get/create a read/write .rdata section.
196 : BlockGraph::Section* rdata_section = block_graph->FindOrAddSection(
197 E : pe::kReadWriteDataSectionName, pe::kReadWriteDataCharacteristics);
198 E : if (rdata_section == NULL) {
199 i : LOG(ERROR) << "Unable to find or create section \""
200 : << pe::kReadWriteDataSectionName << "\".";
201 i : return false;
202 : }
203 :
204 : // Create an empty block that is sufficient to hold all of the coverage
205 : // results. We will initially point basic_block_seen_array at this so that
206 : // even if the call-trace service is down the program can run without
207 : // crashing. We put this in .rdata so that .bbfreq contains only a single
208 : // block.
209 : BlockGraph::Block* bb_seen_array_block =
210 : block_graph->AddBlock(BlockGraph::DATA_BLOCK,
211 : bb_ranges_.size(),
212 E : "Basic Blocks Seen Array");
213 E : DCHECK(bb_seen_array_block != NULL);
214 E : bb_seen_array_block->set_section(rdata_section->id());
215 :
216 : // Hook it up to the coverage_data array pointer.
217 : coverage_data_block_->SetReference(
218 : coverage_data.OffsetOf(coverage_data->frequency_data),
219 : BlockGraph::Reference(
220 : BlockGraph::ABSOLUTE_REF,
221 : sizeof(coverage_data->frequency_data),
222 : bb_seen_array_block,
223 : 0,
224 E : 0));
225 :
226 E : return true;
227 E : }
228 :
229 : } // namespace transforms
230 : } // namespace instrument
|