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

Reply via email to