// 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
|