| // 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 <byteswap.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "base/logging.h" |
| |
| #include "chromiumos-wide-profiling/compat/string.h" |
| #include "chromiumos-wide-profiling/compat/test.h" |
| #include "chromiumos-wide-profiling/perf_reader.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 quipper { |
| |
| using PerfEvent = PerfDataProto_PerfEvent; |
| using SampleEvent = PerfDataProto_SampleEvent; |
| using SampleInfo = PerfDataProto_SampleInfo; |
| |
| TEST(PerfReaderTest, PipedData_IncompleteEventHeader) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP, |
| true /*sample_id_all*/) |
| .WithConfig(123) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_HEADER_EVENT_TYPE |
| const struct event_type_event event_type = { |
| .header = { |
| .type = PERF_RECORD_HEADER_EVENT_TYPE, |
| .misc = 0, |
| .size = sizeof(struct event_type_event), |
| }, |
| .event_type = { |
| /*event_id*/ 123, |
| /*name*/ "cycles", |
| }, |
| }; |
| input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type)); |
| |
| // Incomplete data at the end: |
| input << string(sizeof(struct perf_event_header) - 1, '\0'); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| ASSERT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(123, pr.attrs().Get(0).attr().config()); |
| ASSERT_EQ(1, pr.event_types().size()); |
| EXPECT_EQ("cycles", pr.event_types().Get(0).name()); |
| |
| // Make sure metadata mask was set to indicate EVENT_TYPE was upgraded |
| // to EVENT_DESC. |
| EXPECT_EQ((1 << HEADER_EVENT_DESC), pr.metadata_mask()); |
| } |
| |
| TEST(PerfReaderTest, PipedData_IncompleteEventData) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP, |
| true /*sample_id_all*/) |
| .WithConfig(456) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_HEADER_EVENT_TYPE |
| const struct event_type_event event_type = { |
| .header = { |
| .type = PERF_RECORD_HEADER_EVENT_TYPE, |
| .misc = 0, |
| .size = sizeof(struct event_type_event), |
| }, |
| .event_type = { |
| /*event_id*/ 456, |
| /*name*/ "instructions", |
| }, |
| }; |
| input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type)); |
| |
| // Incomplete data at the end: |
| // Header: |
| const struct perf_event_header incomplete_event_header = { |
| .type = PERF_RECORD_SAMPLE, |
| .misc = 0, |
| .size = sizeof(perf_event_header) + 10, |
| }; |
| input.write(reinterpret_cast<const char*>(&incomplete_event_header), |
| sizeof(incomplete_event_header)); |
| // Incomplete event: |
| input << string(3, '\0'); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| ASSERT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(456, pr.attrs().Get(0).attr().config()); |
| ASSERT_EQ(1, pr.event_types().size()); |
| EXPECT_EQ("instructions", pr.event_types().Get(0).name()); |
| |
| // Make sure metadata mask was set to indicate EVENT_TYPE was upgraded |
| // to EVENT_DESC. |
| EXPECT_EQ((1 << HEADER_EVENT_DESC), pr.metadata_mask()); |
| } |
| |
| TEST(PerfReaderTest, CorruptedFiles) { |
| for (const char* test_file : |
| perf_test_files::GetCorruptedPerfPipedDataFiles()) { |
| string input_perf_data = GetTestInputFilePath(test_file); |
| LOG(INFO) << "Testing " << input_perf_data; |
| ASSERT_TRUE(FileExists(input_perf_data)) << "Test file does not exist!"; |
| PerfReader pr; |
| ASSERT_FALSE(pr.ReadFile(input_perf_data)); |
| } |
| } |
| |
| TEST(PerfReaderTest, ReadsAndWritesTraceMetadata) { |
| 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 |
| CHECK_EQ(header.attrs.offset, static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Tracepoint(73).WriteTo(&input); |
| |
| // data |
| ASSERT_EQ(header.data.offset, static_cast<u64>(input.tellp())); |
| testing::ExamplePerfSampleEvent_Tracepoint().WriteTo(&input); |
| ASSERT_EQ(file_header.data_end(), input.tellp()); |
| |
| // metadata |
| |
| const unsigned int metadata_count = 1; |
| |
| // HEADER_TRACING_DATA |
| testing::ExampleTracingMetadata tracing_metadata( |
| file_header.data_end() + metadata_count*sizeof(perf_file_section)); |
| |
| // write metadata index entries |
| tracing_metadata.index_entry().WriteTo(&input); |
| // write metadata |
| tracing_metadata.data().WriteTo(&input); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| EXPECT_EQ(tracing_metadata.data().value(), pr.tracing_data()); |
| |
| // Write it out and read it in again, it should still be good: |
| std::vector<char> output_perf_data; |
| EXPECT_TRUE(pr.WriteToVector(&output_perf_data)); |
| EXPECT_TRUE(pr.ReadFromVector(output_perf_data)); |
| EXPECT_EQ(tracing_metadata.data().value(), pr.tracing_data()); |
| } |
| |
| TEST(PerfReaderTest, ReadsTracingMetadataEvent) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| const char raw_data[] = "\x17\x08\x44tracing0.5BLAHBLAHBLAH...."; |
| const string trace_metadata(raw_data, sizeof(raw_data)-1); |
| |
| const tracing_data_event trace_event = { |
| .header = { |
| .type = PERF_RECORD_HEADER_TRACING_DATA, |
| .misc = 0, |
| .size = sizeof(tracing_data_event), |
| }, |
| .size = static_cast<u32>(trace_metadata.size()), |
| }; |
| |
| input.write(reinterpret_cast<const char*>(&trace_event), sizeof(trace_event)); |
| input.write(trace_metadata.data(), trace_metadata.size()); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| EXPECT_EQ(trace_metadata, pr.tracing_data()); |
| |
| // Write it out and read it in again, tracing_data() should still be correct. |
| // NB: It does not get written as an event, but in a metadata section. |
| std::vector<char> output_perf_data; |
| EXPECT_TRUE(pr.WriteToVector(&output_perf_data)); |
| EXPECT_TRUE(pr.ReadFromVector(output_perf_data)); |
| EXPECT_EQ(trace_metadata, pr.tracing_data()); |
| } |
| |
| // Regression test for http://crbug.com/484393 |
| TEST(PerfReaderTest, BranchStackMetadataIndexHasZeroSize) { |
| std::stringstream input; |
| |
| const size_t data_size = |
| testing::ExamplePerfSampleEvent_BranchStack::kEventSize; |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(1 << HEADER_BRANCH_STACK); |
| file_header |
| .WithAttrCount(1) |
| .WithDataSize(data_size); |
| file_header.WriteTo(&input); |
| const perf_file_header &header = file_header.header(); |
| |
| // attrs |
| CHECK_EQ(header.attrs.offset, static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware( |
| PERF_SAMPLE_BRANCH_STACK, false /*sample_id_all*/).WriteTo(&input); |
| |
| // data |
| ASSERT_EQ(header.data.offset, static_cast<u64>(input.tellp())); |
| testing::ExamplePerfSampleEvent_BranchStack().WriteTo(&input); |
| ASSERT_EQ(file_header.data_end(), input.tellp()); |
| |
| // metadata |
| |
| // HEADER_BRANCH_STACK |
| const perf_file_section branch_stack_index = { |
| .offset = file_header.data_end_offset(), |
| .size = 0, |
| }; |
| input.write(reinterpret_cast<const char*>(&branch_stack_index), |
| sizeof(branch_stack_index)); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Write it out again. |
| // Initialize the buffer to a non-zero sentinel value so that the bytes |
| // we are checking were written with zero must have been written. |
| const size_t max_predicted_written_size = 1024; |
| std::vector<char> output_perf_data(max_predicted_written_size, '\xaa'); |
| EXPECT_TRUE(pr.WriteToVector(&output_perf_data)); |
| EXPECT_LE(output_perf_data.size(), max_predicted_written_size) |
| << "Bad prediction for written size"; |
| |
| // Specifically check that the metadata index has zero in the size. |
| const auto *output_header = |
| reinterpret_cast<struct perf_file_header*>(output_perf_data.data()); |
| EXPECT_EQ(1 << HEADER_BRANCH_STACK, output_header->adds_features[0]) |
| << "Expected just a HEADER_BRANCH_STACK feature"; |
| const size_t metadata_offset = |
| output_header->data.offset + output_header->data.size; |
| const auto *output_feature_index = |
| reinterpret_cast<struct perf_file_section*>( |
| output_perf_data.data() + metadata_offset); |
| EXPECT_EQ(0, output_feature_index[0].size) |
| << "Regression: Expected zero size for the HEADER_BRANCH_STACK feature " |
| << "metadata index"; |
| } |
| |
| // Regression test for http://crbug.com/427767 |
| TEST(PerfReaderTest, CorrectlyReadsPerfEventAttrSize) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| struct old_perf_event_attr { |
| __u32 type; |
| __u32 size; |
| __u64 config; |
| // union { |
| __u64 sample_period; |
| // __u64 sample_freq; |
| // }; |
| __u64 sample_type; |
| __u64 read_format; |
| // Skip the rest of the fields from perf_event_attr to simulate an |
| // older, smaller version of the struct. |
| }; |
| |
| struct old_attr_event { |
| struct perf_event_header header; |
| struct old_perf_event_attr attr; |
| u64 id[]; |
| }; |
| |
| |
| const old_attr_event attr = { |
| .header = { |
| .type = PERF_RECORD_HEADER_ATTR, |
| .misc = 0, |
| // A count of 8 ids is carefully selected to make the event exceed |
| // 96 bytes (sizeof(perf_event_attr)) so that the test fails instead of |
| // crashes with the old code. |
| .size = sizeof(old_attr_event) + 8*sizeof(u64), |
| }, |
| .attr = { |
| .type = 0, |
| .size = sizeof(old_perf_event_attr), |
| .config = 0, |
| .sample_period = 10000001, |
| .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | |
| PERF_SAMPLE_ID | PERF_SAMPLE_CPU, |
| .read_format = PERF_FORMAT_ID, |
| }, |
| }; |
| |
| input.write(reinterpret_cast<const char*>(&attr), sizeof(attr)); |
| for (u64 id : {301, 302, 303, 304, 305, 306, 307, 308}) |
| input.write(reinterpret_cast<const char*>(&id), sizeof(id)); |
| |
| // Add some sample events so that there's something to over-read. |
| const sample_event sample = { |
| .header = { |
| .type = PERF_RECORD_SAMPLE, |
| .misc = 0, |
| .size = sizeof(perf_event_header) + 5*sizeof(u64), |
| } |
| }; |
| // We don't care about the contents of the SAMPLE events, except for the ID, |
| // which is needed to determine the attr for reading sample info. |
| const u64 sample_event_array[] = { |
| 0, // IP |
| 0, // TID |
| 0, // TIME |
| 308, // ID |
| 0, // CPU |
| }; |
| |
| for (int i = 0; i < 20; i++) { |
| input.write(reinterpret_cast<const char*>(&sample), sizeof(sample)); |
| input.write(reinterpret_cast<const char*>(sample_event_array), |
| sizeof(sample_event_array)); |
| } |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| ASSERT_EQ(pr.attrs().size(), 1); |
| const auto& actual_attr = pr.attrs().Get(0); |
| ASSERT_EQ(8, actual_attr.ids().size()); |
| EXPECT_EQ(301, actual_attr.ids(0)); |
| EXPECT_EQ(302, actual_attr.ids(1)); |
| EXPECT_EQ(303, actual_attr.ids(2)); |
| EXPECT_EQ(304, actual_attr.ids(3)); |
| } |
| |
| TEST(PerfReaderTest, ReadsAndWritesSampleAndSampleIdAll) { |
| using testing::PunU32U64; |
| |
| std::stringstream input; |
| |
| // header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| const u64 sample_type = // * == in sample_id_all |
| PERF_SAMPLE_IP | |
| PERF_SAMPLE_TID | // * |
| PERF_SAMPLE_TIME | // * |
| PERF_SAMPLE_ADDR | |
| PERF_SAMPLE_ID | // * |
| PERF_SAMPLE_STREAM_ID | // * |
| PERF_SAMPLE_CPU | // * |
| PERF_SAMPLE_PERIOD; |
| const size_t num_sample_event_bits = 8; |
| const size_t num_sample_id_bits = 5; |
| // not tested: |
| // PERF_SAMPLE_READ | |
| // PERF_SAMPLE_RAW | |
| // PERF_SAMPLE_CALLCHAIN | |
| // PERF_SAMPLE_BRANCH_STACK | |
| testing::ExamplePerfEventAttrEvent_Hardware(sample_type, |
| true /*sample_id_all*/) |
| .WithId(401) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_SAMPLE |
| const sample_event written_sample_event = { |
| .header = { |
| .type = PERF_RECORD_SAMPLE, |
| .misc = PERF_RECORD_MISC_KERNEL, |
| .size = sizeof(struct sample_event) + num_sample_event_bits*sizeof(u64), |
| } |
| }; |
| const u64 sample_event_array[] = { |
| 0xffffffff01234567, // IP |
| PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) |
| 1415837014*1000000000ULL, // TIME |
| 0x00007f999c38d15a, // ADDR |
| 401, // ID |
| 1, // STREAM_ID |
| 8, // CPU |
| 10001, // PERIOD |
| }; |
| ASSERT_EQ(written_sample_event.header.size, |
| sizeof(written_sample_event.header) + sizeof(sample_event_array)); |
| input.write(reinterpret_cast<const char*>(&written_sample_event), |
| sizeof(written_sample_event)); |
| input.write(reinterpret_cast<const char*>(sample_event_array), |
| sizeof(sample_event_array)); |
| |
| // PERF_RECORD_MMAP |
| ASSERT_EQ(40, offsetof(struct mmap_event, filename)); |
| const size_t mmap_event_size = |
| offsetof(struct mmap_event, filename) + |
| 10+6 /* ==16, nearest 64-bit boundary for filename */ + |
| num_sample_id_bits*sizeof(u64); |
| |
| struct mmap_event written_mmap_event = { |
| .header = { |
| .type = PERF_RECORD_MMAP, |
| .misc = 0, |
| .size = mmap_event_size, |
| }, |
| .pid = 0x68d, .tid = 0x68d, |
| .start = 0x1d000, |
| .len = 0x1000, |
| .pgoff = 0, |
| // .filename = ..., // written separately |
| }; |
| const char mmap_filename[10+6] = "/dev/zero"; |
| const u64 mmap_sample_id[] = { |
| PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) |
| 1415911367*1000000000ULL, // TIME |
| 401, // ID |
| 2, // STREAM_ID |
| 9, // CPU |
| }; |
| const size_t pre_mmap_offset = input.tellp(); |
| input.write(reinterpret_cast<const char*>(&written_mmap_event), |
| offsetof(struct mmap_event, filename)); |
| input.write(mmap_filename, 10+6); |
| input.write(reinterpret_cast<const char*>(mmap_sample_id), |
| sizeof(mmap_sample_id)); |
| const size_t written_mmap_size = |
| static_cast<size_t>(input.tellp()) - pre_mmap_offset; |
| ASSERT_EQ(written_mmap_event.header.size, |
| static_cast<u64>(written_mmap_size)); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr1; |
| ASSERT_TRUE(pr1.ReadFromString(input.str())); |
| // Write it out and read it in again, the two should have the same data. |
| std::vector<char> output_perf_data; |
| ASSERT_TRUE(pr1.WriteToVector(&output_perf_data)); |
| PerfReader pr2; |
| ASSERT_TRUE(pr2.ReadFromVector(output_perf_data)); |
| |
| // Test both versions: |
| for (PerfReader* pr : {&pr1, &pr2}) { |
| // PERF_RECORD_HEADER_ATTR is added to attr(), not events(). |
| EXPECT_EQ(2, pr->events().size()); |
| |
| { |
| const PerfEvent& event = pr->events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| const SampleEvent& sample = event.sample_event(); |
| EXPECT_EQ(0xffffffff01234567, sample.ip()); |
| EXPECT_EQ(0x68d, sample.pid()); |
| EXPECT_EQ(0x68e, sample.tid()); |
| EXPECT_EQ(1415837014*1000000000ULL, sample.sample_time_ns()); |
| EXPECT_EQ(0x00007f999c38d15a, sample.addr()); |
| EXPECT_EQ(401, sample.id()); |
| EXPECT_EQ(1, sample.stream_id()); |
| EXPECT_EQ(8, sample.cpu()); |
| EXPECT_EQ(10001, sample.period()); |
| } |
| |
| { |
| const PerfEvent& event = pr->events().Get(1); |
| EXPECT_EQ(PERF_RECORD_MMAP, event.header().type()); |
| |
| const SampleInfo& sample = event.mmap_event().sample_info(); |
| EXPECT_EQ(0x68d, sample.pid()); |
| EXPECT_EQ(0x68e, sample.tid()); |
| EXPECT_EQ(1415911367*1000000000ULL, sample.sample_time_ns()); |
| EXPECT_EQ(401, sample.id()); |
| EXPECT_EQ(2, sample.stream_id()); |
| EXPECT_EQ(9, sample.cpu()); |
| } |
| } |
| } |
| |
| // Test that PERF_SAMPLE_IDENTIFIER is parsed correctly. This field |
| // is in a different place in PERF_RECORD_SAMPLE events compared to the |
| // struct sample_id placed at the end of all other events. |
| TEST(PerfReaderTest, ReadsAndWritesPerfSampleIdentifier) { |
| std::stringstream input; |
| |
| // header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IDENTIFIER | |
| PERF_SAMPLE_IP | |
| PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithIds({0xdeadbeef, 0xf00dbaad}) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_SAMPLE |
| const sample_event written_sample_event = { |
| .header = { |
| .type = PERF_RECORD_SAMPLE, |
| .misc = PERF_RECORD_MISC_KERNEL, |
| .size = sizeof(struct sample_event) + 3*sizeof(u64), |
| } |
| }; |
| const u64 sample_event_array[] = { |
| 0x00000000deadbeef, // IDENTIFIER |
| 0x00007f999c38d15a, // IP |
| 0x0000068d0000068d, // TID (u32 pid, tid) |
| }; |
| ASSERT_EQ(written_sample_event.header.size, |
| sizeof(written_sample_event.header) + sizeof(sample_event_array)); |
| input.write(reinterpret_cast<const char*>(&written_sample_event), |
| sizeof(written_sample_event)); |
| input.write(reinterpret_cast<const char*>(sample_event_array), |
| sizeof(sample_event_array)); |
| |
| // PERF_RECORD_MMAP |
| ASSERT_EQ(40, offsetof(struct mmap_event, filename)); |
| const size_t mmap_event_size = |
| offsetof(struct mmap_event, filename) + |
| 10+6 /* ==16, nearest 64-bit boundary for filename */ + |
| 2*sizeof(u64); |
| |
| struct mmap_event written_mmap_event = { |
| .header = { |
| .type = PERF_RECORD_MMAP, |
| .misc = 0, |
| .size = mmap_event_size, |
| }, |
| .pid = 0x68d, .tid = 0x68d, |
| .start = 0x1d000, |
| .len = 0x1000, |
| .pgoff = 0, |
| // .filename = ..., // written separately |
| }; |
| const char mmap_filename[10+6] = "/dev/zero"; |
| const u64 mmap_sample_id[] = { |
| // NB: PERF_SAMPLE_IP is not part of sample_id |
| 0x0000068d0000068d, // TID (u32 pid, tid) |
| 0x00000000f00dbaad, // IDENTIFIER |
| }; |
| const size_t pre_mmap_offset = input.tellp(); |
| input.write(reinterpret_cast<const char*>(&written_mmap_event), |
| offsetof(struct mmap_event, filename)); |
| input.write(mmap_filename, 10+6); |
| input.write(reinterpret_cast<const char*>(mmap_sample_id), |
| sizeof(mmap_sample_id)); |
| const size_t written_mmap_size = |
| static_cast<size_t>(input.tellp()) - pre_mmap_offset; |
| ASSERT_EQ(written_mmap_event.header.size, |
| static_cast<u64>(written_mmap_size)); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr1; |
| ASSERT_TRUE(pr1.ReadFromString(input.str())); |
| // Write it out and read it in again, the two should have the same data. |
| std::vector<char> output_perf_data; |
| ASSERT_TRUE(pr1.WriteToVector(&output_perf_data)); |
| PerfReader pr2; |
| ASSERT_TRUE(pr2.ReadFromVector(output_perf_data)); |
| |
| // Test both versions: |
| for (PerfReader* pr : {&pr1, &pr2}) { |
| // PERF_RECORD_HEADER_ATTR is added to attr(), not events(). |
| EXPECT_EQ(2, pr->events().size()); |
| |
| const PerfEvent& ip_event = pr->events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, ip_event.header().type()); |
| EXPECT_EQ(0xdeadbeefULL, ip_event.sample_event().id()); |
| |
| const PerfEvent& mmap_event = pr->events().Get(1); |
| EXPECT_EQ(PERF_RECORD_MMAP, mmap_event.header().type()); |
| EXPECT_EQ(0xf00dbaadULL, mmap_event.mmap_event().sample_info().id()); |
| } |
| } |
| |
| TEST(PerfReaderTest, ReadsAndWritesMmap2Events) { |
| std::stringstream input; |
| |
| // header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP, |
| false /*sample_id_all*/) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_MMAP2 |
| ASSERT_EQ(72, offsetof(struct mmap2_event, filename)); |
| const size_t mmap_event_size = |
| offsetof(struct mmap2_event, filename) + |
| 10+6; /* ==16, nearest 64-bit boundary for filename */ |
| |
| struct mmap2_event written_mmap_event = { |
| .header = { |
| .type = PERF_RECORD_MMAP2, |
| .misc = 0, |
| .size = mmap_event_size, |
| }, |
| .pid = 0x68d, .tid = 0x68d, |
| .start = 0x1d000, |
| .len = 0x1000, |
| .pgoff = 0x2000, |
| .maj = 6, |
| .min = 7, |
| .ino = 8, |
| .ino_generation = 9, |
| .prot = 1|2, // == PROT_READ | PROT_WRITE |
| .flags = 2, // == MAP_PRIVATE |
| // .filename = ..., // written separately |
| }; |
| const char mmap_filename[10+6] = "/dev/zero"; |
| const size_t pre_mmap_offset = input.tellp(); |
| input.write(reinterpret_cast<const char*>(&written_mmap_event), |
| offsetof(struct mmap2_event, filename)); |
| input.write(mmap_filename, 10+6); |
| const size_t written_mmap_size = |
| static_cast<size_t>(input.tellp()) - pre_mmap_offset; |
| ASSERT_EQ(written_mmap_event.header.size, |
| static_cast<u64>(written_mmap_size)); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr1; |
| ASSERT_TRUE(pr1.ReadFromString(input.str())); |
| // Write it out and read it in again, the two should have the same data. |
| std::vector<char> output_perf_data; |
| ASSERT_TRUE(pr1.WriteToVector(&output_perf_data)); |
| PerfReader pr2; |
| ASSERT_TRUE(pr2.ReadFromVector(output_perf_data)); |
| |
| // Test both versions: |
| for (PerfReader* pr : {&pr1, &pr2}) { |
| // PERF_RECORD_HEADER_ATTR is added to attr(), not events(). |
| EXPECT_EQ(1, pr->events().size()); |
| |
| const PerfEvent& event = pr->events().Get(0); |
| EXPECT_EQ(PERF_RECORD_MMAP2, event.header().type()); |
| EXPECT_EQ(0x68d, event.mmap_event().pid()); |
| EXPECT_EQ(0x68d, event.mmap_event().tid()); |
| EXPECT_EQ(0x1d000, event.mmap_event().start()); |
| EXPECT_EQ(0x1000, event.mmap_event().len()); |
| EXPECT_EQ(0x2000, event.mmap_event().pgoff()); |
| EXPECT_EQ(6, event.mmap_event().maj()); |
| EXPECT_EQ(7, event.mmap_event().min()); |
| EXPECT_EQ(8, event.mmap_event().ino()); |
| EXPECT_EQ(9, event.mmap_event().ino_generation()); |
| EXPECT_EQ(1|2, event.mmap_event().prot()); |
| EXPECT_EQ(2, event.mmap_event().flags()); |
| } |
| } |
| |
| // Regression test for http://crbug.com/493533 |
| TEST(PerfReaderTest, ReadsAllAvailableMetadataTypes) { |
| std::stringstream input; |
| |
| const uint32_t features = (1 << HEADER_HOSTNAME) | |
| (1 << HEADER_OSRELEASE) | |
| (1 << HEADER_VERSION) | |
| (1 << HEADER_ARCH) | |
| (1 << HEADER_LAST_FEATURE); |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(features); |
| file_header.WriteTo(&input); |
| |
| // metadata |
| |
| size_t metadata_offset = |
| file_header.data_end() + 5 * sizeof(perf_file_section); |
| |
| // HEADER_HOSTNAME |
| testing::ExampleStringMetadata hostname_metadata("hostname", metadata_offset); |
| metadata_offset += hostname_metadata.size(); |
| |
| // HEADER_OSRELEASE |
| testing::ExampleStringMetadata osrelease_metadata("osrelease", |
| metadata_offset); |
| metadata_offset += osrelease_metadata.size(); |
| |
| // HEADER_VERSION |
| testing::ExampleStringMetadata version_metadata("version", metadata_offset); |
| metadata_offset += version_metadata.size(); |
| |
| // HEADER_ARCH |
| testing::ExampleStringMetadata arch_metadata("arch", metadata_offset); |
| metadata_offset += arch_metadata.size(); |
| |
| // HEADER_LAST_FEATURE -- this is just a dummy metadata that will be skipped |
| // over. In practice, there will not actually be a metadata entry of type |
| // HEADER_LAST_FEATURE. But use because it will never become a supported |
| // metadata type. |
| testing::ExampleStringMetadata last_feature("*unsupported*", metadata_offset); |
| metadata_offset += last_feature.size(); |
| |
| hostname_metadata.index_entry().WriteTo(&input); |
| osrelease_metadata.index_entry().WriteTo(&input); |
| version_metadata.index_entry().WriteTo(&input); |
| arch_metadata.index_entry().WriteTo(&input); |
| last_feature.index_entry().WriteTo(&input); |
| |
| hostname_metadata.WriteTo(&input); |
| osrelease_metadata.WriteTo(&input); |
| version_metadata.WriteTo(&input); |
| arch_metadata.WriteTo(&input); |
| last_feature.WriteTo(&input); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // The dummy metadata should not have prevented the reading of the other |
| // metadata. |
| const auto& string_metadata = pr.string_metadata(); |
| EXPECT_EQ("hostname", string_metadata.hostname().value()); |
| EXPECT_EQ("osrelease", string_metadata.kernel_version().value()); |
| EXPECT_EQ("version", string_metadata.perf_version().value()); |
| EXPECT_EQ("arch", string_metadata.architecture().value()); |
| } |
| |
| // Regression test for http://crbug.com/493533 |
| TEST(PerfReaderTest, ReadsAllAvailableMetadataTypesPiped) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // metadata |
| |
| // Provide these out of order. Order should not matter to the PerfReader, but |
| // this tests whether the reader is capable of skipping an unsupported type. |
| testing::ExampleStringMetadataEvent(PERF_RECORD_HEADER_HOSTNAME, "hostname") |
| .WriteTo(&input); |
| testing::ExampleStringMetadataEvent(PERF_RECORD_HEADER_OSRELEASE, "osrelease") |
| .WriteTo(&input); |
| testing::ExampleStringMetadataEvent(PERF_RECORD_HEADER_MAX, "*unsupported*") |
| .WriteTo(&input); |
| testing::ExampleStringMetadataEvent(PERF_RECORD_HEADER_VERSION, "version") |
| .WriteTo(&input); |
| testing::ExampleStringMetadataEvent(PERF_RECORD_HEADER_ARCH, "arch") |
| .WriteTo(&input); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // The dummy metadata should not have prevented the reading of the other |
| // metadata. |
| const auto& string_metadata = pr.string_metadata(); |
| EXPECT_EQ("hostname", string_metadata.hostname().value()); |
| EXPECT_EQ("osrelease", string_metadata.kernel_version().value()); |
| EXPECT_EQ("version", string_metadata.perf_version().value()); |
| EXPECT_EQ("arch", string_metadata.architecture().value()); |
| } |
| |
| TEST(PerfReaderTest, AttrsWithDifferentSampleTypes) { |
| std::stringstream input; |
| |
| // PERF_RECORD_SAMPLE |
| testing::ExamplePerfSampleEvent sample_event_1(testing::SampleInfo() |
| .Id(51) |
| .Ip(0x00000000002c100a) |
| .Tid(1002)); |
| // PERF_RECORD_SAMPLE |
| testing::ExamplePerfSampleEvent sample_event_2(testing::SampleInfo() |
| .Id(61) |
| .Ip(0x00000000002c100b) |
| .Tid(1002) |
| .Time(1000006)); |
| // PERF_RECORD_SAMPLE |
| testing::ExamplePerfSampleEvent sample_event_3(testing::SampleInfo() |
| .Id(52) |
| .Ip(0x00000000002c100c) |
| .Tid(1002)); |
| |
| const size_t data_size = |
| sample_event_1.GetSize() + |
| sample_event_2.GetSize() + |
| sample_event_3.GetSize(); |
| const uint32_t features = 0; |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(features); |
| file_header |
| .WithAttrIdsCount(3) |
| .WithAttrCount(2) |
| .WithDataSize(data_size); |
| file_header.WriteTo(&input); |
| |
| // attr ids |
| testing::AttrIdsSection attr_ids(input.tellp()); |
| const auto id_section_1 = attr_ids.AddIds({51, 52}); |
| const auto id_section_2 = attr_ids.AddIds({61}); |
| attr_ids.WriteTo(&input); |
| |
| // attrs |
| ASSERT_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IDENTIFIER | |
| PERF_SAMPLE_IP | |
| PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithIds(id_section_1) |
| .WriteTo(&input); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IDENTIFIER | |
| PERF_SAMPLE_IP | |
| PERF_SAMPLE_TID | |
| PERF_SAMPLE_TIME, |
| true /*sample_id_all*/) |
| .WithIds(id_section_2) |
| .WriteTo(&input); |
| |
| // data |
| |
| ASSERT_EQ(file_header.header().data.offset, |
| static_cast<u64>(input.tellp())); |
| sample_event_1.WriteTo(&input); |
| sample_event_2.WriteTo(&input); |
| sample_event_3.WriteTo(&input); |
| ASSERT_EQ(file_header.header().data.offset + data_size, |
| static_cast<u64>(input.tellp())); |
| |
| // no metadata |
| |
| // |
| // Parse input. |
| // |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr ids were read correctly. |
| ASSERT_EQ(2, pr.attrs().size()); |
| ASSERT_EQ(2, pr.attrs().Get(0).ids().size()); |
| EXPECT_EQ(51, pr.attrs().Get(0).ids(0)); |
| EXPECT_EQ(52, pr.attrs().Get(0).ids(1)); |
| ASSERT_EQ(1, pr.attrs().Get(1).ids().size()); |
| EXPECT_EQ(61, pr.attrs().Get(1).ids(0)); |
| |
| // Verify events were read properly. |
| ASSERT_EQ(3, pr.events().size()); |
| { |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| const SampleEvent& sample = event.sample_event(); |
| EXPECT_EQ(51, sample.id()); |
| EXPECT_EQ(0x00000000002c100a, sample.ip()); |
| EXPECT_EQ(1002, sample.tid()); |
| // This event doesn't have a timestamp. |
| EXPECT_FALSE(sample.has_sample_time_ns()); |
| } |
| { |
| const PerfEvent& event = pr.events().Get(1); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| const SampleEvent& sample = event.sample_event(); |
| EXPECT_EQ(61, sample.id()); |
| EXPECT_EQ(0x00000000002c100b, sample.ip()); |
| EXPECT_EQ(1002, sample.tid()); |
| EXPECT_EQ(1000006, sample.sample_time_ns()); |
| } |
| { |
| const PerfEvent& event = pr.events().Get(2); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| const SampleEvent& sample = event.sample_event(); |
| EXPECT_EQ(52, sample.id()); |
| EXPECT_EQ(0x00000000002c100c, sample.ip()); |
| EXPECT_EQ(1002, sample.tid()); |
| // This event doesn't have a timestamp. |
| EXPECT_FALSE(sample.has_sample_time_ns()); |
| } |
| } |
| |
| // Neither PERF_SAMPLE_ID nor PERF_SAMPLE_IDENTIFIER are set. We should |
| // fall back to using the first attr when looking for the sample_format. |
| TEST(PerfReaderTest, NoSampleIdField) { |
| std::stringstream input; |
| |
| // PERF_RECORD_SAMPLE |
| testing::ExamplePerfSampleEvent sample_event( |
| testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002)); |
| |
| const size_t data_size = sample_event.GetSize(); |
| const uint32_t features = 0; |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(features); |
| file_header |
| .WithAttrCount(1) |
| .WithDataSize(data_size); |
| file_header.WriteTo(&input); |
| |
| // attrs |
| ASSERT_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| false /*sample_id_all*/) |
| .WithConfig(456) |
| .WriteTo(&input); |
| |
| // data |
| |
| ASSERT_EQ(file_header.header().data.offset, |
| static_cast<u64>(input.tellp())); |
| sample_event.WriteTo(&input); |
| ASSERT_EQ(file_header.header().data.offset + data_size, |
| static_cast<u64>(input.tellp())); |
| |
| // no metadata |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| ASSERT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(456, pr.attrs().Get(0).attr().config()); |
| |
| // Verify subsequent sample event was read properly. |
| ASSERT_EQ(1, pr.events().size()); |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| EXPECT_EQ(data_size, event.header().size()); |
| |
| EXPECT_EQ(0x00000000002c100a, event.sample_event().ip()); |
| EXPECT_EQ(1002, event.sample_event().tid()); |
| } |
| |
| // When sample_id_all == false, non-sample events should not look for sample_id. |
| TEST(PerfReaderTest, SampleIdFalseMeansDontReadASampleId) { |
| std::stringstream input; |
| |
| // PERF_RECORD_SAMPLE |
| testing::ExamplePerfSampleEvent sample_event( |
| testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002).Id(48)); |
| |
| // PERF_RECORD_MMAP |
| testing::ExampleMmapEvent mmap_event( |
| 1001, 0x1c1000, 0x1000, 0, "/usr/lib/foo.so", |
| // Write a sample_info even though we shouldn't (sample_id_all==false) |
| // Use a bogus ID: if we look at the sample_id and look for an attr, it |
| // should return an error. |
| testing::SampleInfo().Tid(1001).Id(666)); |
| |
| const size_t data_size = |
| sample_event.GetSize() + |
| mmap_event.GetSize(); |
| const uint32_t features = 0; |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(features); |
| file_header |
| .WithAttrIdsCount(1) |
| .WithAttrCount(1) |
| .WithDataSize(data_size); |
| file_header.WriteTo(&input); |
| |
| // attr IDs |
| testing::AttrIdsSection attr_ids(input.tellp()); |
| const auto id_section = attr_ids.AddId(48); |
| attr_ids.WriteTo(&input); |
| |
| // attrs |
| ASSERT_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | |
| PERF_SAMPLE_TID | |
| PERF_SAMPLE_ID, |
| false /*sample_id_all*/) |
| .WithIds(id_section) |
| .WriteTo(&input); |
| |
| // data |
| |
| ASSERT_EQ(file_header.header().data.offset, |
| static_cast<u64>(input.tellp())); |
| sample_event.WriteTo(&input); |
| mmap_event.WriteTo(&input); |
| ASSERT_EQ(file_header.header().data.offset + data_size, |
| static_cast<u64>(input.tellp())); |
| |
| // no metadata |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| EXPECT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Verify events were read properly. |
| ASSERT_EQ(2, pr.events().size()); |
| { |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| EXPECT_EQ(0x00000000002c100a, event.sample_event().ip()); |
| EXPECT_EQ(1002, event.sample_event().tid()); |
| EXPECT_EQ(48, event.sample_event().id()); |
| } |
| |
| { |
| const PerfEvent& event = pr.events().Get(1); |
| EXPECT_EQ(PERF_RECORD_MMAP, event.header().type()); |
| |
| // This event is malformed: it has junk at the end. Therefore, sample_info |
| // should not have TID or ID fields. |
| EXPECT_FALSE(event.mmap_event().sample_info().has_tid()); |
| EXPECT_FALSE(event.mmap_event().sample_info().has_id()); |
| } |
| } |
| |
| // Regression test for http://crbug.com/496441 |
| TEST(PerfReaderTest, LargePerfEventAttr) { |
| std::stringstream input; |
| |
| const size_t attr_size = sizeof(perf_event_attr) + sizeof(u64); |
| testing::ExamplePerfSampleEvent sample_event( |
| testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002)); |
| const size_t data_size = sample_event.GetSize(); |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(0); |
| file_header |
| .WithCustomPerfEventAttrSize(attr_size) |
| .WithAttrCount(1) |
| .WithDataSize(data_size); |
| file_header.WriteTo(&input); |
| |
| // attrs |
| ASSERT_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| false /*sample_id_all*/) |
| .WithAttrSize(attr_size) |
| .WithConfig(456) |
| .WriteTo(&input); |
| |
| // data |
| |
| ASSERT_EQ(file_header.header().data.offset, static_cast<u64>(input.tellp())); |
| sample_event.WriteTo(&input); |
| ASSERT_EQ(file_header.header().data.offset + data_size, |
| static_cast<u64>(input.tellp())); |
| |
| // no metadata |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| EXPECT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(456, pr.attrs().Get(0).attr().config()); |
| |
| // Verify subsequent sample event was read properly. |
| ASSERT_EQ(1, pr.events().size()); |
| |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| EXPECT_EQ(data_size, event.header().size()); |
| |
| const SampleEvent& sample_info = event.sample_event(); |
| EXPECT_EQ(0x00000000002c100a, sample_info.ip()); |
| EXPECT_EQ(1002, sample_info.tid()); |
| } |
| |
| // Regression test for http://crbug.com/496441 |
| TEST(PerfReaderTest, LargePerfEventAttrPiped) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithAttrSize(sizeof(perf_event_attr) + sizeof(u64)) |
| .WithConfig(123) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_HEADER_EVENT_TYPE |
| const struct event_type_event event_type = { |
| .header = { |
| .type = PERF_RECORD_HEADER_EVENT_TYPE, |
| .misc = 0, |
| .size = sizeof(struct event_type_event), |
| }, |
| .event_type = { |
| /*event_id*/ 123, |
| /*name*/ "cycles", |
| }, |
| }; |
| input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type)); |
| |
| testing::ExamplePerfSampleEvent sample_event( |
| testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002)); |
| sample_event.WriteTo(&input); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| EXPECT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(123, pr.attrs().Get(0).attr().config()); |
| ASSERT_EQ(1, pr.event_types().size()); |
| EXPECT_EQ("cycles", pr.event_types().Get(0).name()); |
| |
| // Verify subsequent sample event was read properly. |
| ASSERT_EQ(1, pr.events().size()); |
| |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| EXPECT_EQ(sample_event.GetSize(), event.header().size()); |
| |
| const SampleEvent& sample_info = event.sample_event(); |
| EXPECT_EQ(0x00000000002c100a, sample_info.ip()); |
| EXPECT_EQ(1002, sample_info.tid()); |
| } |
| |
| // Regression test for http://crbug.com/496441 |
| TEST(PerfReaderTest, SmallPerfEventAttr) { |
| std::stringstream input; |
| |
| const size_t attr_size = sizeof(perf_event_attr) - sizeof(u64); |
| testing::ExamplePerfSampleEvent sample_event( |
| testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002)); |
| const size_t data_size = sample_event.GetSize(); |
| |
| // header |
| testing::ExamplePerfDataFileHeader file_header(0); |
| file_header |
| .WithCustomPerfEventAttrSize(attr_size) |
| .WithAttrCount(1) |
| .WithDataSize(data_size); |
| file_header.WriteTo(&input); |
| |
| // attrs |
| CHECK_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| false /*sample_id_all*/) |
| .WithAttrSize(attr_size) |
| .WithConfig(456) |
| .WriteTo(&input); |
| |
| // data |
| |
| ASSERT_EQ(file_header.header().data.offset, |
| static_cast<u64>(input.tellp())); |
| sample_event.WriteTo(&input); |
| ASSERT_EQ(file_header.header().data.offset + data_size, |
| static_cast<u64>(input.tellp())); |
| |
| // no metadata |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| EXPECT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(456, pr.attrs().Get(0).attr().config()); |
| |
| // Verify subsequent sample event was read properly. |
| ASSERT_EQ(1, pr.events().size()); |
| |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| EXPECT_EQ(data_size, event.header().size()); |
| |
| const SampleEvent& sample_info = event.sample_event(); |
| EXPECT_EQ(0x00000000002c100a, sample_info.ip()); |
| EXPECT_EQ(1002, sample_info.tid()); |
| } |
| |
| // Regression test for http://crbug.com/496441 |
| TEST(PerfReaderTest, SmallPerfEventAttrPiped) { |
| std::stringstream input; |
| |
| // pipe header |
| testing::ExamplePipedPerfDataFileHeader().WriteTo(&input); |
| |
| // data |
| |
| // PERF_RECORD_HEADER_ATTR |
| testing::ExamplePerfEventAttrEvent_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithAttrSize(sizeof(perf_event_attr) - sizeof(u64)) |
| .WithConfig(123) |
| .WriteTo(&input); |
| |
| // PERF_RECORD_HEADER_EVENT_TYPE |
| const struct event_type_event event_type = { |
| .header = { |
| .type = PERF_RECORD_HEADER_EVENT_TYPE, |
| .misc = 0, |
| .size = sizeof(struct event_type_event), |
| }, |
| .event_type = { |
| /*event_id*/ 123, |
| /*name*/ "cycles", |
| }, |
| }; |
| input.write(reinterpret_cast<const char*>(&event_type), sizeof(event_type)); |
| |
| testing::ExamplePerfSampleEvent sample_event( |
| testing::SampleInfo().Ip(0x00000000002c100a).Tid(1002)); |
| sample_event.WriteTo(&input); |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| EXPECT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(123, pr.attrs().Get(0).attr().config()); |
| ASSERT_EQ(1, pr.event_types().size()); |
| EXPECT_EQ("cycles", pr.event_types().Get(0).name()); |
| |
| // Verify subsequent sample event was read properly. |
| ASSERT_EQ(1, pr.events().size()); |
| |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| EXPECT_EQ(sample_event.GetSize(), event.header().size()); |
| |
| const SampleEvent& sample_info = event.sample_event(); |
| EXPECT_EQ(0x00000000002c100a, sample_info.ip()); |
| EXPECT_EQ(1002, sample_info.tid()); |
| } |
| |
| TEST(PerfReaderTest, CrossEndianAttrs) { |
| for (bool is_cross_endian : {true, false}) { |
| LOG(INFO) << "Testing with cross endianness = " << is_cross_endian; |
| |
| std::stringstream input; |
| |
| // header |
| const uint32_t features = 0; |
| testing::ExamplePerfDataFileHeader file_header(features); |
| file_header |
| .WithAttrCount(3) |
| .WithCrossEndianness(is_cross_endian) |
| .WriteTo(&input); |
| |
| // attrs |
| CHECK_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| // Provide two attrs with different sample_id_all values to test the |
| // correctness of byte swapping of the bit fields. |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithConfig(123) |
| .WithCrossEndianness(is_cross_endian) |
| .WriteTo(&input); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithConfig(456) |
| .WithCrossEndianness(is_cross_endian) |
| .WriteTo(&input); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| false /*sample_id_all*/) |
| .WithConfig(456) |
| .WithCrossEndianness(is_cross_endian) |
| .WriteTo(&input); |
| |
| // No data. |
| // No metadata. |
| |
| // Read data. |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| EXPECT_EQ(3, pr.attrs().size()); |
| |
| const auto& attr0 = pr.attrs().Get(0).attr(); |
| EXPECT_EQ(123, attr0.config()); |
| EXPECT_EQ(1, attr0.sample_period()); |
| EXPECT_EQ(PERF_SAMPLE_IP | PERF_SAMPLE_TID, attr0.sample_type()); |
| EXPECT_TRUE(attr0.sample_id_all()); |
| EXPECT_EQ(2, attr0.precise_ip()); |
| |
| const auto& attr1 = pr.attrs().Get(1).attr(); |
| EXPECT_EQ(456, attr1.config()); |
| EXPECT_EQ(1, attr1.sample_period()); |
| EXPECT_EQ(PERF_SAMPLE_IP | PERF_SAMPLE_TID, attr1.sample_type()); |
| EXPECT_TRUE(attr1.sample_id_all()); |
| EXPECT_EQ(2, attr1.precise_ip()); |
| |
| const auto& attr2 = pr.attrs().Get(2).attr(); |
| EXPECT_EQ(456, attr2.config()); |
| EXPECT_EQ(1, attr2.sample_period()); |
| EXPECT_EQ(PERF_SAMPLE_IP | PERF_SAMPLE_TID, attr2.sample_type()); |
| EXPECT_FALSE(attr2.sample_id_all()); |
| EXPECT_EQ(2, attr2.precise_ip()); |
| } |
| } |
| |
| TEST(PerfReaderTest, CrossEndianNormalPerfData) { |
| // data |
| // Do this before header to compute the total data size. |
| std::stringstream input_data; |
| testing::ExampleMmapEvent( |
| 1234, 0x0000000000810000, 0x10000, 0x2000, "/usr/lib/foo.so", |
| testing::SampleInfo().Tid(bswap_32(1234), bswap_32(1235))) |
| .WithCrossEndianness(true) |
| .WriteTo(&input_data); |
| testing::ExampleForkEvent( |
| 1236, 1234, 1237, 1235, 30ULL * 1000000000, |
| testing::SampleInfo().Tid(bswap_32(1236), bswap_32(1237))) |
| .WithCrossEndianness(true) |
| .WriteTo(&input_data); |
| testing::ExamplePerfSampleEvent( |
| testing::SampleInfo() |
| .Ip(bswap_64(0x0000000000810100)) |
| .Tid(bswap_32(1234), bswap_32(1235))) |
| .WithCrossEndianness(true) |
| .WriteTo(&input_data); |
| testing::ExamplePerfSampleEvent( |
| testing::SampleInfo() |
| .Ip(bswap_64(0x000000000081ff00)) |
| .Tid(bswap_32(1236), bswap_32(1237))) |
| .WithCrossEndianness(true) |
| .WriteTo(&input_data); |
| |
| std::stringstream input; |
| |
| // header |
| const size_t data_size = input_data.str().size(); |
| const uint32_t features = (1 << HEADER_HOSTNAME) | (1 << HEADER_OSRELEASE); |
| testing::ExamplePerfDataFileHeader file_header(features); |
| file_header |
| .WithAttrCount(1) |
| .WithDataSize(data_size) |
| .WithCrossEndianness(true) |
| .WriteTo(&input); |
| |
| // attrs |
| CHECK_EQ(file_header.header().attrs.offset, |
| static_cast<u64>(input.tellp())); |
| testing::ExamplePerfFileAttr_Hardware(PERF_SAMPLE_IP | PERF_SAMPLE_TID, |
| true /*sample_id_all*/) |
| .WithConfig(456) |
| .WithCrossEndianness(true) |
| .WriteTo(&input); |
| |
| // Write data. |
| |
| u64 data_offset = file_header.header().data.offset; |
| ASSERT_EQ(data_offset, static_cast<u64>(input.tellp())); |
| input << input_data.str(); |
| ASSERT_EQ(data_offset + data_size, static_cast<u64>(input.tellp())); |
| |
| // metadata |
| size_t metadata_offset = |
| file_header.data_end() + 2 * sizeof(perf_file_section); |
| |
| // HEADER_HOSTNAME |
| testing::ExampleStringMetadata hostname_metadata("hostname", metadata_offset); |
| hostname_metadata.WithCrossEndianness(true); |
| metadata_offset += hostname_metadata.size(); |
| |
| // HEADER_OSRELEASE |
| testing::ExampleStringMetadata osrelease_metadata("osrelease", |
| metadata_offset); |
| osrelease_metadata.WithCrossEndianness(true); |
| metadata_offset += osrelease_metadata.size(); |
| |
| hostname_metadata.index_entry().WriteTo(&input); |
| osrelease_metadata.index_entry().WriteTo(&input); |
| |
| hostname_metadata.WriteTo(&input); |
| osrelease_metadata.WriteTo(&input); |
| |
| |
| // |
| // Parse input. |
| // |
| |
| PerfReader pr; |
| ASSERT_TRUE(pr.ReadFromString(input.str())); |
| |
| // Make sure the attr was recorded properly. |
| EXPECT_EQ(1, pr.attrs().size()); |
| EXPECT_EQ(456, pr.attrs().Get(0).attr().config()); |
| EXPECT_TRUE(pr.attrs().Get(0).attr().sample_id_all()); |
| |
| // Verify perf events. |
| ASSERT_EQ(4, pr.events().size()); |
| |
| { |
| const PerfEvent& event = pr.events().Get(0); |
| EXPECT_EQ(PERF_RECORD_MMAP, event.header().type()); |
| EXPECT_EQ(1234, event.mmap_event().pid()); |
| EXPECT_EQ(1234, event.mmap_event().tid()); |
| EXPECT_EQ(string("/usr/lib/foo.so"), event.mmap_event().filename()); |
| EXPECT_EQ(0x0000000000810000, event.mmap_event().start()); |
| EXPECT_EQ(0x10000, event.mmap_event().len()); |
| EXPECT_EQ(0x2000, event.mmap_event().pgoff()); |
| } |
| |
| { |
| const PerfEvent& event = pr.events().Get(1); |
| EXPECT_EQ(PERF_RECORD_FORK, event.header().type()); |
| EXPECT_EQ(1236, event.fork_event().pid()); |
| EXPECT_EQ(1234, event.fork_event().ppid()); |
| EXPECT_EQ(1237, event.fork_event().tid()); |
| EXPECT_EQ(1235, event.fork_event().ptid()); |
| EXPECT_EQ(30ULL * 1000000000, event.fork_event().fork_time_ns()); |
| } |
| |
| { |
| const PerfEvent& event = pr.events().Get(2); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| const SampleEvent& sample_info = event.sample_event(); |
| EXPECT_EQ(0x0000000000810100, sample_info.ip()); |
| EXPECT_EQ(1234, sample_info.pid()); |
| EXPECT_EQ(1235, sample_info.tid()); |
| } |
| |
| { |
| const PerfEvent& event = pr.events().Get(3); |
| EXPECT_EQ(PERF_RECORD_SAMPLE, event.header().type()); |
| |
| const SampleEvent& sample_info = event.sample_event(); |
| EXPECT_EQ(0x000000000081ff00, sample_info.ip()); |
| EXPECT_EQ(1236, sample_info.pid()); |
| EXPECT_EQ(1237, sample_info.tid()); |
| } |
| } |
| |
| TEST(PerfReaderTest, MetadataMaskInitialized) { |
| // The metadata mask is actually an array of uint64's. The accessors/mutator |
| // in PerfReader depend on it being initialized. |
| PerfReader reader; |
| ASSERT_EQ(1U, reader.proto().metadata_mask().size()); |
| EXPECT_EQ(0U, reader.metadata_mask()); |
| } |
| |
| } // namespace quipper |