1 /*- 2 * Copyright (c) 2003,2004 Joseph Koshy 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/types.h> 32 #include <sys/event.h> 33 #include <sys/queue.h> 34 #include <sys/time.h> 35 #include <sys/ttycom.h> 36 #include <sys/wait.h> 37 38 #include <assert.h> 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <limits.h> 43 #include <math.h> 44 #include <pmc.h> 45 #include <signal.h> 46 #include <stdarg.h> 47 #include <stdio.h> 48 #include <stdint.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <sysexits.h> 52 #include <unistd.h> 53 54 /* Operation modes */ 55 56 #define FLAG_HAS_PID 0x00000001 57 #define FLAG_HAS_WAIT_INTERVAL 0x00000002 58 #define FLAG_HAS_LOG_FILE 0x00000004 59 #define FLAG_HAS_PROCESS 0x00000008 60 #define FLAG_USING_SAMPLING 0x00000010 61 #define FLAG_USING_COUNTING 0x00000020 62 #define FLAG_USING_PROCESS_PMC 0x00000040 63 64 #define DEFAULT_SAMPLE_COUNT 65536 65 #define DEFAULT_WAIT_INTERVAL 5.0 66 #define DEFAULT_DISPLAY_HEIGHT 23 67 #define DEFAULT_LOGFILE_NAME "pmcstat.out" 68 69 #define PRINT_HEADER_PREFIX "# " 70 #define READPIPEFD 0 71 #define WRITEPIPEFD 1 72 #define NPIPEFD 2 73 74 struct pmcstat_ev { 75 STAILQ_ENTRY(pmcstat_ev) ev_next; 76 char *ev_spec; /* event specification */ 77 char *ev_name; /* (derived) event name */ 78 enum pmc_mode ev_mode; /* desired mode */ 79 int ev_count; /* associated count if in sampling mode */ 80 int ev_cpu; /* specific cpu if requested */ 81 int ev_descendants; /* attach to descendants */ 82 int ev_cumulative; /* show cumulative counts */ 83 int ev_fieldwidth; /* print width */ 84 int ev_fieldskip; /* #leading spaces */ 85 pmc_value_t ev_saved; /* saved value for incremental counts */ 86 pmc_id_t ev_pmcid; /* allocated ID */ 87 }; 88 89 struct pmcstat_args { 90 int pa_flags; 91 pid_t pa_pid; 92 FILE *pa_outputfile; 93 FILE *pa_logfile; 94 double pa_interval; 95 int pa_argc; 96 char **pa_argv; 97 STAILQ_HEAD(, pmcstat_ev) pa_head; 98 } args; 99 100 int pmcstat_interrupt = 0; 101 int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; 102 int pmcstat_pipefd[NPIPEFD]; 103 int pmcstat_kq; 104 105 /* Function prototypes */ 106 void pmcstat_cleanup(struct pmcstat_args *_a); 107 void pmcstat_print_counters(struct pmcstat_args *_a); 108 void pmcstat_print_headers(struct pmcstat_args *_a); 109 void pmcstat_print_pmcs(struct pmcstat_args *_a); 110 void pmcstat_setup_process(struct pmcstat_args *_a); 111 void pmcstat_show_usage(void); 112 void pmcstat_start_pmcs(struct pmcstat_args *_a); 113 void pmcstat_start_process(struct pmcstat_args *_a); 114 115 116 /* 117 * cleanup 118 */ 119 120 void 121 pmcstat_cleanup(struct pmcstat_args *a) 122 { 123 struct pmcstat_ev *ev, *tmp; 124 125 /* de-configure the log file if present. */ 126 if (a->pa_flags & FLAG_USING_SAMPLING) { 127 (void) pmc_configure_logfile(-1); 128 (void) fclose(a->pa_logfile); 129 } 130 131 /* release allocated PMCs. */ 132 STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp) 133 if (ev->ev_pmcid != PMC_ID_INVALID) { 134 if (pmc_release(ev->ev_pmcid) < 0) 135 err(EX_OSERR, "ERROR: cannot release pmc " 136 "%d \"%s\"", ev->ev_pmcid, ev->ev_name); 137 free(ev->ev_name); 138 free(ev->ev_spec); 139 STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next); 140 free(ev); 141 } 142 } 143 144 void 145 pmcstat_start_pmcs(struct pmcstat_args *a) 146 { 147 struct pmcstat_ev *ev; 148 149 STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 150 151 assert(ev->ev_pmcid != PMC_ID_INVALID); 152 153 if (pmc_start(ev->ev_pmcid) < 0) { 154 warn("ERROR: Cannot start pmc %d \"%s\"", 155 ev->ev_pmcid, ev->ev_name); 156 pmcstat_cleanup(a); 157 } 158 } 159 160 } 161 162 void 163 pmcstat_print_headers(struct pmcstat_args *a) 164 { 165 struct pmcstat_ev *ev; 166 int c; 167 168 (void) fprintf(a->pa_outputfile, PRINT_HEADER_PREFIX); 169 170 STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 171 if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 172 continue; 173 174 c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; 175 176 if (ev->ev_fieldskip != 0) { 177 (void) fprintf(a->pa_outputfile, "%*s%c/%*s ", 178 ev->ev_fieldskip, "", c, 179 ev->ev_fieldwidth - ev->ev_fieldskip - 2, 180 ev->ev_name); 181 } else 182 (void) fprintf(a->pa_outputfile, "%c/%*s ", 183 c, ev->ev_fieldwidth - 2, ev->ev_name); 184 } 185 186 (void) fflush(a->pa_outputfile); 187 } 188 189 void 190 pmcstat_print_counters(struct pmcstat_args *a) 191 { 192 int extra_width; 193 struct pmcstat_ev *ev; 194 pmc_value_t value; 195 196 extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; 197 198 STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 199 200 /* skip sampling mode counters */ 201 if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 202 continue; 203 204 if (pmc_read(ev->ev_pmcid, &value) < 0) 205 err(EX_OSERR, "ERROR: Cannot read pmc " 206 "\"%s\"", ev->ev_name); 207 208 (void) fprintf(a->pa_outputfile, "%*ju ", 209 ev->ev_fieldwidth + extra_width, (uintmax_t) 210 ev->ev_cumulative ? value : (value - ev->ev_saved)); 211 if (ev->ev_cumulative == 0) 212 ev->ev_saved = value; 213 extra_width = 0; 214 } 215 216 (void) fflush(a->pa_outputfile); 217 } 218 219 /* 220 * Print output 221 */ 222 223 void 224 pmcstat_print_pmcs(struct pmcstat_args *a) 225 { 226 static int linecount = 0; 227 228 if (++linecount > pmcstat_displayheight) { 229 (void) fprintf(a->pa_outputfile, "\n"); 230 linecount = 1; 231 } 232 233 if (linecount == 1) 234 pmcstat_print_headers(a); 235 236 (void) fprintf(a->pa_outputfile, "\n"); 237 pmcstat_print_counters(a); 238 239 return; 240 } 241 242 /* 243 * Do process profiling 244 * 245 * If a pid was specified, attach each allocated PMC to the target 246 * process. Otherwise, fork a child and attach the PMCs to the child, 247 * and have the child exec() the target program. 248 */ 249 250 void 251 pmcstat_setup_process(struct pmcstat_args *a) 252 { 253 char token; 254 struct pmcstat_ev *ev; 255 struct kevent kev; 256 257 if (a->pa_flags & FLAG_HAS_PID) { 258 259 STAILQ_FOREACH(ev, &args.pa_head, ev_next) 260 if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 261 err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to " 262 "process %d", ev->ev_name, (int) a->pa_pid); 263 264 } else { 265 266 /* 267 * We need to fork a new process and startup the child 268 * using execvp(). Before doing the exec() the child 269 * process reads its pipe for a token so that the parent 270 * can finish doing its pmc_attach() calls. 271 */ 272 273 if (pipe(pmcstat_pipefd) < 0) 274 err(EX_OSERR, "ERROR: cannot create pipe"); 275 276 switch (a->pa_pid = fork()) { 277 case -1: 278 err(EX_OSERR, "ERROR: cannot fork"); 279 /*NOTREACHED*/ 280 281 case 0: /* child */ 282 283 /* wait for our parent to signal us */ 284 (void) close(pmcstat_pipefd[WRITEPIPEFD]); 285 if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0) 286 err(EX_OSERR, "ERROR (child): cannot read " 287 "token"); 288 (void) close(pmcstat_pipefd[READPIPEFD]); 289 290 /* exec() the program requested */ 291 execvp(*args.pa_argv, args.pa_argv); 292 err(EX_OSERR, "ERROR (child): execvp failed"); 293 /*NOTREACHED*/ 294 295 default: /* parent */ 296 297 (void) close(pmcstat_pipefd[READPIPEFD]); 298 299 /* attach all our PMCs to the child */ 300 STAILQ_FOREACH(ev, &args.pa_head, ev_next) 301 if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) && 302 pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 303 err(EX_OSERR, "ERROR: cannot attach pmc " 304 "\"%s\" to process %d", ev->ev_name, 305 (int) a->pa_pid); 306 307 } 308 } 309 310 /* Ask to be notified via a kevent when the child exits */ 311 EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0); 312 313 if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 314 err(EX_OSERR, "ERROR: cannot monitor process %d", 315 a->pa_pid); 316 317 return; 318 } 319 320 void 321 pmcstat_start_process(struct pmcstat_args *a) 322 { 323 324 /* nothing to do: target is already running */ 325 if (a->pa_flags & FLAG_HAS_PID) 326 return; 327 328 /* write token to child to state that we are ready */ 329 if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1) 330 err(EX_OSERR, "ERROR: write failed"); 331 332 (void) close(pmcstat_pipefd[WRITEPIPEFD]); 333 } 334 335 void 336 pmcstat_show_usage(void) 337 { 338 errx(EX_USAGE, 339 "[options] [commandline]\n" 340 "\t Measure process and/or system performance using hardware\n" 341 "\t performance monitoring counters.\n" 342 "\t Options include:\n" 343 "\t -C\t\t toggle showing cumulative counts\n" 344 "\t -O file\t set sampling log file to \"file\"\n" 345 "\t -P spec\t allocate process-private sampling PMC\n" 346 "\t -S spec\t allocate system-wide sampling PMC\n" 347 "\t -c cpu\t\t set default cpu\n" 348 "\t -d\t\t toggle tracking descendants\n" 349 "\t -n rate\t set sampling rate\n" 350 "\t -o file\t send print output to \"file\"\n" 351 "\t -p spec\t allocate process-private counting PMC\n" 352 "\t -s spec\t allocate system-wide counting PMC\n" 353 "\t -t pid\t attach to running process with pid \"pid\"\n" 354 "\t -w secs\t set printing time interval" 355 ); 356 } 357 358 /* 359 * Main 360 */ 361 362 int 363 main(int argc, char **argv) 364 { 365 double interval; 366 int option, npmc, ncpu; 367 int c, current_cpu, current_sampling_count; 368 int running; 369 int do_descendants, use_cumulative_counts; 370 pid_t pid; 371 char *end; 372 struct pmcstat_ev *ev; 373 struct pmc_op_getpmcinfo *ppmci; 374 struct sigaction sa; 375 struct kevent kev; 376 struct winsize ws; 377 378 current_cpu = 0; 379 current_sampling_count = DEFAULT_SAMPLE_COUNT; 380 do_descendants = 0; 381 use_cumulative_counts = 0; 382 args.pa_flags = 0; 383 args.pa_pid = (pid_t) -1; 384 args.pa_logfile = NULL; 385 args.pa_outputfile = stderr; 386 args.pa_interval = DEFAULT_WAIT_INTERVAL; 387 STAILQ_INIT(&args.pa_head); 388 389 ev = NULL; 390 391 while ((option = getopt(argc, argv, "CO:P:S:c:dn:o:p:s:t:w:")) != -1) 392 switch (option) { 393 case 'C': /* cumulative values */ 394 use_cumulative_counts = !use_cumulative_counts; 395 break; 396 397 case 'c': /* CPU */ 398 current_cpu = strtol(optarg, &end, 0); 399 if (*end != '\0' || current_cpu < 0) 400 errx(EX_USAGE, 401 "ERROR: Illegal CPU number \"%s\"", 402 optarg); 403 404 break; 405 406 case 'd': /* toggle descendents */ 407 do_descendants = !do_descendants; 408 break; 409 410 case 'p': /* process virtual counting PMC */ 411 case 's': /* system-wide counting PMC */ 412 case 'P': /* process virtual sampling PMC */ 413 case 'S': /* system-wide sampling PMC */ 414 if ((ev = malloc(sizeof(*ev))) == NULL) 415 errx(EX_SOFTWARE, "ERROR: Out of memory"); 416 417 switch (option) { 418 case 'p': ev->ev_mode = PMC_MODE_TC; break; 419 case 's': ev->ev_mode = PMC_MODE_SC; break; 420 case 'P': ev->ev_mode = PMC_MODE_TS; break; 421 case 'S': ev->ev_mode = PMC_MODE_SS; break; 422 } 423 424 if (option == 'P' || option == 'p') 425 args.pa_flags |= FLAG_USING_PROCESS_PMC; 426 427 if (option == 'P' || option == 'S') 428 args.pa_flags |= FLAG_USING_SAMPLING; 429 430 if (option == 'p' || option == 's') 431 args.pa_flags |= FLAG_USING_COUNTING; 432 433 ev->ev_spec = strdup(optarg); 434 435 if (option == 'S' || option == 'P') 436 ev->ev_count = current_sampling_count; 437 else 438 ev->ev_count = -1; 439 440 if (option == 'S' || option == 's') 441 ev->ev_cpu = current_cpu; 442 else 443 ev->ev_cpu = PMC_CPU_ANY; 444 445 ev->ev_descendants = do_descendants; 446 ev->ev_cumulative = use_cumulative_counts; 447 448 ev->ev_saved = 0LL; 449 ev->ev_pmcid = PMC_ID_INVALID; 450 451 /* extract event name */ 452 c = strcspn(optarg, ", \t"); 453 ev->ev_name = malloc(c + 1); 454 (void) strncpy(ev->ev_name, optarg, c); 455 *(ev->ev_name + c) = '\0'; 456 457 STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next); 458 459 break; 460 461 case 'n': /* sampling count */ 462 current_sampling_count = strtol(optarg, &end, 0); 463 if (*end != '\0' || current_sampling_count <= 0) 464 errx(EX_USAGE, 465 "ERROR: Illegal count value \"%s\"", 466 optarg); 467 break; 468 469 case 'o': /* outputfile */ 470 if (args.pa_outputfile != NULL) 471 (void) fclose(args.pa_outputfile); 472 473 if ((args.pa_outputfile = fopen(optarg, "w")) == NULL) 474 errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 475 "writing", optarg); 476 477 case 'O': /* sampling output */ 478 if (args.pa_logfile != NULL) 479 (void) fclose(args.pa_logfile); 480 481 if ((args.pa_logfile = fopen(optarg, "w")) == NULL) 482 errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 483 "writing", optarg); 484 break; 485 486 case 't': /* target pid */ 487 pid = strtol(optarg, &end, 0); 488 if (*end != '\0' || pid <= 0) 489 errx(EX_USAGE, "ERROR: Illegal pid value " 490 "\"%s\"", optarg); 491 492 args.pa_flags |= FLAG_HAS_PID; 493 args.pa_pid = pid; 494 495 break; 496 497 case 'w': /* wait interval */ 498 interval = strtod(optarg, &end); 499 if (*end != '\0' || interval <= 0) 500 errx(EX_USAGE, "ERROR: Illegal wait interval " 501 "value \"%s\"", optarg); 502 args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; 503 args.pa_interval = interval; 504 505 break; 506 507 case '?': 508 default: 509 pmcstat_show_usage(); 510 break; 511 512 } 513 514 args.pa_argc = (argc -= optind); 515 args.pa_argv = (argv += optind); 516 517 if (argc) 518 args.pa_flags |= FLAG_HAS_PROCESS; 519 520 /* 521 * Check invocation syntax. 522 */ 523 524 if (STAILQ_EMPTY(&args.pa_head)) { 525 warnx("ERROR: At least one PMC event must be specified"); 526 pmcstat_show_usage(); 527 } 528 529 if (argc == 0) { 530 if (args.pa_pid == -1) { 531 if (args.pa_flags & FLAG_USING_PROCESS_PMC) 532 errx(EX_USAGE, "ERROR: the -P or -p options " 533 "require a target process"); 534 } else if ((args.pa_flags & FLAG_USING_PROCESS_PMC) == 0) 535 errx(EX_USAGE, 536 "ERROR: option -t requires a process-mode pmc " 537 "specification"); 538 } else if (args.pa_pid != -1) 539 errx(EX_USAGE, 540 "ERROR: option -t cannot be specified with a command " 541 "name"); 542 543 if (pmc_init() < 0) 544 err(EX_UNAVAILABLE, 545 "ERROR: Initialization of the pmc(3) library failed"); 546 547 if ((ncpu = pmc_ncpu()) < 0) 548 err(EX_OSERR, "ERROR: Cannot determine the number CPUs " 549 "on the system"); 550 551 if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */ 552 err(EX_OSERR, "ERROR: Cannot determine the number of PMCs " 553 "on CPU %d", 0); 554 555 /* 556 * Allocate PMCs. 557 */ 558 559 if (pmc_pmcinfo(0, &ppmci) < 0) 560 err(EX_OSERR, "ERROR: cannot retrieve pmc information"); 561 562 assert(ppmci != NULL); 563 564 STAILQ_FOREACH(ev, &args.pa_head, ev_next) 565 if (pmc_allocate(ev->ev_spec, ev->ev_mode, 566 (ev->ev_descendants ? PMC_F_DESCENDANTS : 0), 567 ev->ev_cpu, &ev->ev_pmcid) < 0) 568 err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with " 569 "specification \"%s\"", 570 PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process", 571 ev->ev_spec); 572 573 /* compute printout widths */ 574 STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 575 int pmc_width; 576 int pmc_display_width; 577 int pmc_header_width; 578 579 pmc_width = ppmci->pm_pmcs[ev->ev_pmcid].pm_width; 580 pmc_header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */ 581 pmc_display_width = (int) floor(pmc_width / 3.32193) + 1; 582 583 if (pmc_header_width > pmc_display_width) { 584 ev->ev_fieldskip = 0; 585 ev->ev_fieldwidth = pmc_header_width; 586 } else { 587 ev->ev_fieldskip = pmc_display_width - 588 pmc_header_width; 589 ev->ev_fieldwidth = pmc_display_width; 590 } 591 } 592 593 /* Allocate a kqueue */ 594 if ((pmcstat_kq = kqueue()) < 0) 595 err(EX_OSERR, "ERROR: Cannot allocate kqueue"); 596 597 /* 598 * If our output is being set to a terminal, register a handler 599 * for window size changes. 600 */ 601 602 if (isatty(fileno(args.pa_outputfile))) { 603 604 if (ioctl(fileno(args.pa_outputfile), TIOCGWINSZ, &ws) < 0) 605 err(EX_OSERR, "ERROR: Cannot determine window size"); 606 607 pmcstat_displayheight = ws.ws_row - 1; 608 609 EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 610 611 if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 612 err(EX_OSERR, "ERROR: Cannot register kevent for " 613 "SIGWINCH"); 614 } 615 616 EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 617 618 if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 619 err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); 620 621 if (args.pa_flags & FLAG_USING_SAMPLING) { 622 623 /* 624 * configure log file 625 */ 626 627 if (args.pa_logfile == NULL) 628 if ((args.pa_logfile = 629 fopen(DEFAULT_LOGFILE_NAME, "w")) == NULL) 630 err(EX_CANTCREAT, "ERROR: Cannot open sampling " 631 "log file \"%s\"", DEFAULT_LOGFILE_NAME); 632 633 if (pmc_configure_logfile(fileno(args.pa_logfile)) < 0) 634 err(EX_OSERR, "ERROR: Cannot configure sampling " 635 "log"); 636 637 STAILQ_FOREACH(ev, &args.pa_head, ev_next) 638 if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && 639 pmc_set(ev->ev_pmcid, ev->ev_count) < 0) 640 err(EX_OSERR, "ERROR: Cannot set sampling count " 641 "for PMC \"%s\"", ev->ev_name); 642 } 643 644 /* setup a timer for any counting mode PMCs */ 645 if (args.pa_flags & FLAG_USING_COUNTING) { 646 EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 647 args.pa_interval * 1000, NULL); 648 649 if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 650 err(EX_OSERR, "ERROR: Cannot register kevent for " 651 "timer"); 652 } 653 654 /* attach PMCs to the target process, starting it if specified */ 655 if (args.pa_flags & FLAG_HAS_PROCESS) 656 pmcstat_setup_process(&args); 657 658 /* start the pmcs */ 659 pmcstat_start_pmcs(&args); 660 661 /* start the (commandline) process if needed */ 662 if (args.pa_flags & FLAG_HAS_PROCESS) 663 pmcstat_start_process(&args); 664 665 /* Handle SIGINT using the kqueue loop */ 666 sa.sa_handler = SIG_IGN; 667 sa.sa_flags = 0; 668 (void) sigemptyset(&sa.sa_mask); 669 670 if (sigaction(SIGINT, &sa, NULL) < 0) 671 err(EX_OSERR, "ERROR: Cannot install signal handler"); 672 673 /* 674 * loop till either the target process (if any) exits, or we 675 * are killed by a SIGINT. 676 */ 677 678 running = 1; 679 do { 680 if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) { 681 if (errno != EINTR) 682 err(EX_OSERR, "ERROR: kevent failed"); 683 else 684 continue; 685 } 686 687 if (kev.flags & EV_ERROR) 688 errc(EX_OSERR, kev.data, "ERROR: kevent failed"); 689 690 switch (kev.filter) { 691 case EVFILT_PROC: /* target process exited */ 692 running = 0; 693 /* FALLTHROUGH */ 694 695 case EVFILT_TIMER: /* print out counting PMCs */ 696 pmcstat_print_pmcs(&args); 697 698 if (running == 0) /* final newline */ 699 (void) fprintf(args.pa_outputfile, "\n"); 700 break; 701 702 case EVFILT_SIGNAL: 703 if (kev.ident == SIGINT) { 704 /* pass the signal on to the child process */ 705 if ((args.pa_flags & FLAG_HAS_PROCESS) && 706 (args.pa_flags & FLAG_HAS_PID) == 0) 707 if (kill(args.pa_pid, SIGINT) != 0) 708 err(EX_OSERR, "cannot kill " 709 "child"); 710 running = 0; 711 } else if (kev.ident == SIGWINCH) { 712 if (ioctl(fileno(args.pa_outputfile), 713 TIOCGWINSZ, &ws) < 0) 714 err(EX_OSERR, "ERROR: Cannot determine " 715 "window size"); 716 pmcstat_displayheight = ws.ws_row - 1; 717 } else 718 assert(0); 719 720 break; 721 } 722 723 } while (running); 724 725 pmcstat_cleanup(&args); 726 727 return 0; 728 } 729