blob: dea305e4ea064a431bce0417b5e5f0fa6362f66c [file] [log] [blame]
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/grinder/indexed_frequency_data_serializer.h"
#include "base/file_util.h"
#include "base/values.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_reader.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "syzygy/core/unittest_util.h"
#include "syzygy/pe/metadata.h"
#include "syzygy/pe/unittest_util.h"
namespace grinder {
namespace {
using base::DictionaryValue;
using base::FundamentalValue;
using base::JSONReader;
using base::ListValue;
using base::Value;
using basic_block_util::IndexedFrequencyInformation;
using basic_block_util::IndexedFrequencyMap;
using basic_block_util::EntryCountType;
using basic_block_util::ModuleIndexedFrequencyMap;
using basic_block_util::ModuleInformation;
using testing::ContainerEq;
const wchar_t kImageFileName[] = L"foo.dll";
const uint32 kBaseAddress = 0xDEADBEEF;
const uint32 kModuleSize = 0x1000;
const uint32 kImageChecksum = 0xCAFEBABE;
const uint32 kTimeDateStamp = 0xBABECAFE;
class TestIndexedFrequencyDataSerializer
: public IndexedFrequencyDataSerializer {
public:
using IndexedFrequencyDataSerializer::PopulateFromJsonValue;
using IndexedFrequencyDataSerializer::pretty_print_;
};
class IndexedFrequencyDataSerializerTest : public testing::PELibUnitTest {
public:
typedef testing::PELibUnitTest Super;
virtual void SetUp() OVERRIDE {
Super::SetUp();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
void InitModuleInfo(ModuleInformation* module_info) {
ASSERT_TRUE(module_info != NULL);
module_info->path = kImageFileName;
module_info->base_address.set_value(kBaseAddress);
module_info->module_size = kModuleSize;
module_info->module_checksum = kImageChecksum;
module_info->module_time_date_stamp = kTimeDateStamp;
}
protected:
base::ScopedTempDir temp_dir_;
};
} // namespace
TEST_F(IndexedFrequencyDataSerializerTest, Accessors) {
TestIndexedFrequencyDataSerializer serializer;
EXPECT_FALSE(serializer.pretty_print_);
serializer.set_pretty_print(true);
EXPECT_TRUE(serializer.pretty_print_);
}
TEST_F(IndexedFrequencyDataSerializerTest, LoadFromJsonFails) {
TestIndexedFrequencyDataSerializer serializer;
ModuleIndexedFrequencyMap frequency_map;
base::FilePath does_not_exist(
temp_dir_.path().AppendASCII("does_not_exist.json"));
EXPECT_FALSE(serializer.LoadFromJson(does_not_exist, &frequency_map));
base::FilePath some_path(testing::GetExeTestDataRelativePath(
testing::kBranchTraceFiles[0]));
EXPECT_FALSE(serializer.LoadFromJson(some_path, &frequency_map));
}
TEST_F(IndexedFrequencyDataSerializerTest, PopulateFromJsonValueFails) {
ModuleIndexedFrequencyMap frequency_map;
TestIndexedFrequencyDataSerializer serializer;
// It should fail if the outermost JSON object is not a list.
scoped_ptr<Value> int_value(Value::CreateIntegerValue(7));
ASSERT_FALSE(serializer.PopulateFromJsonValue(int_value.get(),
&frequency_map));
// It should fail if the outermost list does not contain dictionaries.
scoped_ptr<ListValue> list_value(new ListValue());
list_value->Append(Value::CreateBooleanValue(true));
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
list_value->Clear();
// It should fail if the list entry does not contain a metadata key.
DictionaryValue* dict_value = new DictionaryValue();
list_value->Append(dict_value);
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
// It should fail if the metadata value is not a dictionary.
dict_value->Set("metadata", Value::CreateStringValue("foo"));
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
// After adding the metadata value, it should still fail since there is no
// information/frequency_map key.
static const char kMetadataStr[] =
"{\n"
" \"command_line\": \"foo.exe\",\n"
" \"creation_time\": \"Wed, 19 Sep 2012 17:33:52 GMT\",\n"
" \"toolchain_version\": {\n"
" \"major\": 0,\n"
" \"minor\": 2,\n"
" \"build\": 7,\n"
" \"patch\": 0,\n"
" \"last_change\": \"0\"\n"
" },\n"
" \"module_signature\": {\n"
" \"path\": \"C:\\\\foo\\\\bar.dll\",\n"
" \"base_address\": 1904279552,\n"
" \"module_size\": 180224,\n"
" \"module_time_date_stamp\": \"0x46F7885059FE32\",\n"
" \"module_checksum\": \"0x257AF\"\n"
" },\n"
"}\n";
std::string error_msg;
scoped_ptr<Value> metadata(JSONReader().ReadAndReturnError(
kMetadataStr, true, NULL, &error_msg));
EXPECT_EQ(std::string(), error_msg);
ASSERT_TRUE(metadata.get() != NULL);
dict_value->Set("metadata", metadata.release());
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
// After adding the description value, it should still fail since there is no
// frequency_map key.
static const char kDescriptionStr[] =
"{\n"
" \"num_entries\": 100,\n"
" \"num_columns\": 3,\n"
" \"data_type\": \"branch\",\n"
" \"frequency_size\": 4\n"
"}\n";
scoped_ptr<Value> information(JSONReader().ReadAndReturnError(
kDescriptionStr, true, NULL, &error_msg));
EXPECT_EQ(std::string(), error_msg);
ASSERT_TRUE(information.get() != NULL);
dict_value->Set("description", information.release());
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
// It should still fail since the frequencies key has the wrong value type.
dict_value->Set("frequencies", Value::CreateStringValue("foo"));
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
// It should still fail since the frequencies list contains an invalid value.
ListValue* frequencies = new ListValue();
dict_value->Set("frequencies", frequencies);
frequencies->Append(Value::CreateStringValue("foo"));
ASSERT_FALSE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
// It should succeed once we start putting numbers into the entry_counts list.
IndexedFrequencyMap expected_values;
frequencies->Clear();
for (size_t i = 0; i < expected_values.size(); ++i) {
scoped_ptr<ListValue> entry(new ListValue());
entry->Append(Value::CreateIntegerValue(i * i));
entry->Append(Value::CreateIntegerValue(100 * i));
entry->Append(Value::CreateIntegerValue(100 * i + 1));
expected_values[std::make_pair(core::RelativeAddress(i * i), 0)] = 100 * i;
expected_values[std::make_pair(core::RelativeAddress(i * i), 1)] =
100 * i + 1;
frequencies->Append(entry.release());
}
ASSERT_TRUE(serializer.PopulateFromJsonValue(list_value.get(),
&frequency_map));
EXPECT_EQ(1U, frequency_map.size());
EXPECT_THAT(frequency_map.begin()->second.frequency_map,
testing::ContainerEq(expected_values));
}
TEST_F(IndexedFrequencyDataSerializerTest, RoundTrip) {
ModuleInformation module_info;
ASSERT_NO_FATAL_FAILURE(InitModuleInfo(&module_info));
size_t num_basic_blocks = 100;
size_t num_columns = 10;
ModuleIndexedFrequencyMap frequency_map;
IndexedFrequencyInformation& frequency_info = frequency_map[module_info];
frequency_info.num_entries = num_basic_blocks;
frequency_info.num_columns = num_columns;
frequency_info.data_type = common::IndexedFrequencyData::BRANCH;
frequency_info.frequency_size = 4;
frequency_info.frequency_map = IndexedFrequencyMap();
IndexedFrequencyMap& counters = frequency_info.frequency_map;
for (size_t i = 0; i < num_basic_blocks; ++i) {
for (size_t c = 0; c < num_columns; ++c)
counters[std::make_pair(core::RelativeAddress(i * i), c)] = i + c + 1;
}
base::FilePath json_path(temp_dir_.path().AppendASCII("test.json"));
TestIndexedFrequencyDataSerializer serializer;
serializer.set_pretty_print(true);
ASSERT_TRUE(serializer.SaveAsJson(frequency_map, json_path));
ModuleIndexedFrequencyMap new_frequency_map;
serializer.LoadFromJson(json_path, &new_frequency_map);
EXPECT_THAT(new_frequency_map, ContainerEq(frequency_map));
}
} // namespace grinder