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