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