// 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. #ifndef CHROMIUMOS_WIDE_PROFILING_TEST_PERF_DATA_H_ #define CHROMIUMOS_WIDE_PROFILING_TEST_PERF_DATA_H_ #include <memory> #include <ostream> #include <vector> #include "binary_data_utils.h" #include "compat/string.h" #include "kernel/perf_internals.h" namespace quipper { namespace testing { // Union for punning 32-bit words into a 64-bit word. union PunU32U64 { u32 v32[2]; u64 v64; }; class StreamWriteable { public: StreamWriteable() : is_cross_endian_(false) {} virtual ~StreamWriteable() {} virtual void WriteTo(std::ostream* out) const = 0; virtual StreamWriteable& WithCrossEndianness(bool value) { is_cross_endian_ = value; return *this; } // Do not call MaybeSwap() directly. The syntax of test data structure // initialization makes data sizes ambiguous, so these force the caller to // explicitly specify value sizes. uint16_t MaybeSwap16(uint16_t value) const { return MaybeSwap(value); } uint32_t MaybeSwap32(uint32_t value) const { return MaybeSwap(value); } uint64_t MaybeSwap64(uint64_t value) const { return MaybeSwap(value); } protected: // Derived classes can call this to determine the cross-endianness. However, // the actual implementation of cross-endianness is up to the derived class, // if it supports it at all. bool is_cross_endian() const { return is_cross_endian_; } private: template <typename T> T MaybeSwap(T value) const { if (is_cross_endian()) ByteSwap(&value); return value; } bool is_cross_endian_; }; // Normal mode header class ExamplePerfDataFileHeader : public StreamWriteable { public: typedef ExamplePerfDataFileHeader SelfT; explicit ExamplePerfDataFileHeader(const unsigned long features); SelfT& WithAttrIdsCount(size_t n); SelfT& WithAttrCount(size_t n); SelfT& WithDataSize(size_t sz); // Used for testing compatibility w.r.t. sizeof(perf_event_attr) SelfT& WithCustomPerfEventAttrSize(size_t sz); const struct perf_file_header& header() const { return header_; } u64 data_end_offset() const { return header_.data.offset + header_.data.size; } ssize_t data_end() const { return static_cast<ssize_t>(data_end_offset()); } void WriteTo(std::ostream* out) const override; protected: struct perf_file_header header_; size_t attr_ids_count_ = 0; private: void UpdateSectionOffsets(); }; // Produces the pipe-mode file header. class ExamplePipedPerfDataFileHeader : public StreamWriteable { public: ExamplePipedPerfDataFileHeader() {} void WriteTo(std::ostream* out) const override; }; // Produces a PERF_RECORD_HEADER_ATTR event with struct perf_event_attr // describing a hardware event. The sample_type mask and the sample_id_all // bit are paramatized. class ExamplePerfEventAttrEvent_Hardware : public StreamWriteable { public: typedef ExamplePerfEventAttrEvent_Hardware SelfT; explicit ExamplePerfEventAttrEvent_Hardware(u64 sample_type, bool sample_id_all) : attr_size_(sizeof(perf_event_attr)), sample_type_(sample_type), read_format_(0), sample_id_all_(sample_id_all), config_(0) {} SelfT& WithConfig(u64 config) { config_ = config; return *this; } SelfT& WithAttrSize(u32 size) { attr_size_ = size; return *this; } SelfT& WithReadFormat(u64 format) { read_format_ = format; return *this; } SelfT& WithId(u64 id) { ids_.push_back(id); return *this; } SelfT& WithIds(std::initializer_list<u64> ids) { ids_.insert(ids_.end(), ids.begin(), ids.end()); return *this; } void WriteTo(std::ostream* out) const override; private: u32 attr_size_; const u64 sample_type_; u64 read_format_; const bool sample_id_all_; u64 config_; std::vector<u64> ids_; }; class AttrIdsSection : public StreamWriteable { public: explicit AttrIdsSection(size_t initial_offset) : offset_(initial_offset) {} perf_file_section AddId(u64 id) { return AddIds({id}); } perf_file_section AddIds(std::initializer_list<u64> ids) { ids_.insert(ids_.end(), ids.begin(), ids.end()); perf_file_section s = { .offset = offset_, .size = ids.size() * sizeof(decltype(ids)::value_type), }; offset_ += s.size; return s; } void WriteTo(std::ostream* out) const override; private: u64 offset_; std::vector<u64> ids_; }; // Produces a struct perf_file_attr with a perf_event_attr describing a // hardware event. class ExamplePerfFileAttr_Hardware : public StreamWriteable { public: typedef ExamplePerfFileAttr_Hardware SelfT; explicit ExamplePerfFileAttr_Hardware(u64 sample_type, bool sample_id_all) : attr_size_(sizeof(perf_event_attr)), sample_type_(sample_type), sample_id_all_(sample_id_all), config_(0), ids_section_({.offset = MaybeSwap64(104), .size = MaybeSwap64(0)}) {} SelfT& WithAttrSize(u32 size) { attr_size_ = size; return *this; } SelfT& WithConfig(u64 config) { config_ = config; return *this; } SelfT& WithIds(const perf_file_section& section) { ids_section_ = section; return *this; } void WriteTo(std::ostream* out) const override; private: u32 attr_size_; const u64 sample_type_; const bool sample_id_all_; u64 config_; perf_file_section ids_section_; }; // Produces a struct perf_file_attr with a perf_event_attr describing a // tracepoint event. class ExamplePerfFileAttr_Tracepoint : public StreamWriteable { public: explicit ExamplePerfFileAttr_Tracepoint(const u64 tracepoint_event_id) : tracepoint_event_id_(tracepoint_event_id) {} void WriteTo(std::ostream* out) const override; private: const u64 tracepoint_event_id_; }; // Produces a sample field array that can be used with either SAMPLE events // or as the sample_id of another event. // NB: This class simply places the fields in the order called. It does not // enforce that they are in the correct order, or match the sample type. // See enum perf_event_type in perf_event.h. class SampleInfo { public: SampleInfo& Ip(u64 ip) { return AddField(ip); } SampleInfo& Tid(u32 pid, u32 tid) { return AddField(PunU32U64{.v32 = {pid, tid}}.v64); } SampleInfo& Tid(u32 pid) { return AddField(PunU32U64{.v32 = {pid, pid}}.v64); } SampleInfo& Time(u64 time) { return AddField(time); } SampleInfo& Id(u64 id) { return AddField(id); } SampleInfo& BranchStack_nr(u64 nr) { return AddField(nr); } SampleInfo& BranchStack_lbr(u64 from, u64 to, u64 flags) { AddField(from); AddField(to); AddField(flags); return *this; } const char* data() const { return reinterpret_cast<const char*>(fields_.data()); } const size_t size() const { return fields_.size() * sizeof(decltype(fields_)::value_type); } private: SampleInfo& AddField(u64 value) { fields_.push_back(value); return *this; } std::vector<u64> fields_; }; // Produces a PERF_RECORD_MMAP event with the given file and mapping. class ExampleMmapEvent : public StreamWriteable { public: ExampleMmapEvent(u32 pid, u64 start, u64 len, u64 pgoff, string filename, const SampleInfo& sample_id) : pid_(pid), start_(start), len_(len), pgoff_(pgoff), filename_(filename), sample_id_(sample_id) {} size_t GetSize() const; void WriteTo(std::ostream* out) const override; private: const u32 pid_; const u64 start_; const u64 len_; const u64 pgoff_; const string filename_; const SampleInfo sample_id_; }; // Produces a PERF_RECORD_MMAP2 event with the given file and mapping. class ExampleMmap2Event : public StreamWriteable { public: typedef ExampleMmap2Event SelfT; // pid is used as both pid and tid. ExampleMmap2Event(u32 pid, u64 start, u64 len, u64 pgoff, string filename, const SampleInfo& sample_id) : ExampleMmap2Event(pid, pid, start, len, pgoff, filename, sample_id) {} ExampleMmap2Event(u32 pid, u32 tid, u64 start, u64 len, u64 pgoff, string filename, const SampleInfo& sample_id) : pid_(pid), tid_(tid), start_(start), len_(len), pgoff_(pgoff), maj_(6), min_(7), ino_(8), filename_(filename), sample_id_(sample_id) {} SelfT& WithDeviceInfo(u32 maj, u32 min, u64 ino) { maj_ = maj; min_ = min; ino_ = ino; return *this; } void WriteTo(std::ostream* out) const override; private: const u32 pid_; const u32 tid_; const u64 start_; const u64 len_; const u64 pgoff_; u32 maj_; u32 min_; u64 ino_; const string filename_; const SampleInfo sample_id_; }; // Produces a PERF_RECORD_FORK or PERF_RECORD_EXIT event. // Cannot be instantiated directly; use a derived class. class ExampleForkExitEvent : public StreamWriteable { public: void WriteTo(std::ostream* out) const override; protected: ExampleForkExitEvent(u32 type, u32 pid, u32 ppid, u32 tid, u32 ptid, u64 time, const SampleInfo& sample_id) : type_(type), pid_(pid), ppid_(ppid), tid_(tid), ptid_(ptid), time_(time), sample_id_(sample_id) {} const u32 type_; // Either PERF_RECORD_FORK or PERF_RECORD_EXIT. private: const u32 pid_; const u32 ppid_; const u32 tid_; const u32 ptid_; const u64 time_; const SampleInfo sample_id_; }; // Produces a PERF_RECORD_FORK event. class ExampleForkEvent : public ExampleForkExitEvent { public: ExampleForkEvent(u32 pid, u32 ppid, u32 tid, u32 ptid, u64 time, const SampleInfo& sample_id) : ExampleForkExitEvent(PERF_RECORD_FORK, pid, ppid, tid, ptid, time, sample_id) {} }; // Produces a PERF_RECORD_EXIT event. class ExampleExitEvent : public ExampleForkExitEvent { public: ExampleExitEvent(u32 pid, u32 ppid, u32 tid, u32 ptid, u64 time, const SampleInfo& sample_id) : ExampleForkExitEvent(PERF_RECORD_EXIT, pid, ppid, tid, ptid, time, sample_id) {} }; // Produces the PERF_RECORD_FINISHED_ROUND event. This event is just a header. class FinishedRoundEvent : public StreamWriteable { public: void WriteTo(std::ostream* out) const override; }; // Produces a simple PERF_RECORD_SAMPLE event with the given sample info. // NB: The sample_info must match the sample_type of the relevant attr. class ExamplePerfSampleEvent : public StreamWriteable { public: explicit ExamplePerfSampleEvent(const SampleInfo& sample_info) : sample_info_(sample_info) {} size_t GetSize() const; void WriteTo(std::ostream* out) const override; private: const SampleInfo sample_info_; }; class ExamplePerfSampleEvent_BranchStack : public ExamplePerfSampleEvent { public: ExamplePerfSampleEvent_BranchStack(); static const size_t kEventSize; }; // Produces a struct sample_event matching ExamplePerfFileAttr_Tracepoint. class ExamplePerfSampleEvent_Tracepoint : public StreamWriteable { public: ExamplePerfSampleEvent_Tracepoint() {} void WriteTo(std::ostream* out) const override; static const size_t kEventSize; }; // Produces a struct perf_file_section suitable for use in the metadata index. class MetadataIndexEntry : public StreamWriteable { public: MetadataIndexEntry(u64 offset, u64 size) : index_entry_{.offset = offset, .size = size} {} void WriteTo(std::ostream* out) const override { struct perf_file_section entry = { .offset = MaybeSwap64(index_entry_.offset), .size = MaybeSwap64(index_entry_.size), }; out->write(reinterpret_cast<const char*>(&entry), sizeof(entry)); } public: const perf_file_section index_entry_; }; // Produces sample string metadata, and corresponding metadata index entry. class ExampleStringMetadata : public StreamWriteable { public: // The input string gets zero-padded/truncated to |kStringAlignSize| bytes if // it is shorter/longer, respectively. explicit ExampleStringMetadata(const string& data, size_t offset) : data_(data), index_entry_(offset, sizeof(u32) + kStringAlignSize) { data_.resize(kStringAlignSize); } void WriteTo(std::ostream* out) const override; const MetadataIndexEntry& index_entry() { return index_entry_; } size_t size() const { return sizeof(u32) + data_.size(); } StreamWriteable& WithCrossEndianness(bool value) override { // Set index_entry_'s endianness since it is owned by this class. index_entry_.WithCrossEndianness(value); return StreamWriteable::WithCrossEndianness(value); } private: string data_; MetadataIndexEntry index_entry_; static const int kStringAlignSize = 64; }; // Produces sample string metadata event for piped mode. class ExampleStringMetadataEvent : public StreamWriteable { public: // The input string gets aligned to |kStringAlignSize|. explicit ExampleStringMetadataEvent(u32 type, const string& data) : type_(type), data_(data) { data_.resize(kStringAlignSize); } void WriteTo(std::ostream* out) const override; private: u32 type_; string data_; static const int kStringAlignSize = 64; }; // Produces sample tracing metadata, and corresponding metadata index entry. class ExampleTracingMetadata { public: class Data : public StreamWriteable { public: static const string kTraceMetadata; explicit Data(ExampleTracingMetadata* parent) : parent_(parent) {} const string& value() const { return kTraceMetadata; } void WriteTo(std::ostream* out) const override; private: ExampleTracingMetadata* parent_; }; explicit ExampleTracingMetadata(size_t offset) : data_(this), index_entry_(offset, data_.value().size()) {} const Data& data() { return data_; } const MetadataIndexEntry& index_entry() { return index_entry_; } private: friend class Data; Data data_; MetadataIndexEntry index_entry_; }; // Produces a PERF_RECORD_AUXTRACE event. class ExampleAuxtraceEvent : public StreamWriteable { public: ExampleAuxtraceEvent(u64 size, u64 offset, u64 reference, u32 idx, u32 tid, u32 cpu, u32 reserved, string trace_data) : size_(size), offset_(offset), reference_(reference), idx_(idx), tid_(tid), cpu_(cpu), reserved_(reserved), trace_data_(std::move(trace_data)) {} size_t GetSize() const; size_t GetTraceSize() const; void WriteTo(std::ostream* out) const override; private: const u64 size_; const u64 offset_; const u64 reference_; const u32 idx_; const u32 tid_; const u32 cpu_; const u32 reserved_; const string trace_data_; }; } // namespace testing } // namespace quipper #endif // CHROMIUMOS_WIDE_PROFILING_TEST_PERF_DATA_H_