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