tprof.c revision 1.6
1/*	$NetBSD: tprof.c,v 1.6 2018/07/13 07:56:29 maxv 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.6 2018/07/13 07:56:29 maxv 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
83int devfd;
84int outfd;
85
86__dead static void
87usage(void)
88{
89
90	fprintf(stderr, "%s [options] command ...\n", getprogname());
91	fprintf(stderr, "\n");
92	fprintf(stderr, "-e name:{u}{k}\t"
93	    "the event to count.\n");
94	fprintf(stderr, "-l\t\t"
95	    "list the events.\n");
96	fprintf(stderr, "-o filename\t"
97	    "output to the file.  [default: -o tprof.out]\n");
98	fprintf(stderr, "-c\t\t"
99	    "output to stdout.  NOTE: the output is a binary stream.\n");
100
101	exit(EXIT_FAILURE);
102}
103
104static void *
105process_samples(void *dummy)
106{
107
108	for (;;) {
109		char buf[4096];
110		const char *cp;
111		ssize_t ssz;
112
113		ssz = read(devfd, buf, sizeof(buf));
114		if (ssz == -1) {
115			err(EXIT_FAILURE, "read");
116		}
117		if (ssz == 0) {
118			break;
119		}
120		cp = buf;
121		while (ssz) {
122			ssize_t wsz;
123
124			wsz = write(outfd, cp, ssz);
125			if (wsz == -1) {
126				err(EXIT_FAILURE, "write");
127			}
128			ssz -= wsz;
129			cp += wsz;
130		}
131	}
132	return NULL;
133}
134
135int
136main(int argc, char *argv[])
137{
138	struct tprof_param param;
139	struct tprof_info info;
140	struct tprof_stat ts;
141	const char *outfile = "tprof.out";
142	bool cflag = false;
143	pid_t pid;
144	pthread_t pt;
145	int error;
146	int ret;
147	int ch;
148	char *tokens[2];
149
150	memset(&param, 0, sizeof(param));
151
152	devfd = open(_PATH_TPROF, O_RDWR);
153	if (devfd == -1) {
154		err(EXIT_FAILURE, "%s", _PATH_TPROF);
155	}
156
157	ret = ioctl(devfd, TPROF_IOC_GETINFO, &info);
158	if (ret == -1) {
159		err(EXIT_FAILURE, "TPROF_IOC_GETINFO");
160	}
161	if (info.ti_version != TPROF_VERSION) {
162		errx(EXIT_FAILURE, "version mismatch: version=%d, expected=%d",
163		    info.ti_version, TPROF_VERSION);
164	}
165	if (tprof_event_init(info.ti_ident) == -1) {
166		err(EXIT_FAILURE, "cpu not supported");
167	}
168
169	while ((ch = getopt(argc, argv, "clo:e:")) != -1) {
170		switch (ch) {
171		case 'c':
172			cflag = true;
173			break;
174		case 'l':
175			tprof_event_list();
176			return 0;
177		case 'o':
178			outfile = optarg;
179			break;
180		case 'e':
181			tokens[0] = strtok(optarg, ":");
182			tokens[1] = strtok(NULL, ":");
183			if (tokens[1] == NULL)
184				usage();
185			tprof_event_lookup(tokens[0], &param);
186			if (strchr(tokens[1], 'u'))
187				param.p_flags |= TPROF_PARAM_USER;
188			if (strchr(tokens[1], 'k'))
189				param.p_flags |= TPROF_PARAM_KERN;
190			break;
191		default:
192			usage();
193		}
194	}
195	argc -= optind;
196	argv += optind;
197	if (argc == 0) {
198		usage();
199	}
200
201	if (param.p_flags == 0) {
202		usage();
203	}
204
205	if (cflag) {
206		outfd = STDOUT_FILENO;
207	} else {
208		outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
209		if (outfd == -1) {
210			err(EXIT_FAILURE, "%s", outfile);
211		}
212	}
213
214	ret = ioctl(devfd, TPROF_IOC_START, &param);
215	if (ret == -1) {
216		err(EXIT_FAILURE, "TPROF_IOC_START");
217	}
218
219	pid = fork();
220	switch (pid) {
221	case -1:
222		err(EXIT_FAILURE, "fork");
223	case 0:
224		close(devfd);
225		execvp(argv[0], argv);
226		_Exit(EXIT_FAILURE);
227	}
228
229	signal(SIGINT, SIG_IGN);
230
231	error = pthread_create(&pt, NULL, process_samples, NULL);
232	if (error != 0) {
233		errx(1, "pthread_create: %s", strerror(error));
234	}
235
236	for (;;) {
237		int status;
238
239		pid = wait4(-1, &status, 0, NULL);
240		if (pid == -1) {
241			if (errno == ECHILD) {
242				break;
243			}
244			err(EXIT_FAILURE, "wait4");
245		}
246		if (pid != 0 && WIFEXITED(status)) {
247			break;
248		}
249	}
250
251	ret = ioctl(devfd, TPROF_IOC_STOP, NULL);
252	if (ret == -1) {
253		err(EXIT_FAILURE, "TPROF_IOC_STOP");
254	}
255
256	pthread_join(pt, NULL);
257
258	ret = ioctl(devfd, TPROF_IOC_GETSTAT, &ts);
259	if (ret == -1) {
260		err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
261	}
262
263	fprintf(stderr, "\n%s statistics:\n", getprogname());
264	fprintf(stderr, "\tsample %" PRIu64 "\n", ts.ts_sample);
265	fprintf(stderr, "\toverflow %" PRIu64 "\n", ts.ts_overflow);
266	fprintf(stderr, "\tbuf %" PRIu64 "\n", ts.ts_buf);
267	fprintf(stderr, "\temptybuf %" PRIu64 "\n", ts.ts_emptybuf);
268	fprintf(stderr, "\tdropbuf %" PRIu64 "\n", ts.ts_dropbuf);
269	fprintf(stderr, "\tdropbuf_sample %" PRIu64 "\n", ts.ts_dropbuf_sample);
270
271	exit(EXIT_SUCCESS);
272}
273