// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#include <inttypes.h>
|
#include <sys/time.h>
|
|
#include <map>
|
#include <sstream>
|
#include <string>
|
|
#include "base/logging.h"
|
#include "base/macros.h"
|
|
#include "compat/string.h"
|
#include "compat/test.h"
|
#include "file_utils.h"
|
#include "perf_data_structures.h"
|
#include "perf_data_utils.h"
|
#include "perf_protobuf_io.h"
|
#include "perf_reader.h"
|
#include "perf_serializer.h"
|
#include "perf_test_files.h"
|
#include "scoped_temp_path.h"
|
#include "test_perf_data.h"
|
#include "test_utils.h"
|
|
namespace {
|
|
// Returns a string representation of an unsigned integer |value|.
|
string UintToString(uint64_t value) {
|
std::stringstream ss;
|
ss << value;
|
return ss.str();
|
}
|
|
} // namespace
|
|
namespace quipper {
|
|
using PerfEvent = PerfDataProto_PerfEvent;
|
using SampleInfo = PerfDataProto_SampleInfo;
|
|
namespace {
|
|
// Set up some parameterized fixtures for test cases that should run
|
// against multiple files.
|
class SerializePerfDataFiles : public ::testing::TestWithParam<const char*> {};
|
class SerializeAllPerfDataFiles : public ::testing::TestWithParam<const char*> {
|
};
|
class SerializePerfDataProtoFiles
|
: public ::testing::TestWithParam<const char*> {};
|
|
// Gets the timestamp from an event field in PerfDataProto.
|
const uint64_t GetSampleTimestampFromEventProto(
|
const PerfDataProto_PerfEvent& event) {
|
// Get SampleInfo from the correct type-specific event field for the event.
|
if (event.has_mmap_event()) {
|
return event.mmap_event().sample_info().sample_time_ns();
|
} else if (event.has_sample_event()) {
|
return event.sample_event().sample_time_ns();
|
} else if (event.has_comm_event()) {
|
return event.comm_event().sample_info().sample_time_ns();
|
} else if (event.has_fork_event()) {
|
return event.fork_event().sample_info().sample_time_ns();
|
} else if (event.has_exit_event()) {
|
return event.exit_event().sample_info().sample_time_ns();
|
} else if (event.has_lost_event()) {
|
return event.lost_event().sample_info().sample_time_ns();
|
} else if (event.has_throttle_event()) {
|
return event.throttle_event().sample_info().sample_time_ns();
|
} else if (event.has_read_event()) {
|
return event.read_event().sample_info().sample_time_ns();
|
} else if (event.has_aux_event()) {
|
return event.aux_event().sample_info().sample_time_ns();
|
}
|
return 0;
|
}
|
|
// Verifies that |proto|'s events are in chronological order. No event should
|
// have an earlier timestamp than a preceding event.
|
void CheckChronologicalOrderOfSerializedEvents(const PerfDataProto& proto) {
|
uint64_t prev_time_ns = 0;
|
for (int i = 0; i < proto.events_size(); ++i) {
|
// Compare each timestamp against the previous event's timestamp.
|
uint64_t time_ns = GetSampleTimestampFromEventProto(proto.events(i));
|
if (i > 0) {
|
EXPECT_GE(time_ns, prev_time_ns);
|
}
|
prev_time_ns = time_ns;
|
}
|
}
|
|
void SerializeAndDeserialize(const string& input, const string& output,
|
bool do_remap, bool discard_unused_events) {
|
PerfDataProto perf_data_proto;
|
PerfParserOptions options;
|
options.do_remap = do_remap;
|
options.deduce_huge_page_mappings = false;
|
options.combine_mappings = false;
|
options.discard_unused_events = discard_unused_events;
|
options.sample_mapping_percentage_threshold = 100.0f;
|
|
ASSERT_TRUE(SerializeFromFileWithOptions(input, options, &perf_data_proto));
|
|
PerfReader reader;
|
ASSERT_TRUE(reader.Deserialize(perf_data_proto));
|
|
PerfParser parser(&reader, options);
|
ASSERT_TRUE(parser.ParseRawEvents());
|
|
// Check perf event stats.
|
const PerfDataProto_PerfEventStats& in_stats = perf_data_proto.stats();
|
PerfEventStats out_stats;
|
PerfSerializer::DeserializeParserStats(perf_data_proto, &out_stats);
|
|
EXPECT_EQ(in_stats.num_sample_events(), out_stats.num_sample_events);
|
EXPECT_EQ(in_stats.num_mmap_events(), out_stats.num_mmap_events);
|
EXPECT_EQ(in_stats.num_fork_events(), out_stats.num_fork_events);
|
EXPECT_EQ(in_stats.num_exit_events(), out_stats.num_exit_events);
|
EXPECT_EQ(in_stats.num_sample_events_mapped(),
|
out_stats.num_sample_events_mapped);
|
EXPECT_EQ(do_remap, in_stats.did_remap());
|
EXPECT_EQ(do_remap, out_stats.did_remap);
|
|
ASSERT_TRUE(reader.WriteFile(output));
|
}
|
|
void SerializeToFileAndBack(const string& input, const string& output) {
|
struct timeval pre_serialize_time;
|
gettimeofday(&pre_serialize_time, NULL);
|
|
// Serialize with and without sorting by chronological order.
|
PerfDataProto input_perf_data_proto;
|
|
// Serialize with and without sorting by chronological order.
|
// PerfSerializer is stateless w/r to Serialize or Deserialize calls so we can
|
// use just one.
|
PerfParserOptions options;
|
options.sort_events_by_time = true;
|
options.deduce_huge_page_mappings = false;
|
options.combine_mappings = false;
|
EXPECT_TRUE(
|
SerializeFromFileWithOptions(input, options, &input_perf_data_proto));
|
CheckChronologicalOrderOfSerializedEvents(input_perf_data_proto);
|
|
input_perf_data_proto.Clear();
|
options.sort_events_by_time = false;
|
EXPECT_TRUE(
|
SerializeFromFileWithOptions(input, options, &input_perf_data_proto));
|
|
// Make sure the timestamp_sec was properly recorded.
|
EXPECT_TRUE(input_perf_data_proto.has_timestamp_sec());
|
// Check it against the current time.
|
struct timeval post_serialize_time;
|
gettimeofday(&post_serialize_time, NULL);
|
EXPECT_GE(input_perf_data_proto.timestamp_sec(), pre_serialize_time.tv_sec);
|
EXPECT_LE(input_perf_data_proto.timestamp_sec(), post_serialize_time.tv_sec);
|
|
// Now store the protobuf into a file.
|
ScopedTempFile input_file;
|
EXPECT_FALSE(input_file.path().empty());
|
string input_filename = input_file.path();
|
ScopedTempFile output_file;
|
EXPECT_FALSE(output_file.path().empty());
|
string output_filename = output_file.path();
|
|
EXPECT_TRUE(WriteProtobufToFile(input_perf_data_proto, input_filename));
|
|
PerfDataProto output_perf_data_proto;
|
EXPECT_TRUE(ReadProtobufFromFile(&output_perf_data_proto, input_filename));
|
|
EXPECT_TRUE(DeserializeToFile(output_perf_data_proto, output));
|
|
EXPECT_TRUE(WriteProtobufToFile(output_perf_data_proto, output_filename));
|
|
EXPECT_NE(GetFileSize(input_filename), 0);
|
ASSERT_TRUE(CompareFileContents(input_filename, output_filename));
|
|
remove(input_filename.c_str());
|
remove(output_filename.c_str());
|
}
|
|
} // namespace
|
|
TEST_P(SerializePerfDataFiles, Test1Cycle) {
|
ScopedTempDir output_dir;
|
ASSERT_FALSE(output_dir.path().empty());
|
string output_path = output_dir.path();
|
|
// Read perf data using the PerfReader class.
|
// Dump it to a protobuf.
|
// Read the protobuf, and reconstruct the perf data.
|
PerfReader input_perf_reader, output_perf_reader, output_perf_reader1,
|
output_perf_reader2;
|
PerfDataProto perf_data_proto, perf_data_proto1;
|
|
const string test_file = GetParam();
|
const string input_perf_data = GetTestInputFilePath(test_file);
|
const string output_perf_data = output_path + test_file + ".serialized.out";
|
const string output_perf_data1 =
|
output_path + test_file + ".serialized.1.out";
|
|
LOG(INFO) << "Testing " << input_perf_data;
|
ASSERT_TRUE(input_perf_reader.ReadFile(input_perf_data));
|
|
// Discard unused events for a pseudorandom selection of half the test data
|
// files. The selection is based on the Md5sum prefix of the file contents,
|
// so that the files can be moved around in the |kPerfDataFiles| list or
|
// renamed.
|
std::vector<char> test_file_data;
|
ASSERT_TRUE(FileToBuffer(input_perf_data, &test_file_data));
|
bool discard = (Md5Prefix(test_file_data) % 2 == 0);
|
|
SerializeAndDeserialize(input_perf_data, output_perf_data, false, discard);
|
output_perf_reader.ReadFile(output_perf_data);
|
SerializeAndDeserialize(output_perf_data, output_perf_data1, false,
|
discard);
|
output_perf_reader1.ReadFile(output_perf_data1);
|
|
ASSERT_TRUE(CompareFileContents(output_perf_data, output_perf_data1));
|
|
string output_perf_data2 = output_path + test_file + ".io.out";
|
SerializeToFileAndBack(input_perf_data, output_perf_data2);
|
output_perf_reader2.ReadFile(output_perf_data2);
|
|
// Make sure the # of events do not increase. They can decrease because
|
// some unused non-sample events may be discarded.
|
if (discard) {
|
ASSERT_LE(output_perf_reader.events().size(),
|
input_perf_reader.events().size());
|
} else {
|
ASSERT_EQ(output_perf_reader.events().size(),
|
input_perf_reader.events().size());
|
}
|
ASSERT_EQ(output_perf_reader1.events().size(),
|
output_perf_reader.events().size());
|
ASSERT_EQ(output_perf_reader2.events().size(),
|
input_perf_reader.events().size());
|
|
EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data));
|
EXPECT_TRUE(ComparePerfBuildIDLists(input_perf_data, output_perf_data));
|
EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data2));
|
EXPECT_TRUE(ComparePerfBuildIDLists(output_perf_data, output_perf_data2));
|
}
|
|
TEST_P(SerializeAllPerfDataFiles, TestRemap) {
|
ScopedTempDir output_dir;
|
ASSERT_FALSE(output_dir.path().empty());
|
const string output_path = output_dir.path();
|
|
// Read perf data using the PerfReader class with address remapping.
|
// Dump it to a protobuf.
|
// Read the protobuf, and reconstruct the perf data.
|
const string test_file = GetParam();
|
const string input_perf_data = GetTestInputFilePath(test_file);
|
LOG(INFO) << "Testing " << input_perf_data;
|
const string output_perf_data = output_path + test_file + ".ser.remap.out";
|
SerializeAndDeserialize(input_perf_data, output_perf_data, true, true);
|
}
|
|
TEST_P(SerializePerfDataFiles, TestCommMd5s) {
|
ScopedTempDir output_dir;
|
ASSERT_FALSE(output_dir.path().empty());
|
string output_path = output_dir.path();
|
|
// Replace command strings with their Md5sums. Test size adjustment for
|
// command strings.
|
const string test_file = GetParam();
|
const string input_perf_data = GetTestInputFilePath(test_file);
|
LOG(INFO) << "Testing COMM Md5sum for " << input_perf_data;
|
|
PerfDataProto perf_data_proto;
|
EXPECT_TRUE(SerializeFromFile(input_perf_data, &perf_data_proto));
|
|
// Need to get file attrs to construct a SampleInfoReader within
|
// |serializer|.
|
ASSERT_GT(perf_data_proto.file_attrs().size(), 0U);
|
ASSERT_TRUE(perf_data_proto.file_attrs(0).has_attr());
|
PerfSerializer serializer;
|
PerfFileAttr attr;
|
const auto& proto_attr = perf_data_proto.file_attrs(0);
|
ASSERT_TRUE(serializer.DeserializePerfFileAttr(proto_attr, &attr));
|
serializer.CreateSampleInfoReader(attr, false /* read_cross_endian */);
|
|
for (int j = 0; j < perf_data_proto.events_size(); ++j) {
|
PerfDataProto_PerfEvent& event = *perf_data_proto.mutable_events(j);
|
if (event.header().type() != PERF_RECORD_COMM) continue;
|
CHECK(event.has_comm_event());
|
|
string comm_md5_string = UintToString(event.comm_event().comm_md5_prefix());
|
// Make sure it fits in the comm string array, accounting for the null
|
// terminator.
|
struct comm_event dummy;
|
if (comm_md5_string.size() > arraysize(dummy.comm) - 1)
|
comm_md5_string.resize(arraysize(dummy.comm) - 1);
|
int64_t string_len_diff =
|
GetUint64AlignedStringLength(comm_md5_string) -
|
GetUint64AlignedStringLength(event.comm_event().comm());
|
event.mutable_comm_event()->set_comm(comm_md5_string);
|
|
// Update with the new size.
|
event.mutable_header()->set_size(event.header().size() + string_len_diff);
|
}
|
|
const string output_perf_data = output_path + test_file + ".ser.comm.out";
|
EXPECT_TRUE(DeserializeToFile(perf_data_proto, output_perf_data));
|
EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data));
|
}
|
|
TEST_P(SerializePerfDataFiles, TestMmapMd5s) {
|
ScopedTempDir output_dir;
|
ASSERT_FALSE(output_dir.path().empty());
|
string output_path = output_dir.path();
|
|
// Replace MMAP filename strings with their Md5sums. Test size adjustment for
|
// MMAP filename strings.
|
const string test_file = GetParam();
|
const string input_perf_data = GetTestInputFilePath(test_file);
|
LOG(INFO) << "Testing MMAP Md5sum for " << input_perf_data;
|
|
PerfDataProto perf_data_proto;
|
EXPECT_TRUE(SerializeFromFile(input_perf_data, &perf_data_proto));
|
|
// Need to get file attrs to construct a SampleInfoReader within
|
// |serializer|.
|
ASSERT_GT(perf_data_proto.file_attrs().size(), 0U);
|
ASSERT_TRUE(perf_data_proto.file_attrs(0).has_attr());
|
PerfSerializer serializer;
|
PerfFileAttr attr;
|
const auto& proto_attr = perf_data_proto.file_attrs(0);
|
ASSERT_TRUE(serializer.DeserializePerfFileAttr(proto_attr, &attr));
|
serializer.CreateSampleInfoReader(attr, false /* read_cross_endian */);
|
|
for (int j = 0; j < perf_data_proto.events_size(); ++j) {
|
PerfDataProto_PerfEvent& event = *perf_data_proto.mutable_events(j);
|
if (event.header().type() != PERF_RECORD_MMAP) continue;
|
ASSERT_TRUE(event.has_mmap_event());
|
|
string filename_md5_string =
|
UintToString(event.mmap_event().filename_md5_prefix());
|
struct mmap_event dummy;
|
// Make sure the Md5 prefix string can fit in the filename buffer,
|
// including the null terminator
|
if (filename_md5_string.size() > arraysize(dummy.filename) - 1)
|
filename_md5_string.resize(arraysize(dummy.filename) - 1);
|
|
int64_t string_len_diff =
|
GetUint64AlignedStringLength(filename_md5_string) -
|
GetUint64AlignedStringLength(event.mmap_event().filename());
|
event.mutable_mmap_event()->set_filename(filename_md5_string);
|
|
// Update with the new size.
|
event.mutable_header()->set_size(event.header().size() + string_len_diff);
|
}
|
|
const string output_perf_data = output_path + test_file + ".ser.mmap.out";
|
// Make sure the data can be deserialized after replacing the filenames with
|
// Md5sum prefixes. No need to check the output.
|
EXPECT_TRUE(DeserializeToFile(perf_data_proto, output_perf_data));
|
}
|
|
TEST_P(SerializePerfDataProtoFiles, TestProtoFiles) {
|
const string test_file = GetParam();
|
string perf_data_proto_file = GetTestInputFilePath(test_file);
|
LOG(INFO) << "Testing " << perf_data_proto_file;
|
std::vector<char> data;
|
ASSERT_TRUE(FileToBuffer(perf_data_proto_file, &data));
|
string text(data.begin(), data.end());
|
|
PerfDataProto perf_data_proto;
|
ASSERT_TRUE(TextFormat::ParseFromString(text, &perf_data_proto));
|
|
// Test deserializing.
|
PerfReader deserializer;
|
EXPECT_TRUE(deserializer.Deserialize(perf_data_proto));
|
}
|
|
TEST_P(SerializePerfDataFiles, TestBuildIDs) {
|
const string test_file = GetParam();
|
string perf_data_file = GetTestInputFilePath(test_file);
|
LOG(INFO) << "Testing " << perf_data_file;
|
|
// Serialize into a protobuf.
|
PerfDataProto perf_data_proto;
|
EXPECT_TRUE(SerializeFromFile(perf_data_file, &perf_data_proto));
|
|
// Test a file with build ID filenames removed.
|
for (int i = 0; i < perf_data_proto.build_ids_size(); ++i) {
|
perf_data_proto.mutable_build_ids(i)->clear_filename();
|
}
|
PerfReader deserializer;
|
EXPECT_TRUE(deserializer.Deserialize(perf_data_proto));
|
}
|
|
TEST(PerfSerializerTest, SerializesAndDeserializesTraceMetadata) {
|
std::stringstream input;
|
|
const size_t data_size =
|
testing::ExamplePerfSampleEvent_Tracepoint::kEventSize;
|
|
// header
|
testing::ExamplePerfDataFileHeader file_header(1 << HEADER_TRACING_DATA);
|
file_header.WithAttrCount(1).WithDataSize(data_size);
|
file_header.WriteTo(&input);
|
const perf_file_header& header = file_header.header();
|
// attrs
|
testing::ExamplePerfFileAttr_Tracepoint(73).WriteTo(&input);
|
// data
|
ASSERT_EQ(static_cast<u64>(input.tellp()), header.data.offset);
|
testing::ExamplePerfSampleEvent_Tracepoint().WriteTo(&input);
|
ASSERT_EQ(input.tellp(), file_header.data_end());
|
// metadata
|
const unsigned int metadata_count = 1;
|
// HEADER_TRACING_DATA
|
testing::ExampleTracingMetadata tracing_metadata(
|
file_header.data_end() + metadata_count * sizeof(perf_file_section));
|
tracing_metadata.index_entry().WriteTo(&input);
|
tracing_metadata.data().WriteTo(&input);
|
|
// Parse and Serialize
|
|
PerfReader reader;
|
ASSERT_TRUE(reader.ReadFromString(input.str()));
|
|
PerfDataProto perf_data_proto;
|
ASSERT_TRUE(reader.Serialize(&perf_data_proto));
|
|
const string& tracing_metadata_str = tracing_metadata.data().value();
|
const auto& tracing_data = perf_data_proto.tracing_data();
|
EXPECT_EQ(tracing_metadata_str, tracing_data.tracing_data());
|
EXPECT_EQ(Md5Prefix(tracing_metadata_str),
|
tracing_data.tracing_data_md5_prefix());
|
|
// Deserialize
|
|
PerfReader deserializer;
|
EXPECT_TRUE(deserializer.Deserialize(perf_data_proto));
|
EXPECT_EQ(tracing_metadata_str, deserializer.tracing_data());
|
}
|
|
TEST(PerfSerializerTest, SerializesAndDeserializesMmapEvents) {
|
std::stringstream input;
|
|
// header
|
testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
|
|
// data
|
|
// PERF_RECORD_HEADER_ATTR
|
testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID,
|
true /*sample_id_all*/)
|
.WriteTo(&input);
|
|
// PERF_RECORD_MMAP
|
testing::ExampleMmapEvent(1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so",
|
testing::SampleInfo().Tid(1001))
|
.WriteTo(&input);
|
|
// PERF_RECORD_MMAP2
|
testing::ExampleMmap2Event(1002, 0x2c1000, 0x2000, 0x3000, "/usr/lib/bar.so",
|
testing::SampleInfo().Tid(1002))
|
.WriteTo(&input);
|
|
// Parse and Serialize
|
|
PerfReader reader;
|
ASSERT_TRUE(reader.ReadFromString(input.str()));
|
|
PerfDataProto perf_data_proto;
|
ASSERT_TRUE(reader.Serialize(&perf_data_proto));
|
|
EXPECT_EQ(2, perf_data_proto.events().size());
|
|
{
|
const PerfDataProto::PerfEvent& event = perf_data_proto.events(0);
|
EXPECT_EQ(PERF_RECORD_MMAP, event.header().type());
|
EXPECT_TRUE(event.has_mmap_event());
|
const PerfDataProto::MMapEvent& mmap = event.mmap_event();
|
EXPECT_EQ(1001, mmap.pid());
|
EXPECT_EQ(1001, mmap.tid());
|
EXPECT_EQ(0x1c1000, mmap.start());
|
EXPECT_EQ(0x1000, mmap.len());
|
EXPECT_EQ(0, mmap.pgoff());
|
EXPECT_EQ("/usr/lib/foo.so", mmap.filename());
|
}
|
|
{
|
const PerfDataProto::PerfEvent& event = perf_data_proto.events(1);
|
EXPECT_EQ(PERF_RECORD_MMAP2, event.header().type());
|
EXPECT_TRUE(event.has_mmap_event());
|
const PerfDataProto::MMapEvent& mmap = event.mmap_event();
|
EXPECT_EQ(1002, mmap.pid());
|
EXPECT_EQ(1002, mmap.tid());
|
EXPECT_EQ(0x2c1000, mmap.start());
|
EXPECT_EQ(0x2000, mmap.len());
|
EXPECT_EQ(0x3000, mmap.pgoff());
|
EXPECT_EQ("/usr/lib/bar.so", mmap.filename());
|
// These values are hard-coded in ExampleMmap2Event:
|
EXPECT_EQ(6, mmap.maj());
|
EXPECT_EQ(7, mmap.min());
|
EXPECT_EQ(8, mmap.ino());
|
EXPECT_EQ(9, mmap.ino_generation());
|
EXPECT_EQ(1 | 2, mmap.prot());
|
EXPECT_EQ(2, mmap.flags());
|
}
|
}
|
|
TEST(PerfSerializerTest, SerializesAndDeserializesAuxtraceEvents) {
|
std::stringstream input;
|
|
// header
|
testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
|
|
// data
|
|
// PERF_RECORD_HEADER_ATTR
|
testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP,
|
true /*sample_id_all*/)
|
.WriteTo(&input);
|
|
// PERF_RECORD_MMAP
|
testing::ExampleAuxtraceEvent(9, 0x2000, 7, 3, 0x68d, 4, 0, "/dev/zero")
|
.WriteTo(&input);
|
|
// Parse and Serialize
|
|
PerfReader reader;
|
ASSERT_TRUE(reader.ReadFromString(input.str()));
|
|
PerfDataProto perf_data_proto;
|
ASSERT_TRUE(reader.Serialize(&perf_data_proto));
|
|
EXPECT_EQ(1, perf_data_proto.events().size());
|
|
{
|
const PerfDataProto::PerfEvent& event = perf_data_proto.events(0);
|
EXPECT_EQ(PERF_RECORD_AUXTRACE, event.header().type());
|
EXPECT_TRUE(event.has_auxtrace_event());
|
const PerfDataProto::AuxtraceEvent& auxtrace_event = event.auxtrace_event();
|
EXPECT_EQ(9, auxtrace_event.size());
|
EXPECT_EQ(0x2000, auxtrace_event.offset());
|
EXPECT_EQ(7, auxtrace_event.reference());
|
EXPECT_EQ(3, auxtrace_event.idx());
|
EXPECT_EQ(0x68d, auxtrace_event.tid());
|
EXPECT_EQ("/dev/zero", auxtrace_event.trace_data());
|
}
|
}
|
|
// Regression test for http://crbug.com/501004.
|
TEST(PerfSerializerTest, SerializesAndDeserializesBuildIDs) {
|
std::stringstream input;
|
|
// header
|
testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
|
|
// no data
|
|
// PERF_RECORD_HEADER_ATTR
|
testing::ExamplePerfEventAttrEvent_Hardware(
|
PERF_SAMPLE_TID | PERF_SAMPLE_TIME, true /*sample_id_all*/)
|
.WriteTo(&input);
|
|
PerfReader reader;
|
ASSERT_TRUE(reader.ReadFromString(input.str()));
|
|
std::map<string, string> build_id_map;
|
build_id_map["file1"] = "0123456789abcdef0123456789abcdef01234567";
|
build_id_map["file2"] = "0123456789abcdef0123456789abcdef01230000";
|
build_id_map["file3"] = "0123456789abcdef0123456789abcdef00000000";
|
build_id_map["file4"] = "0123456789abcdef0123456789abcdef0000";
|
build_id_map["file5"] = "0123456789abcdef0123456789abcdef";
|
build_id_map["file6"] = "0123456789abcdef0123456789ab0000";
|
build_id_map["file7"] = "0123456789abcdef012345670000";
|
build_id_map["file8"] = "0123456789abcdef01234567";
|
build_id_map["file9"] = "00000000";
|
reader.InjectBuildIDs(build_id_map);
|
|
PerfDataProto perf_data_proto;
|
ASSERT_TRUE(reader.Serialize(&perf_data_proto));
|
|
// Verify that the build ID info was properly injected.
|
EXPECT_EQ(9, perf_data_proto.build_ids_size());
|
for (int i = 0; i < perf_data_proto.build_ids_size(); ++i) {
|
EXPECT_TRUE(perf_data_proto.build_ids(i).has_filename());
|
EXPECT_TRUE(perf_data_proto.build_ids(i).has_build_id_hash());
|
}
|
|
// Verify that the serialized build IDs have had their trailing zeroes
|
// trimmed.
|
EXPECT_EQ("file1", perf_data_proto.build_ids(0).filename());
|
EXPECT_EQ("0123456789abcdef0123456789abcdef01234567",
|
RawDataToHexString(perf_data_proto.build_ids(0).build_id_hash()));
|
|
EXPECT_EQ("file2", perf_data_proto.build_ids(1).filename());
|
EXPECT_EQ("0123456789abcdef0123456789abcdef01230000",
|
RawDataToHexString(perf_data_proto.build_ids(1).build_id_hash()));
|
|
EXPECT_EQ("file3", perf_data_proto.build_ids(2).filename());
|
EXPECT_EQ("0123456789abcdef0123456789abcdef",
|
RawDataToHexString(perf_data_proto.build_ids(2).build_id_hash()));
|
|
EXPECT_EQ("file4", perf_data_proto.build_ids(3).filename());
|
EXPECT_EQ("0123456789abcdef0123456789abcdef",
|
RawDataToHexString(perf_data_proto.build_ids(3).build_id_hash()));
|
|
EXPECT_EQ("file5", perf_data_proto.build_ids(4).filename());
|
EXPECT_EQ("0123456789abcdef0123456789abcdef",
|
RawDataToHexString(perf_data_proto.build_ids(4).build_id_hash()));
|
|
EXPECT_EQ("file6", perf_data_proto.build_ids(5).filename());
|
EXPECT_EQ("0123456789abcdef0123456789ab0000",
|
RawDataToHexString(perf_data_proto.build_ids(5).build_id_hash()));
|
|
EXPECT_EQ("file7", perf_data_proto.build_ids(6).filename());
|
EXPECT_EQ("0123456789abcdef01234567",
|
RawDataToHexString(perf_data_proto.build_ids(6).build_id_hash()));
|
|
EXPECT_EQ("file8", perf_data_proto.build_ids(7).filename());
|
EXPECT_EQ("0123456789abcdef01234567",
|
RawDataToHexString(perf_data_proto.build_ids(7).build_id_hash()));
|
|
EXPECT_EQ("file9", perf_data_proto.build_ids(8).filename());
|
EXPECT_EQ("",
|
RawDataToHexString(perf_data_proto.build_ids(8).build_id_hash()));
|
|
// Check deserialization.
|
PerfReader out_reader;
|
EXPECT_TRUE(out_reader.Deserialize(perf_data_proto));
|
const auto& build_ids = out_reader.build_ids();
|
ASSERT_EQ(9, build_ids.size());
|
|
std::vector<malloced_unique_ptr<build_id_event>> raw_build_ids(
|
build_ids.size());
|
|
// Convert the build IDs back to raw build ID events.
|
PerfSerializer serializer;
|
for (int i = 0; i < build_ids.size(); ++i) {
|
ASSERT_TRUE(serializer.DeserializeBuildIDEvent(build_ids.Get(i),
|
&raw_build_ids[i]));
|
}
|
|
// All trimmed build IDs should be padded to the full 20 byte length.
|
EXPECT_EQ(string("file1"), raw_build_ids[0]->filename);
|
EXPECT_EQ("0123456789abcdef0123456789abcdef01234567",
|
RawDataToHexString(raw_build_ids[0]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file2"), raw_build_ids[1]->filename);
|
EXPECT_EQ("0123456789abcdef0123456789abcdef01230000",
|
RawDataToHexString(raw_build_ids[1]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file3"), raw_build_ids[2]->filename);
|
EXPECT_EQ("0123456789abcdef0123456789abcdef00000000",
|
RawDataToHexString(raw_build_ids[2]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file4"), raw_build_ids[3]->filename);
|
EXPECT_EQ("0123456789abcdef0123456789abcdef00000000",
|
RawDataToHexString(raw_build_ids[3]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file5"), raw_build_ids[4]->filename);
|
EXPECT_EQ("0123456789abcdef0123456789abcdef00000000",
|
RawDataToHexString(raw_build_ids[4]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file6"), raw_build_ids[5]->filename);
|
EXPECT_EQ("0123456789abcdef0123456789ab000000000000",
|
RawDataToHexString(raw_build_ids[5]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file7"), raw_build_ids[6]->filename);
|
EXPECT_EQ("0123456789abcdef012345670000000000000000",
|
RawDataToHexString(raw_build_ids[6]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file8"), raw_build_ids[7]->filename);
|
EXPECT_EQ("0123456789abcdef012345670000000000000000",
|
RawDataToHexString(raw_build_ids[7]->build_id, kBuildIDArraySize));
|
|
EXPECT_EQ(string("file9"), raw_build_ids[8]->filename);
|
EXPECT_EQ("0000000000000000000000000000000000000000",
|
RawDataToHexString(raw_build_ids[8]->build_id, kBuildIDArraySize));
|
}
|
|
// Regression test for http://crbug.com/500746.
|
TEST(PerfSerializerTest, SerializesAndDeserializesForkAndExitEvents) {
|
std::stringstream input;
|
|
// header
|
testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
|
|
// data
|
|
// PERF_RECORD_HEADER_ATTR
|
testing::ExamplePerfEventAttrEvent_Hardware(
|
PERF_SAMPLE_TID | PERF_SAMPLE_TIME, true /*sample_id_all*/)
|
.WriteTo(&input);
|
|
// PERF_RECORD_FORK
|
testing::ExampleForkEvent(
|
1010, 1020, 1030, 1040, 355ULL * 1000000000,
|
testing::SampleInfo().Tid(2010, 2020).Time(356ULL * 1000000000))
|
.WriteTo(&input);
|
|
// PERF_RECORD_EXIT
|
testing::ExampleExitEvent(
|
3010, 3020, 3030, 3040, 432ULL * 1000000000,
|
testing::SampleInfo().Tid(4010, 4020).Time(433ULL * 1000000000))
|
.WriteTo(&input);
|
|
// Parse and serialize.
|
PerfReader reader;
|
ASSERT_TRUE(reader.ReadFromString(input.str()));
|
|
PerfDataProto perf_data_proto;
|
ASSERT_TRUE(reader.Serialize(&perf_data_proto));
|
|
ASSERT_EQ(2, perf_data_proto.events_size());
|
|
{
|
const PerfDataProto_PerfEvent& event = perf_data_proto.events(0);
|
EXPECT_EQ(PERF_RECORD_FORK, event.header().type());
|
EXPECT_TRUE(event.has_fork_event());
|
EXPECT_FALSE(event.has_exit_event());
|
|
EXPECT_EQ(1010, event.fork_event().pid());
|
EXPECT_EQ(1020, event.fork_event().ppid());
|
EXPECT_EQ(1030, event.fork_event().tid());
|
EXPECT_EQ(1040, event.fork_event().ptid());
|
EXPECT_EQ(355ULL * 1000000000, event.fork_event().fork_time_ns());
|
|
EXPECT_EQ(2010, event.fork_event().sample_info().pid());
|
EXPECT_EQ(2020, event.fork_event().sample_info().tid());
|
EXPECT_EQ(356ULL * 1000000000,
|
event.fork_event().sample_info().sample_time_ns());
|
}
|
|
{
|
const PerfDataProto_PerfEvent& event = perf_data_proto.events(1);
|
EXPECT_EQ(PERF_RECORD_EXIT, event.header().type());
|
EXPECT_FALSE(event.has_fork_event());
|
EXPECT_TRUE(event.has_exit_event());
|
|
EXPECT_EQ(3010, event.exit_event().pid());
|
EXPECT_EQ(3020, event.exit_event().ppid());
|
EXPECT_EQ(3030, event.exit_event().tid());
|
EXPECT_EQ(3040, event.exit_event().ptid());
|
EXPECT_EQ(432ULL * 1000000000, event.exit_event().fork_time_ns());
|
|
EXPECT_EQ(4010, event.exit_event().sample_info().pid());
|
EXPECT_EQ(4020, event.exit_event().sample_info().tid());
|
EXPECT_EQ(433ULL * 1000000000,
|
event.exit_event().sample_info().sample_time_ns());
|
}
|
|
// Deserialize and verify events.
|
PerfReader out_reader;
|
ASSERT_TRUE(out_reader.Deserialize(perf_data_proto));
|
|
EXPECT_EQ(2, out_reader.events().size());
|
|
{
|
const PerfEvent& event = out_reader.events().Get(0);
|
EXPECT_EQ(PERF_RECORD_FORK, event.header().type());
|
|
EXPECT_EQ(1010, event.fork_event().pid());
|
EXPECT_EQ(1020, event.fork_event().ppid());
|
EXPECT_EQ(1030, event.fork_event().tid());
|
EXPECT_EQ(1040, event.fork_event().ptid());
|
EXPECT_EQ(355ULL * 1000000000, event.fork_event().fork_time_ns());
|
|
const SampleInfo& sample_info = event.fork_event().sample_info();
|
EXPECT_EQ(2010, sample_info.pid());
|
EXPECT_EQ(2020, sample_info.tid());
|
EXPECT_EQ(356ULL * 1000000000, sample_info.sample_time_ns());
|
}
|
|
{
|
const PerfEvent& event = out_reader.events().Get(1);
|
EXPECT_EQ(PERF_RECORD_EXIT, event.header().type());
|
|
EXPECT_EQ(3010, event.exit_event().pid());
|
EXPECT_EQ(3020, event.exit_event().ppid());
|
EXPECT_EQ(3030, event.exit_event().tid());
|
EXPECT_EQ(3040, event.exit_event().ptid());
|
EXPECT_EQ(432ULL * 1000000000, event.exit_event().fork_time_ns());
|
|
const SampleInfo& sample_info = event.exit_event().sample_info();
|
EXPECT_EQ(4010, sample_info.pid());
|
EXPECT_EQ(4020, sample_info.tid());
|
EXPECT_EQ(433ULL * 1000000000, sample_info.sample_time_ns());
|
}
|
}
|
|
// Regression test for http://crbug.com/500746.
|
TEST(PerfSerializerTest, DeserializeLegacyExitEvents) {
|
std::stringstream input;
|
|
// header
|
testing::ExamplePipedPerfDataFileHeader().WriteTo(&input);
|
|
// data
|
|
// PERF_RECORD_HEADER_ATTR
|
testing::ExamplePerfEventAttrEvent_Hardware(
|
PERF_SAMPLE_TID | PERF_SAMPLE_TIME, true /*sample_id_all*/)
|
.WriteTo(&input);
|
|
// PERF_RECORD_EXIT
|
testing::ExampleExitEvent(
|
3010, 3020, 3030, 3040, 432ULL * 1000000000,
|
testing::SampleInfo().Tid(4010, 4020).Time(433ULL * 1000000000))
|
.WriteTo(&input);
|
|
// Parse and serialize.
|
PerfReader reader;
|
ASSERT_TRUE(reader.ReadFromString(input.str()));
|
|
PerfDataProto proto;
|
ASSERT_TRUE(reader.Serialize(&proto));
|
|
ASSERT_EQ(1, proto.events_size());
|
ASSERT_TRUE(proto.events(0).has_exit_event());
|
ASSERT_FALSE(proto.events(0).has_fork_event());
|
|
// Modify the protobuf to store the exit event in the |fork_event| field
|
// instead.
|
PerfDataProto_ForkEvent ex;
|
ex.CopyFrom(proto.events(0).exit_event());
|
proto.mutable_events(0)->clear_exit_event();
|
proto.mutable_events(0)->mutable_fork_event()->CopyFrom(ex);
|
|
PerfReader out_reader;
|
ASSERT_TRUE(out_reader.Deserialize(proto));
|
|
EXPECT_EQ(1U, out_reader.events().size());
|
|
const PerfEvent& event = out_reader.events().Get(0);
|
EXPECT_EQ(PERF_RECORD_EXIT, event.header().type());
|
EXPECT_EQ(3010, event.fork_event().pid());
|
EXPECT_EQ(3020, event.fork_event().ppid());
|
EXPECT_EQ(3030, event.fork_event().tid());
|
EXPECT_EQ(3040, event.fork_event().ptid());
|
EXPECT_EQ(432ULL * 1000000000, event.fork_event().fork_time_ns());
|
|
const SampleInfo& sample_info = event.fork_event().sample_info();
|
EXPECT_EQ(4010, sample_info.pid());
|
EXPECT_EQ(4020, sample_info.tid());
|
EXPECT_EQ(433ULL * 1000000000, sample_info.sample_time_ns());
|
}
|
|
namespace {
|
std::vector<const char*> AllPerfData() {
|
const auto& files = perf_test_files::GetPerfDataFiles();
|
const auto& piped = perf_test_files::GetPerfPipedDataFiles();
|
|
std::vector<const char*> ret(std::begin(files), std::end(files));
|
ret.insert(std::end(ret), std::begin(piped), std::end(piped));
|
return ret;
|
}
|
} // namespace
|
|
INSTANTIATE_TEST_CASE_P(
|
PerfSerializerTest, SerializePerfDataFiles,
|
::testing::ValuesIn(perf_test_files::GetPerfDataFiles()));
|
INSTANTIATE_TEST_CASE_P(PerfSerializerTest, SerializeAllPerfDataFiles,
|
::testing::ValuesIn(AllPerfData()));
|
INSTANTIATE_TEST_CASE_P(
|
PerfSerializerTest, SerializePerfDataProtoFiles,
|
::testing::ValuesIn(perf_test_files::GetPerfDataProtoFiles()));
|
} // namespace quipper
|