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

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: 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
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
36 const char kNopName[] = "NOP";
37
38 // Finds all blocks whose names appear in @p target_list, and writes the mapping
39 // fron name to block in @p result. Returns true iff all names in @p target_list
40 // are found.
41 bool FindAllBlocks(BlockGraph* block_graph,
42 const std::vector<std::string>& target_list,
43 std::map<std::string, BlockGraph::Block*>* result) {
44 DCHECK(result && result->empty());
45 std::set<std::string> name_set(target_list.begin(), target_list.end());
46 for (auto& it : block_graph->blocks_mutable()) {
47 std::string block_name = it.second.name();
48 if (name_set.find(block_name) != name_set.end()) {
49 (*result)[block_name] = &it.second;
50 name_set.erase(block_name);
51 }
52 }
53 return name_set.empty();
54 }
55
56 // Finds code offsets and sizes of every NOP in @p instructions and stores the
57 // result in @p nop_offsets. Returns the number of @p instructions.
58 size_t FindAllNops(BasicBlock::Instructions* instructions,
59 std::map<size_t, int>* nop_offsets) {
60 // Find code offsets of every NOP that appears.
61 size_t offset = 0LL;
62 BasicBlock::Instructions::iterator inst_it = instructions->begin();
63 for (; inst_it != instructions->end(); ++inst_it, ++offset) {
64 if (!::strcmp(inst_it->GetName(), kNopName))
65 (*nop_offsets)[offset] = inst_it->size();
66 }
67 return offset;
68 }
69
70 class TestNopInjector : public NopInjector {
71 public:
72 explicit TestNopInjector(const NopSpec& nop_spec) : NopInjector(nop_spec) { }
73 };
74
75 class NopInjectorTest : public testing::Test {
76 public:
77 typedef testing::Test Super;
78
79 // Creates a new length @p n instruction list for testing.
80 void CreateInstructions(int n) {
81 using assm::eax;
82 using assm::ebp;
83 using assm::esp;
84 using assm::kSize32Bit;
85
86 instructions_.reset(new BasicBlock::Instructions);
87 if (n == 0)
88 return;
89
90 block_graph::BasicBlockAssembler
91 assm(instructions_->begin(), instructions_.get());
92 bool setup_stack = false;
93 --n; // Reserve for ret.
94 if (n > 3) {
95 setup_stack = true;
96 n -= 3; // Reserve for stack frame setup.
97 }
98 if (setup_stack) {
99 assm.push(ebp);
100 assm.mov(ebp, esp);
101 }
102 for (; n > 0; --n)
103 assm.mov(eax, block_graph::Immediate(n, kSize32Bit));
104 if (setup_stack)
105 assm.pop(ebp);
106 assm.ret(0);
107 }
108
109 protected:
110 scoped_ptr<BasicBlock::Instructions> instructions_;
111 };
112
113 class TestFillerTransform : public FillerTransform {
114 public:
115 explicit TestFillerTransform(const std::vector<std::string>& target_list)
116 : FillerTransform(target_list) { }
117 };
118
119 class FillerTransformTest : public testing::TestDllTransformTest {
120 public:
121 typedef testing::TestDllTransformTest Super;
122
123 // Checks that every basic code block in a block has NOP placed at expected
124 // code offsets.
125 void CheckBlockNops(BlockGraph::Block* block) {
126 using block_graph::BasicBlockDecomposer;
127 using block_graph::BasicBlockSubGraph;
128 using block_graph::BasicCodeBlock;
129
130 // Decompose target block to subgraph.
131 BasicBlockSubGraph subgraph;
132 BasicBlockDecomposer bb_decomposer(block, &subgraph);
133 ASSERT_TRUE(bb_decomposer.Decompose());
134
135 // Examine each code subblock.
136 BasicBlockSubGraph::BBCollection& basic_blocks =
137 subgraph.basic_blocks();
138 for (auto bb = basic_blocks.begin(); bb != basic_blocks.end(); ++bb) {
139 BasicCodeBlock* bc_block = BasicCodeBlock::Cast(*bb);
140 if (bc_block == nullptr)
141 continue;
142
143 std::map<size_t, int> nop_offsets;
144 size_t num_inst = FindAllNops(&bc_block->instructions(), &nop_offsets);
145 // The checks here depend on how NopInjector is initialized in
146 // FillerBasicBlockTransform.
147 EXPECT_TRUE(1 >= num_inst || nop_offsets[1] == 1);
148 }
149 }
150 };
151
152 } // namespace
153
154 // Sanity check for helper CreateInstructions().
155 TEST_F(NopInjectorTest, CreateInstructions) {
156 for (int i = 0; i < 10; ++i) {
157 CreateInstructions(i);
158 size_t count = 0;
159 for (auto inst : *instructions_.get())
160 ++count;
161 EXPECT_EQ(i, count);
162 }
163 }
164
165 TEST_F(NopInjectorTest, InjectToStart) {
166 CreateInstructions(5);
167 TestNopInjector injector1({{0, NopInjector::NOP4}});
168 EXPECT_EQ(1U, injector1.Inject(instructions_.get()));
169 std::map<size_t, int> nop_offsets1;
170 EXPECT_EQ(6U, FindAllNops(instructions_.get(), &nop_offsets1));
171 EXPECT_EQ(1U, nop_offsets1.size());
172 EXPECT_EQ(4U, nop_offsets1[0]);
173
174 // Inject another NOP, when a NOP already exists.
175 TestNopInjector injector2({{0, NopInjector::NOP1}});
176 EXPECT_EQ(1U, injector2.Inject(instructions_.get()));
177 std::map<size_t, int> nop_offsets2;
178 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_offsets2));
179 EXPECT_EQ(2U, nop_offsets2.size()); // New + existing.
180 EXPECT_EQ(1U, nop_offsets2[0]); // From |injector2|.
181 EXPECT_EQ(4U, nop_offsets2[1]); // From |injector1|.
182 }
183
184 TEST_F(NopInjectorTest, InjectToBeforeEnd) {
185 CreateInstructions(7);
186 TestNopInjector injector({{6, NopInjector::NOP2}});
187 EXPECT_EQ(1U, injector.Inject(instructions_.get()));
188 std::map<size_t, int> nop_offsets;
189 EXPECT_EQ(8U, FindAllNops(instructions_.get(), &nop_offsets));
190 EXPECT_EQ(1U, nop_offsets.size());
191 EXPECT_EQ(2U, nop_offsets[6]);
192 }
193
194 TEST_F(NopInjectorTest, CannotInjectBeyondEnd) {
195 CreateInstructions(7);
196 TestNopInjector injector({{7, NopInjector::NOP1}, {17, NopInjector::NOP1}});
197 EXPECT_EQ(0U, injector.Inject(instructions_.get()));
198 std::map<size_t, int> nop_offsets;
199 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_offsets));
200 EXPECT_EQ(0U, nop_offsets.size());
201 }
202
203 TEST_F(NopInjectorTest, InjectToEmpty) {
204 CreateInstructions(0);
205 TestNopInjector injector({{0, NopInjector::NOP1}, {1, NopInjector::NOP2}});
206 EXPECT_EQ(0U, injector.Inject(instructions_.get()));
207 std::map<size_t, int> nop_offsets;
208 EXPECT_EQ(0U, FindAllNops(instructions_.get(), &nop_offsets));
209 EXPECT_EQ(0U, nop_offsets.size());
210 }
211
212 TEST_F(NopInjectorTest, InjectToSingle) {
213 CreateInstructions(1);
214 TestNopInjector injector({
215 {0, NopInjector::NOP5},
216 {1, NopInjector::NOP8},
217 {3, NopInjector::NOP2}}); // Gets ignored.
218 EXPECT_EQ(2U, injector.Inject(instructions_.get()));
219 std::map<size_t, int> nop_offsets;
220 EXPECT_EQ(3U, FindAllNops(instructions_.get(), &nop_offsets));
221 EXPECT_EQ(2U, nop_offsets.size());
222 EXPECT_EQ(5U, nop_offsets[0]);
223 EXPECT_EQ(8U, nop_offsets[1]);
224 }
225
226 TEST_F(NopInjectorTest, InjectNone) {
227 CreateInstructions(7);
228 TestNopInjector injector({});
229 EXPECT_EQ(0U, injector.Inject(instructions_.get()));
230 std::map<size_t, int> nop_offsets;
231 EXPECT_EQ(7U, FindAllNops(instructions_.get(), &nop_offsets));
232 EXPECT_EQ(0U, nop_offsets.size());
233 }
234
235 TEST_F(NopInjectorTest, InjectConsecutive) {
236 CreateInstructions(4);
237 TestNopInjector injector({
238 {0, NopInjector::NOP1},
239 {1, NopInjector::NOP2},
240 {2, NopInjector::NOP3},
241 {3, NopInjector::NOP5},
242 {4, NopInjector::NOP7},
243 {5, NopInjector::NOP11}});
244 EXPECT_EQ(6U, injector.Inject(instructions_.get()));
245 std::map<size_t, int> nop_offsets;
246 EXPECT_EQ(10U, FindAllNops(instructions_.get(), &nop_offsets));
247 EXPECT_EQ(6U, nop_offsets.size());
248 EXPECT_EQ(1U, nop_offsets[0]);
249 EXPECT_EQ(2U, nop_offsets[1]);
250 EXPECT_EQ(3U, nop_offsets[2]);
251 EXPECT_EQ(5U, nop_offsets[3]);
252 EXPECT_EQ(7U, nop_offsets[4]);
253 EXPECT_EQ(11U, nop_offsets[5]);
254 }
255
256 TEST_F(NopInjectorTest, InjectAlternate) {
257 CreateInstructions(4);
258 TestNopInjector injector({
259 {0, NopInjector::NOP10},
260 {2, NopInjector::NOP9},
261 {4, NopInjector::NOP8},
262 {6, NopInjector::NOP7},
263 {8, NopInjector::NOP6}, // Gets ignored.
264 {10, NopInjector::NOP5}}); // Gets ignored.
265 EXPECT_EQ(4U, injector.Inject(instructions_.get()));
266 std::map<size_t, int> nop_offsets;
267 EXPECT_EQ(8U, FindAllNops(instructions_.get(), &nop_offsets));
268 EXPECT_EQ(4U, nop_offsets.size());
269 EXPECT_EQ(10U, nop_offsets[0]);
270 EXPECT_EQ(9U, nop_offsets[2]);
271 EXPECT_EQ(8U, nop_offsets[4]);
272 EXPECT_EQ(7U, nop_offsets[6]);
273 }
274
275 TEST_F(FillerTransformTest, Apply) {
276 std::vector<std::string> target_list = {
277 "Used::M",
278 "TestUnusedFuncs"
279 };
280
281 ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
282
283 // Apply the transform.
284 TestFillerTransform tx(target_list);
285 ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
286 &tx, policy_, &block_graph_, header_block_));
287
288 // Check results. First find and store target blocks.
289 EXPECT_EQ(2U, tx.num_target_updated());
290 std::map<std::string, BlockGraph::Block*> target_map;
291 ASSERT_TRUE(FindAllBlocks(&block_graph_, target_list, &target_map));
292 ASSERT_TRUE(target_map.size() == target_list.size());
293
294 // Check the block for each target.
295 for (auto& it : target_map) {
296 BlockGraph::Block* target_block = it.second;
297 CheckBlockNops(target_block);
298 }
299 }
300
301 } // namespace transforms
302 } // namespace instrument
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698