tprof.c revision 1.11
1/* $NetBSD: tprof.c,v 1.11 2018/07/14 16:34:15 jmcneill 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.11 2018/07/14 16:34:15 jmcneill 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 86static void tprof_list(int, char **); 87static void tprof_monitor(int, char **) __dead; 88 89static 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 102usage(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 [-C] [-k] [-L] [-P] [-p pid] [-s] file\n"); 113 fprintf(stderr, "\t\tAnalyze the samples of the file 'file'.\n"); 114 115 exit(EXIT_FAILURE); 116} 117 118static void * 119process_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 149static void 150tprof_list(int argc, char **argv) 151{ 152 tprof_event_list(); 153} 154 155static void 156tprof_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 263int 264main(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