tprof.c revision 1.12 1 /* $NetBSD: tprof.c,v 1.12 2018/07/18 16:50:05 wiz 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.12 2018/07/18 16:50:05 wiz 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 int devfd;
84 int outfd;
85
86 static void tprof_list(int, char **);
87 static void tprof_monitor(int, char **) __dead;
88
89 static struct cmdtab {
90 const char *label;
91 bool takesargs;
92 bool argsoptional;
93 void (*func)(int, char **);
94 } const tprof_cmdtab[] = {
95 { "list", false, false, tprof_list },
96 { "monitor", true, false, tprof_monitor },
97 { "analyze", true, true, tprof_analyze },
98 { NULL, false, false, NULL },
99 };
100
101 __dead static void
102 usage(void)
103 {
104
105 fprintf(stderr, "%s op [arguments]\n", getprogname());
106 fprintf(stderr, "\n");
107 fprintf(stderr, "\tlist\n");
108 fprintf(stderr, "\t\tList the available events.\n");
109 fprintf(stderr, "\tmonitor -e name:option [-o outfile] command\n");
110 fprintf(stderr, "\t\tMonitor the event 'name' with option 'option'\n"
111 "\t\tcounted during the execution of 'command'.\n");
112 fprintf(stderr, "\tanalyze [-CkLPs] [-p pid] file\n");
113 fprintf(stderr, "\t\tAnalyze the samples of the file 'file'.\n");
114
115 exit(EXIT_FAILURE);
116 }
117
118 static void *
119 process_samples(void *dummy)
120 {
121
122 for (;;) {
123 char buf[4096];
124 const char *cp;
125 ssize_t ssz;
126
127 ssz = read(devfd, buf, sizeof(buf));
128 if (ssz == -1) {
129 err(EXIT_FAILURE, "read");
130 }
131 if (ssz == 0) {
132 break;
133 }
134 cp = buf;
135 while (ssz) {
136 ssize_t wsz;
137
138 wsz = write(outfd, cp, ssz);
139 if (wsz == -1) {
140 err(EXIT_FAILURE, "write");
141 }
142 ssz -= wsz;
143 cp += wsz;
144 }
145 }
146 return NULL;
147 }
148
149 static void
150 tprof_list(int argc, char **argv)
151 {
152 tprof_event_list();
153 }
154
155 static void
156 tprof_monitor(int argc, char **argv)
157 {
158 const char *outfile = "tprof.out";
159 struct tprof_param param;
160 struct tprof_stat ts;
161 pid_t pid;
162 pthread_t pt;
163 int ret, ch;
164 char *tokens[2];
165
166 memset(¶m, 0, sizeof(param));
167
168 while ((ch = getopt(argc, argv, "o:e:")) != -1) {
169 switch (ch) {
170 case 'o':
171 outfile = optarg;
172 break;
173 case 'e':
174 tokens[0] = strtok(optarg, ":");
175 tokens[1] = strtok(NULL, ":");
176 if (tokens[1] == NULL)
177 usage();
178 tprof_event_lookup(tokens[0], ¶m);
179 if (strchr(tokens[1], 'u'))
180 param.p_flags |= TPROF_PARAM_USER;
181 if (strchr(tokens[1], 'k'))
182 param.p_flags |= TPROF_PARAM_KERN;
183 break;
184 default:
185 usage();
186 }
187 }
188 argc -= optind;
189 argv += optind;
190 if (argc == 0) {
191 usage();
192 }
193
194 if (param.p_flags == 0) {
195 usage();
196 }
197
198 outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666);
199 if (outfd == -1) {
200 err(EXIT_FAILURE, "%s", outfile);
201 }
202
203 ret = ioctl(devfd, TPROF_IOC_START, ¶m);
204 if (ret == -1) {
205 err(EXIT_FAILURE, "TPROF_IOC_START");
206 }
207
208 pid = fork();
209 switch (pid) {
210 case -1:
211 err(EXIT_FAILURE, "fork");
212 case 0:
213 close(devfd);
214 execvp(argv[0], argv);
215 _Exit(EXIT_FAILURE);
216 }
217
218 signal(SIGINT, SIG_IGN);
219
220 ret = pthread_create(&pt, NULL, process_samples, NULL);
221 if (ret != 0) {
222 errx(1, "pthread_create: %s", strerror(ret));
223 }
224
225 for (;;) {
226 int status;
227
228 pid = wait4(-1, &status, 0, NULL);
229 if (pid == -1) {
230 if (errno == ECHILD) {
231 break;
232 }
233 err(EXIT_FAILURE, "wait4");
234 }
235 if (pid != 0 && WIFEXITED(status)) {
236 break;
237 }
238 }
239
240 ret = ioctl(devfd, TPROF_IOC_STOP, NULL);
241 if (ret == -1) {
242 err(EXIT_FAILURE, "TPROF_IOC_STOP");
243 }
244
245 pthread_join(pt, NULL);
246
247 ret = ioctl(devfd, TPROF_IOC_GETSTAT, &ts);
248 if (ret == -1) {
249 err(EXIT_FAILURE, "TPROF_IOC_GETSTAT");
250 }
251
252 fprintf(stderr, "\n%s statistics:\n", getprogname());
253 fprintf(stderr, "\tsample %" PRIu64 "\n", ts.ts_sample);
254 fprintf(stderr, "\toverflow %" PRIu64 "\n", ts.ts_overflow);
255 fprintf(stderr, "\tbuf %" PRIu64 "\n", ts.ts_buf);
256 fprintf(stderr, "\temptybuf %" PRIu64 "\n", ts.ts_emptybuf);
257 fprintf(stderr, "\tdropbuf %" PRIu64 "\n", ts.ts_dropbuf);
258 fprintf(stderr, "\tdropbuf_sample %" PRIu64 "\n", ts.ts_dropbuf_sample);
259
260 exit(EXIT_SUCCESS);
261 }
262
263 int
264 main(int argc, char *argv[])
265 {
266 struct tprof_info info;
267 const struct cmdtab *ct;
268 int ret;
269
270 setprogname(argv[0]);
271 argv += 1, argc -= 1;
272
273 devfd = open(_PATH_TPROF, O_RDWR);
274 if (devfd == -1) {
275 err(EXIT_FAILURE, "%s", _PATH_TPROF);
276 }
277
278 ret = ioctl(devfd, TPROF_IOC_GETINFO, &info);
279 if (ret == -1) {
280 err(EXIT_FAILURE, "TPROF_IOC_GETINFO");
281 }
282 if (info.ti_version != TPROF_VERSION) {
283 errx(EXIT_FAILURE, "version mismatch: version=%d, expected=%d",
284 info.ti_version, TPROF_VERSION);
285 }
286 if (tprof_event_init(info.ti_ident) == -1) {
287 err(EXIT_FAILURE, "cpu not supported");
288 }
289
290 if (argc == 0)
291 usage();
292
293 for (ct = tprof_cmdtab; ct->label != NULL; ct++) {
294 if (strcmp(argv[0], ct->label) == 0) {
295 if (!ct->argsoptional &&
296 ((ct->takesargs == 0) ^ (argv[1] == NULL)))
297 {
298 usage();
299 }
300 (*ct->func)(argc, argv);
301 break;
302 }
303 }
304 if (ct->label == NULL) {
305 usage();
306 }
307 }
308