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
83 int devfd;
84 int outfd;
85
86 __dead static void
87 usage(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
104 static void *
105 process_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
135 int
136 main(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(¶m, 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], ¶m);
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, ¶m);
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