1a79a051eSAli Mashtizadeh /*-
2a79a051eSAli Mashtizadeh * SPDX-License-Identifier: BSD-2-Clause
3a79a051eSAli Mashtizadeh *
4a79a051eSAli Mashtizadeh * Copyright (c) 2026, Netflix, Inc.
5a79a051eSAli Mashtizadeh *
6a79a051eSAli Mashtizadeh * This software was developed by Ali Mashtizadeh under the sponsorship from
7a79a051eSAli Mashtizadeh * Netflix, Inc.
8a79a051eSAli Mashtizadeh *
9a79a051eSAli Mashtizadeh * Redistribution and use in source and binary forms, with or without
10a79a051eSAli Mashtizadeh * modification, are permitted provided that the following conditions
11a79a051eSAli Mashtizadeh * are met:
12a79a051eSAli Mashtizadeh * 1. Redistributions of source code must retain the above copyright
13a79a051eSAli Mashtizadeh * notice, this list of conditions and the following disclaimer.
14a79a051eSAli Mashtizadeh * 2. Redistributions in binary form must reproduce the above copyright
15a79a051eSAli Mashtizadeh * notice, this list of conditions and the following disclaimer in the
16a79a051eSAli Mashtizadeh * documentation and/or other materials provided with the distribution.
17a79a051eSAli Mashtizadeh *
18a79a051eSAli Mashtizadeh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19a79a051eSAli Mashtizadeh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20a79a051eSAli Mashtizadeh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21a79a051eSAli Mashtizadeh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22a79a051eSAli Mashtizadeh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23a79a051eSAli Mashtizadeh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24a79a051eSAli Mashtizadeh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25a79a051eSAli Mashtizadeh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26a79a051eSAli Mashtizadeh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27a79a051eSAli Mashtizadeh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28a79a051eSAli Mashtizadeh * SUCH DAMAGE.
29a79a051eSAli Mashtizadeh *
30a79a051eSAli Mashtizadeh */
31a79a051eSAli Mashtizadeh
32a79a051eSAli Mashtizadeh #include <sys/param.h>
33a79a051eSAli Mashtizadeh #include <sys/cdefs.h>
34a79a051eSAli Mashtizadeh #include <sys/cpuset.h>
35a79a051eSAli Mashtizadeh #include <sys/event.h>
36a79a051eSAli Mashtizadeh #include <sys/queue.h>
37a79a051eSAli Mashtizadeh #include <sys/socket.h>
38a79a051eSAli Mashtizadeh #include <sys/stat.h>
39a79a051eSAli Mashtizadeh #include <sys/sysctl.h>
40a79a051eSAli Mashtizadeh #include <sys/time.h>
41a79a051eSAli Mashtizadeh #include <sys/ttycom.h>
42a79a051eSAli Mashtizadeh #include <sys/user.h>
43a79a051eSAli Mashtizadeh #include <sys/wait.h>
44a79a051eSAli Mashtizadeh
45a79a051eSAli Mashtizadeh #include <assert.h>
46a79a051eSAli Mashtizadeh #include <curses.h>
47a79a051eSAli Mashtizadeh #include <err.h>
48a79a051eSAli Mashtizadeh #include <errno.h>
49a79a051eSAli Mashtizadeh #include <fcntl.h>
50a79a051eSAli Mashtizadeh #include <getopt.h>
51a79a051eSAli Mashtizadeh #include <kvm.h>
52a79a051eSAli Mashtizadeh #include <libgen.h>
53a79a051eSAli Mashtizadeh #include <limits.h>
54a79a051eSAli Mashtizadeh #include <locale.h>
55a79a051eSAli Mashtizadeh #include <math.h>
56a79a051eSAli Mashtizadeh #include <pmc.h>
57a79a051eSAli Mashtizadeh #include <pmclog.h>
58a79a051eSAli Mashtizadeh #include <regex.h>
59a79a051eSAli Mashtizadeh #include <signal.h>
60a79a051eSAli Mashtizadeh #include <stdalign.h>
61a79a051eSAli Mashtizadeh #include <stdarg.h>
62a79a051eSAli Mashtizadeh #include <stdint.h>
63a79a051eSAli Mashtizadeh #include <stdio.h>
64a79a051eSAli Mashtizadeh #include <stdlib.h>
65a79a051eSAli Mashtizadeh #include <stddef.h>
66a79a051eSAli Mashtizadeh #include <string.h>
67a79a051eSAli Mashtizadeh #include <sysexits.h>
68a79a051eSAli Mashtizadeh #include <unistd.h>
69a79a051eSAli Mashtizadeh
70a79a051eSAli Mashtizadeh #include <machine/cpufunc.h>
71a79a051eSAli Mashtizadeh
72a79a051eSAli Mashtizadeh #include <libpmcstat.h>
73a79a051eSAli Mashtizadeh #include "cmd_pmc.h"
74a79a051eSAli Mashtizadeh
75a79a051eSAli Mashtizadeh #include <iostream>
76a79a051eSAli Mashtizadeh #include <map>
77a79a051eSAli Mashtizadeh #include <string>
78a79a051eSAli Mashtizadeh #include <vector>
79a79a051eSAli Mashtizadeh
80a79a051eSAli Mashtizadeh #include "display.hh"
81a79a051eSAli Mashtizadeh #include "headers.hh"
82a79a051eSAli Mashtizadeh
83a79a051eSAli Mashtizadeh #define DEFAULT_RATE 65536
84a79a051eSAli Mashtizadeh
85a79a051eSAli Mashtizadeh class pmc_config
86a79a051eSAli Mashtizadeh {
87a79a051eSAli Mashtizadeh public:
88a79a051eSAli Mashtizadeh std::string event;
89a79a051eSAli Mashtizadeh uint64_t count;
90a79a051eSAli Mashtizadeh cpuset_t cpumask;
91a79a051eSAli Mashtizadeh uint32_t caps;
pmc_config()92a79a051eSAli Mashtizadeh pmc_config() : event(), count(0), ids()
93a79a051eSAli Mashtizadeh {
94a79a051eSAli Mashtizadeh CPU_ZERO(&cpumask);
95a79a051eSAli Mashtizadeh }
pmc_config(const std::string & _event,uint64_t _count,cpuset_t _cpumask)96*78bbe9fcSJohn Baldwin pmc_config(const std::string &_event, uint64_t _count,
97*78bbe9fcSJohn Baldwin cpuset_t _cpumask)
98*78bbe9fcSJohn Baldwin : event(_event), count(_count), ids()
99a79a051eSAli Mashtizadeh {
100*78bbe9fcSJohn Baldwin CPU_COPY(&_cpumask, &cpumask);
101a79a051eSAli Mashtizadeh }
102a79a051eSAli Mashtizadeh /*
103a79a051eSAli Mashtizadeh * Read the capabilities and fills in the cpumask for a given event
104a79a051eSAli Mashtizadeh * counter. The way that the hwpmc module currently works we do not
105a79a051eSAli Mashtizadeh * know which pmc class will back our counter until we look it up. The
106a79a051eSAli Mashtizadeh * easiest way is to program the counter into the first CPU present in
107a79a051eSAli Mashtizadeh * the mask and then retrieve the capabilities.
108a79a051eSAli Mashtizadeh */
getcaps()109a79a051eSAli Mashtizadeh void getcaps() {
110a79a051eSAli Mashtizadeh int status;
111a79a051eSAli Mashtizadeh int testcpu;
112a79a051eSAli Mashtizadeh pmc_id_t id;
113a79a051eSAli Mashtizadeh
114a79a051eSAli Mashtizadeh testcpu = CPU_FFS(&cpumask) - 1;
115a79a051eSAli Mashtizadeh
116a79a051eSAli Mashtizadeh status = pmc_allocate(event.c_str(), PMC_MODE_SS, PMC_F_CALLCHAIN,
117a79a051eSAli Mashtizadeh testcpu, &id, count);
118a79a051eSAli Mashtizadeh if (status < 0)
119a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot allocate event '%s'", event.c_str());
120a79a051eSAli Mashtizadeh
121a79a051eSAli Mashtizadeh status = pmc_capabilities(id, &caps);
122a79a051eSAli Mashtizadeh if (status < 0)
123a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot get pmc capabilities");
124a79a051eSAli Mashtizadeh
125a79a051eSAli Mashtizadeh pmc_release(id);
126a79a051eSAli Mashtizadeh
127a79a051eSAli Mashtizadeh if (caps & PMC_CAP_SYSWIDE) {
128a79a051eSAli Mashtizadeh CPU_ZERO(&cpumask);
129a79a051eSAli Mashtizadeh CPU_SET(0, &cpumask);
130a79a051eSAli Mashtizadeh }
131a79a051eSAli Mashtizadeh if (caps & PMC_CAP_DOMWIDE) {
132a79a051eSAli Mashtizadeh int domains;
133a79a051eSAli Mashtizadeh size_t len;
134a79a051eSAli Mashtizadeh
135a79a051eSAli Mashtizadeh CPU_ZERO(&cpumask);
136a79a051eSAli Mashtizadeh
137a79a051eSAli Mashtizadeh len = sizeof(domains);
138a79a051eSAli Mashtizadeh if (sysctlbyname("vm.ndomains", &domains, &len, NULL, 0) == -1)
139a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot get number of domains");
140a79a051eSAli Mashtizadeh
141a79a051eSAli Mashtizadeh for (int i = 1; i < domains; i++) {
142a79a051eSAli Mashtizadeh cpuset_t dmask;
143a79a051eSAli Mashtizadeh
144a79a051eSAli Mashtizadeh CPU_ZERO(&dmask);
145a79a051eSAli Mashtizadeh status = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_DOMAIN, i,
146a79a051eSAli Mashtizadeh sizeof(dmask), &dmask);
147a79a051eSAli Mashtizadeh if (status < 0)
148a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot get domain mask");
149a79a051eSAli Mashtizadeh CPU_SET(CPU_FFS(&dmask) - 1, &cpumask);
150a79a051eSAli Mashtizadeh }
151a79a051eSAli Mashtizadeh }
152a79a051eSAli Mashtizadeh }
153a79a051eSAli Mashtizadeh /*
154a79a051eSAli Mashtizadeh * Allocate the PMC across all CPUs.
155a79a051eSAli Mashtizadeh */
allocate()156a79a051eSAli Mashtizadeh void allocate() {
157a79a051eSAli Mashtizadeh int cpu;
158a79a051eSAli Mashtizadeh int status;
159a79a051eSAli Mashtizadeh pmc_id_t id;
160a79a051eSAli Mashtizadeh
161a79a051eSAli Mashtizadeh for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
162a79a051eSAli Mashtizadeh if (!CPU_ISSET(cpu, &cpumask))
163a79a051eSAli Mashtizadeh continue;
164a79a051eSAli Mashtizadeh
165a79a051eSAli Mashtizadeh status = pmc_allocate(event.c_str(), PMC_MODE_SS, PMC_F_CALLCHAIN,
166a79a051eSAli Mashtizadeh cpu, &id, count);
167a79a051eSAli Mashtizadeh if (status < 0)
168a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot allocate event '%s'",
169a79a051eSAli Mashtizadeh event.c_str());
170a79a051eSAli Mashtizadeh
171a79a051eSAli Mashtizadeh status = pmc_set(id, count);
172a79a051eSAli Mashtizadeh if (status < 0)
173a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot set sampling count for event '%s'",
174a79a051eSAli Mashtizadeh event.c_str());
175a79a051eSAli Mashtizadeh
176a79a051eSAli Mashtizadeh ids.push_back(id);
177a79a051eSAli Mashtizadeh }
178a79a051eSAli Mashtizadeh }
179a79a051eSAli Mashtizadeh /*
180a79a051eSAli Mashtizadeh * Release all PMC instances (one per CPU).
181a79a051eSAli Mashtizadeh */
release()182a79a051eSAli Mashtizadeh void release() {
183a79a051eSAli Mashtizadeh for (auto i : ids) {
184a79a051eSAli Mashtizadeh if (pmc_release(i) < 0) {
185a79a051eSAli Mashtizadeh perror("pmc_start");
186a79a051eSAli Mashtizadeh }
187a79a051eSAli Mashtizadeh }
188a79a051eSAli Mashtizadeh }
189a79a051eSAli Mashtizadeh /*
190a79a051eSAli Mashtizadeh * Start all PMC instances (one per CPU).
191a79a051eSAli Mashtizadeh */
start()192a79a051eSAli Mashtizadeh void start() {
193a79a051eSAli Mashtizadeh for (auto i : ids) {
194a79a051eSAli Mashtizadeh if (pmc_start(i) < 0) {
195a79a051eSAli Mashtizadeh perror("pmc_start");
196a79a051eSAli Mashtizadeh }
197a79a051eSAli Mashtizadeh }
198a79a051eSAli Mashtizadeh }
199a79a051eSAli Mashtizadeh /*
200a79a051eSAli Mashtizadeh * Stop all PMC instances (one per CPU).
201a79a051eSAli Mashtizadeh */
stop()202a79a051eSAli Mashtizadeh void stop() {
203a79a051eSAli Mashtizadeh for (auto i : ids) {
204a79a051eSAli Mashtizadeh if (pmc_stop(i) < 0) {
205a79a051eSAli Mashtizadeh perror("pmc_start");
206a79a051eSAli Mashtizadeh }
207a79a051eSAli Mashtizadeh }
208a79a051eSAli Mashtizadeh }
209a79a051eSAli Mashtizadeh private:
210a79a051eSAli Mashtizadeh std::vector<pmc_id_t> ids;
211a79a051eSAli Mashtizadeh };
212a79a051eSAli Mashtizadeh
213a79a051eSAli Mashtizadeh static std::vector<pmc_config> pmcs = std::vector<pmc_config>();
214a79a051eSAli Mashtizadeh static struct option longopts[] = {
215a79a051eSAli Mashtizadeh { "rate", required_argument, NULL, 'n' },
216a79a051eSAli Mashtizadeh { "counter", required_argument, NULL, 'c' },
217a79a051eSAli Mashtizadeh { "time", required_argument, NULL, 't' },
218a79a051eSAli Mashtizadeh { "study", required_argument, NULL, 's' },
219a79a051eSAli Mashtizadeh { NULL, 0, NULL, 0 }
220a79a051eSAli Mashtizadeh };
221a79a051eSAli Mashtizadeh static struct timespec start;
222a79a051eSAli Mashtizadeh static int timelimit = 0;
223a79a051eSAli Mashtizadeh
224a79a051eSAli Mashtizadeh static void
usage(void)225a79a051eSAli Mashtizadeh usage(void)
226a79a051eSAli Mashtizadeh {
227a79a051eSAli Mashtizadeh printf("Usage: pmc record [options] [output.pmc]\n\n");
228a79a051eSAli Mashtizadeh printf("Record a study\n\n");
229a79a051eSAli Mashtizadeh printf("Options:\n");
230a79a051eSAli Mashtizadeh printf("\t-c Sampling counter\n");
231a79a051eSAli Mashtizadeh printf("\t-r Sample rate (default: %d)\n", DEFAULT_RATE);
232a79a051eSAli Mashtizadeh printf("\t-s Specify a study\n");
233a79a051eSAli Mashtizadeh printf("\t-t Run the study for specified amount of time\n");
234a79a051eSAli Mashtizadeh printf("\nStudies:\n");
235a79a051eSAli Mashtizadeh #if defined(__i386__) || defined(__amd64__)
236a79a051eSAli Mashtizadeh printf("\tbranches Study branch misprediction (Requires AMD IBS)\n");
237a79a051eSAli Mashtizadeh printf("\tc2c Study cache to cache communications (Requires AMD IBS)\n");
238a79a051eSAli Mashtizadeh #endif
239a79a051eSAli Mashtizadeh printf("\tdefault Instruction sampling for general analysis\n");
240a79a051eSAli Mashtizadeh printf("\tflamegraph Instruction sampling for general analysis\n");
241a79a051eSAli Mashtizadeh #if defined(__i386__) || defined(__amd64__)
242a79a051eSAli Mashtizadeh printf("\tfrontend Study frontend stalls (Requires AMD IBS)\n");
243a79a051eSAli Mashtizadeh printf("\tmemory Study memory operations (Requires AMD IBS)\n");
244a79a051eSAli Mashtizadeh #endif
245a79a051eSAli Mashtizadeh }
246a79a051eSAli Mashtizadeh
247a79a051eSAli Mashtizadeh int
writelog(int logfd,const void * buf,size_t len)248a79a051eSAli Mashtizadeh writelog(int logfd, const void *buf, size_t len)
249a79a051eSAli Mashtizadeh {
250a79a051eSAli Mashtizadeh int status;
251a79a051eSAli Mashtizadeh const char *cur = (const char *)buf;
252a79a051eSAli Mashtizadeh
253a79a051eSAli Mashtizadeh while (len != 0) {
254a79a051eSAli Mashtizadeh status = write(logfd, cur, len);
255a79a051eSAli Mashtizadeh if (status < 0) {
256a79a051eSAli Mashtizadeh if (errno == EINTR || errno == EAGAIN)
257a79a051eSAli Mashtizadeh continue;
258a79a051eSAli Mashtizadeh else
259a79a051eSAli Mashtizadeh return status;
260a79a051eSAli Mashtizadeh }
261a79a051eSAli Mashtizadeh if (status == 0)
262a79a051eSAli Mashtizadeh return status;
263a79a051eSAli Mashtizadeh
264a79a051eSAli Mashtizadeh cur += status;
265a79a051eSAli Mashtizadeh len -= status;
266a79a051eSAli Mashtizadeh }
267a79a051eSAli Mashtizadeh
268a79a051eSAli Mashtizadeh return 0;
269a79a051eSAli Mashtizadeh }
270a79a051eSAli Mashtizadeh
271a79a051eSAli Mashtizadeh /*
272a79a051eSAli Mashtizadeh * Write the PMC header. Each of the header payloads have their own header
273a79a051eSAli Mashtizadeh * containing the payloads size. Thus the pmc tools can just skip over regions
274a79a051eSAli Mashtizadeh * that they do not know how to handle.
275a79a051eSAli Mashtizadeh */
276a79a051eSAli Mashtizadeh int
write_header(int logfd)277a79a051eSAli Mashtizadeh write_header(int logfd)
278a79a051eSAli Mashtizadeh {
279a79a051eSAli Mashtizadeh int status;
280a79a051eSAli Mashtizadeh pmchdr_header hdr;
281a79a051eSAli Mashtizadeh
282a79a051eSAli Mashtizadeh hdr.magic = PMC_HEADER_MAGIC;
283a79a051eSAli Mashtizadeh hdr.version = PMC_HEADER_VERSION;
284a79a051eSAli Mashtizadeh #if defined(__amd64__)
285a79a051eSAli Mashtizadeh hdr.arch = PMC_ARCH_AMD64;
286a79a051eSAli Mashtizadeh #elif defined(__aarch64__)
287a79a051eSAli Mashtizadeh hdr.arch = PMC_ARCH_ARM64;
288a79a051eSAli Mashtizadeh #elif defined(__powerpc64__)
289a79a051eSAli Mashtizadeh hdr.arch = PMC_ARCH_PPC64;
290a79a051eSAli Mashtizadeh #elif defined(__riscv)
291a79a051eSAli Mashtizadeh hdr.arch = PMC_ARCH_RISCV64;
292a79a051eSAli Mashtizadeh #elif defined(__arm__)
293a79a051eSAli Mashtizadeh hdr.arch = PMC_ARCH_ARM;
294a79a051eSAli Mashtizadeh #else
295a79a051eSAli Mashtizadeh hdr.arch = 0;
296a79a051eSAli Mashtizadeh #endif
297a79a051eSAli Mashtizadeh
298a79a051eSAli Mashtizadeh status = writelog(logfd, &hdr, sizeof(hdr));
299a79a051eSAli Mashtizadeh if (status < 0) {
300a79a051eSAli Mashtizadeh perror("writelog");
301a79a051eSAli Mashtizadeh }
302a79a051eSAli Mashtizadeh
303a79a051eSAli Mashtizadeh return status;
304a79a051eSAli Mashtizadeh }
305a79a051eSAli Mashtizadeh
306a79a051eSAli Mashtizadeh /*
307a79a051eSAli Mashtizadeh * Write out the sysinfo header.
308a79a051eSAli Mashtizadeh */
309a79a051eSAli Mashtizadeh int
write_sysinfo(int logfd)310a79a051eSAli Mashtizadeh write_sysinfo(int logfd)
311a79a051eSAli Mashtizadeh {
312a79a051eSAli Mashtizadeh int status;
313a79a051eSAli Mashtizadeh pmchdr_infohdr hdr;
314a79a051eSAli Mashtizadeh pmchdr_sysinfo sys;
315a79a051eSAli Mashtizadeh char val[64];
316a79a051eSAli Mashtizadeh size_t valsz;
317a79a051eSAli Mashtizadeh
318a79a051eSAli Mashtizadeh valsz = sizeof(val);
319a79a051eSAli Mashtizadeh status = sysctlbyname("hw.model", &val, &valsz, NULL, 0);
320a79a051eSAli Mashtizadeh if (status < 0) {
321a79a051eSAli Mashtizadeh perror("sysctlbyname");
322a79a051eSAli Mashtizadeh return status;
323a79a051eSAli Mashtizadeh }
324a79a051eSAli Mashtizadeh strlcpy(sys.cpumodel, val, sizeof(sys.cpumodel));
325a79a051eSAli Mashtizadeh
326a79a051eSAli Mashtizadeh valsz = sizeof(val);
327a79a051eSAli Mashtizadeh status = sysctlbyname("kern.osrelease", &val, &valsz, NULL, 0);
328a79a051eSAli Mashtizadeh if (status < 0) {
329a79a051eSAli Mashtizadeh perror("sysctlbyname");
330a79a051eSAli Mashtizadeh return status;
331a79a051eSAli Mashtizadeh }
332a79a051eSAli Mashtizadeh strlcpy(sys.osrelease, val, sizeof(sys.osrelease));
333a79a051eSAli Mashtizadeh
334a79a051eSAli Mashtizadeh valsz = sizeof(val);
335a79a051eSAli Mashtizadeh status = sysctlbyname("kern.build_id", &val, &valsz, NULL, 0);
336a79a051eSAli Mashtizadeh if (status < 0) {
337a79a051eSAli Mashtizadeh perror("sysctlbyname");
338a79a051eSAli Mashtizadeh return status;
339a79a051eSAli Mashtizadeh }
340a79a051eSAli Mashtizadeh strlcpy(sys.buildid, val, sizeof(sys.buildid));
341a79a051eSAli Mashtizadeh
342a79a051eSAli Mashtizadeh hdr.type = INFOHDR_TYPE_SYSINFO;
343a79a051eSAli Mashtizadeh hdr.length = sizeof(sys);
344a79a051eSAli Mashtizadeh status = writelog(logfd, &hdr, sizeof(hdr));
345a79a051eSAli Mashtizadeh if (status < 0) {
346a79a051eSAli Mashtizadeh err(EX_IOERR, "writelog");
347a79a051eSAli Mashtizadeh }
348a79a051eSAli Mashtizadeh
349a79a051eSAli Mashtizadeh status = writelog(logfd, &sys, sizeof(sys));
350a79a051eSAli Mashtizadeh if (status < 0) {
351a79a051eSAli Mashtizadeh err(EX_IOERR, "writelog");
352a79a051eSAli Mashtizadeh }
353a79a051eSAli Mashtizadeh
354a79a051eSAli Mashtizadeh return status;
355a79a051eSAli Mashtizadeh };
356a79a051eSAli Mashtizadeh
357a79a051eSAli Mashtizadeh #if defined(__i386__) || defined(__amd64__)
358a79a051eSAli Mashtizadeh #define CPUID_ROOT_BASE 0x00000000
359a79a051eSAli Mashtizadeh #define CPUID_ROOT_VM 0x40000000
360a79a051eSAli Mashtizadeh #define CPUID_ROOT_EXT 0x80000000
361a79a051eSAli Mashtizadeh
362a79a051eSAli Mashtizadeh /*
363a79a051eSAli Mashtizadeh * Write out the CPU Info block. After the standard header that declares the
364a79a051eSAli Mashtizadeh * size of the payload, we write out all the CPUID root and leafs for each of
365a79a051eSAli Mashtizadeh * the three major roots: base, VM, and extended artribute space. On all
366a79a051eSAli Mashtizadeh * modern processors the root contains the maximum leaf as the first value
367a79a051eSAli Mashtizadeh * allowing us to decode how many leafs there are.
368a79a051eSAli Mashtizadeh */
369a79a051eSAli Mashtizadeh int
write_cpuinfo(int logfd)370a79a051eSAli Mashtizadeh write_cpuinfo(int logfd)
371a79a051eSAli Mashtizadeh {
372a79a051eSAli Mashtizadeh int status;
373a79a051eSAli Mashtizadeh u_int tmp[4];
374a79a051eSAli Mashtizadeh uint32_t base_max, vm_max, ext_max;
375a79a051eSAli Mashtizadeh uint32_t len;
376a79a051eSAli Mashtizadeh pmchdr_infohdr *hdr;
377a79a051eSAli Mashtizadeh uint32_t *buf, *off;
378a79a051eSAli Mashtizadeh
379a79a051eSAli Mashtizadeh /*
380a79a051eSAli Mashtizadeh * Find the length of the main, VM, and extended CPUID leafs
381a79a051eSAli Mashtizadeh */
382a79a051eSAli Mashtizadeh do_cpuid(CPUID_ROOT_BASE, tmp);
383a79a051eSAli Mashtizadeh base_max = tmp[0];
384a79a051eSAli Mashtizadeh len = base_max + 1;
385a79a051eSAli Mashtizadeh
386a79a051eSAli Mashtizadeh do_cpuid(CPUID_ROOT_VM, tmp);
387a79a051eSAli Mashtizadeh vm_max = tmp[0];
388a79a051eSAli Mashtizadeh if (vm_max != 0)
389a79a051eSAli Mashtizadeh len += vm_max - CPUID_ROOT_VM + 1;
390a79a051eSAli Mashtizadeh
391a79a051eSAli Mashtizadeh do_cpuid(CPUID_ROOT_EXT, tmp);
392a79a051eSAli Mashtizadeh ext_max = tmp[0];
393a79a051eSAli Mashtizadeh if (ext_max != 0)
394a79a051eSAli Mashtizadeh len += ext_max - CPUID_ROOT_EXT + 1;
395a79a051eSAli Mashtizadeh
396a79a051eSAli Mashtizadeh // 4 Registers per Leaf x 4 Bytes per Register
397a79a051eSAli Mashtizadeh len *= 4;
398a79a051eSAli Mashtizadeh
399a79a051eSAli Mashtizadeh buf = (uint32_t *)new uint32_t[len + sizeof(pmchdr_infohdr) / 4];
400a79a051eSAli Mashtizadeh hdr = (pmchdr_infohdr *)buf;
401a79a051eSAli Mashtizadeh hdr->type = INFOHDR_TYPE_CPUID;
402a79a051eSAli Mashtizadeh hdr->length = 4 * len;
403a79a051eSAli Mashtizadeh off = (uint32_t *)(hdr + 1);
404a79a051eSAli Mashtizadeh
405a79a051eSAli Mashtizadeh for (uint32_t i = 0; i <= base_max; i++) {
406a79a051eSAli Mashtizadeh do_cpuid(i, off);
407a79a051eSAli Mashtizadeh off += 4;
408a79a051eSAli Mashtizadeh }
409a79a051eSAli Mashtizadeh
410a79a051eSAli Mashtizadeh if (vm_max) {
411a79a051eSAli Mashtizadeh for (uint32_t i = CPUID_ROOT_VM; i <= vm_max; i++) {
412a79a051eSAli Mashtizadeh do_cpuid(i, off);
413a79a051eSAli Mashtizadeh off += 4;
414a79a051eSAli Mashtizadeh }
415a79a051eSAli Mashtizadeh }
416a79a051eSAli Mashtizadeh if (ext_max) {
417a79a051eSAli Mashtizadeh for (uint32_t i = CPUID_ROOT_EXT; i <= ext_max; i++) {
418a79a051eSAli Mashtizadeh do_cpuid(i, off);
419a79a051eSAli Mashtizadeh off += 4;
420a79a051eSAli Mashtizadeh }
421a79a051eSAli Mashtizadeh }
422a79a051eSAli Mashtizadeh
423a79a051eSAli Mashtizadeh status = writelog(logfd, buf, 4 * len + sizeof(*hdr));
424a79a051eSAli Mashtizadeh if (status < 0) {
425a79a051eSAli Mashtizadeh delete[] buf;
426a79a051eSAli Mashtizadeh err(EX_IOERR, "writelog");
427a79a051eSAli Mashtizadeh }
428a79a051eSAli Mashtizadeh
429a79a051eSAli Mashtizadeh delete[] buf;
430a79a051eSAli Mashtizadeh
431a79a051eSAli Mashtizadeh return 0;
432a79a051eSAli Mashtizadeh }
433a79a051eSAli Mashtizadeh #elif defined(__aarch64__)
434a79a051eSAli Mashtizadeh int
write_cpuinfo(__unused int logfd)435a79a051eSAli Mashtizadeh write_cpuinfo(__unused int logfd)
436a79a051eSAli Mashtizadeh {
437a79a051eSAli Mashtizadeh return 0;
438a79a051eSAli Mashtizadeh }
439a79a051eSAli Mashtizadeh #elif defined(__powerpc64__)
440a79a051eSAli Mashtizadeh int
write_cpuinfo(__unused int logfd)441a79a051eSAli Mashtizadeh write_cpuinfo(__unused int logfd)
442a79a051eSAli Mashtizadeh {
443a79a051eSAli Mashtizadeh return 0;
444a79a051eSAli Mashtizadeh }
445a79a051eSAli Mashtizadeh #else
446a79a051eSAli Mashtizadeh int
write_cpuinfo(__unused int logfd)447a79a051eSAli Mashtizadeh write_cpuinfo(__unused int logfd)
448a79a051eSAli Mashtizadeh {
449a79a051eSAli Mashtizadeh return 0;
450a79a051eSAli Mashtizadeh }
451a79a051eSAli Mashtizadeh #endif
452a79a051eSAli Mashtizadeh
453a79a051eSAli Mashtizadeh int
write_footer(int logfd)454a79a051eSAli Mashtizadeh write_footer(int logfd)
455a79a051eSAli Mashtizadeh {
456a79a051eSAli Mashtizadeh int status;
457a79a051eSAli Mashtizadeh pmchdr_infohdr hdr;
458a79a051eSAli Mashtizadeh
459a79a051eSAli Mashtizadeh hdr.type = INFOHDR_TYPE_DONE;
460a79a051eSAli Mashtizadeh hdr.length = 0;
461a79a051eSAli Mashtizadeh
462a79a051eSAli Mashtizadeh status = writelog(logfd, &hdr, sizeof(hdr));
463a79a051eSAli Mashtizadeh if (status < 0) {
464a79a051eSAli Mashtizadeh perror("writelog");
465a79a051eSAli Mashtizadeh }
466a79a051eSAli Mashtizadeh
467a79a051eSAli Mashtizadeh return status;
468a79a051eSAli Mashtizadeh }
469a79a051eSAli Mashtizadeh
470a79a051eSAli Mashtizadeh #if defined(__i386__) || defined(__amd64__)
471a79a051eSAli Mashtizadeh int
setup_study(const std::string & study,uint64_t rate,cpuset_t mask)472a79a051eSAli Mashtizadeh setup_study(const std::string &study, uint64_t rate, cpuset_t mask)
473a79a051eSAli Mashtizadeh {
474a79a051eSAli Mashtizadeh u_int tmp[4];
475a79a051eSAli Mashtizadeh alignas(4) char vendor[16];
476a79a051eSAli Mashtizadeh std::string event;
477a79a051eSAli Mashtizadeh uint32_t ext_max;
478a79a051eSAli Mashtizadeh uint32_t ibs_features;
479a79a051eSAli Mashtizadeh bool is_amd, is_intel;
480a79a051eSAli Mashtizadeh
481a79a051eSAli Mashtizadeh do_cpuid(CPUID_ROOT_BASE, tmp);
482a79a051eSAli Mashtizadeh
483a79a051eSAli Mashtizadeh /* Find the brand */
484a79a051eSAli Mashtizadeh is_intel = false;
485a79a051eSAli Mashtizadeh is_amd = false;
486a79a051eSAli Mashtizadeh
487a79a051eSAli Mashtizadeh /* i386 complains without explicit alignment */
488a79a051eSAli Mashtizadeh ((u_int *)&vendor)[0] = tmp[1];
489a79a051eSAli Mashtizadeh ((u_int *)&vendor)[1] = tmp[3];
490a79a051eSAli Mashtizadeh ((u_int *)&vendor)[2] = tmp[2];
491a79a051eSAli Mashtizadeh vendor[12] = 0;
492a79a051eSAli Mashtizadeh if (strncmp(vendor, INTEL_VENDOR_ID, 12) == 0)
493a79a051eSAli Mashtizadeh is_intel = true;
494a79a051eSAli Mashtizadeh if (strncmp(vendor, AMD_VENDOR_ID, 12) == 0)
495a79a051eSAli Mashtizadeh is_amd = true;
496a79a051eSAli Mashtizadeh if (strncmp(vendor, HYGON_VENDOR_ID, 12) == 0)
497a79a051eSAli Mashtizadeh is_amd = true;
498a79a051eSAli Mashtizadeh
499a79a051eSAli Mashtizadeh ibs_features = 0;
500a79a051eSAli Mashtizadeh do_cpuid(CPUID_ROOT_EXT, tmp);
501a79a051eSAli Mashtizadeh ext_max = tmp[0];
502a79a051eSAli Mashtizadeh if (is_amd && ext_max >= CPUID_IBSID) {
503a79a051eSAli Mashtizadeh do_cpuid(CPUID_IBSID, tmp);
504a79a051eSAli Mashtizadeh ibs_features = tmp[0];
505a79a051eSAli Mashtizadeh }
506a79a051eSAli Mashtizadeh
507a79a051eSAli Mashtizadeh if (study == "default" || study == "flamegraph") {
508a79a051eSAli Mashtizadeh pmcs.push_back(pmc_config("unhalted-cycles", rate, mask));
509a79a051eSAli Mashtizadeh return 0;
510a79a051eSAli Mashtizadeh }
511a79a051eSAli Mashtizadeh
512a79a051eSAli Mashtizadeh if (is_intel) {
513a79a051eSAli Mashtizadeh err(EX_SOFTWARE, "ERROR: Study is unsupported on Intel");
514a79a051eSAli Mashtizadeh }
515a79a051eSAli Mashtizadeh
516a79a051eSAli Mashtizadeh if (study == "frontend") {
517a79a051eSAli Mashtizadeh event = "ibs-fetch,randomize";
518a79a051eSAli Mashtizadeh } else if (study == "branches" || study == "memory") {
519a79a051eSAli Mashtizadeh event = "ibs-op";
520a79a051eSAli Mashtizadeh if (ibs_features & CPUID_IBSID_OPCNT)
521a79a051eSAli Mashtizadeh event += ",opcount";
522a79a051eSAli Mashtizadeh } else if (study == "c2c") {
523a79a051eSAli Mashtizadeh event = "ibs-op";
524a79a051eSAli Mashtizadeh if (ibs_features & CPUID_IBSID_IBSLOADLATENCYFILT)
525a79a051eSAli Mashtizadeh event += ",l3miss";
526a79a051eSAli Mashtizadeh if (ibs_features & CPUID_IBSID_OPCNT)
527a79a051eSAli Mashtizadeh event += ",opcount";
528a79a051eSAli Mashtizadeh } else {
529a79a051eSAli Mashtizadeh err(EX_USAGE, "ERROR: Study '%s' unknown", study.c_str());
530a79a051eSAli Mashtizadeh }
531a79a051eSAli Mashtizadeh
532a79a051eSAli Mashtizadeh pmcs.push_back(pmc_config(event, rate, mask));
533a79a051eSAli Mashtizadeh
534a79a051eSAli Mashtizadeh return 0;
535a79a051eSAli Mashtizadeh }
536a79a051eSAli Mashtizadeh #else
537a79a051eSAli Mashtizadeh int
setup_study(const std::string & study,uint64_t rate,cpuset_t mask)538a79a051eSAli Mashtizadeh setup_study(const std::string &study, uint64_t rate, cpuset_t mask)
539a79a051eSAli Mashtizadeh {
540a79a051eSAli Mashtizadeh if (study == "default" || study == "flamegraph") {
541a79a051eSAli Mashtizadeh pmcs.push_back(pmc_config("unhalted-cycles", rate, mask));
542a79a051eSAli Mashtizadeh return 0;
543a79a051eSAli Mashtizadeh }
544a79a051eSAli Mashtizadeh
545a79a051eSAli Mashtizadeh err(EX_SOFTWARE, "ERROR: Study is unsupported on this architecture");
546a79a051eSAli Mashtizadeh }
547a79a051eSAli Mashtizadeh #endif
548a79a051eSAli Mashtizadeh
549a79a051eSAli Mashtizadeh /*
550a79a051eSAli Mashtizadeh * Compute the current runtime and print it to the screen, if it exceeds
551a79a051eSAli Mashtizadeh * timelimit return 1 otherwise 0.
552a79a051eSAli Mashtizadeh */
553a79a051eSAli Mashtizadeh int
update_status(int logfd)554a79a051eSAli Mashtizadeh update_status(int logfd)
555a79a051eSAli Mashtizadeh {
556a79a051eSAli Mashtizadeh int status;
557a79a051eSAli Mashtizadeh long sec, nsec;
558a79a051eSAli Mashtizadeh struct timespec end;
559a79a051eSAli Mashtizadeh struct stat sb;
560a79a051eSAli Mashtizadeh std::string filesz;
561a79a051eSAli Mashtizadeh
562a79a051eSAli Mashtizadeh status = clock_gettime(CLOCK_MONOTONIC, &end);
563a79a051eSAli Mashtizadeh if (status < 0) {
564a79a051eSAli Mashtizadeh err(EX_OSERR, "Could not read current time");
565a79a051eSAli Mashtizadeh }
566a79a051eSAli Mashtizadeh
567a79a051eSAli Mashtizadeh sec = end.tv_sec - start.tv_sec;
568a79a051eSAli Mashtizadeh nsec = end.tv_nsec - start.tv_nsec;
569a79a051eSAli Mashtizadeh if (nsec < 0) {
570a79a051eSAli Mashtizadeh sec--;
571a79a051eSAli Mashtizadeh nsec += 1000000000L;
572a79a051eSAli Mashtizadeh }
573a79a051eSAli Mashtizadeh
574a79a051eSAli Mashtizadeh // Do this another way for sockets
575a79a051eSAli Mashtizadeh status = fstat(logfd, &sb);
576a79a051eSAli Mashtizadeh if (status < 0) {
577a79a051eSAli Mashtizadeh err(EX_OSERR, "Failed to fstat log file");
578a79a051eSAli Mashtizadeh }
579a79a051eSAli Mashtizadeh
580a79a051eSAli Mashtizadeh filesz = format_binprefix(sb.st_size);
581a79a051eSAli Mashtizadeh
582a79a051eSAli Mashtizadeh /*
583a79a051eSAli Mashtizadeh * Use %-10s to ensure we print spaces after in case the size string
584a79a051eSAli Mashtizadeh * shrinks.
585a79a051eSAli Mashtizadeh */
586a79a051eSAli Mashtizadeh printf("Elapsed: %ld.%03ld seconds, Log file size: %-10s\r",
587a79a051eSAli Mashtizadeh sec, nsec / 1000000,
588a79a051eSAli Mashtizadeh filesz.c_str());
589a79a051eSAli Mashtizadeh fflush(stdout);
590a79a051eSAli Mashtizadeh
591a79a051eSAli Mashtizadeh if (timelimit != 0 && sec >= timelimit) {
592a79a051eSAli Mashtizadeh printf("\nElapsed time completed\n");
593a79a051eSAli Mashtizadeh return 1;
594a79a051eSAli Mashtizadeh }
595a79a051eSAli Mashtizadeh
596a79a051eSAli Mashtizadeh return 0;
597a79a051eSAli Mashtizadeh }
598a79a051eSAli Mashtizadeh
599a79a051eSAli Mashtizadeh int
cmd_pmc_record(int argc,char ** argv)600a79a051eSAli Mashtizadeh cmd_pmc_record(int argc, char **argv)
601a79a051eSAli Mashtizadeh {
602a79a051eSAli Mashtizadeh struct kevent kev;
603a79a051eSAli Mashtizadeh cpuset_t mask;
604a79a051eSAli Mashtizadeh uint64_t rate = DEFAULT_RATE;
605a79a051eSAli Mashtizadeh const char *logfile = "default.log";
606a79a051eSAli Mashtizadeh int status;
607a79a051eSAli Mashtizadeh int option, logfd, kq;
608a79a051eSAli Mashtizadeh
609a79a051eSAli Mashtizadeh CPU_ZERO(&mask);
610a79a051eSAli Mashtizadeh if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
611a79a051eSAli Mashtizadeh sizeof(mask), &mask) == -1)
612a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot determine the available CPUs");
613a79a051eSAli Mashtizadeh
614a79a051eSAli Mashtizadeh if (pmc_init() < 0)
615a79a051eSAli Mashtizadeh err(EX_SOFTWARE, "ERROR: Failed to initialize libpmc");
616a79a051eSAli Mashtizadeh
617a79a051eSAli Mashtizadeh while ((option = getopt_long(argc, argv, "c:r:s:t:", longopts, NULL)) != -1) {
618a79a051eSAli Mashtizadeh switch (option) {
619a79a051eSAli Mashtizadeh case 'c':
620a79a051eSAli Mashtizadeh pmcs.push_back(pmc_config(optarg, rate, mask));
621a79a051eSAli Mashtizadeh break;
622a79a051eSAli Mashtizadeh case 'r':
623a79a051eSAli Mashtizadeh rate = strtoull(optarg, NULL, 0);
624a79a051eSAli Mashtizadeh break;
625a79a051eSAli Mashtizadeh case 's':
626a79a051eSAli Mashtizadeh setup_study(optarg, rate, mask);
627a79a051eSAli Mashtizadeh break;
628a79a051eSAli Mashtizadeh case 't':
629a79a051eSAli Mashtizadeh timelimit = atoi(optarg);
630a79a051eSAli Mashtizadeh break;
631a79a051eSAli Mashtizadeh case '?':
632a79a051eSAli Mashtizadeh default:
633a79a051eSAli Mashtizadeh usage();
634a79a051eSAli Mashtizadeh }
635a79a051eSAli Mashtizadeh }
636a79a051eSAli Mashtizadeh argc -= optind;
637a79a051eSAli Mashtizadeh argv += optind;
638a79a051eSAli Mashtizadeh if (argc != 0 && argc != 1) {
639a79a051eSAli Mashtizadeh usage();
640a79a051eSAli Mashtizadeh exit(EX_USAGE);
641a79a051eSAli Mashtizadeh }
642a79a051eSAli Mashtizadeh if (argc == 1)
643a79a051eSAli Mashtizadeh logfile = argv[0];
644a79a051eSAli Mashtizadeh
645a79a051eSAli Mashtizadeh if (pmcs.size() == 0) {
646a79a051eSAli Mashtizadeh errx(EX_NOINPUT, "ERROR: Please select one or more counters or performance studies.");
647a79a051eSAli Mashtizadeh }
648a79a051eSAli Mashtizadeh
649a79a051eSAli Mashtizadeh if ((logfd = open(logfile, O_CREAT|O_EXCL|O_WRONLY,
650a79a051eSAli Mashtizadeh S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) {
651a79a051eSAli Mashtizadeh errx(EX_OSERR, "ERROR: Cannot open \"%s\" for writing: %s.", logfile,
652a79a051eSAli Mashtizadeh strerror(errno));
653a79a051eSAli Mashtizadeh }
654a79a051eSAli Mashtizadeh
655a79a051eSAli Mashtizadeh setup_screen();
656a79a051eSAli Mashtizadeh
657a79a051eSAli Mashtizadeh for (auto &p : pmcs) {
658a79a051eSAli Mashtizadeh p.getcaps();
659a79a051eSAli Mashtizadeh }
660a79a051eSAli Mashtizadeh
661a79a051eSAli Mashtizadeh write_header(logfd);
662a79a051eSAli Mashtizadeh write_sysinfo(logfd);
663a79a051eSAli Mashtizadeh write_cpuinfo(logfd);
664a79a051eSAli Mashtizadeh write_footer(logfd);
665a79a051eSAli Mashtizadeh
666a79a051eSAli Mashtizadeh // Record pmclog
667a79a051eSAli Mashtizadeh kq = kqueue();
668a79a051eSAli Mashtizadeh if (kq < 0)
669a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: kqueue creation failed");
670a79a051eSAli Mashtizadeh
671a79a051eSAli Mashtizadeh EV_SET(&kev, fileno(stdin), EVFILT_READ, EV_ADD, 0, 0, NULL);
672a79a051eSAli Mashtizadeh if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
673a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: kevent failed to register stdin");
674a79a051eSAli Mashtizadeh
675a79a051eSAli Mashtizadeh EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
676a79a051eSAli Mashtizadeh if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
677a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: kevent failed to register SIGINT");
678a79a051eSAli Mashtizadeh EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
679a79a051eSAli Mashtizadeh if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
680a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: kevent failed to register SIGIO");
681a79a051eSAli Mashtizadeh
682a79a051eSAli Mashtizadeh EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 100, NULL);
683a79a051eSAli Mashtizadeh if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
684a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Cannot register kevent for timer");
685a79a051eSAli Mashtizadeh
686a79a051eSAli Mashtizadeh pmc_configure_logfile(logfd);
687a79a051eSAli Mashtizadeh
688a79a051eSAli Mashtizadeh for (auto &p : pmcs) {
689a79a051eSAli Mashtizadeh p.allocate();
690a79a051eSAli Mashtizadeh }
691a79a051eSAli Mashtizadeh
692a79a051eSAli Mashtizadeh for (auto &p : pmcs) {
693a79a051eSAli Mashtizadeh p.start();
694a79a051eSAli Mashtizadeh }
695a79a051eSAli Mashtizadeh
696a79a051eSAli Mashtizadeh if (clock_gettime(CLOCK_MONOTONIC, &start) < 0)
697a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: Could not get the current time");
698a79a051eSAli Mashtizadeh
699a79a051eSAli Mashtizadeh printf("Recording performance trace press Ctrl-C to stop\n");
700a79a051eSAli Mashtizadeh
701a79a051eSAli Mashtizadeh /*
702a79a051eSAli Mashtizadeh * loop till either the target process (if any) exits, or we
703a79a051eSAli Mashtizadeh * are killed by a SIGINT or we reached the time duration.
704a79a051eSAli Mashtizadeh */
705a79a051eSAli Mashtizadeh while (1) {
706a79a051eSAli Mashtizadeh status = kevent(kq, NULL, 0, &kev, 1, NULL);
707a79a051eSAli Mashtizadeh if (status <= 0) {
708a79a051eSAli Mashtizadeh if (errno != EINTR)
709a79a051eSAli Mashtizadeh err(EX_OSERR, "ERROR: kevent failed");
710a79a051eSAli Mashtizadeh else
711a79a051eSAli Mashtizadeh continue;
712a79a051eSAli Mashtizadeh }
713a79a051eSAli Mashtizadeh
714a79a051eSAli Mashtizadeh if (kev.flags & EV_ERROR)
715a79a051eSAli Mashtizadeh errc(EX_OSERR, kev.data, "ERROR: kevent failed");
716a79a051eSAli Mashtizadeh
717a79a051eSAli Mashtizadeh switch (kev.filter) {
718a79a051eSAli Mashtizadeh case EVFILT_READ: /* log file data is present */
719a79a051eSAli Mashtizadeh if (kev.ident == (unsigned)fileno(stdin)) {
720a79a051eSAli Mashtizadeh // Check for exit key
721a79a051eSAli Mashtizadeh }
722a79a051eSAli Mashtizadeh break;
723a79a051eSAli Mashtizadeh case EVFILT_SIGNAL:
724a79a051eSAli Mashtizadeh if (kev.ident == SIGIO) {
725a79a051eSAli Mashtizadeh fprintf(stderr, "ERROR: IO error");
726a79a051eSAli Mashtizadeh status = EX_OSERR;
727a79a051eSAli Mashtizadeh goto done;
728a79a051eSAli Mashtizadeh } else if (kev.ident == SIGINT) {
729a79a051eSAli Mashtizadeh status = EX_OK;
730a79a051eSAli Mashtizadeh goto done;
731a79a051eSAli Mashtizadeh } else {
732a79a051eSAli Mashtizadeh err(EX_OSERR, "Unknown signal recieved");
733a79a051eSAli Mashtizadeh }
734a79a051eSAli Mashtizadeh break;
735a79a051eSAli Mashtizadeh case EVFILT_TIMER:
736a79a051eSAli Mashtizadeh if (update_status(logfd) == 1)
737a79a051eSAli Mashtizadeh goto done;
738a79a051eSAli Mashtizadeh break;
739a79a051eSAli Mashtizadeh }
740a79a051eSAli Mashtizadeh }
741a79a051eSAli Mashtizadeh
742a79a051eSAli Mashtizadeh done:
743a79a051eSAli Mashtizadeh for (auto &p : pmcs) {
744a79a051eSAli Mashtizadeh p.stop();
745a79a051eSAli Mashtizadeh p.release();
746a79a051eSAli Mashtizadeh }
747a79a051eSAli Mashtizadeh close(logfd);
748a79a051eSAli Mashtizadeh
749a79a051eSAli Mashtizadeh return (status);
750a79a051eSAli Mashtizadeh }
751a79a051eSAli Mashtizadeh
752