// Copyright (c) 2014 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 "test_perf_data.h" #include <stddef.h> #include <algorithm> #include <ostream> #include <vector> #include "base/logging.h" #include "binary_data_utils.h" #include "compat/string.h" #include "kernel/perf_internals.h" #include "perf_data_utils.h" namespace quipper { namespace testing { namespace { // Write extra bytes to an output stream. void WriteExtraBytes(size_t size, std::ostream* out) { std::vector<char> padding(size); out->write(padding.data(), size); } u8 ReverseByte(u8 x) { x = (x & 0xf0) >> 4 | (x & 0x0f) << 4; // exchange nibbles x = (x & 0xcc) >> 2 | (x & 0x33) << 2; // exchange pairs x = (x & 0xaa) >> 1 | (x & 0x55) << 1; // exchange neighbors return x; } void SwapBitfieldOfBits(u8* field, size_t len) { for (size_t i = 0; i < len; i++) { field[i] = ReverseByte(field[i]); } } } // namespace ExamplePerfDataFileHeader::ExamplePerfDataFileHeader( const unsigned long features) { CHECK_EQ(112U, sizeof(perf_file_attr)) << "perf_file_attr has changed size"; header_ = { .magic = kPerfMagic, .size = 104, .attr_size = sizeof(struct perf_file_attr), .attrs = {.offset = 104, .size = 0}, .data = {.offset = 104, .size = 0}, .event_types = {0}, .adds_features = {features, 0, 0, 0}, }; } ExamplePerfDataFileHeader& ExamplePerfDataFileHeader::WithAttrIdsCount( size_t n) { attr_ids_count_ = n; UpdateSectionOffsets(); return *this; } ExamplePerfDataFileHeader& ExamplePerfDataFileHeader::WithAttrCount(size_t n) { header_.attrs.size = n * header_.attr_size; UpdateSectionOffsets(); return *this; } ExamplePerfDataFileHeader& ExamplePerfDataFileHeader::WithDataSize(size_t sz) { header_.data.size = sz; UpdateSectionOffsets(); return *this; } ExamplePerfDataFileHeader& ExamplePerfDataFileHeader::WithCustomPerfEventAttrSize(size_t sz) { size_t n_attrs = header_.attrs.size / header_.attr_size; // Calculate sizeof(perf_file_attr) given the custom sizeof(perf_event_attr) header_.attr_size = sz + sizeof(perf_file_section); // Re-calculate the attrs section size and update offsets. return WithAttrCount(n_attrs); } void ExamplePerfDataFileHeader::UpdateSectionOffsets() { u64 offset = header_.size; offset += attr_ids_count_ * sizeof(u64); header_.attrs.offset = offset; offset += header_.attrs.size; header_.data.offset = offset; offset += header_.data.size; CHECK_EQ(data_end_offset(), offset); // aka, the metadata offset. } void ExamplePerfDataFileHeader::WriteTo(std::ostream* out) const { struct perf_file_header local_header = { .magic = MaybeSwap64(header_.magic), .size = MaybeSwap64(header_.size), .attr_size = MaybeSwap64(header_.attr_size), .attrs = {.offset = MaybeSwap64(header_.attrs.offset), .size = MaybeSwap64(header_.attrs.size)}, .data = {.offset = MaybeSwap64(header_.data.offset), .size = MaybeSwap64(header_.data.size)}, .event_types = {.offset = MaybeSwap64(header_.event_types.offset), .size = MaybeSwap64(header_.event_types.size)}, .adds_features = {0}, }; // Copy over the features bits manually since the byte swapping is more // complicated. for (size_t i = 0; i < sizeof(header_.adds_features) / sizeof(uint64_t); ++i) { reinterpret_cast<uint64_t*>(local_header.adds_features)[i] = MaybeSwap64( reinterpret_cast<const uint64_t*>(header_.adds_features)[i]); } out->write(reinterpret_cast<const char*>(&local_header), sizeof(local_header)); // Use original header values that weren't endian-swapped. CHECK_EQ(static_cast<u64>(out->tellp()), header_.size); } void ExamplePipedPerfDataFileHeader::WriteTo(std::ostream* out) const { const perf_pipe_file_header header = { .magic = kPerfMagic, .size = 16, }; out->write(reinterpret_cast<const char*>(&header), sizeof(header)); CHECK_EQ(static_cast<u64>(out->tellp()), header.size); } void ExamplePerfEventAttrEvent_Hardware::WriteTo(std::ostream* out) const { // Due to the unnamed union fields (eg, sample_period), this structure can't // be initialized with designated initializers. perf_event_attr attr = {}; attr.type = PERF_TYPE_HARDWARE; attr.size = attr_size_; attr.config = config_; attr.sample_period = 100001; attr.sample_type = sample_type_; attr.read_format = read_format_; attr.sample_id_all = sample_id_all_; const size_t event_size = sizeof(perf_event_header) + attr.size + ids_.size() * sizeof(decltype(ids_)::value_type); const perf_event_header header = { .type = PERF_RECORD_HEADER_ATTR, .misc = 0, .size = static_cast<u16>(event_size), }; out->write(reinterpret_cast<const char*>(&header), sizeof(header)); out->write(reinterpret_cast<const char*>(&attr), std::min(sizeof(attr), static_cast<size_t>(attr_size_))); if (sizeof(attr) < attr_size_) WriteExtraBytes(attr_size_ - sizeof(attr), out); out->write(reinterpret_cast<const char*>(ids_.data()), ids_.size() * sizeof(decltype(ids_)::value_type)); } void AttrIdsSection::WriteTo(std::ostream* out) const { out->write(reinterpret_cast<const char*>(ids_.data()), ids_.size() * sizeof(decltype(ids_)::value_type)); } void ExamplePerfFileAttr_Hardware::WriteTo(std::ostream* out) const { // Due to the unnamed union fields (eg, sample_period), this structure can't // be initialized with designated initializers. perf_event_attr attr = {0}; attr.type = MaybeSwap32(PERF_TYPE_HARDWARE); attr.size = MaybeSwap32(attr_size_); attr.config = MaybeSwap64(config_); attr.sample_period = MaybeSwap64(1); attr.sample_type = MaybeSwap64(sample_type_); // Bit fields. attr.sample_id_all = sample_id_all_; attr.precise_ip = 2; // For testing a bit field that is more than one bit. if (is_cross_endian()) { // The order of operations here is for native-to-cross-endian conversion. // Contrast with similar code in PerfReader for cross-endian-to-native // conversion, which performs these swap operations in reverse order. const auto tmp = attr.precise_ip; attr.precise_ip = (tmp & 0x2) >> 1 | (tmp & 0x1) << 1; auto* const bitfield_start = &attr.read_format + 1; SwapBitfieldOfBits(reinterpret_cast<u8*>(bitfield_start), sizeof(u64)); } // perf_event_attr can be of a size other than the static struct size. Thus we // cannot simply statically create a perf_file_attr (which contains a // perf_event_attr and a perf_file_section). Instead, create and write each // component separately. out->write(reinterpret_cast<const char*>(&attr), std::min(sizeof(attr), static_cast<size_t>(attr_size_))); if (sizeof(attr) < attr_size_) WriteExtraBytes(attr_size_ - sizeof(attr), out); out->write(reinterpret_cast<const char*>(&ids_section_), sizeof(ids_section_)); } void ExamplePerfFileAttr_Tracepoint::WriteTo(std::ostream* out) const { // Due to the unnamed union fields (eg, sample_period), this structure can't // be initialized with designated initializers. perf_event_attr attr = {}; // See kernel src: tools/perf/util/evsel.c perf_evsel__newtp() attr.type = PERF_TYPE_TRACEPOINT; attr.size = sizeof(perf_event_attr); attr.config = tracepoint_event_id_; attr.sample_period = 1; attr.sample_type = (PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_RAW); const perf_file_attr file_attr = { .attr = attr, .ids = {.offset = 104, .size = 0}, }; out->write(reinterpret_cast<const char*>(&file_attr), sizeof(file_attr)); } size_t ExampleMmapEvent::GetSize() const { return offsetof(struct mmap_event, filename) + GetUint64AlignedStringLength(filename_) + sample_id_.size(); // sample_id_all } void ExampleMmapEvent::WriteTo(std::ostream* out) const { const size_t event_size = GetSize(); struct mmap_event event = { .header = { .type = MaybeSwap32(PERF_RECORD_MMAP), .misc = 0, .size = MaybeSwap16(static_cast<u16>(event_size)), }, .pid = MaybeSwap32(pid_), .tid = MaybeSwap32(pid_), .start = MaybeSwap64(start_), .len = MaybeSwap64(len_), .pgoff = MaybeSwap64(pgoff_), // .filename = ..., // written separately }; const size_t pre_mmap_offset = out->tellp(); out->write(reinterpret_cast<const char*>(&event), offsetof(struct mmap_event, filename)); const size_t filename_aligned_length = GetUint64AlignedStringLength(filename_); *out << filename_ << string(filename_aligned_length - filename_.size(), '\0'); out->write(sample_id_.data(), sample_id_.size()); const size_t written_event_size = static_cast<size_t>(out->tellp()) - pre_mmap_offset; CHECK_EQ(event_size, static_cast<u64>(written_event_size)); } void ExampleMmap2Event::WriteTo(std::ostream* out) const { const size_t filename_aligned_length = GetUint64AlignedStringLength(filename_); const size_t event_size = offsetof(struct mmap2_event, filename) + filename_aligned_length + sample_id_.size(); // sample_id_all struct mmap2_event event = { .header = { .type = PERF_RECORD_MMAP2, .misc = 0, .size = static_cast<u16>(event_size), }, .pid = pid_, .tid = tid_, .start = start_, .len = len_, .pgoff = pgoff_, .maj = maj_, .min = min_, .ino = ino_, .ino_generation = 9, .prot = 1 | 2, // == PROT_READ | PROT_WRITE .flags = 2, // == MAP_PRIVATE // .filename = ..., // written separately }; const size_t pre_mmap_offset = out->tellp(); out->write(reinterpret_cast<const char*>(&event), offsetof(struct mmap2_event, filename)); *out << filename_ << string(filename_aligned_length - filename_.size(), '\0'); out->write(sample_id_.data(), sample_id_.size()); const size_t written_event_size = static_cast<size_t>(out->tellp()) - pre_mmap_offset; CHECK_EQ(event.header.size, static_cast<u64>(written_event_size)); } void ExampleForkExitEvent::WriteTo(std::ostream* out) const { const size_t event_size = sizeof(struct fork_event) + sample_id_.size(); struct fork_event event = { .header = { .type = MaybeSwap32(type_), .misc = 0, .size = MaybeSwap16(static_cast<u16>(event_size)), }, .pid = MaybeSwap32(pid_), .ppid = MaybeSwap32(ppid_), .tid = MaybeSwap32(tid_), .ptid = MaybeSwap32(ptid_), .time = MaybeSwap64(time_), }; const size_t pre_event_offset = out->tellp(); out->write(reinterpret_cast<const char*>(&event), sizeof(event)); out->write(sample_id_.data(), sample_id_.size()); const size_t written_event_size = static_cast<size_t>(out->tellp()) - pre_event_offset; CHECK_EQ(MaybeSwap16(event.header.size), static_cast<u64>(written_event_size)); } void FinishedRoundEvent::WriteTo(std::ostream* out) const { const perf_event_header event = { .type = PERF_RECORD_FINISHED_ROUND, .misc = 0, .size = sizeof(struct perf_event_header), }; out->write(reinterpret_cast<const char*>(&event), sizeof(event)); } size_t ExamplePerfSampleEvent::GetSize() const { return sizeof(struct sample_event) + sample_info_.size(); } void ExamplePerfSampleEvent::WriteTo(std::ostream* out) const { const sample_event event = { .header = { .type = MaybeSwap32(PERF_RECORD_SAMPLE), .misc = MaybeSwap16(PERF_RECORD_MISC_USER), .size = MaybeSwap16(static_cast<u16>(GetSize())), }}; out->write(reinterpret_cast<const char*>(&event), sizeof(event)); out->write(sample_info_.data(), sample_info_.size()); } ExamplePerfSampleEvent_BranchStack::ExamplePerfSampleEvent_BranchStack() : ExamplePerfSampleEvent( SampleInfo() .BranchStack_nr(16) .BranchStack_lbr(0x00007f4a313bb8cc, 0x00007f4a313bdb40, 0x02) .BranchStack_lbr(0x00007f4a30ce4de2, 0x00007f4a313bb8b3, 0x02) .BranchStack_lbr(0x00007f4a313bb8b0, 0x00007f4a30ce4de0, 0x01) .BranchStack_lbr(0x00007f4a30ff45c1, 0x00007f4a313bb8a0, 0x02) .BranchStack_lbr(0x00007f4a30ff49f2, 0x00007f4a30ff45bb, 0x02) .BranchStack_lbr(0x00007f4a30ff4a98, 0x00007f4a30ff49ed, 0x02) .BranchStack_lbr(0x00007f4a30ff4a7c, 0x00007f4a30ff4a91, 0x02) .BranchStack_lbr(0x00007f4a30ff4a34, 0x00007f4a30ff4a46, 0x02) .BranchStack_lbr(0x00007f4a30ff4c22, 0x00007f4a30ff4a0e, 0x02) .BranchStack_lbr(0x00007f4a30ff4bb3, 0x00007f4a30ff4c1b, 0x01) .BranchStack_lbr(0x00007f4a30ff4a09, 0x00007f4a30ff4b60, 0x02) .BranchStack_lbr(0x00007f4a30ff49e8, 0x00007f4a30ff4a00, 0x02) .BranchStack_lbr(0x00007f4a30ff42db, 0x00007f4a30ff49e0, 0x02) .BranchStack_lbr(0x00007f4a30ff42bb, 0x00007f4a30ff42d4, 0x02) .BranchStack_lbr(0x00007f4a333bf88b, 0x00007f4a30ff42ac, 0x02) .BranchStack_lbr(0x00007f4a333bf853, 0x00007f4a333bf885, 0x02)) {} // Event size matching the event produced above const size_t ExamplePerfSampleEvent_BranchStack::kEventSize = (1 /*perf_event_header*/ + 1 /*nr*/ + 16 * 3 /*lbr*/) * sizeof(u64); void ExamplePerfSampleEvent_Tracepoint::WriteTo(std::ostream* out) const { const sample_event event = {.header = { .type = PERF_RECORD_SAMPLE, .misc = PERF_RECORD_MISC_USER, .size = 0x0078, }}; const u64 sample_event_array[] = { 0x00007f999c38d15a, // IP 0x0000068d0000068d, // TID (u32 pid, tid) 0x0001e0211cbab7b9, // TIME 0x0000000000000000, // CPU 0x0000000000000001, // PERIOD 0x0000004900000044, // RAW (u32 size = 0x44 = 68 = 4 + 8*sizeof(u64)) 0x000000090000068d, // . 0x0000000000000000, // . 0x0000100000000000, // . 0x0000000300000000, // . 0x0000002200000000, // . 0xffffffff00000000, // . 0x0000000000000000, // . 0x0000000000000000, // . }; CHECK_EQ(event.header.size, sizeof(event.header) + sizeof(sample_event_array)); out->write(reinterpret_cast<const char*>(&event), sizeof(event)); out->write(reinterpret_cast<const char*>(sample_event_array), sizeof(sample_event_array)); } // Event size matching the event produced above const size_t ExamplePerfSampleEvent_Tracepoint::kEventSize = (1 /*perf_event_header*/ + 14 /*sample array*/) * sizeof(u64); void ExampleStringMetadata::WriteTo(std::ostream* out) const { const perf_file_section& index_entry = index_entry_.index_entry_; CHECK_EQ(static_cast<u64>(out->tellp()), index_entry.offset); const u32 data_size_value = MaybeSwap32(data_.size()); out->write(reinterpret_cast<const char*>(&data_size_value), sizeof(data_size_value)); out->write(data_.data(), data_.size()); CHECK_EQ(static_cast<u64>(out->tellp()), index_entry.offset + size()); } void ExampleStringMetadataEvent::WriteTo(std::ostream* out) const { const size_t initial_position = out->tellp(); const u32 data_size = data_.size(); const perf_event_header header = { .type = type_, .misc = 0, .size = static_cast<u16>(sizeof(header) + sizeof(data_size) + data_.size()), }; out->write(reinterpret_cast<const char*>(&header), sizeof(header)); out->write(reinterpret_cast<const char*>(&data_size), sizeof(data_size)); out->write(reinterpret_cast<const char*>(data_.data()), data_.size()); CHECK_EQ(static_cast<u64>(out->tellp()), initial_position + header.size); } static const char kTraceMetadataValue[] = "\x17\x08\x44tracing0.5BLAHBLAHBLAH...."; const string ExampleTracingMetadata::Data::kTraceMetadata( kTraceMetadataValue, sizeof(kTraceMetadataValue) - 1); void ExampleTracingMetadata::Data::WriteTo(std::ostream* out) const { const perf_file_section& index_entry = parent_->index_entry_.index_entry_; CHECK_EQ(static_cast<u64>(out->tellp()), index_entry.offset); out->write(kTraceMetadata.data(), kTraceMetadata.size()); CHECK_EQ(static_cast<u64>(out->tellp()), index_entry.offset + index_entry.size); } size_t ExampleAuxtraceEvent::GetSize() const { return sizeof(struct auxtrace_event); } size_t ExampleAuxtraceEvent::GetTraceSize() const { return trace_data_.size(); } void ExampleAuxtraceEvent::WriteTo(std::ostream* out) const { const size_t event_size = GetSize(); struct auxtrace_event event = { .header = { .type = MaybeSwap32(PERF_RECORD_AUXTRACE), .misc = 0, .size = MaybeSwap16(static_cast<u16>(event_size)), }, .size = MaybeSwap64(size_), .offset = MaybeSwap64(offset_), .reference = MaybeSwap64(reference_), .idx = MaybeSwap32(idx_), .tid = MaybeSwap32(tid_), .cpu = MaybeSwap32(cpu_), .reserved__ = MaybeSwap32(reserved_), }; out->write(reinterpret_cast<const char*>(&event), event_size); out->write(trace_data_.data(), trace_data_.size()); } } // namespace testing } // namespace quipper