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
83 struct tprof_info tprof_info;
84 u_int ncounters;
85 int devfd;
86 int outfd;
87 u_int nevent;
88
89 static void tprof_list(int, char **);
90 static void tprof_monitor(int, char **) __dead;
91
92 static 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
105 usage(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
121 static void *
122 process_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
152 static void
153 tprof_list(int argc, char **argv)
154 {
155 tprof_event_list();
156 }
157
158 static void
159 tprof_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], ¶ms[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, ¶ms[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
277 int
278 main(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