Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(182)

Side by Side Diff: syzygy/instrument/transforms/filler_transform_unittest.cc

Issue 1169603003: [Syzygy Instrumenter] Add FillerTransform. (Closed) Base URL: https://code.google.com/p/syzygy.git@master
Patch Set: Sync. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « syzygy/instrument/transforms/filler_transform.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 Google Inc. All Rights Reserved.
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/filler_transform.h"
16
17 #include <set>
18
19 #include "base/memory/scoped_ptr.h"
20 #include "gtest/gtest.h"
21 #include "syzygy/assm/assembler_base.h"
22 #include "syzygy/block_graph/basic_block.h"
23 #include "syzygy/block_graph/basic_block_assembler.h"
24 #include "syzygy/block_graph/basic_block_subgraph.h"
25 #include "syzygy/block_graph/basic_block_test_util.h"
26 #include "syzygy/block_graph/block_graph.h"
27 #include "syzygy/instrument/transforms/unittest_util.h"
28
29 namespace instrument {
30 namespace transforms {
31 namespace {
32
33 using block_graph::BasicBlock;
34 using block_graph::BlockGraph;
35 using block_graph::BasicCodeBlock;
36
37 const char kNopName[] = "NOP";
38
39 // Finds instruction indices and sizes of every NOP in @p instructions and
40 // stores the result in @p nop_indices. Returns the @p instructions count.
41 size_t FindAllNops(BasicBlock::Instructions* instructions,
42 std::map<size_t, int>* nop_indices) {
43 size_t index = 0LL;
44 BasicBlock::Instructions::iterator inst_it = instructions->begin();
45 for (; inst_it != instructions->end(); ++inst_it, ++index) {
46 if (!::strcmp(inst_it->GetName(), kNopName))
47 (*nop_indices)[index] = inst_it->size();
48 }
49 return index;
50 }
51
52 class FillerBasicBlockTransformTest : public testing::Test {
53 public:
54 typedef testing::Test Super;
55 typedef FillerBasicBlockTransform::NopSizes NopSizes;
56 typedef FillerBasicBlockTransform::NopSpec NopSpec;
57
58 // Creates a new length @p n instruction list for testing.
59 void CreateInstructions(int n) {
60 using assm::eax;
61 using assm::ebp;
62 using assm::esp;
63 using assm::kSize32Bit;
64
65 instructions_.reset(new BasicBlock::Instructions);
66 if (n == 0)
67 return;
68
69 block_graph::BasicBlockAssembler
70 assm(instructions_->begin(), instructions_.get());
71 bool setup_stack = false;
72 --n; // Reserve for ret.
73 if (n > 3) {
74 setup_stack = true;
75 n -= 3; // Reserve for stack frame setup.
76 }
77 if (setup_stack) {
78 assm.push(ebp);
79 assm.mov(ebp, esp);
80 }
81 for (; n > 0; --n)
82 assm.mov(eax, block_graph::Immediate(n, kSize32Bit));
83 if (setup_stack)
84 assm.pop(ebp);
85 assm.ret(0);
86 }
87
88 protected:
89 scoped_ptr<BasicBlock::Instructions> instructions_;
90 };
91
92 class TestFillerTransform : public FillerTransform {
93 public:
94 explicit TestFillerTransform(const std::vector<std::string>& target_list)
95 : FillerTransform(target_list) { }
96 };
97
98 class FillerTransformTest : public testing::TestDllTransformTest {
99 public:
100 typedef testing::TestDllTransformTest Super;
101 typedef BlockGraph::Block::SourceRange SourceRange;
102
103 // Finds all blocks whose names appear in @p target_list, and writes the
104 // mapping fron name to block in @p result. Returns true iff all names in
105 // @p target_list are found.
106 static bool FindAllBlocks(
107 BlockGraph* block_graph,
108 const std::vector<std::string>& target_list,
109 std::map<std::string, BlockGraph::Block*>* result) {
110 DCHECK(result && result->empty());
111 std::set<std::string> name_set(target_list.begin(), target_list.end());
112 for (auto& it : block_graph->blocks_mutable()) {
113 std::string block_name = it.second.name();
114 if (name_set.find(block_name) != name_set.end()) {
115 (*result)[block_name] = &it.second;
116 name_set.erase(block_name);
117 }
118 }
119 return name_set.empty();
120 }
121
122 // Verifies that @p instructions contains all expected NOPs except for NOPs
123 // that would be inserted beyond the last instruction.
124 static void CheckNops(BasicBlock::Instructions* instructions) {
125 std::map<size_t, int> nop_indices;
126 size_t num_inst = FindAllNops(instructions, &nop_indices);
127 // The checks here depend on how NopSpec is initialized in
128 // FillerBasicBlockTransform.
129 EXPECT_TRUE(1 >= num_inst || nop_indices[1] == 1);
130 }
131
132 // Verifies that all contiguous NOPs in @p instuctions are followed by a
133 // non-NOP instruction, which shares the same source range as the NOP run.
134 static void CheckNopSourceRange(BasicBlock::Instructions* instructions) {
135 std::vector<SourceRange> range_queue;
136 BasicBlock::Instructions::iterator inst_it = instructions->begin();
137 for (; inst_it != instructions->end(); ++inst_it) {
138 if (!::strcmp(inst_it->GetName(), kNopName)) {
139 // Found NOP: add to queue.
140 range_queue.push_back(inst_it->source_range());
141 } else {
142 // Found non-NOP: verify stored ranges in the queue and then clear it.
143 for (SourceRange& nop_source_range : range_queue) {
144 EXPECT_EQ(inst_it->source_range(), nop_source_range);
145 }
146 range_queue.clear();
147 }
148 }
149 // Expect there's no trailing NOP.
150 EXPECT_TRUE(range_queue.empty());
151 }
152 };
153
154 } // namespace
155
156 // Sanity check for helper CreateInstructions().
157 TEST_F(FillerBasicBlockTransformTest, CreateInstructions) {
158 for (int i = 0; i < 10; ++i) {
159 CreateInstructions(i);
160 size_t count = 0;
161 for (auto inst : *instructions_.get())
162 ++count;
163 EXPECT_EQ(i, count);
164 }
165 }
166
167 TEST_F(FillerBasicBlockTransformTest, InjectRegular) {
168 CreateInstructions(8);
169 NopSpec nop_spec = {
170 {1, NopSizes::NOP3},
171 {2, NopSizes::NOP1},
172 {3, NopSizes::NOP4},
173 {6, NopSizes::NOP1},
174 {8, NopSizes::NOP5},
175 {9, NopSizes::NOP9}};
176 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
177 std::map<size_t, int> nop_indices;
178 EXPECT_EQ(8U + 6U, FindAllNops(instructions_.get(), &nop_indices));
179 EXPECT_EQ(6U, nop_indices.size());
180 EXPECT_EQ(3U, nop_indices[1]);
181 EXPECT_EQ(1U, nop_indices[2]);
182 EXPECT_EQ(4U, nop_indices[3]);
183 EXPECT_EQ(1U, nop_indices[6]);
184 EXPECT_EQ(5U, nop_indices[8]);
185 EXPECT_EQ(9U, nop_indices[9]);
186 }
187
188 TEST_F(FillerBasicBlockTransformTest, InjectToStart) {
189 CreateInstructions(5);
190 NopSpec nop_spec1 = {{0, NopSizes::NOP4}};
191 FillerBasicBlockTransform::InjectNop(nop_spec1, false, instructions_.get());
192 std::map<size_t, int> nop_indices1;
193 EXPECT_EQ(5U + 1U, FindAllNops(instructions_.get(), &nop_indices1));
194 EXPECT_EQ(1U, nop_indices1.size());
195 EXPECT_EQ(4U, nop_indices1[0]);
196
197 // Inject another NOP, when a NOP already exists.
198 NopSpec nop_spec2 = {{0, NopSizes::NOP1}};
199 FillerBasicBlockTransform::InjectNop(nop_spec2, false, instructions_.get());
200 std::map<size_t, int> nop_indices2;
201 EXPECT_EQ(5U + 1U + 1U, FindAllNops(instructions_.get(), &nop_indices2));
202 EXPECT_EQ(2U, nop_indices2.size()); // New + existing.
203 EXPECT_EQ(1U, nop_indices2[0]); // From |nop_spec2|.
204 EXPECT_EQ(4U, nop_indices2[1]); // From |nop_spec1|.
205 }
206
207 TEST_F(FillerBasicBlockTransformTest, InjectToBeforeEnd) {
208 CreateInstructions(7);
209 NopSpec nop_spec = {{6, NopSizes::NOP2}};
210 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
211 std::map<size_t, int> nop_indices;
212 EXPECT_EQ(7U + 1U, FindAllNops(instructions_.get(), &nop_indices));
213 EXPECT_EQ(1U, nop_indices.size());
214 EXPECT_EQ(2U, nop_indices[6]);
215 }
216
217 TEST_F(FillerBasicBlockTransformTest, CannotInjectBeyondEnd) {
218 CreateInstructions(7);
219 NopSpec nop_spec = {{7, NopSizes::NOP1}, {17, NopSizes::NOP1}};
220 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
221 std::map<size_t, int> nop_indices;
222 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_indices));
223 EXPECT_EQ(0U, nop_indices.size());
224 }
225
226 TEST_F(FillerBasicBlockTransformTest, InjectToEmpty) {
227 CreateInstructions(0);
228 NopSpec nop_spec = {{0, NopSizes::NOP1}, {1, NopSizes::NOP2}};
229 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
230 std::map<size_t, int> nop_indices;
231 EXPECT_EQ(0U, FindAllNops(instructions_.get(), &nop_indices));
232 EXPECT_EQ(0U, nop_indices.size());
233 }
234
235 TEST_F(FillerBasicBlockTransformTest, InjectToSingle) {
236 CreateInstructions(1);
237 NopSpec nop_spec = {
238 {0, NopSizes::NOP5},
239 {1, NopSizes::NOP8},
240 {3, NopSizes::NOP2}}; // Gets ignored.
241 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
242 std::map<size_t, int> nop_indices;
243 EXPECT_EQ(1U + 2U, FindAllNops(instructions_.get(), &nop_indices));
244 EXPECT_EQ(2U, nop_indices.size());
245 EXPECT_EQ(5U, nop_indices[0]);
246 EXPECT_EQ(8U, nop_indices[1]);
247 }
248
249 TEST_F(FillerBasicBlockTransformTest, InjectNone) {
250 CreateInstructions(7);
251 NopSpec nop_spec = {};
252 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
253 std::map<size_t, int> nop_indices;
254 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_indices));
255 EXPECT_EQ(0U, nop_indices.size());
256 }
257
258 TEST_F(FillerBasicBlockTransformTest, InjectConsecutive) {
259 CreateInstructions(4);
260 NopSpec nop_spec = {
261 {0, NopSizes::NOP1},
262 {1, NopSizes::NOP2},
263 {2, NopSizes::NOP3},
264 {3, NopSizes::NOP5},
265 {4, NopSizes::NOP7},
266 {5, NopSizes::NOP11}};
267 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
268 std::map<size_t, int> nop_indices;
269 EXPECT_EQ(4U + 6U, FindAllNops(instructions_.get(), &nop_indices));
270 EXPECT_EQ(6U, nop_indices.size());
271 EXPECT_EQ(1U, nop_indices[0]);
272 EXPECT_EQ(2U, nop_indices[1]);
273 EXPECT_EQ(3U, nop_indices[2]);
274 EXPECT_EQ(5U, nop_indices[3]);
275 EXPECT_EQ(7U, nop_indices[4]);
276 EXPECT_EQ(11U, nop_indices[5]);
277 }
278
279 TEST_F(FillerBasicBlockTransformTest, InjectAlternate) {
280 CreateInstructions(4);
281 NopSpec nop_spec = {
282 {0, NopSizes::NOP10},
283 {2, NopSizes::NOP9},
284 {4, NopSizes::NOP8},
285 {6, NopSizes::NOP7},
286 {8, NopSizes::NOP6}, // Gets ignored.
287 {10, NopSizes::NOP5}}; // Gets ignored.
288 FillerBasicBlockTransform::InjectNop(nop_spec, false, instructions_.get());
289 std::map<size_t, int> nop_indices;
290 EXPECT_EQ(4U + 4U, FindAllNops(instructions_.get(), &nop_indices));
291 EXPECT_EQ(4U, nop_indices.size());
292 EXPECT_EQ(10U, nop_indices[0]);
293 EXPECT_EQ(9U, nop_indices[2]);
294 EXPECT_EQ(8U, nop_indices[4]);
295 EXPECT_EQ(7U, nop_indices[6]);
296 }
297
298 TEST_F(FillerTransformTest, Apply) {
299 std::vector<std::string> target_list = {
300 "Used::M",
301 "TestUnusedFuncs"
302 };
303
304 ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
305
306 // Apply the transform.
307 TestFillerTransform tx(target_list);
308 tx.set_debug_friendly(true); // Copy source ranges to injected NOPs.
309 ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
310 &tx, policy_, &block_graph_, header_block_));
311
312 // Check results. First find and store target blocks.
313 EXPECT_EQ(2U, tx.num_targets_updated());
314 std::map<std::string, BlockGraph::Block*> target_map;
315 ASSERT_TRUE(FindAllBlocks(&block_graph_, target_list, &target_map));
316 ASSERT_TRUE(target_map.size() == target_list.size());
317
318 // Check the block for each target.
319 for (auto& it : target_map) {
320 BlockGraph::Block* target_block = it.second;
321
322 // Decompose target block to subgraph.
323 block_graph::BasicBlockSubGraph subgraph;
324 block_graph::BasicBlockDecomposer bb_decomposer(target_block, &subgraph);
325 ASSERT_TRUE(bb_decomposer.Decompose());
326
327 // Examine each basic code block.
328 block_graph::BasicBlockSubGraph::BBCollection& basic_blocks =
329 subgraph.basic_blocks();
330 for (auto bb = basic_blocks.begin(); bb != basic_blocks.end(); ++bb) {
331 BasicCodeBlock* bc_block = BasicCodeBlock::Cast(*bb);
332 if (bc_block != nullptr) {
333 CheckNops(&bc_block->instructions());
334 CheckNopSourceRange(&bc_block->instructions());
335 }
336 }
337 }
338 }
339
340 } // namespace transforms
341 } // namespace instrument
OLDNEW
« no previous file with comments | « syzygy/instrument/transforms/filler_transform.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698