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