tprof.c revision 1.14
1/*	$NetBSD: tprof.c,v 1.14 2022/12/01 00:32:52 ryo Exp $	*/
2
3/*
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Maxime Villard.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c)2008 YAMAMOTO Takashi,
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
58#include <sys/cdefs.h>
59#ifndef lint
60__RCSID("$NetBSD: tprof.c,v 1.14 2022/12/01 00:32:52 ryo Exp $");
61#endif /* not lint */
62
63#include <sys/ioctl.h>
64#include <sys/wait.h>
65
66#include <dev/tprof/tprof_ioctl.h>
67
68#include <err.h>
69#include <errno.h>
70#include <fcntl.h>
71#include <inttypes.h>
72#include <pthread.h>
73#include <signal.h>
74#include <stdbool.h>
75#include <stdio.h>
76#include <stdlib.h>
77#include <string.h>
78#include <unistd.h>
79#include "tprof.h"
80
81#define	_PATH_TPROF	"/dev/tprof"
82
83struct tprof_info tprof_info;
84u_int ncounters;
85int devfd;
86int outfd;
87u_int nevent;
88
89static void tprof_list(int, char **);
90static void tprof_monitor(int, char **) __dead;
91
92static struct cmdtab {
93	const char *label;
94	bool takesargs;
95	bool argsoptional;
96	void (*func)(int, char **);
97} const tprof_cmdtab[] = {
98	{ "list",	false, false, tprof_list },
99	{ "monitor",	true,  false, tprof_monitor },
100	{ "analyze",	true,  true,  tprof_analyze },
101	{ NULL,		false, false, NULL },
102};
103
104__dead static void
105usage(void)
106{
107
108	fprintf(stderr, "%s op [arguments]\n", getprogname());
109	fprintf(stderr, "\n");
110	fprintf(stderr, "\tlist\n");
111	fprintf(stderr, "\t\tList the available events.\n");
112	fprintf(stderr, "\tmonitor -e name:option [-e ...] [-o outfile] command\n");
113	fprintf(stderr, "\t\tMonitor the event 'name' with option 'option'\n"
114	    "\t\tcounted during the execution of 'command'.\n");
115	fprintf(stderr, "\tanalyze [-CkLPs] [-p pid] file\n");
116	fprintf(stderr, "\t\tAnalyze the samples of the file 'file'.\n");
117
118	exit(EXIT_FAILURE);
119}
120
121static void *
122process_samples(void *dummy)
123{
124
125	for (;;) {
126		char buf[4096];
127		const char *cp;
128		ssize_t ssz;
129
130		ssz = read(devfd, buf, sizeof(buf));
131		if (ssz == -1) {
132			err(EXIT_FAILURE, "read");
133		}
134		if (ssz == 0) {
135			break;
136		}
137		cp = buf;
138		while (ssz) {
139			ssize_t wsz;
140
141			wsz = write(outfd, cp, ssz);
142			if (wsz == -1) {
143				err(EXIT_FAILURE, "write");
144			}
145			ssz -= wsz;
146			cp += wsz;
147		}
148	}
149	return NULL;
150}
151
152static void
153tprof_list(int argc, char **argv)
154{
155	tprof_event_list();
156}
157
158static void
159tprof_monitor(int argc, char **argv)
160{
161	const char *outfile = "tprof.out";
162	struct tprof_stat ts;
163	tprof_param_t params[TPROF_MAXCOUNTERS];
164	pid_t pid;
165	pthread_t pt;
166	int ret, ch, i;
167	char *tokens[2];
168	tprof_countermask_t mask = TPROF_COUNTERMASK_ALL;
169
170	memset(params, 0, sizeof(params));
171
172	while ((ch = getopt(argc, argv, "o:e:")) != -1) {
173		switch (ch) {
174		case 'o':
175			outfile = optarg;
176			break;
177		case 'e':
178			tokens[0] = strtok(optarg, ":");
179			tokens[1] = strtok(NULL, ":");
180			if (tokens[1] == NULL)
181				usage();
182			tprof_event_lookup(tokens[0], &params[nevent]);
183			if (strchr(tokens[1], 'u'))
184				params[nevent].p_flags |= TPROF_PARAM_USER;
185			if (strchr(tokens[1], 'k'))
186				params[nevent].p_flags |= TPROF_PARAM_KERN;
187			if (params[nevent].p_flags == 0)
188				usage();
189			nevent++;
190			if (nevent > __arraycount(params) ||
191			    nevent > ncounters)
192				errx(EXIT_FAILURE, "Too many events");
193			break;
194		default:
195			usage();
196		}
197	}
198	argc -= optind;
199	argv += optind;
200	if (argc == 0 || nevent == 0) {
201		usage();
202	}
203
204	outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
205	if (outfd == -1) {
206		err(EXIT_FAILURE, "%s", outfile);
207	}
208
209	for (i = 0; i < (int)nevent; i++) {
210		params[i].p_counter = i;
211		params[i].p_flags |= TPROF_PARAM_PROFILE;
212		ret = ioctl(devfd, TPROF_IOC_CONFIGURE_EVENT, &params[i]);
213		if (ret == -1)
214			err(EXIT_FAILURE, "TPROF_IOC_CONFIGURE_EVENT");
215	}
216
217	ret = ioctl(devfd, TPROF_IOC_START, &mask);
218	if (ret == -1) {
219		err(EXIT_FAILURE, "TPROF_IOC_START");
220	}
221
222	pid = fork();
223	switch (pid) {
224	case -1:
225		err(EXIT_FAILURE, "fork");
226	case 0:
227		close(devfd);
228		execvp(argv[0], argv);
229		_Exit(EXIT_FAILURE);
230	}
231
232	signal(SIGINT, SIG_IGN);
233
234	ret = pthread_create(&pt, NULL, process_samples, NULL);
235	if (ret != 0) {
236		errx(1, "pthread_create: %s", strerror(ret));
237	}
238
239	for (;;) {
240		int status;
241
242		pid = wait4(-1, &status, 0, NULL);
243		if (pid == -1) {
244			if (errno == ECHILD) {
245				break;
246			}
247			err(EXIT_FAILURE, "wait4");
248		}
249		if (pid != 0 && WIFEXITED(status)) {
250			break;
251		}
252	}
253
254	ret = ioctl(devfd, TPROF_IOC_STOP, &mask);
255	if (ret == -1) {
256		err(EXIT_FAILURE, "TPROF_IOC_STOP");
257	}
258
259	pthread_join(pt, NULL);
260
261	ret = ioctl(devfd, TPROF_IOC_GETSTAT, &ts);
262	if (ret == -1) {
263		err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
264	}
265
266	fprintf(stderr, "\n%s statistics:\n", getprogname());
267	fprintf(stderr, "\tsample %" PRIu64 "\n", ts.ts_sample);
268	fprintf(stderr, "\toverflow %" PRIu64 "\n", ts.ts_overflow);
269	fprintf(stderr, "\tbuf %" PRIu64 "\n", ts.ts_buf);
270	fprintf(stderr, "\temptybuf %" PRIu64 "\n", ts.ts_emptybuf);
271	fprintf(stderr, "\tdropbuf %" PRIu64 "\n", ts.ts_dropbuf);
272	fprintf(stderr, "\tdropbuf_sample %" PRIu64 "\n", ts.ts_dropbuf_sample);
273
274	exit(EXIT_SUCCESS);
275}
276
277int
278main(int argc, char *argv[])
279{
280	const struct cmdtab *ct;
281	int ret;
282
283	setprogname(argv[0]);
284	argv += 1, argc -= 1;
285
286	devfd = open(_PATH_TPROF, O_RDWR);
287	if (devfd == -1) {
288		err(EXIT_FAILURE, "%s", _PATH_TPROF);
289	}
290
291	ret = ioctl(devfd, TPROF_IOC_GETINFO, &tprof_info);
292	if (ret == -1) {
293		err(EXIT_FAILURE, "TPROF_IOC_GETINFO");
294	}
295	if (tprof_info.ti_version != TPROF_VERSION) {
296		errx(EXIT_FAILURE, "version mismatch: version=%d, expected=%d",
297		    tprof_info.ti_version, TPROF_VERSION);
298	}
299	if (tprof_event_init(tprof_info.ti_ident) == -1) {
300		errx(EXIT_FAILURE, "cpu not supported");
301	}
302
303	ret = ioctl(devfd, TPROF_IOC_GETNCOUNTERS, &ncounters);
304	if (ret == -1) {
305		err(EXIT_FAILURE, "TPROF_IOC_GETNCOUNTERS");
306	}
307	if (ncounters == 0) {
308		errx(EXIT_FAILURE, "no available counters");
309	}
310
311	if (argc == 0)
312		usage();
313
314	for (ct = tprof_cmdtab; ct->label != NULL; ct++) {
315		if (strcmp(argv[0], ct->label) == 0) {
316			if (!ct->argsoptional &&
317			    ((ct->takesargs == 0) ^ (argv[1] == NULL)))
318			{
319				usage();
320			}
321			(*ct->func)(argc, argv);
322			break;
323		}
324	}
325	if (ct->label == NULL) {
326		usage();
327	}
328}
329