Define the Linux perf.data records, attributes, sample fields, and feature
structures consumed by afdo_create. This patch contains ABI declarations
only; parsing is introduced separately.
afdo_tools/ChangeLog:
* src/perf.h: New file.
Signed-off-by: Kugan Vivekanandarajah <[email protected]>
Co-authored-by: Dhruv Chawla <[email protected]>
Co-authored-by: Rahul Shinde <[email protected]>
---
afdo_tools/src/perf.h | 406 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 406 insertions(+)
create mode 100644 afdo_tools/src/perf.h
diff --git a/afdo_tools/src/perf.h b/afdo_tools/src/perf.h
new file mode 100644
index 00000000000..c90b5ad026a
--- /dev/null
+++ b/afdo_tools/src/perf.h
@@ -0,0 +1,406 @@
+/* Copyright The GNU Toolchain Authors
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_PERF_H
+#define GCC_PERF_H
+
+#include "hash_util.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <limits>
+#include <utility>
+#include <vector>
+#include <string>
+#include <unordered_map>
+
+namespace perf_data {
+
+/* Scalar loaders for little-endian perf binary payloads. afdo_tools
+ supports only little-endian hosts (enforced by configure) and
+ little-endian perf.data files. memcpy into a native integer. */
+
+inline uint16_t
+read_u16 (const uint8_t *p)
+{
+ uint16_t v;
+ std::memcpy (&v, p, sizeof (v));
+ return v;
+}
+
+inline uint32_t
+read_u32 (const uint8_t *p)
+{
+ uint32_t v;
+ std::memcpy (&v, p, sizeof (v));
+ return v;
+}
+
+inline uint64_t
+read_u64 (const uint8_t *p)
+{
+ uint64_t v;
+ std::memcpy (&v, p, sizeof (v));
+ return v;
+}
+
+struct perf_record_type
+{
+ enum : unsigned
+ {
+ MMAP = 1,
+ LOST = 2,
+ COMM = 3,
+ EXIT = 4,
+ THROTTLE = 5,
+ UNTHROTTLE = 6,
+ FORK = 7,
+ READ = 8,
+ SAMPLE = 9,
+ MMAP2 = 10,
+ AUX = 11,
+ LOST_SAMPLES = 13,
+ AUXTRACE = 71, /* PERF_RECORD_AUXTRACE (user); inline tail after struct.
*/
+ COMPRESSED = 81,
+ COMPRESSED2 = 83
+ };
+};
+
+/* Bit layout matches Linux `PERF_SAMPLE_*` (include/uapi/linux/perf_event.h).
+ perf.data attrs.sample_type uses these values. */
+struct perf_sample_type
+{
+ enum : uint64_t
+ {
+ IP = 1ULL << 0,
+ TID = 1ULL << 1,
+ TIME = 1ULL << 2,
+ ADDR = 1ULL << 3,
+ READ = 1ULL << 4,
+ CALLCHAIN = 1ULL << 5,
+ ID = 1ULL << 6,
+ CPU = 1ULL << 7,
+ PERIOD = 1ULL << 8,
+ STREAM_ID = 1ULL << 9,
+ RAW = 1ULL << 10,
+ BRANCH_STACK = 1ULL << 11,
+ REGS_USER = 1ULL << 12,
+ STACK_USER = 1ULL << 13,
+ WEIGHT = 1ULL << 14,
+ DATA_SRC = 1ULL << 15,
+ IDENTIFIER = 1ULL << 16,
+ TRANSACTION = 1ULL << 17,
+ REGS_INTR = 1ULL << 18,
+ PHYS_ADDR = 1ULL << 19,
+ AUX = 1ULL << 20,
+ CGROUP = 1ULL << 21,
+ DATA_PAGE_SIZE = 1ULL << 22,
+ CODE_PAGE_SIZE = 1ULL << 23,
+ WEIGHT_STRUCT = 1ULL << 24,
+ };
+ static constexpr uint64_t WEIGHT_TYPE = WEIGHT | WEIGHT_STRUCT;
+};
+
+constexpr uint64_t DEFAULT_BRANCH_STACK_SAMPLE_TYPE
+ = perf_sample_type::IP | perf_sample_type::TID | perf_sample_type::TIME
+ | perf_sample_type::ADDR | perf_sample_type::CPU | perf_sample_type::PERIOD
+ | perf_sample_type::BRANCH_STACK;
+
+/* Linux `PERF_SAMPLE_BRANCH_*` bits affecting branch stack payload layout. */
+struct perf_branch_stack_format
+{
+ enum : uint64_t
+ {
+ HW_INDEX = 1ULL << 17,
+ COUNTERS = 1ULL << 19,
+ };
+};
+
+#pragma pack(push, 1)
+struct perf_branch_stack_entry
+{
+ uint64_t from;
+ uint64_t to;
+ uint64_t flags;
+};
+
+/* Branch-stack speculation (Linux perf_event.h PERF_BR_SPEC_* in flags).
+ The speculation outcome is a 2-bit field at offset 24 of the flags word. */
+struct perf_branch_spec
+{
+ enum : uint64_t
+ {
+ MASK = 3ULL << 24, /* Both speculation bits. */
+ WRONG_PATH = 1ULL << 24, /* PERF_BR_SPEC_WRONG_PATH. */
+ };
+};
+#pragma pack(pop)
+
+/* perf_event_read_format bits (PERF_FORMAT_*). */
+struct perf_read_format
+{
+ enum : unsigned
+ {
+ TOTAL_TIME_ENABLED = 1ULL << 0,
+ TOTAL_TIME_RUNNING = 1ULL << 1,
+ ID = 1ULL << 2,
+ GROUP = 1ULL << 3,
+ LOST = 1ULL << 4,
+ };
+};
+
+struct simple_mmap
+{
+ uint64_t start_addr;
+ uint64_t end_addr;
+ uint64_t file_offset; /* p_offset of the segment in the ELF file.
*/
+ uint64_t segment_vaddr; /* p_vaddr (link-time address) for DWARF lookup. */
+ bool is_pie = false; /* ET_DYN: DWARF uses 0-based addresses. */
+
+ simple_mmap (uint64_t start, uint64_t end, uint64_t offset,
+ uint64_t vaddr = 0, bool pie = false)
+ : start_addr (start), end_addr (end), file_offset (offset),
+ segment_vaddr (vaddr), is_pie (pie)
+ {}
+
+ bool contains (uint64_t addr) const
+ {
+ return addr >= start_addr && addr < end_addr;
+ }
+
+ /* Return link-time address for DWARF lookup.
+ - Non-PIE (segment_vaddr != 0): pass segment_vaddr + offset.
+ - PIE (is_pie or segment_vaddr == 0): DWARF uses 0-based, pass offset.
+ - Non-PIE with failed ELF lookup (segment_vaddr == 0, !is_pie): pass
+ runtime address. */
+ uint64_t to_dwarf_address (uint64_t runtime_addr) const
+ {
+ if (!contains (runtime_addr))
+ return runtime_addr;
+ uint64_t offset_in_segment = runtime_addr - start_addr;
+ if (segment_vaddr != 0)
+ return segment_vaddr + offset_in_segment;
+ if (is_pie)
+ return file_offset + offset_in_segment;
+ return runtime_addr;
+ }
+};
+
+/* Executable mappings of the target binary, keyed by the mapping PID. */
+using pid_mmap_map = std::unordered_map<uint32_t, std::vector<simple_mmap>>;
+
+#pragma pack(push, 1)
+/* Little-endian PERFILE2 header. FEATURES is only the first word of Linux
+ perf's multiword feature bitmap; remaining feature words are unused. */
+struct perf_file_header
+{
+ uint64_t magic;
+ uint64_t size;
+ uint64_t attr_size;
+ uint64_t attrs_offset;
+ uint64_t attrs_size;
+ uint64_t data_offset;
+ uint64_t data_size;
+ uint64_t event_types_offset;
+ uint64_t event_types_size;
+ uint64_t features;
+};
+
+struct perf_event_header
+{
+ uint32_t type;
+ uint16_t misc;
+ uint16_t size;
+};
+
+struct perf_mmap_record
+{
+ uint32_t pid;
+ uint32_t tid;
+ uint64_t addr;
+ uint64_t len;
+ uint64_t pgoff;
+};
+
+struct perf_mmap2_record
+{
+ uint32_t pid;
+ uint32_t tid;
+ uint64_t addr;
+ uint64_t len;
+ uint64_t pgoff;
+ union
+ {
+ struct
+ {
+ uint32_t maj;
+ uint32_t min;
+ uint64_t ino;
+ uint64_t ino_generation;
+ } mmap;
+ struct
+ {
+ uint8_t build_id_size;
+ uint8_t __reserved_1;
+ uint16_t __reserved_2;
+ uint8_t build_id[20];
+ } build;
+ } id;
+ uint32_t prot;
+ uint32_t flags;
+};
+#pragma pack(pop)
+
+/* Helpers using the structs above (MMAP records, simple_mmap lists). */
+
+/* Store START + LENGTH in END, rejecting empty and wrapping mappings. */
+inline bool
+try_compute_mmap_end (uint64_t start, uint64_t length, uint64_t *end)
+{
+ if (end == nullptr || length == 0 || length > UINT64_MAX - start)
+ return false;
+ *end = start + length;
+ return true;
+}
+
+/* Copy the record-trailing FILENAME, which need not be NUL terminated
+ within its FILENAME_MAX bytes. */
+inline std::string
+perf_mmap_filename_view (const char *filename, size_t filename_max)
+{
+ size_t len = 0;
+ while (len < filename_max && filename[len] != '\0')
+ ++len;
+ return std::string (filename, len);
+}
+
+using branch_stack_edge = std::pair<uint64_t, uint64_t>;
+
+struct branch_stack_edge_hash
+{
+ size_t operator() (const branch_stack_edge &e) const noexcept
+ {
+ size_t hash = std::hash<uint64_t>{}(e.first);
+ autofdo::hash_combine (hash, std::hash<uint64_t>{}(e.second));
+ return hash;
+ }
+};
+
+/* Aggregated from perf branch-stack samples (x86 LBR, ARM BRBE, etc.). */
+using branch_stack_edge_count_map
+ = std::unordered_map<branch_stack_edge, uint64_t, branch_stack_edge_hash>;
+
+/* Sorted range/branch count rows after load finalize. */
+struct profile_range
+{
+ uint64_t beg = 0;
+ uint64_t end = 0;
+ uint64_t count = 0;
+
+ bool operator< (const profile_range &other) const { return beg < other.beg; }
+};
+
+struct profile_branch
+{
+ uint64_t from = 0;
+ uint64_t to = 0;
+ uint64_t count = 0;
+
+ bool operator< (const profile_branch &other) const
+ {
+ if (from != other.from)
+ return from < other.from;
+ return to < other.to;
+ }
+};
+
+/* Lowest and highest address carrying mass in RANGES, as MIN_ADDR and
+ MAX_ADDR. False when no range carries any. */
+inline bool
+branch_stack_range_bounds (const branch_stack_edge_count_map &ranges,
+ uint64_t &min_addr, uint64_t &max_addr)
+{
+ min_addr = std::numeric_limits<uint64_t>::max ();
+ max_addr = 0;
+ for (const auto &entry : ranges)
+ {
+ const branch_stack_edge &range = entry.first;
+ if (entry.second == 0 || range.second < range.first)
+ continue;
+ min_addr = std::min (min_addr, range.first);
+ max_addr = std::max (max_addr, range.second);
+ }
+ return min_addr <= max_addr;
+}
+
+/* As total_mass_from_range_counts, for the unfinalized runtime map. */
+inline uint64_t
+total_mass_from_runtime_range_map (const branch_stack_edge_count_map &ranges)
+{
+ uint64_t total = 0;
+ for (const auto &entry : ranges)
+ {
+ const branch_stack_edge &range = entry.first;
+ const uint64_t count = entry.second;
+ if (range.second >= range.first)
+ total += count * (1 + (range.second - range.first));
+ }
+ return total;
+}
+
+inline uint64_t
+total_mass_from_address_frequencies (
+ const std::unordered_map<uint64_t, uint64_t> &address_frequencies)
+{
+ uint64_t total = 0;
+ for (const auto &entry : address_frequencies)
+ total += entry.second;
+ return total;
+}
+
+inline bool
+target_mmaps_contain (const std::vector<simple_mmap> &mmaps, uint64_t address)
+{
+ for (const auto &m : mmaps)
+ if (m.contains (address))
+ return true;
+ return false;
+}
+
+/* Translate RUNTIME_ADDR through MMAPS. The return value indicates success
+ because a valid PIE address may translate to zero. */
+inline bool
+try_translate_runtime_with_mmaps (const std::vector<simple_mmap> &mmaps,
+ uint64_t runtime_addr, uint64_t *out)
+{
+ if (out == nullptr)
+ return false;
+ for (const auto &m : mmaps)
+ if (m.contains (runtime_addr))
+ {
+ *out = m.to_dwarf_address (runtime_addr);
+ return true;
+ }
+ return false;
+}
+
+} /* namespace perf_data. */
+
+#endif /* GCC_PERF_H. */
--
2.34.1