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 : #include "syzygy/grinder/grinders/indexed_frequency_data_grinder.h"
16 :
17 : #include "base/values.h"
18 : #include "base/files/file_util.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/core/unittest_util.h"
25 : #include "syzygy/pe/metadata.h"
26 : #include "syzygy/pe/unittest_util.h"
27 : #include "syzygy/version/syzygy_version.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::IndexedFrequencyMap;
38 : using basic_block_util::EntryCountType;
39 : using basic_block_util::ModuleIndexedFrequencyMap;
40 : using basic_block_util::IsValidFrequencySize;
41 : using basic_block_util::ModuleInformation;
42 : using version::kSyzygyVersion;
43 : using base::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 TestIndexedFrequencyDataGrinder : public IndexedFrequencyDataGrinder {
62 : public:
63 : using IndexedFrequencyDataGrinder::UpdateBasicBlockFrequencyData;
64 : using IndexedFrequencyDataGrinder::InstrumentedModuleInformation;
65 : using IndexedFrequencyDataGrinder::parser_;
66 : };
67 :
68 : class IndexedFrequencyDataGrinderTest : public testing::PELibUnitTest {
69 : public:
70 : typedef testing::PELibUnitTest Super;
71 : typedef TestIndexedFrequencyDataGrinder::InstrumentedModuleInformation
72 : InstrumentedModuleInformation;
73 :
74 : static const size_t kNumBasicBlocks = 5;
75 : static const size_t kNumColumns = 3;
76 :
77 E : IndexedFrequencyDataGrinderTest()
78 : : cmd_line_(base::FilePath(L"indexed_frequency_data_grinder.exe")) {
79 E : }
80 :
81 E : void SetUp() override {
82 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
83 E : }
84 :
85 : void InitParser(trace::parser::ParseEventHandlerImpl* handler,
86 E : const wchar_t* file_path) {
87 E : ASSERT_TRUE(handler != NULL);
88 E : ASSERT_TRUE(parser_.Init(handler));
89 E : base::FilePath trace_file(testing::GetExeTestDataRelativePath(file_path));
90 E : ASSERT_TRUE(parser_.OpenTraceFile(trace_file));
91 E : }
92 :
93 : void RunGrinderTest(const wchar_t* trace_file,
94 E : ModuleIndexedFrequencyMap* module_entry_counts) {
95 E : ASSERT_TRUE(trace_file != NULL);
96 E : ASSERT_TRUE(module_entry_counts != NULL);
97 E : base::FilePath json_path;
98 E : ASSERT_NO_FATAL_FAILURE(GrindTraceFileToJson(trace_file, &json_path));
99 E : ASSERT_NO_FATAL_FAILURE(LoadJson(json_path, module_entry_counts));
100 E : }
101 :
102 : void GrindTraceFileToJson(const wchar_t* trace_file,
103 E : base::FilePath* json_path) {
104 E : ASSERT_TRUE(trace_file != NULL);
105 E : ASSERT_TRUE(json_path != NULL);
106 :
107 E : json_path->clear();
108 :
109 : // Consume the trace file.
110 E : TestIndexedFrequencyDataGrinder grinder;
111 E : cmd_line_.AppendSwitch("pretty-print");
112 E : grinder.ParseCommandLine(&cmd_line_);
113 E : ASSERT_NO_FATAL_FAILURE(InitParser(&grinder, trace_file));
114 E : grinder.SetParser(&parser_);
115 E : ASSERT_TRUE(parser_.Consume());
116 :
117 : // Grind and output the data to a JSON file.
118 E : base::FilePath temp_path;
119 : base::ScopedFILE json_file(
120 E : CreateAndOpenTemporaryFileInDir(temp_dir_.path(), &temp_path));
121 E : ASSERT_TRUE(json_file.get() != NULL);
122 E : ASSERT_TRUE(grinder.Grind());
123 E : ASSERT_TRUE(grinder.OutputData(json_file.get()));
124 E : *json_path = temp_path;
125 E : }
126 :
127 : void LoadJson(const base::FilePath& json_path,
128 E : ModuleIndexedFrequencyMap* module_entry_counts) {
129 E : ASSERT_TRUE(!json_path.empty());
130 E : ASSERT_TRUE(module_entry_counts != NULL);
131 :
132 E : IndexedFrequencyDataSerializer serializer;
133 E : ASSERT_TRUE(serializer.LoadFromJson(json_path, module_entry_counts));
134 E : }
135 :
136 E : void CreateExpectedCounts(int multiplier, IndexedFrequencyMap* expected) {
137 E : ASSERT_TRUE(expected != NULL);
138 E : expected->clear();
139 :
140 E : for (size_t i = 0; i < kNumBasicBlocks; ++i) {
141 E : for (size_t c = 0; c < kNumColumns; ++c) {
142 : using grinder::basic_block_util::RelativeAddress;
143 :
144 : (*expected)[std::make_pair(RelativeAddress(i * i), c)] =
145 E : (i + c + 1) * multiplier;
146 E : }
147 E : }
148 E : }
149 :
150 E : void InitModuleInfo(InstrumentedModuleInformation* module_info) {
151 E : ASSERT_TRUE(module_info != NULL);
152 : using core::AbsoluteAddress;
153 E : module_info->original_module.path = kImageFileName;
154 E : module_info->original_module.base_address = AbsoluteAddress(kBaseAddress);
155 E : module_info->original_module.module_size = kModuleSize;
156 E : module_info->original_module.module_checksum = kImageChecksum;
157 E : module_info->original_module.module_time_date_stamp = kTimeDateStamp;
158 :
159 E : for (size_t i = 0; i < kNumBasicBlocks; ++i) {
160 : using grinder::basic_block_util::RelativeAddress;
161 : using grinder::basic_block_util::RelativeAddressRange;
162 :
163 : module_info->block_ranges.push_back(
164 E : RelativeAddressRange(RelativeAddress(i * i), i + 1));
165 E : }
166 E : }
167 :
168 : void GetFrequencyData(const ModuleInformation& module_info,
169 : size_t frequency_size,
170 E : ScopedFrequencyData* data) {
171 E : ASSERT_TRUE(IsValidFrequencySize(frequency_size));
172 E : ASSERT_TRUE(data != NULL);
173 :
174 : static const size_t kMaxDataSize =
175 : kNumColumns * kNumBasicBlocks * sizeof(uint32);
176 : static const size_t kBufferSize =
177 : sizeof(TraceIndexedFrequencyData) + kMaxDataSize - 1;
178 :
179 E : uint8* buffer = new uint8[kBufferSize];
180 E : ASSERT_TRUE(buffer != NULL);
181 E : ::memset(buffer, 0, kBufferSize);
182 :
183 E : data->reset(reinterpret_cast<TraceIndexedFrequencyData*>(buffer));
184 : (*data)->module_base_addr =
185 E : reinterpret_cast<ModuleAddr>(module_info.base_address.value());
186 E : (*data)->module_base_size = module_info.module_size;
187 E : (*data)->module_checksum = module_info.module_checksum;
188 E : (*data)->module_time_date_stamp = module_info.module_time_date_stamp;
189 E : (*data)->num_entries = kNumBasicBlocks;
190 E : (*data)->num_columns = kNumColumns;
191 E : (*data)->data_type = common::IndexedFrequencyData::BRANCH;
192 E : (*data)->frequency_size = frequency_size;
193 :
194 E : for (size_t i = 0; i < kNumBasicBlocks; ++i) {
195 E : for (size_t c = 0; c < kNumColumns; ++c) {
196 E : uint8 value = i + c + 1;
197 E : size_t offset = (i * kNumColumns) + c;
198 E : switch (frequency_size) {
199 : case 1:
200 E : (*data)->frequency_data[offset] = value;
201 E : break;
202 : case 2:
203 E : reinterpret_cast<uint16*>(&(*data)->frequency_data)[offset] = value;
204 E : break;
205 : case 4:
206 E : reinterpret_cast<uint32*>(&(*data)->frequency_data)[offset] = value;
207 : break;
208 : }
209 E : }
210 E : }
211 E : }
212 :
213 : protected:
214 : base::ScopedTempDir temp_dir_;
215 : base::CommandLine cmd_line_;
216 : trace::parser::Parser parser_;
217 : };
218 :
219 : } // namespace
220 :
221 E : TEST_F(IndexedFrequencyDataGrinderTest, ParseCommandLineSucceeds) {
222 E : TestIndexedFrequencyDataGrinder grinder1;
223 E : EXPECT_TRUE(grinder1.ParseCommandLine(&cmd_line_));
224 :
225 E : TestIndexedFrequencyDataGrinder grinder2;
226 E : cmd_line_.AppendSwitch("pretty-print");
227 E : EXPECT_TRUE(grinder2.ParseCommandLine(&cmd_line_));
228 E : }
229 :
230 E : TEST_F(IndexedFrequencyDataGrinderTest, SetParserSucceeds) {
231 E : TestIndexedFrequencyDataGrinder grinder;
232 :
233 E : grinder.ParseCommandLine(&cmd_line_);
234 :
235 : ASSERT_NO_FATAL_FAILURE(InitParser(
236 E : &grinder, testing::kBranchTraceFiles[0]));
237 :
238 E : grinder.SetParser(&parser_);
239 E : EXPECT_EQ(&parser_, grinder.parser_);
240 E : }
241 :
242 E : TEST_F(IndexedFrequencyDataGrinderTest, GrindFailsOnNoEvents) {
243 E : TestIndexedFrequencyDataGrinder grinder;
244 :
245 E : grinder.ParseCommandLine(&cmd_line_);
246 :
247 : ASSERT_NO_FATAL_FAILURE(InitParser(
248 E : &grinder, testing::kBranchTraceFiles[0]));
249 E : grinder.SetParser(&parser_);
250 :
251 E : EXPECT_FALSE(grinder.Grind());
252 E : }
253 :
254 E : TEST_F(IndexedFrequencyDataGrinderTest, UpdateBasicBlockFrequencyData) {
255 E : InstrumentedModuleInformation module_info;
256 E : ASSERT_NO_FATAL_FAILURE(InitModuleInfo(&module_info));
257 :
258 : // Validate for all valid frequency sizes.
259 E : const uint8 kMaxFrequencySize = 4;
260 E : for (size_t frequency_size = 1;
261 E : frequency_size <= kMaxFrequencySize;
262 E : frequency_size *= 2) {
263 E : TestIndexedFrequencyDataGrinder grinder;
264 E : ScopedFrequencyData data;
265 E : IndexedFrequencyMap expected_counts;
266 :
267 : ASSERT_NO_FATAL_FAILURE(GetFrequencyData(module_info.original_module,
268 : frequency_size,
269 E : &data));
270 E : ASSERT_EQ(common::IndexedFrequencyData::BRANCH, data->data_type);
271 E : ASSERT_EQ(frequency_size, data->frequency_size);
272 :
273 E : grinder.UpdateBasicBlockFrequencyData(module_info, data.get());
274 E : EXPECT_EQ(1U, grinder.frequency_data_map().size());
275 E : CreateExpectedCounts(1, &expected_counts);
276 : EXPECT_THAT(grinder.frequency_data_map().begin()->second.frequency_map,
277 E : testing::ContainerEq(expected_counts));
278 :
279 E : grinder.UpdateBasicBlockFrequencyData(module_info, data.get());
280 E : EXPECT_EQ(1U, grinder.frequency_data_map().size());
281 E : CreateExpectedCounts(2, &expected_counts);
282 : EXPECT_THAT(grinder.frequency_data_map().begin()->second.frequency_map,
283 E : testing::ContainerEq(expected_counts));
284 :
285 E : grinder.UpdateBasicBlockFrequencyData(module_info, data.get());
286 E : EXPECT_EQ(1U, grinder.frequency_data_map().size());
287 E : CreateExpectedCounts(3, &expected_counts);
288 : EXPECT_THAT(grinder.frequency_data_map().begin()->second.frequency_map,
289 E : testing::ContainerEq(expected_counts));
290 E : }
291 E : }
292 :
293 E : TEST_F(IndexedFrequencyDataGrinderTest, GrindBranchEntryDataSucceeds) {
294 E : ModuleIndexedFrequencyMap entry_counts;
295 : ASSERT_NO_FATAL_FAILURE(
296 E : RunGrinderTest(testing::kBranchTraceFiles[0], &entry_counts));
297 : // TODO(rogerm): Inspect value for bb-entry specific expected data.
298 E : }
299 :
300 :
301 : } // namespace grinders
302 : } // namespace grinder
|