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