// Copyright 2015 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 "sample_info_reader.h" #include <byteswap.h> #include "compat/test.h" #include "kernel/perf_event.h" #include "kernel/perf_internals.h" #include "test_perf_data.h" #include "test_utils.h" namespace quipper { using testing::PunU32U64; TEST(SampleInfoReaderTest, ReadSampleEvent) { // clang-format off uint64_t 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 | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_DATA_SRC | PERF_SAMPLE_TRANSACTION; // clang-format on struct perf_event_attr attr = {0}; attr.sample_type = sample_type; SampleInfoReader reader(attr, false /* read_cross_endian */); const u64 sample_event_array[] = { 0xffffffff01234567, // IP PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) 1415837014 * 1000000000ULL, // TIME 0x00007f999c38d15a, // ADDR 2, // ID 1, // STREAM_ID 8, // CPU 10001, // PERIOD 12345, // WEIGHT 0x68100142, // DATA_SRC 67890, // TRANSACTIONS }; const sample_event sample_event_struct = { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0, .size = sizeof(sample_event) + sizeof(sample_event_array), }}; std::stringstream input; input.write(reinterpret_cast<const char*>(&sample_event_struct), sizeof(sample_event_struct)); input.write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(0xffffffff01234567, sample.ip); EXPECT_EQ(0x68d, sample.pid); EXPECT_EQ(0x68e, sample.tid); EXPECT_EQ(1415837014 * 1000000000ULL, sample.time); EXPECT_EQ(0x00007f999c38d15a, sample.addr); EXPECT_EQ(2, sample.id); EXPECT_EQ(1, sample.stream_id); EXPECT_EQ(8, sample.cpu); EXPECT_EQ(10001, sample.period); EXPECT_EQ(12345, sample.weight); EXPECT_EQ(0x68100142, sample.data_src); EXPECT_EQ(67890, sample.transaction); } TEST(SampleInfoReaderTest, ReadSampleEventCrossEndian) { // clang-format off uint64_t 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; // clang-format on struct perf_event_attr attr = {0}; attr.sample_type = sample_type; SampleInfoReader reader(attr, true /* read_cross_endian */); const u64 sample_event_array[] = { 0xffffffff01234567, // IP PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) 1415837014 * 1000000000ULL, // TIME 0x00007f999c38d15a, // ADDR 2, // ID 1, // STREAM_ID 8, // CPU 10001, // PERIOD }; const sample_event sample_event_struct = { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0, .size = sizeof(sample_event) + sizeof(sample_event_array), }}; std::stringstream input; input.write(reinterpret_cast<const char*>(&sample_event_struct), sizeof(sample_event_struct)); input.write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(bswap_64(0xffffffff01234567), sample.ip); EXPECT_EQ(bswap_32(0x68d), sample.pid); // 32-bit EXPECT_EQ(bswap_32(0x68e), sample.tid); // 32-bit EXPECT_EQ(bswap_64(1415837014 * 1000000000ULL), sample.time); EXPECT_EQ(bswap_64(0x00007f999c38d15a), sample.addr); EXPECT_EQ(bswap_64(2), sample.id); EXPECT_EQ(bswap_64(1), sample.stream_id); EXPECT_EQ(bswap_32(8), sample.cpu); // 32-bit EXPECT_EQ(bswap_64(10001), sample.period); } TEST(SampleInfoReaderTest, ReadMmapEvent) { // clang-format off uint64_t 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; // clang-format on struct perf_event_attr attr = {0}; attr.sample_type = sample_type; attr.sample_id_all = true; SampleInfoReader reader(attr, false /* read_cross_endian */); // PERF_RECORD_MMAP ASSERT_EQ(40, offsetof(struct mmap_event, filename)); const u64 mmap_sample_id[] = { PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) 1415911367 * 1000000000ULL, // TIME 3, // ID 2, // STREAM_ID 9, // CPU }; // clang-format off const size_t mmap_event_size = offsetof(struct mmap_event, filename) + 10+6 /* ==16, nearest 64-bit boundary for filename */ + sizeof(mmap_sample_id); // clang-format on const char mmap_filename[10 + 6] = "/dev/zero"; 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 }; std::stringstream input; 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)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(0x68d, sample.pid); EXPECT_EQ(0x68e, sample.tid); EXPECT_EQ(1415911367 * 1000000000ULL, sample.time); EXPECT_EQ(3, sample.id); EXPECT_EQ(2, sample.stream_id); EXPECT_EQ(9, sample.cpu); } TEST(SampleInfoReaderTest, ReadReadInfoAllFields) { struct perf_event_attr attr = {0}; attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_READ; attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; SampleInfoReader reader(attr, false /* read_cross_endian */); // PERF_RECORD_SAMPLE const u64 sample_event_array[] = { 0xffffffff01234567, // IP PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) // From kernel/perf_event.h: // struct read_format { // { u64 value; // { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED // { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING // { u64 id; } && PERF_FORMAT_ID // } && !PERF_FORMAT_GROUP // }; 1000000, // READ: value 1415837014 * 1000000000ULL, // READ: time_enabled 1234567890 * 1000000000ULL, // READ: time_running 0xabcdef, // READ: id }; sample_event sample_event_struct = { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0, .size = sizeof(sample_event) + sizeof(sample_event_array), }}; std::stringstream input; input.write(reinterpret_cast<const char*>(&sample_event_struct), sizeof(sample_event_struct)); input.write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(0xffffffff01234567, sample.ip); EXPECT_EQ(0x68d, sample.pid); EXPECT_EQ(0x68e, sample.tid); EXPECT_EQ(1415837014 * 1000000000ULL, sample.read.time_enabled); EXPECT_EQ(1234567890 * 1000000000ULL, sample.read.time_running); EXPECT_EQ(0xabcdef, sample.read.one.id); EXPECT_EQ(1000000, sample.read.one.value); } TEST(SampleInfoReaderTest, ReadReadInfoOmitTotalTimeFields) { struct perf_event_attr attr = {0}; attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_READ; // Omit the PERF_FORMAT_TOTAL_TIME_* fields. attr.read_format = PERF_FORMAT_ID; SampleInfoReader reader(attr, false /* read_cross_endian */); // PERF_RECORD_SAMPLE const u64 sample_event_array[] = { 0xffffffff01234567, // IP PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) 1000000, // READ: value 0xabcdef, // READ: id }; sample_event sample_event_struct = { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0, .size = sizeof(sample_event) + sizeof(sample_event_array), }}; std::stringstream input; input.write(reinterpret_cast<const char*>(&sample_event_struct), sizeof(sample_event_struct)); input.write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(0xffffffff01234567, sample.ip); EXPECT_EQ(0x68d, sample.pid); EXPECT_EQ(0x68e, sample.tid); EXPECT_EQ(0xabcdef, sample.read.one.id); EXPECT_EQ(1000000, sample.read.one.value); } TEST(SampleInfoReaderTest, ReadReadInfoValueFieldOnly) { struct perf_event_attr attr = {0}; attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_READ; // Omit all optional fields. The |value| field still remains. attr.read_format = 0; SampleInfoReader reader(attr, false /* read_cross_endian */); // PERF_RECORD_SAMPLE const u64 sample_event_array[] = { 0xffffffff01234567, // IP PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) 1000000, // READ: value }; sample_event sample_event_struct = { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0, .size = sizeof(sample_event) + sizeof(sample_event_array), }}; std::stringstream input; input.write(reinterpret_cast<const char*>(&sample_event_struct), sizeof(sample_event_struct)); input.write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(0xffffffff01234567, sample.ip); EXPECT_EQ(0x68d, sample.pid); EXPECT_EQ(0x68e, sample.tid); EXPECT_EQ(1000000, sample.read.one.value); } TEST(SampleInfoReaderTest, ReadReadInfoWithGroups) { struct perf_event_attr attr = {0}; attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_READ; // Omit the PERF_FORMAT_TOTAL_TIME_* fields. attr.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; SampleInfoReader reader(attr, false /* read_cross_endian */); // PERF_RECORD_SAMPLE const u64 sample_event_array[] = { 0xffffffff01234567, // IP PunU32U64{.v32 = {0x68d, 0x68e}}.v64, // TID (u32 pid, tid) 3, // READ: nr 1000000, // READ: values[0].value 0xabcdef, // READ: values[0].id 2000000, // READ: values[1].value 0xdecaf0, // READ: values[1].id 3000000, // READ: values[2].value 0xbeef00, // READ: values[2].id }; sample_event sample_event_struct = { .header = { .type = PERF_RECORD_SAMPLE, .misc = 0, .size = sizeof(sample_event) + sizeof(sample_event_array), }}; std::stringstream input; input.write(reinterpret_cast<const char*>(&sample_event_struct), sizeof(sample_event_struct)); input.write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); string input_string = input.str(); const event_t& event = *reinterpret_cast<const event_t*>(input_string.data()); perf_sample sample; ASSERT_TRUE(reader.ReadPerfSampleInfo(event, &sample)); EXPECT_EQ(0xffffffff01234567, sample.ip); EXPECT_EQ(0x68d, sample.pid); EXPECT_EQ(0x68e, sample.tid); EXPECT_EQ(3, sample.read.group.nr); ASSERT_NE(static_cast<void*>(NULL), sample.read.group.values); EXPECT_EQ(0xabcdef, sample.read.group.values[0].id); EXPECT_EQ(1000000, sample.read.group.values[0].value); EXPECT_EQ(0xdecaf0, sample.read.group.values[1].id); EXPECT_EQ(2000000, sample.read.group.values[1].value); EXPECT_EQ(0xbeef00, sample.read.group.values[2].id); EXPECT_EQ(3000000, sample.read.group.values[2].value); } } // namespace quipper