apt: apt-pkg/contrib/perf.h Source File

apt 3.3.2 commandline package manager

perf.h
1/*
2 * Performance measurements
3 *
4 * Copyright (C) 2026 Julian Andres Klode <jak@debian.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 */
8
9#ifndef APT_PERF_H
10#define APT_PERF_H
11#if defined(APT_COMPILING_APT) && defined(__linux__)
12#include <apt-pkg/macros.h>
13
14#include <array>
15#include <cstdint>
16#include <cstdio>
17#include <cstdlib>
18#include <cstring>
19#include <sstream>
20
21#include <fcntl.h>
22#include <linux/perf_event.h>
23#include <sys/file.h>
24#include <sys/ioctl.h>
25#include <sys/syscall.h>
26#include <unistd.h>
27
28namespace APT
29{
30
37class PerformanceContext
38{
39 struct measurement
40 {
41 uint32_t type;
42 uint64_t config;
43 const char *name;
44 };
45
46 static constexpr std::array<measurement, 10> measurements{
47 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, "instructions"},
48 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, "cpu_cycles"},
49 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, "ref_cpu_cycles"},
50 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, "cache_references"},
51 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, "cache_misses"},
52 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "branch_instructions"},
53 measurement{PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES, "branch_misses"},
54 measurement{PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, "cpu_clock"},
55 measurement{PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS, "page_faults"},
56 measurement{PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS, "cpu_migrations"},
57 };
58
60 std::string out;
62 std::string name;
64 std::array<int, measurements.size()> fds;
65
66 // Wrapper for the system call
67 static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
68 int cpu, int group_fd, unsigned long flags)
69 {
70 return syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
71 }
72
73 // Wrapper for the system call
74 static int open_perf_counter(uint32_t type, uint64_t config)
75 {
76 struct perf_event_attr pe;
77 memset(&pe, 0, sizeof(struct perf_event_attr));
78 pe.type = type;
79 pe.size = sizeof(struct perf_event_attr);
80 pe.config = config;
81 pe.disabled = 1;
82 pe.exclude_kernel = 1;
83 pe.exclude_hv = 1;
84
85 int fd = perf_event_open(&pe, 0, -1, -1, 0);
86 return fd;
87 }
88
89 public:
91 PerformanceContext(std::string name) : name(name)
92 {
93 if (auto out = getenv("APT_PERFORMANCE_LOG"))
94 this->out = out;
95 if (likely(out.empty()))
96 return;
97 for (size_t i = 0; i < measurements.size(); ++i)
98 fds[i] = open_perf_counter(measurements[i].type, measurements[i].config);
99 for (auto fd : fds)
100 must_succeed(fd == -1 || ioctl(fd, PERF_EVENT_IOC_RESET, 0) != -1);
101 for (auto fd : fds)
102 must_succeed(fd == -1 || ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) != -1);
103 }
105 ~PerformanceContext()
106 {
107 if (likely(out.empty()))
108 return;
109 for (auto fd : fds)
110 must_succeed(fd == -1 || ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) != -1);
111
112 std::array<long long, measurements.size()> values;
113 for (size_t i = 0; i < measurements.size(); ++i)
114 must_succeed(fds[i] == -1 || read(fds[i], &values[i], sizeof(values[i])) == sizeof(values[i]));
115 for (auto fd : fds)
116 must_succeed(fd == -1 || close(fd) == 0);
117
118 std::stringstream ss;
119 ss.imbue(std::locale::classic());
120 ss << "{\"context\": " << '"' << name << '"';
121 for (size_t i = 0; i < measurements.size(); ++i)
122 {
123 ss << ", ";
124 ss << '"' << measurements[i].name << '"' << ": " << values[i];
125 }
126
127 ss << "}\n";
128
129 std::string entry = ss.str();
130
131 // Atomically append a line to the JSONL file, allowing all users to read it
132 int fd = open(out.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
133 must_succeed(fd != -1);
134 must_succeed(flock(fd, LOCK_EX) == 0);
135 must_succeed(write(fd, entry.c_str(), entry.size()) == static_cast<ssize_t>(entry.size()));
136 must_succeed(flock(fd, LOCK_UN) == 0);
137 must_succeed(close(fd) == 0);
138 }
139};
140
141} // namespace APT
142
143#else
144namespace APT
145{
146 struct PerformanceContext
147{
148 PerformanceContext(const char *) {};
149 ~PerformanceContext() {};
150};
151} // namespace APT
152#endif
153#endif
APT::PerformanceContext
Definition perf.h:147