1 : // Copyright 2012 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 : // Basic-block entry hook instrumentation transform unit-tests.
16 :
17 : #include "syzygy/instrument/transforms/basic_block_entry_hook_transform.h"
18 :
19 : #include "gtest/gtest.h"
20 : #include "syzygy/agent/basic_block_entry/basic_block_entry.h"
21 : #include "syzygy/block_graph/basic_block.h"
22 : #include "syzygy/block_graph/basic_block_decomposer.h"
23 : #include "syzygy/block_graph/basic_block_subgraph.h"
24 : #include "syzygy/block_graph/block_graph.h"
25 : #include "syzygy/block_graph/typed_block.h"
26 : #include "syzygy/common/indexed_frequency_data.h"
27 : #include "syzygy/core/unittest_util.h"
28 : #include "syzygy/instrument/transforms/unittest_util.h"
29 : #include "syzygy/pe/unittest_util.h"
30 :
31 : #include "mnemonics.h" // NOLINT
32 :
33 : namespace instrument {
34 : namespace transforms {
35 : namespace {
36 :
37 : using agent::basic_block_entry::BasicBlockEntry;
38 : using block_graph::BasicBlock;
39 : using block_graph::BasicBlockDecomposer;
40 : using block_graph::BasicBlockSubGraph;
41 : using block_graph::BasicCodeBlock;
42 : using block_graph::BlockGraph;
43 : using block_graph::Instruction;
44 : using common::IndexedFrequencyData;
45 : using common::ThreadLocalIndexedFrequencyData;
46 : using common::kBasicBlockEntryAgentId;
47 : using common::kBasicBlockFrequencyDataVersion;
48 :
49 : class TestBasicBlockEntryHookTransform : public BasicBlockEntryHookTransform {
50 : public:
51 : using BasicBlockEntryHookTransform::bb_entry_hook_ref_;
52 : using BasicBlockEntryHookTransform::thunk_section_;
53 :
54 E : BlockGraph::Block* frequency_data_block() {
55 E : return add_frequency_data_.frequency_data_block();
56 E : }
57 :
58 E : BlockGraph::Block* frequency_data_buffer_block() {
59 E : return add_frequency_data_.frequency_data_buffer_block();
60 E : }
61 : };
62 :
63 : class BasicBlockEntryHookTransformTest : public testing::TestDllTransformTest {
64 : public:
65 : enum InstrumentationKind {
66 : kAgentInstrumentation,
67 : kFastPathInstrumentation
68 : };
69 :
70 : void CheckBasicBlockInstrumentation(InstrumentationKind kind);
71 :
72 : protected:
73 : TestBasicBlockEntryHookTransform tx_;
74 : };
75 :
76 : void BasicBlockEntryHookTransformTest::CheckBasicBlockInstrumentation(
77 E : InstrumentationKind kind) {
78 : // Let's examine each eligible block to verify that its basic blocks have been
79 : // instrumented.
80 E : size_t num_decomposed_blocks = 0;
81 E : size_t total_basic_blocks = 0;
82 : BlockGraph::BlockMap::const_iterator block_iter =
83 E : block_graph_.blocks().begin();
84 E : for (; block_iter != block_graph_.blocks().end(); ++block_iter) {
85 E : const BlockGraph::Block& block = block_iter->second;
86 :
87 : // Skip non-code blocks.
88 E : if (block.type() != BlockGraph::CODE_BLOCK)
89 E : continue;
90 :
91 : // We'll skip thunks, they're a mixed bag of things.
92 E : if (block.section() == tx_.thunk_section_->id())
93 E : continue;
94 :
95 : // Blocks which are not bb-decomposable should be thunked. While there may
96 : // be some internal referrers, the only external referrers should be thunks.
97 E : if (!policy_->BlockIsSafeToBasicBlockDecompose(&block)) {
98 E : size_t num_external_thunks = 0;
99 : BlockGraph::Block::ReferrerSet::const_iterator ref_iter =
100 E : block.referrers().begin();
101 E : for (; ref_iter != block.referrers().end(); ++ref_iter) {
102 E : if (ref_iter->first != &block) {
103 E : ASSERT_EQ(tx_.thunk_section_->id(), ref_iter->first->section());
104 E : ++num_external_thunks;
105 : }
106 E : }
107 :
108 : // Each of the thunks for a non-decomposable block will reuse the same
109 : // id to source range map entry, so we increment total_basic_blocks once
110 : // if num_external_thunks is non-zero. Note that we cannot assert that
111 : // num_external_thunks > 0 because the block could be statically dead
112 : // (in a debug build, for example).
113 E : if (num_external_thunks != 0U)
114 E : ++total_basic_blocks;
115 E : continue;
116 : }
117 :
118 : // Note that we have attempted to validate a block.
119 E : ++num_decomposed_blocks;
120 :
121 : // Decompose the block to basic-blocks.
122 E : BasicBlockSubGraph subgraph;
123 E : BasicBlockDecomposer bb_decomposer(&block, &subgraph);
124 E : ASSERT_TRUE(bb_decomposer.Decompose());
125 :
126 : // Check if each non-padding basic code-block begins with the
127 : // instrumentation sequence.
128 E : size_t num_basic_blocks = 0;
129 : BasicBlockSubGraph::BBCollection::const_iterator bb_iter =
130 E : subgraph.basic_blocks().begin();
131 E : for (; bb_iter != subgraph.basic_blocks().end(); ++bb_iter) {
132 E : const BasicCodeBlock* bb = BasicCodeBlock::Cast(*bb_iter);
133 E : if (bb == NULL || bb->is_padding())
134 E : continue;
135 E : ++num_basic_blocks;
136 :
137 E : if (kind == kAgentInstrumentation) {
138 E : ASSERT_LE(3U, bb->instructions().size());
139 : BasicBlock::Instructions::const_iterator inst_iter =
140 E : bb->instructions().begin();
141 :
142 : // Instruction 1 should push the basic block id.
143 E : const Instruction& inst1 = *inst_iter;
144 E : EXPECT_EQ(I_PUSH, inst1.representation().opcode);
145 :
146 : // Instruction 2 should push the frequency data block pointer.
147 E : const Instruction& inst2 = *(++inst_iter);
148 E : EXPECT_EQ(I_PUSH, inst2.representation().opcode);
149 E : ASSERT_EQ(1U, inst2.references().size());
150 : EXPECT_EQ(tx_.frequency_data_block(),
151 E : inst2.references().begin()->second.block());
152 :
153 : // Instruction 3 should be a call to the bb entry hook.
154 E : const Instruction& inst3 = *(++inst_iter);
155 E : EXPECT_EQ(I_CALL, inst3.representation().opcode);
156 E : ASSERT_EQ(1U, inst3.references().size());
157 : EXPECT_EQ(tx_.bb_entry_hook_ref_.referenced(),
158 E : inst3.references().begin()->second.block());
159 E : } else {
160 i : DCHECK(kind == kFastPathInstrumentation);
161 i : ASSERT_LE(2U, bb->instructions().size());
162 : BasicBlock::Instructions::const_iterator inst_iter =
163 i : bb->instructions().begin();
164 :
165 : // Instruction 1 should push the basic block id.
166 i : const Instruction& inst1 = *inst_iter;
167 i : EXPECT_EQ(I_PUSH, inst1.representation().opcode);
168 :
169 : // Instruction 2 should be a call to the fast bb entry hook.
170 i : const Instruction& inst2 = *(++inst_iter);
171 i : EXPECT_EQ(I_CALL, inst2.representation().opcode);
172 i : ASSERT_EQ(1U, inst2.references().size());
173 : }
174 E : }
175 E : EXPECT_NE(0U, num_basic_blocks);
176 E : total_basic_blocks += num_basic_blocks;
177 E : }
178 :
179 E : EXPECT_NE(0U, num_decomposed_blocks);
180 E : EXPECT_EQ(total_basic_blocks, tx_.bb_ranges().size());
181 E : }
182 :
183 : } // namespace
184 :
185 E : TEST_F(BasicBlockEntryHookTransformTest, SetInlinePathFlag) {
186 E : EXPECT_FALSE(tx_.inline_fast_path());
187 E : tx_.set_inline_fast_path(true);
188 E : EXPECT_TRUE(tx_.inline_fast_path());
189 E : tx_.set_inline_fast_path(false);
190 E : EXPECT_FALSE(tx_.inline_fast_path());
191 E : }
192 :
193 E : TEST_F(BasicBlockEntryHookTransformTest, ApplyAgentInstrumentation) {
194 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
195 :
196 : // Apply the transform.
197 E : tx_.set_src_ranges_for_thunks(true);
198 : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
199 E : &tx_, policy_, &block_graph_, header_block_));
200 E : ASSERT_TRUE(tx_.frequency_data_block() != NULL);
201 E : ASSERT_TRUE(tx_.thunk_section_ != NULL);
202 E : ASSERT_TRUE(tx_.bb_entry_hook_ref_.IsValid());
203 E : ASSERT_LT(0u, tx_.bb_ranges().size());
204 :
205 : // Validate the basic-block frequency data structure.
206 E : block_graph::ConstTypedBlock<IndexedFrequencyData> frequency_data;
207 E : ASSERT_TRUE(frequency_data.Init(0, tx_.frequency_data_block()));
208 E : EXPECT_EQ(kBasicBlockEntryAgentId, frequency_data->agent_id);
209 E : EXPECT_EQ(kBasicBlockFrequencyDataVersion, frequency_data->version);
210 E : EXPECT_EQ(IndexedFrequencyData::BASIC_BLOCK_ENTRY, frequency_data->data_type);
211 E : EXPECT_EQ(tx_.bb_ranges().size(), frequency_data->num_entries);
212 E : EXPECT_EQ(sizeof(uint32), frequency_data->frequency_size);
213 : EXPECT_TRUE(frequency_data.HasReferenceAt(
214 E : frequency_data.OffsetOf(frequency_data->frequency_data)));
215 : EXPECT_EQ(sizeof(ThreadLocalIndexedFrequencyData),
216 E : tx_.frequency_data_block()->size());
217 : EXPECT_EQ(sizeof(ThreadLocalIndexedFrequencyData),
218 E : tx_.frequency_data_block()->data_size());
219 : EXPECT_EQ(frequency_data->num_entries * frequency_data->frequency_size,
220 E : tx_.frequency_data_buffer_block()->size());
221 :
222 : // Validate that all basic block have been instrumented.
223 E : CheckBasicBlockInstrumentation(kAgentInstrumentation);
224 E : }
225 :
226 : } // namespace transforms
227 : } // namespace instrument
|