xref: /freebsd/usr.sbin/pmc/cmd_pmc_record.cc (revision 78bbe9fc3e3529731df07e29464f103f96f6ebed)
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