1 : // Copyright 2013 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 : // Jump table case count instrumentation transform unit-tests.
16 :
17 : #include "syzygy/instrument/transforms/jump_table_count_transform.h"
18 :
19 : #include "gtest/gtest.h"
20 : #include "syzygy/block_graph/basic_block.h"
21 : #include "syzygy/block_graph/basic_block_decomposer.h"
22 : #include "syzygy/block_graph/basic_block_subgraph.h"
23 : #include "syzygy/block_graph/block_graph.h"
24 : #include "syzygy/block_graph/block_util.h"
25 : #include "syzygy/block_graph/typed_block.h"
26 : #include "syzygy/common/indexed_frequency_data.h"
27 : #include "syzygy/instrument/transforms/unittest_util.h"
28 :
29 : #include "mnemonics.h" // NOLINT
30 :
31 : namespace instrument {
32 : namespace transforms {
33 :
34 : namespace {
35 :
36 : using block_graph::BasicBlock;
37 : using block_graph::BasicCodeBlock;
38 : using block_graph::BasicBlockDecomposer;
39 : using block_graph::BasicBlockSubGraph;
40 : using block_graph::BlockGraph;
41 : using block_graph::Instruction;
42 : using common::IndexedFrequencyData;
43 :
44 : class TestJumpTableCaseCountTransform : public JumpTableCaseCountTransform {
45 : public:
46 : using JumpTableCaseCountTransform::add_frequency_data;
47 : using JumpTableCaseCountTransform::jump_table_case_counter_hook_ref;
48 : using JumpTableCaseCountTransform::thunk_section;
49 :
50 E : BlockGraph::Block* frequency_data_block() {
51 E : return add_frequency_data()->frequency_data_block();
52 E : }
53 :
54 E : BlockGraph::Block* frequency_data_buffer_block() {
55 E : return add_frequency_data()->frequency_data_buffer_block();
56 E : }
57 : };
58 :
59 : typedef testing::TestDllTransformTest JumpTableCaseCountTransformTest;
60 :
61 : // Ensures that the @p block is a jump table case count thunk.
62 E : void CheckBlockIsAThunk(BlockGraph::Block* block) {
63 : // Decompose the block to basic-blocks.
64 E : BasicBlockSubGraph subgraph;
65 E : BasicBlockDecomposer bb_decomposer(block, &subgraph);
66 E : ASSERT_TRUE(bb_decomposer.Decompose());
67 :
68 E : ASSERT_EQ(2u, subgraph.basic_blocks().size());
69 : const BasicCodeBlock* bb = BasicCodeBlock::Cast(
70 E : *subgraph.basic_blocks().begin());
71 E : ASSERT_TRUE(bb != NULL);
72 E : ASSERT_FALSE(bb->is_padding());
73 :
74 E : ASSERT_EQ(2U, bb->instructions().size());
75 : BasicBlock::Instructions::const_iterator inst_iter =
76 E : bb->instructions().begin();
77 :
78 : // Instruction 1 should push the case id.
79 E : EXPECT_EQ(I_PUSH, inst_iter->representation().opcode);
80 :
81 : // Instruction 2 should call the jump table counter hook.
82 E : ++inst_iter;
83 E : EXPECT_EQ(I_CALL, inst_iter->representation().opcode);
84 :
85 : EXPECT_EQ(BasicBlock::BASIC_END_BLOCK,
86 E : (*subgraph.basic_blocks().rbegin())->type());
87 E : }
88 :
89 : } // namespace
90 :
91 E : TEST_F(JumpTableCaseCountTransformTest, Apply) {
92 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
93 :
94 : // Apply the transform.
95 E : TestJumpTableCaseCountTransform tx;
96 : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
97 E : &tx, policy_, &block_graph_, header_block_));
98 E : ASSERT_TRUE(tx.frequency_data_block() != NULL);
99 E : ASSERT_TRUE(tx.thunk_section() != NULL);
100 E : ASSERT_TRUE(tx.jump_table_case_counter_hook_ref() != NULL);
101 E : ASSERT_TRUE(tx.jump_table_case_counter_hook_ref()->IsValid());
102 :
103 : // Validate the jump table frequency data structure.
104 E : block_graph::ConstTypedBlock<IndexedFrequencyData> frequency_data;
105 E : ASSERT_TRUE(frequency_data.Init(0, tx.frequency_data_block()));
106 E : EXPECT_EQ(sizeof(uint32), frequency_data->frequency_size);
107 E : EXPECT_EQ(common::kJumpTableCountAgentId, frequency_data->agent_id);
108 E : EXPECT_EQ(common::kJumpTableFrequencyDataVersion, frequency_data->version);
109 E : EXPECT_EQ(IndexedFrequencyData::JUMP_TABLE, frequency_data->data_type);
110 E : EXPECT_EQ(sizeof(IndexedFrequencyData), tx.frequency_data_block()->size());
111 : EXPECT_EQ(sizeof(IndexedFrequencyData),
112 E : tx.frequency_data_block()->data_size());
113 : EXPECT_TRUE(frequency_data.HasReferenceAt(
114 E : frequency_data.OffsetOf(frequency_data->frequency_data)));
115 E : BlockGraph::Reference frequency_data_reference;
116 : ASSERT_TRUE(frequency_data.block()->GetReference(
117 : frequency_data.OffsetOf(frequency_data->frequency_data),
118 E : &frequency_data_reference));
119 : EXPECT_EQ(frequency_data_reference.referenced(),
120 E : tx.frequency_data_buffer_block());
121 : EXPECT_EQ(frequency_data->num_entries * frequency_data->frequency_size,
122 E : tx.frequency_data_buffer_block()->size());
123 :
124 : // Examine each eligible block to verify that all the jump tables have
125 : // been instrumented.
126 E : size_t jump_table_entries = 0;
127 E : for (BlockGraph::BlockMap::const_iterator block_iter(
128 : block_graph_.blocks().begin());
129 E : block_iter != block_graph_.blocks().end();
130 E : ++block_iter) {
131 E : const BlockGraph::Block& block = block_iter->second;
132 :
133 : // Skip non-code blocks.
134 E : if (block.type() != BlockGraph::CODE_BLOCK)
135 E : continue;
136 :
137 : // We don't want to check the thunk blocks.
138 E : if (block.section() == tx.thunk_section()->id())
139 E : continue;
140 :
141 : // Iterate over the labels to find the jump tables.
142 E : for (BlockGraph::Block::LabelMap::const_iterator iter_label(
143 : block.labels().begin());
144 E : iter_label != block.labels().end();
145 E : ++iter_label) {
146 E : if (!iter_label->second.has_attributes(BlockGraph::JUMP_TABLE_LABEL))
147 E : continue;
148 :
149 E : size_t table_size = 0;
150 : ASSERT_TRUE(
151 E : block_graph::GetJumpTableSize(&block, iter_label, &table_size));
152 :
153 : BlockGraph::Block::ReferenceMap::const_iterator iter_ref =
154 E : block.references().find(iter_label->first);
155 E : ASSERT_TRUE(iter_ref != block.references().end());
156 :
157 : // Iterate over the references and ensure that they are thunked.
158 E : for (size_t i = 0; i < table_size; ++i) {
159 E : BlockGraph::Block* ref_block = iter_ref->second.referenced();
160 E : CheckBlockIsAThunk(ref_block);
161 E : ++iter_ref;
162 E : }
163 :
164 E : jump_table_entries += table_size;
165 E : }
166 E : }
167 E : DCHECK_EQ(frequency_data->num_entries, jump_table_entries);
168 E : }
169 :
170 : } // namespace transforms
171 : } // namespace instrument
|