| // 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 "chromiumos-wide-profiling/compat/string.h" |
| #include "chromiumos-wide-profiling/compat/test.h" |
| #include "chromiumos-wide-profiling/perf_protobuf_io.h" |
| #include "chromiumos-wide-profiling/perf_reader.h" |
| #include "chromiumos-wide-profiling/perf_serializer.h" |
| #include "chromiumos-wide-profiling/perf_test_files.h" |
| #include "chromiumos-wide-profiling/scoped_temp_path.h" |
| #include "chromiumos-wide-profiling/test_perf_data.h" |
| #include "chromiumos-wide-profiling/test_utils.h" |
| #include "chromiumos-wide-profiling/utils.h" |
| |
| namespace { |
| |
| // Returns a string representation of an unsigned integer |value|. |
| string UintToString(uint64_t value) { |
| stringstream ss; |
| ss << value; |
| return ss.str(); |
| } |
| |
| } // namespace |
| |
| namespace quipper { |
| |
| namespace { |
| |
| // 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(); |
| } |
| 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; |
| PerfParser::Options options; |
| options.do_remap = do_remap; |
| options.discard_unused_events = discard_unused_events; |
| options.sample_mapping_percentage_threshold = 100.0f; |
| PerfSerializer serializer; |
| serializer.set_options(options); |
| EXPECT_TRUE(serializer.SerializeFromFile(input, &perf_data_proto)); |
| |
| PerfSerializer deserializer; |
| deserializer.set_options(options); |
| EXPECT_TRUE(deserializer.DeserializeToFile(perf_data_proto, output)); |
| |
| // Check perf event stats. |
| const PerfEventStats& in_stats = serializer.stats(); |
| const PerfEventStats& out_stats = deserializer.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); |
| } |
| |
| 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 sorted_serializer; |
| sorted_serializer.set_serialize_sorted_events(true); |
| EXPECT_TRUE( |
| sorted_serializer.SerializeFromFile(input, &input_perf_data_proto)); |
| CheckChronologicalOrderOfSerializedEvents(input_perf_data_proto); |
| |
| input_perf_data_proto.Clear(); |
| PerfSerializer unsorted_serializer; |
| unsorted_serializer.set_serialize_sorted_events(false); |
| EXPECT_TRUE( |
| unsorted_serializer.SerializeFromFile(input, &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)); |
| |
| PerfSerializer deserializer; |
| EXPECT_TRUE(deserializer.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()); |
| } |
| |
| // Converts build ID data (stored as a string) to an actual string containing |
| // hex digits. |
| string BuildIDToString(const string& raw_build_id_data) { |
| return HexToString(reinterpret_cast<const u8*>(raw_build_id_data.c_str()), |
| raw_build_id_data.size()); |
| } |
| |
| } // namespace |
| |
| TEST(PerfSerializerTest, 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. |
| // TODO(sque): test exact number of events after discarding unused events. |
| for (const char* test_file : perf_test_files::kPerfDataFiles) { |
| PerfReader input_perf_reader, output_perf_reader, output_perf_reader1, |
| output_perf_reader2; |
| PerfDataProto perf_data_proto, perf_data_proto1; |
| |
| string input_perf_data = GetTestInputFilePath(test_file); |
| string output_perf_data = output_path + test_file + ".serialized.out"; |
| string output_perf_data1 = output_path + test_file + ".serialized.1.out"; |
| |
| LOG(INFO) << "Testing " << input_perf_data; |
| 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, so that the files can |
| // be moved around in the |kPerfDataFiles| list. |
| bool discard = (Md5Prefix(test_file) % 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(PerfSerializerTest, TestRemap) { |
| ScopedTempDir output_dir; |
| ASSERT_FALSE(output_dir.path().empty()); |
| 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. |
| for (const char* test_file : perf_test_files::kPerfDataFiles) { |
| 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); |
| } |
| |
| for (const char* test_file : perf_test_files::kPerfPipedDataFiles) { |
| 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(PerfSerializeTest, 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. |
| for (const char* test_file : perf_test_files::kPerfDataFiles) { |
| const string input_perf_data = GetTestInputFilePath(test_file); |
| LOG(INFO) << "Testing COMM Md5sum for " << input_perf_data; |
| |
| PerfDataProto perf_data_proto; |
| PerfSerializer serializer; |
| EXPECT_TRUE(serializer.SerializeFromFile(input_perf_data, |
| &perf_data_proto)); |
| |
| 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); |
| event.mutable_comm_event()->set_comm(comm_md5_string); |
| } |
| |
| PerfSerializer deserializer; |
| const string output_perf_data = output_path + test_file + ".ser.comm.out"; |
| EXPECT_TRUE(deserializer.DeserializeToFile(perf_data_proto, |
| output_perf_data)); |
| EXPECT_TRUE(CheckPerfDataAgainstBaseline(output_perf_data)); |
| } |
| } |
| |
| TEST(PerfSerializeTest, 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. |
| for (const char* test_file : perf_test_files::kPerfDataFiles) { |
| const string input_perf_data = GetTestInputFilePath(test_file); |
| LOG(INFO) << "Testing MMAP Md5sum for " << input_perf_data; |
| |
| PerfDataProto perf_data_proto; |
| PerfSerializer serializer; |
| EXPECT_TRUE(serializer.SerializeFromFile(input_perf_data, |
| &perf_data_proto)); |
| |
| 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; |
| CHECK(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); |
| event.mutable_mmap_event()->set_filename(filename_md5_string); |
| } |
| |
| PerfSerializer deserializer; |
| 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(deserializer.DeserializeToFile(perf_data_proto, |
| output_perf_data)); |
| } |
| } |
| |
| TEST(PerfSerializerTest, TestProtoFiles) { |
| for (const char* test_file : perf_test_files::kPerfDataProtoFiles) { |
| 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)); |
| LOG(INFO) << perf_data_proto.file_attrs_size(); |
| |
| // Test deserializing. |
| PerfSerializer perf_serializer; |
| EXPECT_TRUE(perf_serializer.Deserialize(perf_data_proto)); |
| } |
| } |
| |
| TEST(PerfSerializerTest, TestBuildIDs) { |
| for (const char* test_file : perf_test_files::kPerfDataFiles) { |
| string perf_data_file = GetTestInputFilePath(test_file); |
| LOG(INFO) << "Testing " << perf_data_file; |
| |
| // Serialize into a protobuf. |
| PerfSerializer perf_serializer; |
| PerfDataProto perf_data_proto; |
| EXPECT_TRUE(perf_serializer.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(); |
| } |
| PerfSerializer perf_serializer_build_ids; |
| EXPECT_TRUE(perf_serializer_build_ids.Deserialize(perf_data_proto)); |
| } |
| } |
| |
| TEST(PerfSerializerTest, SerializesAndDeserializesTraceMetadata) { |
| std::stringstream input; |
| |
| const size_t attr_count = 1; |
| const size_t data_size = |
| testing::ExamplePerfSampleEvent_Tracepoint::kEventSize; |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(attr_count, data_size, |
| 1 << HEADER_TRACING_DATA); |
| 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 |
| |
| PerfSerializer perf_serializer; |
| // Disable mapping threshold--we have no mappable events. |
| PerfParser::Options options; |
| options.sample_mapping_percentage_threshold = 0.0; |
| perf_serializer.set_options(options); |
| PerfDataProto perf_data_proto; |
| EXPECT_TRUE(perf_serializer.ReadFromString(input.str())); |
| EXPECT_TRUE(perf_serializer.Serialize(&perf_data_proto)); |
| |
| const std::vector<char> &tracing_metadata_value = |
| tracing_metadata.data().value(); |
| const string tracing_metadata_str(tracing_metadata_value.data(), |
| tracing_metadata_value.size()); |
| uint64_t tracing_metadata_md5_prefix = |
| Md5Prefix(tracing_metadata_value); |
| EXPECT_EQ(tracing_metadata_str, |
| perf_data_proto.tracing_data().tracing_data()); |
| EXPECT_EQ(tracing_metadata_md5_prefix, |
| perf_data_proto.tracing_data().tracing_data_md5_prefix()); |
| |
| // Deserialize |
| |
| PerfSerializer deserializer; |
| deserializer.set_options(options); |
| EXPECT_TRUE(deserializer.Deserialize(perf_data_proto)); |
| EXPECT_EQ(tracing_metadata_value, 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 |
| |
| PerfSerializer perf_serializer; |
| // Disable mapping threshold--we have no mappable events. |
| PerfParser::Options options; |
| options.sample_mapping_percentage_threshold = 0.0; |
| perf_serializer.set_options(options); |
| PerfDataProto perf_data_proto; |
| EXPECT_TRUE(perf_serializer.ReadFromString(input.str())); |
| EXPECT_TRUE(perf_serializer.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()); |
| } |
| } |
| |
| // 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); |
| |
| PerfSerializer serializer; |
| EXPECT_TRUE(serializer.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"; |
| EXPECT_TRUE(serializer.InjectBuildIDs(build_id_map)); |
| |
| PerfDataProto perf_data_proto; |
| EXPECT_TRUE(serializer.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", |
| BuildIDToString(perf_data_proto.build_ids(0).build_id_hash())); |
| |
| EXPECT_EQ("file2", perf_data_proto.build_ids(1).filename()); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef01230000", |
| BuildIDToString(perf_data_proto.build_ids(1).build_id_hash())); |
| |
| EXPECT_EQ("file3", perf_data_proto.build_ids(2).filename()); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef", |
| BuildIDToString(perf_data_proto.build_ids(2).build_id_hash())); |
| |
| EXPECT_EQ("file4", perf_data_proto.build_ids(3).filename()); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef", |
| BuildIDToString(perf_data_proto.build_ids(3).build_id_hash())); |
| |
| EXPECT_EQ("file5", perf_data_proto.build_ids(4).filename()); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef", |
| BuildIDToString(perf_data_proto.build_ids(4).build_id_hash())); |
| |
| EXPECT_EQ("file6", perf_data_proto.build_ids(5).filename()); |
| EXPECT_EQ("0123456789abcdef0123456789ab0000", |
| BuildIDToString(perf_data_proto.build_ids(5).build_id_hash())); |
| |
| EXPECT_EQ("file7", perf_data_proto.build_ids(6).filename()); |
| EXPECT_EQ("0123456789abcdef01234567", |
| BuildIDToString(perf_data_proto.build_ids(6).build_id_hash())); |
| |
| EXPECT_EQ("file8", perf_data_proto.build_ids(7).filename()); |
| EXPECT_EQ("0123456789abcdef01234567", |
| BuildIDToString(perf_data_proto.build_ids(7).build_id_hash())); |
| |
| EXPECT_EQ("file9", perf_data_proto.build_ids(8).filename()); |
| EXPECT_EQ("", BuildIDToString(perf_data_proto.build_ids(8).build_id_hash())); |
| |
| // Check deserialization. |
| PerfSerializer deserializer; |
| EXPECT_TRUE(deserializer.Deserialize(perf_data_proto)); |
| const std::vector<malloced_unique_ptr<build_id_event>>& build_id_events = |
| deserializer.build_id_events(); |
| EXPECT_EQ(9, build_id_events.size()); |
| |
| // All trimmed build IDs should be padded to the full 20 byte length. |
| EXPECT_EQ(string("file1"), build_id_events[0]->filename); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef01234567", |
| HexToString(build_id_events[0]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file2"), build_id_events[1]->filename); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef01230000", |
| HexToString(build_id_events[1]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file3"), build_id_events[2]->filename); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef00000000", |
| HexToString(build_id_events[2]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file4"), build_id_events[3]->filename); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef00000000", |
| HexToString(build_id_events[3]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file5"), build_id_events[4]->filename); |
| EXPECT_EQ("0123456789abcdef0123456789abcdef00000000", |
| HexToString(build_id_events[4]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file6"), build_id_events[5]->filename); |
| EXPECT_EQ("0123456789abcdef0123456789ab000000000000", |
| HexToString(build_id_events[5]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file7"), build_id_events[6]->filename); |
| EXPECT_EQ("0123456789abcdef012345670000000000000000", |
| HexToString(build_id_events[6]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file8"), build_id_events[7]->filename); |
| EXPECT_EQ("0123456789abcdef012345670000000000000000", |
| HexToString(build_id_events[7]->build_id, kBuildIDArraySize)); |
| |
| EXPECT_EQ(string("file9"), build_id_events[8]->filename); |
| EXPECT_EQ("0000000000000000000000000000000000000000", |
| HexToString(build_id_events[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. |
| PerfSerializer serializer; |
| EXPECT_TRUE(serializer.ReadFromString(input.str())); |
| PerfDataProto perf_data_proto; |
| EXPECT_TRUE(serializer.Serialize(&perf_data_proto)); |
| |
| EXPECT_EQ(1, perf_data_proto.stats().num_fork_events()); |
| EXPECT_EQ(1, perf_data_proto.stats().num_exit_events()); |
| EXPECT_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 raw events. |
| PerfSerializer deserializer; |
| EXPECT_TRUE(deserializer.Deserialize(perf_data_proto)); |
| |
| EXPECT_EQ(2, deserializer.events().size()); |
| |
| { |
| const event_t& event = *deserializer.events()[0].get(); |
| EXPECT_EQ(PERF_RECORD_FORK, event.header.type); |
| |
| EXPECT_EQ(1010, event.fork.pid); |
| EXPECT_EQ(1020, event.fork.ppid); |
| EXPECT_EQ(1030, event.fork.tid); |
| EXPECT_EQ(1040, event.fork.ptid); |
| EXPECT_EQ(355ULL * 1000000000, event.fork.time); |
| |
| struct perf_sample sample_info; |
| EXPECT_TRUE(deserializer.ReadPerfSampleInfo(event, &sample_info)); |
| EXPECT_EQ(2010, sample_info.pid); |
| EXPECT_EQ(2020, sample_info.tid); |
| EXPECT_EQ(356ULL * 1000000000, sample_info.time); |
| } |
| |
| { |
| const event_t& event = *deserializer.events()[1].get(); |
| EXPECT_EQ(PERF_RECORD_EXIT, event.header.type); |
| |
| EXPECT_EQ(3010, event.fork.pid); |
| EXPECT_EQ(3020, event.fork.ppid); |
| EXPECT_EQ(3030, event.fork.tid); |
| EXPECT_EQ(3040, event.fork.ptid); |
| EXPECT_EQ(432ULL * 1000000000, event.fork.time); |
| |
| struct perf_sample sample_info; |
| EXPECT_TRUE(deserializer.ReadPerfSampleInfo(event, &sample_info)); |
| EXPECT_EQ(4010, sample_info.pid); |
| EXPECT_EQ(4020, sample_info.tid); |
| EXPECT_EQ(433ULL * 1000000000, sample_info.time); |
| } |
| } |
| |
| // 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. |
| PerfSerializer serializer; |
| ASSERT_TRUE(serializer.ReadFromString(input.str())); |
| PerfDataProto proto; |
| ASSERT_TRUE(serializer.Serialize(&proto)); |
| |
| EXPECT_EQ(1, proto.stats().num_exit_events()); |
| EXPECT_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. |
| proto.mutable_events(0)->mutable_fork_event()->Swap( |
| proto.mutable_events(0)->mutable_exit_event()); |
| proto.mutable_events(0)->clear_exit_event(); |
| |
| PerfSerializer deserializer; |
| ASSERT_TRUE(deserializer.Deserialize(proto)); |
| |
| EXPECT_EQ(1U, deserializer.events().size()); |
| |
| const event_t& event = *deserializer.events()[0].get(); |
| EXPECT_EQ(PERF_RECORD_EXIT, event.header.type); |
| |
| EXPECT_EQ(3010, event.fork.pid); |
| EXPECT_EQ(3020, event.fork.ppid); |
| EXPECT_EQ(3030, event.fork.tid); |
| EXPECT_EQ(3040, event.fork.ptid); |
| EXPECT_EQ(432ULL * 1000000000, event.fork.time); |
| |
| struct perf_sample sample_info; |
| EXPECT_TRUE(deserializer.ReadPerfSampleInfo(event, &sample_info)); |
| EXPECT_EQ(4010, sample_info.pid); |
| EXPECT_EQ(4020, sample_info.tid); |
| EXPECT_EQ(433ULL * 1000000000, sample_info.time); |
| } |
| |
| } // namespace quipper |
| |
| int main(int argc, char * argv[]) { |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |