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 : #include "syzygy/grinder/basic_block_entry_count_grinder.h"
16 :
17 : #include "base/file_util.h"
18 : #include "base/scoped_temp_dir.h"
19 : #include "base/values.h"
20 : #include "base/json/json_reader.h"
21 : #include "gmock/gmock.h"
22 : #include "gtest/gtest.h"
23 : #include "syzygy/common/syzygy_version.h"
24 : #include "syzygy/core/unittest_util.h"
25 : #include "syzygy/pe/metadata.h"
26 : #include "syzygy/pe/unittest_util.h"
27 :
28 : namespace grinder {
29 :
30 : namespace {
31 :
32 : using base::DictionaryValue;
33 : using base::ListValue;
34 : using base::Value;
35 : using basic_block_util::EntryCountMap;
36 : using basic_block_util::EntryCountType;
37 : using basic_block_util::ModuleEntryCountMap;
38 : using basic_block_util::IsValidFrequencySize;
39 : using basic_block_util::ModuleInformation;
40 : using common::kSyzygyVersion;
41 : using file_util::CreateAndOpenTemporaryFileInDir;
42 :
43 : const wchar_t kImageFileName[] = L"foo.dll";
44 : const uint32 kBaseAddress = 0xDEADBEEF;
45 : const uint32 kModuleSize = 0x1000;
46 : const uint32 kImageChecksum = 0xCAFEBABE;
47 : const uint32 kTimeDateStamp = 0xBABECAFE;
48 :
49 : class TestBasicBlockEntryCountGrinder : public BasicBlockEntryCountGrinder {
50 : public:
51 : using BasicBlockEntryCountGrinder::UpdateBasicBlockEntryCount;
52 : using BasicBlockEntryCountGrinder::InstrumentedModuleInformation;
53 : using BasicBlockEntryCountGrinder::parser_;
54 : };
55 :
56 : class BasicBlockEntryCountGrinderTest : public testing::PELibUnitTest {
57 : public:
58 : typedef testing::PELibUnitTest Super;
59 : typedef TestBasicBlockEntryCountGrinder::InstrumentedModuleInformation
60 : InstrumentedModuleInformation;
61 :
62 : static const size_t kNumBasicBlocks = 5;
63 :
64 E : BasicBlockEntryCountGrinderTest()
65 : : cmd_line_(FilePath(L"basic_block_entry_count_grinder.exe")) {
66 E : }
67 :
68 E : virtual void SetUp() OVERRIDE {
69 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
70 E : }
71 :
72 : void InitParser(trace::parser::ParseEventHandlerImpl* handler,
73 E : const wchar_t* file_path) {
74 E : ASSERT_TRUE(handler != NULL);
75 E : ASSERT_TRUE(parser_.Init(handler));
76 E : FilePath trace_file(testing::GetExeTestDataRelativePath(file_path));
77 E : ASSERT_TRUE(parser_.OpenTraceFile(trace_file));
78 E : }
79 :
80 : void RunGrinderTest(const wchar_t* trace_file,
81 E : ModuleEntryCountMap* module_entry_counts) {
82 E : ASSERT_TRUE(trace_file != NULL);
83 E : ASSERT_TRUE(module_entry_counts != NULL);
84 E : FilePath json_path;
85 E : ASSERT_NO_FATAL_FAILURE(GrindTraceFileToJson(trace_file, &json_path));
86 E : ASSERT_NO_FATAL_FAILURE(LoadJson(json_path, module_entry_counts));
87 E : }
88 :
89 : void GrindTraceFileToJson(const wchar_t* trace_file,
90 E : FilePath* json_path) {
91 E : ASSERT_TRUE(trace_file != NULL);
92 E : ASSERT_TRUE(json_path != NULL);
93 :
94 E : json_path->clear();
95 :
96 : // Consume the trace file.
97 E : TestBasicBlockEntryCountGrinder grinder;
98 E : cmd_line_.AppendSwitch("pretty-print");
99 E : grinder.ParseCommandLine(&cmd_line_);
100 E : ASSERT_NO_FATAL_FAILURE(InitParser(&grinder, trace_file));
101 E : grinder.SetParser(&parser_);
102 E : ASSERT_TRUE(parser_.Consume());
103 :
104 : // Grind and output the data to a JSON file.
105 E : FilePath temp_path;
106 : file_util::ScopedFILE json_file(
107 E : CreateAndOpenTemporaryFileInDir(temp_dir_.path(), &temp_path));
108 E : ASSERT_TRUE(json_file.get() != NULL);
109 E : ASSERT_TRUE(grinder.Grind());
110 E : ASSERT_TRUE(grinder.OutputData(json_file.get()));
111 E : *json_path = temp_path;
112 E : }
113 :
114 : void LoadJson(const FilePath& json_path,
115 E : ModuleEntryCountMap* module_entry_counts) {
116 E : ASSERT_TRUE(!json_path.empty());
117 E : ASSERT_TRUE(module_entry_counts != NULL);
118 :
119 E : BasicBlockEntryCountSerializer serializer;
120 E : ASSERT_TRUE(serializer.LoadFromJson(json_path, module_entry_counts));
121 E : }
122 :
123 E : void CreateExpectedCounts(int multiplier, EntryCountMap* expected) {
124 E : ASSERT_TRUE(expected != NULL);
125 E : expected->clear();
126 :
127 E : for (size_t i = 0; i < kNumBasicBlocks; ++i) {
128 E : (*expected)[i * i] = (i + 1) * multiplier;
129 E : }
130 E : }
131 :
132 E : void InitModuleInfo(InstrumentedModuleInformation* module_info) {
133 E : ASSERT_TRUE(module_info != NULL);
134 E : module_info->original_module.image_file_name = kImageFileName;
135 E : module_info->original_module.base_address = kBaseAddress;
136 E : module_info->original_module.module_size = kModuleSize;
137 E : module_info->original_module.image_checksum = kImageChecksum;
138 E : module_info->original_module.time_date_stamp = kTimeDateStamp;
139 :
140 E : for (size_t i = 0; i < kNumBasicBlocks; ++i) {
141 : using grinder::basic_block_util::RelativeAddress;
142 : using grinder::basic_block_util::RelativeAddressRange;
143 :
144 : module_info->block_ranges.push_back(
145 E : RelativeAddressRange(RelativeAddress(i * i), i + 1));
146 E : }
147 E : }
148 :
149 : void GetFrequencyData(const ModuleInformation& module_info,
150 : size_t frequency_size,
151 E : scoped_ptr<TraceIndexedFrequencyData>* data) {
152 E : ASSERT_TRUE(IsValidFrequencySize(frequency_size));
153 E : ASSERT_TRUE(data != NULL);
154 :
155 : static const size_t kMaxDataSize = kNumBasicBlocks * sizeof(uint32);
156 : static const size_t kBufferSize =
157 : sizeof(TraceIndexedFrequencyData) + kMaxDataSize - 1;
158 :
159 E : uint8* buffer = new uint8[kBufferSize];
160 E : ASSERT_TRUE(buffer != NULL);
161 E : ::memset(buffer, 0, kBufferSize);
162 :
163 E : data->reset(reinterpret_cast<TraceIndexedFrequencyData*>(buffer));
164 E : (*data)->data_type = TraceIndexedFrequencyData::BASIC_BLOCK;
165 : (*data)->module_base_addr =
166 E : reinterpret_cast<ModuleAddr>(module_info.base_address);
167 E : (*data)->module_base_size = module_info.module_size;
168 E : (*data)->module_checksum = module_info.image_checksum;
169 E : (*data)->module_time_date_stamp = module_info.time_date_stamp;
170 E : (*data)->frequency_size = frequency_size;
171 E : (*data)->num_entries = kNumBasicBlocks;
172 :
173 E : for (size_t i = 0; i < kNumBasicBlocks; ++i) {
174 E : uint8 value = i + 1;
175 E : switch (frequency_size) {
176 : case 1:
177 E : (*data)->frequency_data[i] = value;
178 E : break;
179 : case 2:
180 E : reinterpret_cast<uint16*>(&(*data)->frequency_data)[i] = value;
181 E : break;
182 : case 4:
183 E : reinterpret_cast<uint32*>(&(*data)->frequency_data)[i] = value;
184 : break;
185 : }
186 E : }
187 E : }
188 :
189 : protected:
190 : ScopedTempDir temp_dir_;
191 : CommandLine cmd_line_;
192 : trace::parser::Parser parser_;
193 : };
194 :
195 : } // namespace
196 :
197 E : TEST_F(BasicBlockEntryCountGrinderTest, ParseCommandLineSucceeds) {
198 E : TestBasicBlockEntryCountGrinder grinder1;
199 E : EXPECT_TRUE(grinder1.ParseCommandLine(&cmd_line_));
200 :
201 E : TestBasicBlockEntryCountGrinder grinder2;
202 E : cmd_line_.AppendSwitch("pretty-print");
203 E : EXPECT_TRUE(grinder2.ParseCommandLine(&cmd_line_));
204 E : }
205 :
206 E : TEST_F(BasicBlockEntryCountGrinderTest, SetParserSucceeds) {
207 E : TestBasicBlockEntryCountGrinder grinder;
208 :
209 E : grinder.ParseCommandLine(&cmd_line_);
210 :
211 : ASSERT_NO_FATAL_FAILURE(InitParser(
212 E : &grinder, testing::kBBEntryTraceFiles[0]));
213 :
214 E : grinder.SetParser(&parser_);
215 E : EXPECT_EQ(&parser_, grinder.parser_);
216 E : }
217 :
218 E : TEST_F(BasicBlockEntryCountGrinderTest, GrindFailsOnNoEvents) {
219 E : TestBasicBlockEntryCountGrinder grinder;
220 :
221 E : grinder.ParseCommandLine(&cmd_line_);
222 :
223 : ASSERT_NO_FATAL_FAILURE(InitParser(
224 E : &grinder, testing::kBBEntryTraceFiles[0]));
225 E : grinder.SetParser(&parser_);
226 :
227 E : EXPECT_FALSE(grinder.Grind());
228 E : }
229 :
230 E : TEST_F(BasicBlockEntryCountGrinderTest, UpdateBasicBlockEntryCount) {
231 E : InstrumentedModuleInformation module_info;
232 E : ASSERT_NO_FATAL_FAILURE(InitModuleInfo(&module_info));
233 :
234 E : TestBasicBlockEntryCountGrinder grinder;
235 E : scoped_ptr<TraceIndexedFrequencyData> data;
236 : // Validate 1-byte frequency data.
237 : ASSERT_NO_FATAL_FAILURE(
238 E : GetFrequencyData(module_info.original_module, 1, &data));
239 E : ASSERT_EQ(TraceIndexedFrequencyData::BASIC_BLOCK, data->data_type);
240 E : ASSERT_EQ(1U, data->frequency_size);
241 E : grinder.UpdateBasicBlockEntryCount(module_info, data.get());
242 E : EXPECT_EQ(1U, grinder.entry_count_map().size());
243 :
244 E : EntryCountMap expected_counts;
245 E : CreateExpectedCounts(1, &expected_counts);
246 : EXPECT_THAT(grinder.entry_count_map().begin()->second,
247 E : testing::ContainerEq(expected_counts));
248 :
249 E : data.reset();
250 : // Validate 2-byte frequency data.
251 : ASSERT_NO_FATAL_FAILURE(
252 E : GetFrequencyData(module_info.original_module, 2, &data));
253 E : ASSERT_EQ(2U, data->frequency_size);
254 E : grinder.UpdateBasicBlockEntryCount(module_info, data.get());
255 E : EXPECT_EQ(1U, grinder.entry_count_map().size());
256 :
257 E : CreateExpectedCounts(2, &expected_counts);
258 : EXPECT_THAT(grinder.entry_count_map().begin()->second,
259 E : testing::ContainerEq(expected_counts));
260 :
261 E : data.reset();
262 : // Validate 4-byte frequency data.
263 : ASSERT_NO_FATAL_FAILURE(
264 E : GetFrequencyData(module_info.original_module, 4, &data));
265 E : ASSERT_EQ(4U, data->frequency_size);
266 E : grinder.UpdateBasicBlockEntryCount(module_info, data.get());
267 E : EXPECT_EQ(1U, grinder.entry_count_map().size());
268 :
269 E : CreateExpectedCounts(3, &expected_counts);
270 : EXPECT_THAT(grinder.entry_count_map().begin()->second,
271 E : testing::ContainerEq(expected_counts));
272 E : }
273 :
274 E : TEST_F(BasicBlockEntryCountGrinderTest, GrindBasicBlockEntryDataSucceeds) {
275 E : ModuleEntryCountMap entry_counts;
276 : ASSERT_NO_FATAL_FAILURE(
277 E : RunGrinderTest(testing::kBBEntryTraceFiles[0], &entry_counts));
278 : // TODO(rogerm): Inspect value for bb-entry specific expected data.
279 E : }
280 :
281 E : TEST_F(BasicBlockEntryCountGrinderTest, GrindCoverageDataSucceeds) {
282 E : ModuleEntryCountMap entry_counts;
283 : ASSERT_NO_FATAL_FAILURE(
284 E : RunGrinderTest(testing::kCoverageTraceFiles[0], &entry_counts));
285 : // TODO(rogerm): Inspect value for coverage specific expected data.
286 E : }
287 :
288 : } // namespace grinder
|