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/indexed_frequency_data_serializer.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/core/unittest_util.h"
24 : #include "syzygy/pe/metadata.h"
25 : #include "syzygy/pe/unittest_util.h"
26 :
27 : namespace grinder {
28 :
29 : namespace {
30 :
31 : using base::DictionaryValue;
32 : using base::FundamentalValue;
33 : using base::JSONReader;
34 : using base::ListValue;
35 : using base::Value;
36 : using basic_block_util::IndexedFrequencyInformation;
37 : using basic_block_util::IndexedFrequencyMap;
38 : using basic_block_util::EntryCountType;
39 : using basic_block_util::ModuleIndexedFrequencyMap;
40 : using basic_block_util::ModuleInformation;
41 : using testing::ContainerEq;
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 : class TestIndexedFrequencyDataSerializer
50 : : public IndexedFrequencyDataSerializer {
51 : public:
52 : using IndexedFrequencyDataSerializer::PopulateFromJsonValue;
53 : using IndexedFrequencyDataSerializer::pretty_print_;
54 : };
55 :
56 : class IndexedFrequencyDataSerializerTest : public testing::PELibUnitTest {
57 : public:
58 : typedef testing::PELibUnitTest Super;
59 :
60 E : virtual void SetUp() override {
61 E : Super::SetUp();
62 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
63 E : }
64 :
65 E : void InitModuleInfo(ModuleInformation* module_info) {
66 E : ASSERT_TRUE(module_info != NULL);
67 E : module_info->path = kImageFileName;
68 E : module_info->base_address.set_value(kBaseAddress);
69 E : module_info->module_size = kModuleSize;
70 E : module_info->module_checksum = kImageChecksum;
71 E : module_info->module_time_date_stamp = kTimeDateStamp;
72 E : }
73 : protected:
74 : base::ScopedTempDir temp_dir_;
75 : };
76 :
77 : } // namespace
78 :
79 E : TEST_F(IndexedFrequencyDataSerializerTest, Accessors) {
80 E : TestIndexedFrequencyDataSerializer serializer;
81 E : EXPECT_FALSE(serializer.pretty_print_);
82 :
83 E : serializer.set_pretty_print(true);
84 E : EXPECT_TRUE(serializer.pretty_print_);
85 E : }
86 :
87 E : TEST_F(IndexedFrequencyDataSerializerTest, LoadFromJsonFails) {
88 E : TestIndexedFrequencyDataSerializer serializer;
89 E : ModuleIndexedFrequencyMap frequency_map;
90 :
91 : base::FilePath does_not_exist(
92 E : temp_dir_.path().AppendASCII("does_not_exist.json"));
93 E : EXPECT_FALSE(serializer.LoadFromJson(does_not_exist, &frequency_map));
94 :
95 : base::FilePath some_path(testing::GetExeTestDataRelativePath(
96 E : testing::kBranchTraceFiles[0]));
97 E : EXPECT_FALSE(serializer.LoadFromJson(some_path, &frequency_map));
98 E : }
99 :
100 E : TEST_F(IndexedFrequencyDataSerializerTest, PopulateFromJsonValueFails) {
101 E : ModuleIndexedFrequencyMap frequency_map;
102 E : TestIndexedFrequencyDataSerializer serializer;
103 :
104 : // It should fail if the outermost JSON object is not a list.
105 E : scoped_ptr<Value> int_value(new base::FundamentalValue(7));
106 : ASSERT_FALSE(serializer.PopulateFromJsonValue(int_value.get(),
107 E : &frequency_map));
108 :
109 : // It should fail if the outermost list does not contain dictionaries.
110 E : scoped_ptr<ListValue> list_value(new ListValue());
111 E : list_value->Append(new base::FundamentalValue(true));
112 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
113 E : &frequency_map));
114 E : list_value->Clear();
115 :
116 : // It should fail if the list entry does not contain a metadata key.
117 E : DictionaryValue* dict_value = new DictionaryValue();
118 E : list_value->Append(dict_value);
119 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
120 E : &frequency_map));
121 :
122 : // It should fail if the metadata value is not a dictionary.
123 E : dict_value->Set("metadata", new base::FundamentalValue("foo"));
124 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
125 E : &frequency_map));
126 :
127 : // After adding the metadata value, it should still fail since there is no
128 : // information/frequency_map key.
129 : static const char kMetadataStr[] =
130 : "{\n"
131 : " \"command_line\": \"foo.exe\",\n"
132 : " \"creation_time\": \"Wed, 19 Sep 2012 17:33:52 GMT\",\n"
133 : " \"toolchain_version\": {\n"
134 : " \"major\": 0,\n"
135 : " \"minor\": 2,\n"
136 : " \"build\": 7,\n"
137 : " \"patch\": 0,\n"
138 : " \"last_change\": \"0\"\n"
139 : " },\n"
140 : " \"module_signature\": {\n"
141 : " \"path\": \"C:\\\\foo\\\\bar.dll\",\n"
142 : " \"base_address\": 1904279552,\n"
143 : " \"module_size\": 180224,\n"
144 : " \"module_time_date_stamp\": \"0x46F7885059FE32\",\n"
145 : " \"module_checksum\": \"0x257AF\"\n"
146 : " },\n"
147 : "}\n";
148 :
149 E : std::string error_msg;
150 : scoped_ptr<Value> metadata(JSONReader().ReadAndReturnError(
151 E : kMetadataStr, true, NULL, &error_msg));
152 E : EXPECT_EQ(std::string(), error_msg);
153 E : ASSERT_TRUE(metadata.get() != NULL);
154 E : dict_value->Set("metadata", metadata.release());
155 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
156 E : &frequency_map));
157 :
158 : // After adding the description value, it should still fail since there is no
159 : // frequency_map key.
160 : static const char kDescriptionStr[] =
161 : "{\n"
162 : " \"num_entries\": 100,\n"
163 : " \"num_columns\": 3,\n"
164 : " \"data_type\": \"branch\",\n"
165 : " \"frequency_size\": 4\n"
166 : "}\n";
167 :
168 : scoped_ptr<Value> information(JSONReader().ReadAndReturnError(
169 E : kDescriptionStr, true, NULL, &error_msg));
170 E : EXPECT_EQ(std::string(), error_msg);
171 E : ASSERT_TRUE(information.get() != NULL);
172 E : dict_value->Set("description", information.release());
173 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
174 E : &frequency_map));
175 :
176 : // It should still fail since the frequencies key has the wrong value type.
177 E : dict_value->Set("frequencies", new base::StringValue("foo"));
178 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
179 E : &frequency_map));
180 :
181 : // It should still fail since the frequencies list contains an invalid value.
182 E : ListValue* frequencies = new ListValue();
183 E : dict_value->Set("frequencies", frequencies);
184 E : frequencies->Append(new base::StringValue("foo"));
185 : ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
186 E : &frequency_map));
187 :
188 : // It should succeed once we start putting numbers into the entry_counts list.
189 E : IndexedFrequencyMap expected_values;
190 E : frequencies->Clear();
191 E : for (int i = 0; i < static_cast<int>(expected_values.size()); ++i) {
192 i : scoped_ptr<ListValue> entry(new ListValue());
193 i : entry->Append(new base::FundamentalValue(i * i));
194 i : entry->Append(new base::FundamentalValue(100 * i));
195 i : entry->Append(new base::FundamentalValue(100 * i + 1));
196 i : expected_values[std::make_pair(core::RelativeAddress(i * i), 0)] = 100 * i;
197 : expected_values[std::make_pair(core::RelativeAddress(i * i), 1)] =
198 i : 100 * i + 1;
199 :
200 i : frequencies->Append(entry.release());
201 i : }
202 :
203 : ASSERT_TRUE(serializer.PopulateFromJsonValue(list_value.get(),
204 E : &frequency_map));
205 :
206 E : EXPECT_EQ(1U, frequency_map.size());
207 : EXPECT_THAT(frequency_map.begin()->second.frequency_map,
208 E : testing::ContainerEq(expected_values));
209 E : }
210 :
211 E : TEST_F(IndexedFrequencyDataSerializerTest, RoundTrip) {
212 E : ModuleInformation module_info;
213 E : ASSERT_NO_FATAL_FAILURE(InitModuleInfo(&module_info));
214 :
215 E : size_t num_basic_blocks = 100;
216 E : size_t num_columns = 10;
217 :
218 E : ModuleIndexedFrequencyMap frequency_map;
219 E : IndexedFrequencyInformation& frequency_info = frequency_map[module_info];
220 E : frequency_info.num_entries = num_basic_blocks;
221 E : frequency_info.num_columns = num_columns;
222 E : frequency_info.data_type = common::IndexedFrequencyData::BRANCH;
223 E : frequency_info.frequency_size = 4;
224 E : frequency_info.frequency_map = IndexedFrequencyMap();
225 :
226 E : IndexedFrequencyMap& counters = frequency_info.frequency_map;
227 E : for (size_t i = 0; i < num_basic_blocks; ++i) {
228 E : for (size_t c = 0; c < num_columns; ++c)
229 E : counters[std::make_pair(core::RelativeAddress(i * i), c)] = i + c + 1;
230 E : }
231 :
232 E : base::FilePath json_path(temp_dir_.path().AppendASCII("test.json"));
233 :
234 E : TestIndexedFrequencyDataSerializer serializer;
235 E : serializer.set_pretty_print(true);
236 E : ASSERT_TRUE(serializer.SaveAsJson(frequency_map, json_path));
237 :
238 E : ModuleIndexedFrequencyMap new_frequency_map;
239 E : serializer.LoadFromJson(json_path, &new_frequency_map);
240 :
241 E : EXPECT_THAT(new_frequency_map, ContainerEq(frequency_map));
242 E : }
243 :
244 : } // namespace grinder
|