pmcstat.c revision 149805
1145256Sjkoshy/*- 2147191Sjkoshy * Copyright (c) 2003-2005, Joseph Koshy 3145256Sjkoshy * All rights reserved. 4145256Sjkoshy * 5145256Sjkoshy * Redistribution and use in source and binary forms, with or without 6145256Sjkoshy * modification, are permitted provided that the following conditions 7145256Sjkoshy * are met: 8145256Sjkoshy * 1. Redistributions of source code must retain the above copyright 9145256Sjkoshy * notice, this list of conditions and the following disclaimer. 10145256Sjkoshy * 2. Redistributions in binary form must reproduce the above copyright 11145256Sjkoshy * notice, this list of conditions and the following disclaimer in the 12145256Sjkoshy * documentation and/or other materials provided with the distribution. 13145256Sjkoshy * 14145256Sjkoshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15145256Sjkoshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16145256Sjkoshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17145256Sjkoshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18145256Sjkoshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19145256Sjkoshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20145256Sjkoshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21145256Sjkoshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22145256Sjkoshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23145256Sjkoshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24145256Sjkoshy * SUCH DAMAGE. 25145256Sjkoshy */ 26145256Sjkoshy 27145256Sjkoshy#include <sys/cdefs.h> 28145256Sjkoshy__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcstat.c 149805 2005-09-05 18:31:09Z jkoshy $"); 29145256Sjkoshy 30145256Sjkoshy#include <sys/types.h> 31145256Sjkoshy#include <sys/event.h> 32145256Sjkoshy#include <sys/queue.h> 33147708Sjkoshy#include <sys/socket.h> 34147708Sjkoshy#include <sys/stat.h> 35145256Sjkoshy#include <sys/time.h> 36145256Sjkoshy#include <sys/ttycom.h> 37145256Sjkoshy#include <sys/wait.h> 38145256Sjkoshy 39145256Sjkoshy#include <assert.h> 40145256Sjkoshy#include <err.h> 41145256Sjkoshy#include <errno.h> 42145256Sjkoshy#include <fcntl.h> 43145256Sjkoshy#include <limits.h> 44145256Sjkoshy#include <math.h> 45145256Sjkoshy#include <pmc.h> 46147191Sjkoshy#include <pmclog.h> 47145256Sjkoshy#include <signal.h> 48145256Sjkoshy#include <stdarg.h> 49147708Sjkoshy#include <stdint.h> 50145256Sjkoshy#include <stdio.h> 51145256Sjkoshy#include <stdlib.h> 52145256Sjkoshy#include <string.h> 53145256Sjkoshy#include <sysexits.h> 54145256Sjkoshy#include <unistd.h> 55145256Sjkoshy 56147708Sjkoshy#include "pmcstat.h" 57147708Sjkoshy 58147191Sjkoshy/* 59147191Sjkoshy * A given invocation of pmcstat(8) can manage multiple PMCs of both 60147191Sjkoshy * the system-wide and per-process variety. Each of these could be in 61147191Sjkoshy * 'counting mode' or in 'sampling mode'. 62147191Sjkoshy * 63147191Sjkoshy * For 'counting mode' PMCs, pmcstat(8) will periodically issue a 64147191Sjkoshy * pmc_read() at the configured time interval and print out the value 65147191Sjkoshy * of the requested PMCs. 66147191Sjkoshy * 67147191Sjkoshy * For 'sampling mode' PMCs it can log to a file for offline analysis, 68147191Sjkoshy * or can analyse sampling data "on the fly", either by converting 69147191Sjkoshy * samples to printed textual form or by creating gprof(1) compatible 70147191Sjkoshy * profiles, one per program executed. When creating gprof(1) 71147191Sjkoshy * profiles it can optionally merge entries from multiple processes 72147191Sjkoshy * for a given executable into a single profile file. 73147191Sjkoshy */ 74147191Sjkoshy 75147708Sjkoshy/* Globals */ 76145256Sjkoshy 77145256Sjkoshyint pmcstat_interrupt = 0; 78145256Sjkoshyint pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; 79145256Sjkoshyint pmcstat_pipefd[NPIPEFD]; 80145256Sjkoshyint pmcstat_kq; 81145256Sjkoshy 82145256Sjkoshy/* 83145256Sjkoshy * cleanup 84145256Sjkoshy */ 85145256Sjkoshy 86145256Sjkoshyvoid 87145256Sjkoshypmcstat_cleanup(struct pmcstat_args *a) 88145256Sjkoshy{ 89145256Sjkoshy struct pmcstat_ev *ev, *tmp; 90145256Sjkoshy 91145256Sjkoshy /* release allocated PMCs. */ 92145256Sjkoshy STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp) 93145256Sjkoshy if (ev->ev_pmcid != PMC_ID_INVALID) { 94145256Sjkoshy if (pmc_release(ev->ev_pmcid) < 0) 95145256Sjkoshy err(EX_OSERR, "ERROR: cannot release pmc " 96147191Sjkoshy "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); 97145256Sjkoshy free(ev->ev_name); 98145256Sjkoshy free(ev->ev_spec); 99145256Sjkoshy STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next); 100145256Sjkoshy free(ev); 101145256Sjkoshy } 102147191Sjkoshy 103147863Sjkoshy /* de-configure the log file if present. */ 104147863Sjkoshy if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) 105147863Sjkoshy (void) pmc_configure_logfile(-1); 106147863Sjkoshy 107147191Sjkoshy if (a->pa_logparser) { 108147191Sjkoshy pmclog_close(a->pa_logparser); 109147191Sjkoshy a->pa_logparser = NULL; 110147191Sjkoshy } 111147708Sjkoshy 112147708Sjkoshy if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) 113147708Sjkoshy pmcstat_shutdown_logging(); 114145256Sjkoshy} 115145256Sjkoshy 116145256Sjkoshyvoid 117145256Sjkoshypmcstat_start_pmcs(struct pmcstat_args *a) 118145256Sjkoshy{ 119145256Sjkoshy struct pmcstat_ev *ev; 120145256Sjkoshy 121145256Sjkoshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 122145256Sjkoshy 123145256Sjkoshy assert(ev->ev_pmcid != PMC_ID_INVALID); 124145256Sjkoshy 125145256Sjkoshy if (pmc_start(ev->ev_pmcid) < 0) { 126147191Sjkoshy warn("ERROR: Cannot start pmc 0x%x \"%s\"", 127145256Sjkoshy ev->ev_pmcid, ev->ev_name); 128145256Sjkoshy pmcstat_cleanup(a); 129147191Sjkoshy exit(EX_OSERR); 130145256Sjkoshy } 131145256Sjkoshy } 132145256Sjkoshy 133145256Sjkoshy} 134145256Sjkoshy 135145256Sjkoshyvoid 136145256Sjkoshypmcstat_print_headers(struct pmcstat_args *a) 137145256Sjkoshy{ 138145256Sjkoshy struct pmcstat_ev *ev; 139145256Sjkoshy int c; 140145256Sjkoshy 141147708Sjkoshy (void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX); 142145256Sjkoshy 143145256Sjkoshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 144145256Sjkoshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 145145256Sjkoshy continue; 146145256Sjkoshy 147145256Sjkoshy c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; 148145256Sjkoshy 149145256Sjkoshy if (ev->ev_fieldskip != 0) { 150147708Sjkoshy (void) fprintf(a->pa_printfile, "%*s%c/%*s ", 151145256Sjkoshy ev->ev_fieldskip, "", c, 152145256Sjkoshy ev->ev_fieldwidth - ev->ev_fieldskip - 2, 153145256Sjkoshy ev->ev_name); 154145256Sjkoshy } else 155147708Sjkoshy (void) fprintf(a->pa_printfile, "%c/%*s ", 156145256Sjkoshy c, ev->ev_fieldwidth - 2, ev->ev_name); 157145256Sjkoshy } 158145256Sjkoshy 159147708Sjkoshy (void) fflush(a->pa_printfile); 160145256Sjkoshy} 161145256Sjkoshy 162145256Sjkoshyvoid 163145256Sjkoshypmcstat_print_counters(struct pmcstat_args *a) 164145256Sjkoshy{ 165145256Sjkoshy int extra_width; 166145256Sjkoshy struct pmcstat_ev *ev; 167145256Sjkoshy pmc_value_t value; 168145256Sjkoshy 169145256Sjkoshy extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; 170145256Sjkoshy 171145256Sjkoshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 172145256Sjkoshy 173145256Sjkoshy /* skip sampling mode counters */ 174145256Sjkoshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 175145256Sjkoshy continue; 176145256Sjkoshy 177145256Sjkoshy if (pmc_read(ev->ev_pmcid, &value) < 0) 178145256Sjkoshy err(EX_OSERR, "ERROR: Cannot read pmc " 179145256Sjkoshy "\"%s\"", ev->ev_name); 180145256Sjkoshy 181147708Sjkoshy (void) fprintf(a->pa_printfile, "%*ju ", 182147708Sjkoshy ev->ev_fieldwidth + extra_width, 183147708Sjkoshy (uintmax_t) ev->ev_cumulative ? value : 184147708Sjkoshy (value - ev->ev_saved)); 185147708Sjkoshy 186145256Sjkoshy if (ev->ev_cumulative == 0) 187145256Sjkoshy ev->ev_saved = value; 188145256Sjkoshy extra_width = 0; 189145256Sjkoshy } 190145256Sjkoshy 191147708Sjkoshy (void) fflush(a->pa_printfile); 192145256Sjkoshy} 193145256Sjkoshy 194145256Sjkoshy/* 195145256Sjkoshy * Print output 196145256Sjkoshy */ 197145256Sjkoshy 198145256Sjkoshyvoid 199145256Sjkoshypmcstat_print_pmcs(struct pmcstat_args *a) 200145256Sjkoshy{ 201145256Sjkoshy static int linecount = 0; 202145256Sjkoshy 203147708Sjkoshy /* check if we need to print a header line */ 204145256Sjkoshy if (++linecount > pmcstat_displayheight) { 205147708Sjkoshy (void) fprintf(a->pa_printfile, "\n"); 206145256Sjkoshy linecount = 1; 207145256Sjkoshy } 208145256Sjkoshy if (linecount == 1) 209145256Sjkoshy pmcstat_print_headers(a); 210147708Sjkoshy (void) fprintf(a->pa_printfile, "\n"); 211145256Sjkoshy 212145256Sjkoshy pmcstat_print_counters(a); 213145256Sjkoshy 214145256Sjkoshy return; 215145256Sjkoshy} 216145256Sjkoshy 217145256Sjkoshy/* 218145256Sjkoshy * Do process profiling 219145256Sjkoshy * 220145256Sjkoshy * If a pid was specified, attach each allocated PMC to the target 221145256Sjkoshy * process. Otherwise, fork a child and attach the PMCs to the child, 222145256Sjkoshy * and have the child exec() the target program. 223145256Sjkoshy */ 224145256Sjkoshy 225145256Sjkoshyvoid 226145256Sjkoshypmcstat_setup_process(struct pmcstat_args *a) 227145256Sjkoshy{ 228145256Sjkoshy char token; 229145256Sjkoshy struct pmcstat_ev *ev; 230145256Sjkoshy struct kevent kev; 231145256Sjkoshy 232145256Sjkoshy if (a->pa_flags & FLAG_HAS_PID) { 233147191Sjkoshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) 234145256Sjkoshy if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 235145256Sjkoshy err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to " 236145256Sjkoshy "process %d", ev->ev_name, (int) a->pa_pid); 237145256Sjkoshy } else { 238145256Sjkoshy 239145256Sjkoshy /* 240145256Sjkoshy * We need to fork a new process and startup the child 241145256Sjkoshy * using execvp(). Before doing the exec() the child 242145256Sjkoshy * process reads its pipe for a token so that the parent 243145256Sjkoshy * can finish doing its pmc_attach() calls. 244145256Sjkoshy */ 245145256Sjkoshy if (pipe(pmcstat_pipefd) < 0) 246145256Sjkoshy err(EX_OSERR, "ERROR: cannot create pipe"); 247145256Sjkoshy 248145256Sjkoshy switch (a->pa_pid = fork()) { 249145256Sjkoshy case -1: 250145256Sjkoshy err(EX_OSERR, "ERROR: cannot fork"); 251145256Sjkoshy /*NOTREACHED*/ 252145256Sjkoshy 253145256Sjkoshy case 0: /* child */ 254145256Sjkoshy 255145256Sjkoshy /* wait for our parent to signal us */ 256145256Sjkoshy (void) close(pmcstat_pipefd[WRITEPIPEFD]); 257145256Sjkoshy if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0) 258145256Sjkoshy err(EX_OSERR, "ERROR (child): cannot read " 259145256Sjkoshy "token"); 260145256Sjkoshy (void) close(pmcstat_pipefd[READPIPEFD]); 261145256Sjkoshy 262145256Sjkoshy /* exec() the program requested */ 263147191Sjkoshy execvp(*a->pa_argv, a->pa_argv); 264147191Sjkoshy /* and if that fails, notify the parent */ 265147191Sjkoshy kill(getppid(), SIGCHLD); 266147191Sjkoshy err(EX_OSERR, "ERROR: execvp \"%s\" failed", 267147191Sjkoshy *a->pa_argv); 268145256Sjkoshy /*NOTREACHED*/ 269145256Sjkoshy 270145256Sjkoshy default: /* parent */ 271145256Sjkoshy 272145256Sjkoshy (void) close(pmcstat_pipefd[READPIPEFD]); 273145256Sjkoshy 274145256Sjkoshy /* attach all our PMCs to the child */ 275145256Sjkoshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) 276145256Sjkoshy if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) && 277145256Sjkoshy pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 278145256Sjkoshy err(EX_OSERR, "ERROR: cannot attach pmc " 279145256Sjkoshy "\"%s\" to process %d", ev->ev_name, 280145256Sjkoshy (int) a->pa_pid); 281145256Sjkoshy 282145256Sjkoshy } 283145256Sjkoshy } 284145256Sjkoshy 285147191Sjkoshy /* Ask to be notified via a kevent when the target process exits */ 286147191Sjkoshy EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0, 287147191Sjkoshy NULL); 288145256Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 289147191Sjkoshy err(EX_OSERR, "ERROR: cannot monitor child process %d", 290145256Sjkoshy a->pa_pid); 291145256Sjkoshy return; 292145256Sjkoshy} 293145256Sjkoshy 294145256Sjkoshyvoid 295145256Sjkoshypmcstat_start_process(struct pmcstat_args *a) 296145256Sjkoshy{ 297145256Sjkoshy 298145256Sjkoshy /* nothing to do: target is already running */ 299145256Sjkoshy if (a->pa_flags & FLAG_HAS_PID) 300145256Sjkoshy return; 301145256Sjkoshy 302145256Sjkoshy /* write token to child to state that we are ready */ 303145256Sjkoshy if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1) 304145256Sjkoshy err(EX_OSERR, "ERROR: write failed"); 305145256Sjkoshy 306145256Sjkoshy (void) close(pmcstat_pipefd[WRITEPIPEFD]); 307145256Sjkoshy} 308145256Sjkoshy 309145256Sjkoshyvoid 310145256Sjkoshypmcstat_show_usage(void) 311145256Sjkoshy{ 312145256Sjkoshy errx(EX_USAGE, 313145256Sjkoshy "[options] [commandline]\n" 314145256Sjkoshy "\t Measure process and/or system performance using hardware\n" 315145256Sjkoshy "\t performance monitoring counters.\n" 316145256Sjkoshy "\t Options include:\n" 317147191Sjkoshy "\t -C\t\t (toggle) show cumulative counts\n" 318147191Sjkoshy "\t -D path\t create profiles in directory \"path\"\n" 319147191Sjkoshy "\t -E\t\t (toggle) show counts at process exit\n" 320147191Sjkoshy "\t -O file\t send log output to \"file\"\n" 321147191Sjkoshy "\t -P spec\t allocate a process-private sampling PMC\n" 322147191Sjkoshy "\t -R file\t read events from \"file\"\n" 323147191Sjkoshy "\t -S spec\t allocate a system-wide sampling PMC\n" 324147191Sjkoshy "\t -W\t\t (toggle) show counts per context switch\n" 325147191Sjkoshy "\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n" 326147191Sjkoshy "\t -d\t\t (toggle) track descendants\n" 327147191Sjkoshy "\t -g\t\t produce gprof(1) compatible profiles\n" 328148329Sjkoshy "\t -k file\t set the path to the kernel\n" 329145256Sjkoshy "\t -n rate\t set sampling rate\n" 330145256Sjkoshy "\t -o file\t send print output to \"file\"\n" 331147191Sjkoshy "\t -p spec\t allocate a process-private counting PMC\n" 332147191Sjkoshy "\t -s spec\t allocate a system-wide counting PMC\n" 333145796Sjkoshy "\t -t pid\t\t attach to running process with pid \"pid\"\n" 334145256Sjkoshy "\t -w secs\t set printing time interval" 335145256Sjkoshy ); 336145256Sjkoshy} 337145256Sjkoshy 338145256Sjkoshy/* 339145256Sjkoshy * Main 340145256Sjkoshy */ 341145256Sjkoshy 342145256Sjkoshyint 343145256Sjkoshymain(int argc, char **argv) 344145256Sjkoshy{ 345145256Sjkoshy double interval; 346145256Sjkoshy int option, npmc, ncpu; 347145256Sjkoshy int c, current_cpu, current_sampling_count; 348147191Sjkoshy int do_print, do_descendants; 349147191Sjkoshy int do_logproccsw, do_logprocexit; 350147191Sjkoshy int pipefd[2]; 351147191Sjkoshy int use_cumulative_counts; 352145256Sjkoshy pid_t pid; 353145256Sjkoshy char *end; 354147191Sjkoshy const char *errmsg; 355147191Sjkoshy enum pmcstat_state runstate; 356145256Sjkoshy struct pmcstat_ev *ev; 357145256Sjkoshy struct sigaction sa; 358145256Sjkoshy struct kevent kev; 359145256Sjkoshy struct winsize ws; 360147708Sjkoshy struct stat sb; 361145256Sjkoshy 362145256Sjkoshy current_cpu = 0; 363145256Sjkoshy current_sampling_count = DEFAULT_SAMPLE_COUNT; 364145256Sjkoshy do_descendants = 0; 365147191Sjkoshy do_logproccsw = 0; 366147191Sjkoshy do_logprocexit = 0; 367145256Sjkoshy use_cumulative_counts = 0; 368147191Sjkoshy args.pa_required = 0; 369145256Sjkoshy args.pa_flags = 0; 370145256Sjkoshy args.pa_pid = (pid_t) -1; 371147708Sjkoshy args.pa_logfd = -1; 372147708Sjkoshy args.pa_samplesdir = "."; 373147708Sjkoshy args.pa_kernel = "/boot/kernel/kernel"; 374147708Sjkoshy args.pa_printfile = stderr; 375145256Sjkoshy args.pa_interval = DEFAULT_WAIT_INTERVAL; 376145256Sjkoshy STAILQ_INIT(&args.pa_head); 377145256Sjkoshy 378145256Sjkoshy ev = NULL; 379145256Sjkoshy 380147708Sjkoshy while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgk:n:o:p:s:t:w:")) 381147191Sjkoshy != -1) 382145256Sjkoshy switch (option) { 383145256Sjkoshy case 'C': /* cumulative values */ 384145256Sjkoshy use_cumulative_counts = !use_cumulative_counts; 385147191Sjkoshy args.pa_required |= FLAG_HAS_COUNTING_PMCS; 386145256Sjkoshy break; 387145256Sjkoshy 388145256Sjkoshy case 'c': /* CPU */ 389145256Sjkoshy current_cpu = strtol(optarg, &end, 0); 390145256Sjkoshy if (*end != '\0' || current_cpu < 0) 391145256Sjkoshy errx(EX_USAGE, 392147191Sjkoshy "ERROR: Illegal CPU number \"%s\".", 393145256Sjkoshy optarg); 394147191Sjkoshy args.pa_required |= FLAG_HAS_SYSTEM_PMCS; 395145256Sjkoshy break; 396145256Sjkoshy 397147708Sjkoshy case 'D': 398147708Sjkoshy if (stat(optarg, &sb) < 0) 399147708Sjkoshy err(EX_OSERR, "ERROR: Cannot stat \"%s\"", 400147708Sjkoshy optarg); 401147708Sjkoshy if (!S_ISDIR(sb.st_mode)) 402147708Sjkoshy errx(EX_USAGE, "ERROR: \"%s\" is not a " 403147708Sjkoshy "directory", optarg); 404147708Sjkoshy args.pa_samplesdir = optarg; 405147708Sjkoshy args.pa_flags |= FLAG_HAS_SAMPLESDIR; 406147708Sjkoshy args.pa_required |= FLAG_DO_GPROF; 407147708Sjkoshy break; 408147708Sjkoshy 409145256Sjkoshy case 'd': /* toggle descendents */ 410145256Sjkoshy do_descendants = !do_descendants; 411147191Sjkoshy args.pa_required |= FLAG_HAS_PROCESS_PMCS; 412145256Sjkoshy break; 413145256Sjkoshy 414147191Sjkoshy case 'g': /* produce gprof compatible profiles */ 415147191Sjkoshy args.pa_flags |= FLAG_DO_GPROF; 416147191Sjkoshy break; 417147191Sjkoshy 418147708Sjkoshy case 'k': /* pathname to the kernel */ 419147708Sjkoshy args.pa_kernel = optarg; 420147708Sjkoshy args.pa_required |= FLAG_DO_GPROF; 421147708Sjkoshy args.pa_flags |= FLAG_HAS_KERNELPATH; 422147191Sjkoshy break; 423147191Sjkoshy 424147191Sjkoshy case 'E': /* log process exit */ 425147191Sjkoshy do_logprocexit = !do_logprocexit; 426147191Sjkoshy args.pa_required |= (FLAG_HAS_PROCESS_PMCS | 427147708Sjkoshy FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE); 428147191Sjkoshy break; 429147191Sjkoshy 430145256Sjkoshy case 'p': /* process virtual counting PMC */ 431145256Sjkoshy case 's': /* system-wide counting PMC */ 432145256Sjkoshy case 'P': /* process virtual sampling PMC */ 433145256Sjkoshy case 'S': /* system-wide sampling PMC */ 434145256Sjkoshy if ((ev = malloc(sizeof(*ev))) == NULL) 435147191Sjkoshy errx(EX_SOFTWARE, "ERROR: Out of memory."); 436145256Sjkoshy 437145256Sjkoshy switch (option) { 438145256Sjkoshy case 'p': ev->ev_mode = PMC_MODE_TC; break; 439145256Sjkoshy case 's': ev->ev_mode = PMC_MODE_SC; break; 440145256Sjkoshy case 'P': ev->ev_mode = PMC_MODE_TS; break; 441145256Sjkoshy case 'S': ev->ev_mode = PMC_MODE_SS; break; 442145256Sjkoshy } 443145256Sjkoshy 444147191Sjkoshy if (option == 'P' || option == 'p') { 445147191Sjkoshy args.pa_flags |= FLAG_HAS_PROCESS_PMCS; 446147708Sjkoshy args.pa_required |= (FLAG_HAS_COMMANDLINE | 447147191Sjkoshy FLAG_HAS_PID); 448147191Sjkoshy } 449145256Sjkoshy 450147191Sjkoshy if (option == 'P' || option == 'S') { 451147191Sjkoshy args.pa_flags |= FLAG_HAS_SAMPLING_PMCS; 452147708Sjkoshy args.pa_required |= (FLAG_HAS_PIPE | 453147708Sjkoshy FLAG_HAS_OUTPUT_LOGFILE); 454147191Sjkoshy } 455145256Sjkoshy 456145256Sjkoshy if (option == 'p' || option == 's') 457147191Sjkoshy args.pa_flags |= FLAG_HAS_COUNTING_PMCS; 458145256Sjkoshy 459147191Sjkoshy if (option == 's' || option == 'S') 460147191Sjkoshy args.pa_flags |= FLAG_HAS_SYSTEM_PMCS; 461147191Sjkoshy 462145256Sjkoshy ev->ev_spec = strdup(optarg); 463145256Sjkoshy 464145256Sjkoshy if (option == 'S' || option == 'P') 465145256Sjkoshy ev->ev_count = current_sampling_count; 466145256Sjkoshy else 467145256Sjkoshy ev->ev_count = -1; 468145256Sjkoshy 469145256Sjkoshy if (option == 'S' || option == 's') 470145256Sjkoshy ev->ev_cpu = current_cpu; 471145256Sjkoshy else 472145256Sjkoshy ev->ev_cpu = PMC_CPU_ANY; 473145256Sjkoshy 474147191Sjkoshy ev->ev_flags = 0; 475147191Sjkoshy if (do_descendants) 476147191Sjkoshy ev->ev_flags |= PMC_F_DESCENDANTS; 477147191Sjkoshy if (do_logprocexit) 478147191Sjkoshy ev->ev_flags |= PMC_F_LOG_PROCEXIT; 479147191Sjkoshy if (do_logproccsw) 480147191Sjkoshy ev->ev_flags |= PMC_F_LOG_PROCCSW; 481147191Sjkoshy 482145256Sjkoshy ev->ev_cumulative = use_cumulative_counts; 483145256Sjkoshy 484145256Sjkoshy ev->ev_saved = 0LL; 485145256Sjkoshy ev->ev_pmcid = PMC_ID_INVALID; 486145256Sjkoshy 487145256Sjkoshy /* extract event name */ 488145256Sjkoshy c = strcspn(optarg, ", \t"); 489145256Sjkoshy ev->ev_name = malloc(c + 1); 490145256Sjkoshy (void) strncpy(ev->ev_name, optarg, c); 491145256Sjkoshy *(ev->ev_name + c) = '\0'; 492145256Sjkoshy 493145256Sjkoshy STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next); 494145256Sjkoshy 495145256Sjkoshy break; 496145256Sjkoshy 497145256Sjkoshy case 'n': /* sampling count */ 498145256Sjkoshy current_sampling_count = strtol(optarg, &end, 0); 499145256Sjkoshy if (*end != '\0' || current_sampling_count <= 0) 500145256Sjkoshy errx(EX_USAGE, 501147191Sjkoshy "ERROR: Illegal count value \"%s\".", 502145256Sjkoshy optarg); 503147191Sjkoshy args.pa_required |= FLAG_HAS_SAMPLING_PMCS; 504145256Sjkoshy break; 505145256Sjkoshy 506145256Sjkoshy case 'o': /* outputfile */ 507147708Sjkoshy if (args.pa_printfile != NULL) 508147708Sjkoshy (void) fclose(args.pa_printfile); 509147708Sjkoshy if ((args.pa_printfile = fopen(optarg, "w")) == NULL) 510145256Sjkoshy errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 511147191Sjkoshy "writing.", optarg); 512147708Sjkoshy args.pa_flags |= FLAG_DO_PRINT; 513147191Sjkoshy break; 514145256Sjkoshy 515145256Sjkoshy case 'O': /* sampling output */ 516147708Sjkoshy if (args.pa_outputpath) 517147708Sjkoshy errx(EX_USAGE, "ERROR: option -O may only be " 518147191Sjkoshy "specified once."); 519147708Sjkoshy args.pa_outputpath = optarg; 520147708Sjkoshy args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE; 521145256Sjkoshy break; 522145256Sjkoshy 523147708Sjkoshy case 'R': /* read an existing log file */ 524147708Sjkoshy if (args.pa_logparser != NULL) 525147708Sjkoshy errx(EX_USAGE, "ERROR: option -R may only be " 526147708Sjkoshy "specified once."); 527147708Sjkoshy args.pa_inputpath = optarg; 528147708Sjkoshy if (args.pa_printfile == stderr) 529147708Sjkoshy args.pa_printfile = stdout; 530147708Sjkoshy args.pa_flags |= FLAG_READ_LOGFILE; 531147708Sjkoshy break; 532147708Sjkoshy 533145256Sjkoshy case 't': /* target pid */ 534145256Sjkoshy pid = strtol(optarg, &end, 0); 535145256Sjkoshy if (*end != '\0' || pid <= 0) 536145256Sjkoshy errx(EX_USAGE, "ERROR: Illegal pid value " 537147191Sjkoshy "\"%s\".", optarg); 538145256Sjkoshy 539145256Sjkoshy args.pa_flags |= FLAG_HAS_PID; 540147191Sjkoshy args.pa_required |= FLAG_HAS_PROCESS_PMCS; 541145256Sjkoshy args.pa_pid = pid; 542145256Sjkoshy break; 543145256Sjkoshy 544145256Sjkoshy case 'w': /* wait interval */ 545145256Sjkoshy interval = strtod(optarg, &end); 546145256Sjkoshy if (*end != '\0' || interval <= 0) 547145256Sjkoshy errx(EX_USAGE, "ERROR: Illegal wait interval " 548147191Sjkoshy "value \"%s\".", optarg); 549145256Sjkoshy args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; 550147708Sjkoshy args.pa_required |= FLAG_HAS_COUNTING_PMCS; 551145256Sjkoshy args.pa_interval = interval; 552147191Sjkoshy break; 553145256Sjkoshy 554147191Sjkoshy case 'W': /* toggle LOG_CSW */ 555147191Sjkoshy do_logproccsw = !do_logproccsw; 556147191Sjkoshy args.pa_required |= (FLAG_HAS_PROCESS_PMCS | 557147708Sjkoshy FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE); 558145256Sjkoshy break; 559145256Sjkoshy 560145256Sjkoshy case '?': 561145256Sjkoshy default: 562145256Sjkoshy pmcstat_show_usage(); 563145256Sjkoshy break; 564145256Sjkoshy 565145256Sjkoshy } 566145256Sjkoshy 567145256Sjkoshy args.pa_argc = (argc -= optind); 568145256Sjkoshy args.pa_argv = (argv += optind); 569145256Sjkoshy 570147708Sjkoshy if (argc) /* command line present */ 571147708Sjkoshy args.pa_flags |= FLAG_HAS_COMMANDLINE; 572145256Sjkoshy 573145256Sjkoshy /* 574145256Sjkoshy * Check invocation syntax. 575145256Sjkoshy */ 576145256Sjkoshy 577147708Sjkoshy /* disallow -O and -R together */ 578147708Sjkoshy if (args.pa_outputpath && args.pa_inputpath) 579147708Sjkoshy errx(EX_USAGE, "ERROR: options -O and -R are mutually " 580147708Sjkoshy "exclusive."); 581147708Sjkoshy 582147708Sjkoshy if (args.pa_flags & FLAG_READ_LOGFILE) { 583147191Sjkoshy errmsg = NULL; 584147708Sjkoshy if (args.pa_flags & FLAG_HAS_COMMANDLINE) 585147191Sjkoshy errmsg = "a command line specification"; 586147191Sjkoshy else if (args.pa_flags & FLAG_HAS_PID) 587147191Sjkoshy errmsg = "option -t"; 588147191Sjkoshy else if (!STAILQ_EMPTY(&args.pa_head)) 589147191Sjkoshy errmsg = "a PMC event specification"; 590147191Sjkoshy if (errmsg) 591147191Sjkoshy errx(EX_USAGE, "ERROR: option -R may not be used with " 592147191Sjkoshy "%s.", errmsg); 593147708Sjkoshy } else if (STAILQ_EMPTY(&args.pa_head)) 594147708Sjkoshy /* All other uses require a PMC spec. */ 595145256Sjkoshy pmcstat_show_usage(); 596145256Sjkoshy 597147191Sjkoshy /* check for -t pid without a process PMC spec */ 598147191Sjkoshy if ((args.pa_required & FLAG_HAS_PID) && 599147191Sjkoshy (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) 600147191Sjkoshy errx(EX_USAGE, "ERROR: option -t requires a process mode PMC " 601147191Sjkoshy "to be specified."); 602147191Sjkoshy 603147191Sjkoshy /* check for process-mode options without a command or -t pid */ 604147191Sjkoshy if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && 605147708Sjkoshy (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0) 606147708Sjkoshy errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require " 607147708Sjkoshy "a command line or target process."); 608147191Sjkoshy 609147191Sjkoshy /* check for -p | -P without a target process of some sort */ 610147708Sjkoshy if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) && 611147708Sjkoshy (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0) 612147708Sjkoshy errx(EX_USAGE, "ERROR: options -P and -p require a " 613147191Sjkoshy "target process or a command line."); 614147191Sjkoshy 615147191Sjkoshy /* check for process-mode options without a process-mode PMC */ 616147191Sjkoshy if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && 617147191Sjkoshy (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) 618147708Sjkoshy errx(EX_USAGE, "ERROR: options -d, -E, and -W require a " 619147191Sjkoshy "process mode PMC to be specified."); 620147191Sjkoshy 621147191Sjkoshy /* check for -c cpu and not system mode PMCs */ 622147191Sjkoshy if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) && 623147191Sjkoshy (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0) 624147191Sjkoshy errx(EX_USAGE, "ERROR: option -c requires at least one " 625147191Sjkoshy "system mode PMC to be specified."); 626147191Sjkoshy 627147191Sjkoshy /* check for counting mode options without a counting PMC */ 628147191Sjkoshy if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) && 629147191Sjkoshy (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0) 630149805Sjkoshy errx(EX_USAGE, "ERROR: options -C, -W, -o and -w require at " 631149805Sjkoshy "least one counting mode PMC to be specified."); 632147191Sjkoshy 633147191Sjkoshy /* check for sampling mode options without a sampling PMC spec */ 634147191Sjkoshy if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) && 635147191Sjkoshy (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0) 636147708Sjkoshy errx(EX_USAGE, "ERROR: options -n and -O require at least " 637147708Sjkoshy "one sampling mode PMC to be specified."); 638147191Sjkoshy 639147708Sjkoshy if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) == 640147708Sjkoshy (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) 641145256Sjkoshy errx(EX_USAGE, 642145256Sjkoshy "ERROR: option -t cannot be specified with a command " 643147191Sjkoshy "line."); 644145256Sjkoshy 645147708Sjkoshy /* check if -g is being used correctly */ 646147708Sjkoshy if ((args.pa_flags & FLAG_DO_GPROF) && 647147708Sjkoshy !(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE))) 648147708Sjkoshy errx(EX_USAGE, "ERROR: option -g requires sampling PMCs or -R " 649147708Sjkoshy "to be specified."); 650147708Sjkoshy 651147191Sjkoshy /* check if -O was spuriously specified */ 652147708Sjkoshy if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) && 653147708Sjkoshy (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) 654147191Sjkoshy errx(EX_USAGE, 655147191Sjkoshy "ERROR: option -O is used only with options " 656147708Sjkoshy "-E, -P, -S and -W."); 657147191Sjkoshy 658147708Sjkoshy /* -D dir and -k kernel path require -g */ 659147708Sjkoshy if ((args.pa_flags & FLAG_HAS_KERNELPATH) && 660147708Sjkoshy ((args.pa_flags & FLAG_DO_GPROF) == 0)) 661147708Sjkoshy errx(EX_USAGE, "ERROR: option -k is only used with -g."); 662147708Sjkoshy 663147708Sjkoshy if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) && 664147708Sjkoshy ((args.pa_flags & FLAG_DO_GPROF) == 0)) 665147708Sjkoshy errx(EX_USAGE, "ERROR: option -D is only used with -g."); 666147708Sjkoshy 667147708Sjkoshy /* 668147708Sjkoshy * Disallow textual output of sampling PMCs if counting PMCs 669147708Sjkoshy * have also been asked for, mostly because the combined output 670147708Sjkoshy * is difficult to make sense of. 671147708Sjkoshy */ 672147708Sjkoshy if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && 673147708Sjkoshy (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) && 674147708Sjkoshy ((args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)) 675147708Sjkoshy errx(EX_USAGE, "ERROR: option -O is required if counting and " 676147708Sjkoshy "sampling PMCs are specified together."); 677147708Sjkoshy 678147191Sjkoshy /* if we've been asked to process a log file, do that and exit */ 679147708Sjkoshy if (args.pa_flags & FLAG_READ_LOGFILE) { 680147708Sjkoshy /* 681147708Sjkoshy * Print the log in textual form if we haven't been 682147708Sjkoshy * asked to generate gmon.out files. 683147708Sjkoshy */ 684147708Sjkoshy if ((args.pa_flags & FLAG_DO_GPROF) == 0) 685147708Sjkoshy args.pa_flags |= FLAG_DO_PRINT; 686147708Sjkoshy 687147708Sjkoshy pmcstat_initialize_logging(&args); 688147708Sjkoshy if ((args.pa_logfd = pmcstat_open(args.pa_inputpath, 689147708Sjkoshy PMCSTAT_OPEN_FOR_READ)) < 0) 690147708Sjkoshy err(EX_OSERR, "ERROR: Cannot open \"%s\" for " 691147708Sjkoshy "reading", args.pa_inputpath); 692147708Sjkoshy if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL) 693147708Sjkoshy err(EX_OSERR, "ERROR: Cannot create parser"); 694147191Sjkoshy pmcstat_process_log(&args); 695147191Sjkoshy exit(EX_OK); 696147191Sjkoshy } 697147191Sjkoshy 698147191Sjkoshy /* otherwise, we've been asked to collect data */ 699145256Sjkoshy if (pmc_init() < 0) 700145256Sjkoshy err(EX_UNAVAILABLE, 701145256Sjkoshy "ERROR: Initialization of the pmc(3) library failed"); 702145256Sjkoshy 703145256Sjkoshy if ((ncpu = pmc_ncpu()) < 0) 704145256Sjkoshy err(EX_OSERR, "ERROR: Cannot determine the number CPUs " 705145256Sjkoshy "on the system"); 706145256Sjkoshy 707145256Sjkoshy if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */ 708145256Sjkoshy err(EX_OSERR, "ERROR: Cannot determine the number of PMCs " 709145256Sjkoshy "on CPU %d", 0); 710145256Sjkoshy 711147708Sjkoshy /* Allocate a kqueue */ 712147708Sjkoshy if ((pmcstat_kq = kqueue()) < 0) 713147708Sjkoshy err(EX_OSERR, "ERROR: Cannot allocate kqueue"); 714147708Sjkoshy 715145256Sjkoshy /* 716147708Sjkoshy * Configure the specified log file or setup a default log 717147708Sjkoshy * consumer via a pipe. 718147708Sjkoshy */ 719147708Sjkoshy if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) { 720147708Sjkoshy if (args.pa_outputpath) { 721147708Sjkoshy if ((args.pa_logfd = pmcstat_open(args.pa_outputpath, 722147708Sjkoshy PMCSTAT_OPEN_FOR_WRITE)) < 0) 723147708Sjkoshy err(EX_OSERR, "ERROR: Cannot open \"%s\" for " 724147708Sjkoshy "writing", args.pa_outputpath); 725147708Sjkoshy } else { 726147708Sjkoshy /* 727147708Sjkoshy * process the log on the fly by reading it in 728147708Sjkoshy * through a pipe. 729147708Sjkoshy */ 730147708Sjkoshy if (pipe(pipefd) < 0) 731147708Sjkoshy err(EX_OSERR, "ERROR: pipe(2) failed"); 732147708Sjkoshy 733147708Sjkoshy if (fcntl(pipefd[READPIPEFD], F_SETFL, O_NONBLOCK) < 0) 734147708Sjkoshy err(EX_OSERR, "ERROR: fcntl(2) failed"); 735147708Sjkoshy 736147708Sjkoshy EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD, 737147708Sjkoshy 0, 0, NULL); 738147708Sjkoshy 739147708Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 740147708Sjkoshy err(EX_OSERR, "ERROR: Cannot register kevent"); 741147708Sjkoshy 742147708Sjkoshy args.pa_logfd = pipefd[WRITEPIPEFD]; 743147708Sjkoshy 744147708Sjkoshy args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT); 745147708Sjkoshy args.pa_logparser = pmclog_open(pipefd[READPIPEFD]); 746147708Sjkoshy } 747147708Sjkoshy 748147708Sjkoshy if (pmc_configure_logfile(args.pa_logfd) < 0) 749147708Sjkoshy err(EX_OSERR, "ERROR: Cannot configure log file"); 750147708Sjkoshy } 751147708Sjkoshy 752147708Sjkoshy /* 753145256Sjkoshy * Allocate PMCs. 754145256Sjkoshy */ 755145256Sjkoshy 756147708Sjkoshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 757145256Sjkoshy if (pmc_allocate(ev->ev_spec, ev->ev_mode, 758147191Sjkoshy ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0) 759145256Sjkoshy err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with " 760145256Sjkoshy "specification \"%s\"", 761145256Sjkoshy PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process", 762145256Sjkoshy ev->ev_spec); 763145256Sjkoshy 764147708Sjkoshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && 765147708Sjkoshy pmc_set(ev->ev_pmcid, ev->ev_count) < 0) 766147708Sjkoshy err(EX_OSERR, "ERROR: Cannot set sampling count " 767147708Sjkoshy "for PMC \"%s\"", ev->ev_name); 768147708Sjkoshy } 769147708Sjkoshy 770145256Sjkoshy /* compute printout widths */ 771145256Sjkoshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 772145774Sjkoshy int counter_width; 773145774Sjkoshy int display_width; 774145774Sjkoshy int header_width; 775145256Sjkoshy 776145774Sjkoshy (void) pmc_width(ev->ev_pmcid, &counter_width); 777145774Sjkoshy header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */ 778145774Sjkoshy display_width = (int) floor(counter_width / 3.32193) + 1; 779145256Sjkoshy 780145774Sjkoshy if (header_width > display_width) { 781145256Sjkoshy ev->ev_fieldskip = 0; 782145774Sjkoshy ev->ev_fieldwidth = header_width; 783145256Sjkoshy } else { 784145774Sjkoshy ev->ev_fieldskip = display_width - 785145774Sjkoshy header_width; 786145774Sjkoshy ev->ev_fieldwidth = display_width; 787145256Sjkoshy } 788145256Sjkoshy } 789145256Sjkoshy 790145256Sjkoshy /* 791145256Sjkoshy * If our output is being set to a terminal, register a handler 792145256Sjkoshy * for window size changes. 793145256Sjkoshy */ 794145256Sjkoshy 795147708Sjkoshy if (isatty(fileno(args.pa_printfile))) { 796145256Sjkoshy 797147708Sjkoshy if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0) 798145256Sjkoshy err(EX_OSERR, "ERROR: Cannot determine window size"); 799145256Sjkoshy 800145256Sjkoshy pmcstat_displayheight = ws.ws_row - 1; 801145256Sjkoshy 802145256Sjkoshy EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 803145256Sjkoshy 804145256Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 805145256Sjkoshy err(EX_OSERR, "ERROR: Cannot register kevent for " 806145256Sjkoshy "SIGWINCH"); 807145256Sjkoshy } 808145256Sjkoshy 809145256Sjkoshy EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 810145256Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 811145256Sjkoshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); 812145256Sjkoshy 813147191Sjkoshy EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 814147191Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 815147191Sjkoshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO"); 816145256Sjkoshy 817147191Sjkoshy /* 818147191Sjkoshy * An exec() failure of a forked child is signalled by the 819147191Sjkoshy * child sending the parent a SIGCHLD. We don't register an 820147191Sjkoshy * actual signal handler for SIGCHLD, but instead use our 821147191Sjkoshy * kqueue to pick up the signal. 822147191Sjkoshy */ 823147191Sjkoshy EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 824147191Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 825147191Sjkoshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD"); 826145256Sjkoshy 827147708Sjkoshy /* setup a timer if we have counting mode PMCs needing to be printed */ 828147708Sjkoshy if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && 829147708Sjkoshy (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { 830145256Sjkoshy EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 831145256Sjkoshy args.pa_interval * 1000, NULL); 832145256Sjkoshy 833145256Sjkoshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 834145256Sjkoshy err(EX_OSERR, "ERROR: Cannot register kevent for " 835145256Sjkoshy "timer"); 836145256Sjkoshy } 837145256Sjkoshy 838145256Sjkoshy /* attach PMCs to the target process, starting it if specified */ 839147708Sjkoshy if (args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) 840145256Sjkoshy pmcstat_setup_process(&args); 841145256Sjkoshy 842145256Sjkoshy /* start the pmcs */ 843145256Sjkoshy pmcstat_start_pmcs(&args); 844145256Sjkoshy 845145256Sjkoshy /* start the (commandline) process if needed */ 846147708Sjkoshy if (args.pa_flags & FLAG_HAS_COMMANDLINE) 847145256Sjkoshy pmcstat_start_process(&args); 848145256Sjkoshy 849147708Sjkoshy /* initialize logging if printing the configured log */ 850147708Sjkoshy if ((args.pa_flags & FLAG_DO_PRINT) && 851147708Sjkoshy (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))) 852147708Sjkoshy pmcstat_initialize_logging(&args); 853147708Sjkoshy 854145256Sjkoshy /* Handle SIGINT using the kqueue loop */ 855145256Sjkoshy sa.sa_handler = SIG_IGN; 856145256Sjkoshy sa.sa_flags = 0; 857145256Sjkoshy (void) sigemptyset(&sa.sa_mask); 858145256Sjkoshy 859145256Sjkoshy if (sigaction(SIGINT, &sa, NULL) < 0) 860145256Sjkoshy err(EX_OSERR, "ERROR: Cannot install signal handler"); 861145256Sjkoshy 862145256Sjkoshy /* 863145256Sjkoshy * loop till either the target process (if any) exits, or we 864145256Sjkoshy * are killed by a SIGINT. 865145256Sjkoshy */ 866147191Sjkoshy runstate = PMCSTAT_RUNNING; 867147191Sjkoshy do_print = 0; 868145256Sjkoshy do { 869145256Sjkoshy if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) { 870145256Sjkoshy if (errno != EINTR) 871145256Sjkoshy err(EX_OSERR, "ERROR: kevent failed"); 872145256Sjkoshy else 873145256Sjkoshy continue; 874145256Sjkoshy } 875145256Sjkoshy 876145256Sjkoshy if (kev.flags & EV_ERROR) 877145256Sjkoshy errc(EX_OSERR, kev.data, "ERROR: kevent failed"); 878145256Sjkoshy 879145256Sjkoshy switch (kev.filter) { 880147191Sjkoshy case EVFILT_PROC: /* target has exited */ 881147708Sjkoshy if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | 882147708Sjkoshy FLAG_HAS_PIPE)) 883147191Sjkoshy runstate = pmcstat_close_log(&args); 884148688Sjkoshy else 885148688Sjkoshy runstate = PMCSTAT_FINISHED; 886148688Sjkoshy do_print = 1; 887147191Sjkoshy break; 888145256Sjkoshy 889147191Sjkoshy case EVFILT_READ: /* log file data is present */ 890147863Sjkoshy runstate = pmcstat_process_log(&args); 891145256Sjkoshy break; 892145256Sjkoshy 893145256Sjkoshy case EVFILT_SIGNAL: 894147191Sjkoshy if (kev.ident == SIGCHLD) { 895147191Sjkoshy /* 896147191Sjkoshy * The child process sends us a 897147191Sjkoshy * SIGCHLD if its exec() failed. We 898147191Sjkoshy * wait for it to exit and then exit 899147191Sjkoshy * ourselves. 900147191Sjkoshy */ 901147191Sjkoshy (void) wait(&c); 902147191Sjkoshy runstate = PMCSTAT_FINISHED; 903147191Sjkoshy } else if (kev.ident == SIGIO) { 904147191Sjkoshy /* 905147191Sjkoshy * We get a SIGIO if a PMC loses all 906147191Sjkoshy * of its targets, or if logfile 907147191Sjkoshy * writes encounter an error. 908147191Sjkoshy */ 909147708Sjkoshy if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | 910147708Sjkoshy FLAG_HAS_PIPE)) { 911147191Sjkoshy runstate = pmcstat_close_log(&args); 912147708Sjkoshy if (args.pa_flags & 913147708Sjkoshy (FLAG_DO_PRINT|FLAG_DO_GPROF)) 914147708Sjkoshy pmcstat_process_log(&args); 915147708Sjkoshy } 916147191Sjkoshy do_print = 1; /* print PMCs at exit */ 917147191Sjkoshy runstate = PMCSTAT_FINISHED; 918147191Sjkoshy } else if (kev.ident == SIGINT) { 919147708Sjkoshy /* Kill the child process if we started it */ 920147708Sjkoshy if (args.pa_flags & FLAG_HAS_COMMANDLINE) 921145256Sjkoshy if (kill(args.pa_pid, SIGINT) != 0) 922147191Sjkoshy err(EX_OSERR, "ERROR: cannot " 923147191Sjkoshy "signal child process"); 924147191Sjkoshy runstate = PMCSTAT_FINISHED; 925145256Sjkoshy } else if (kev.ident == SIGWINCH) { 926147708Sjkoshy if (ioctl(fileno(args.pa_printfile), 927145256Sjkoshy TIOCGWINSZ, &ws) < 0) 928145256Sjkoshy err(EX_OSERR, "ERROR: Cannot determine " 929145256Sjkoshy "window size"); 930145256Sjkoshy pmcstat_displayheight = ws.ws_row - 1; 931145256Sjkoshy } else 932145256Sjkoshy assert(0); 933145256Sjkoshy 934145256Sjkoshy break; 935147191Sjkoshy 936147191Sjkoshy case EVFILT_TIMER: /* print out counting PMCs */ 937147191Sjkoshy do_print = 1; 938147191Sjkoshy break; 939147191Sjkoshy 940145256Sjkoshy } 941145256Sjkoshy 942147708Sjkoshy if (do_print && 943147708Sjkoshy (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { 944147191Sjkoshy pmcstat_print_pmcs(&args); 945147708Sjkoshy if (runstate == PMCSTAT_FINISHED && /* final newline */ 946147708Sjkoshy (args.pa_flags & FLAG_DO_PRINT) == 0) 947147708Sjkoshy (void) fprintf(args.pa_printfile, "\n"); 948147191Sjkoshy do_print = 0; 949147191Sjkoshy } 950145256Sjkoshy 951147191Sjkoshy } while (runstate != PMCSTAT_FINISHED); 952147191Sjkoshy 953147191Sjkoshy /* flush any pending log entries */ 954147708Sjkoshy if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) 955147191Sjkoshy pmc_flush_logfile(); 956147191Sjkoshy 957145256Sjkoshy pmcstat_cleanup(&args); 958145256Sjkoshy 959145256Sjkoshy return 0; 960145256Sjkoshy} 961