1ebccf1e3SJoseph Koshy /*- 2f263522aSJoseph Koshy * Copyright (c) 2003-2005, Joseph Koshy 3ebccf1e3SJoseph Koshy * All rights reserved. 4ebccf1e3SJoseph Koshy * 5ebccf1e3SJoseph Koshy * Redistribution and use in source and binary forms, with or without 6ebccf1e3SJoseph Koshy * modification, are permitted provided that the following conditions 7ebccf1e3SJoseph Koshy * are met: 8ebccf1e3SJoseph Koshy * 1. Redistributions of source code must retain the above copyright 9ebccf1e3SJoseph Koshy * notice, this list of conditions and the following disclaimer. 10ebccf1e3SJoseph Koshy * 2. Redistributions in binary form must reproduce the above copyright 11ebccf1e3SJoseph Koshy * notice, this list of conditions and the following disclaimer in the 12ebccf1e3SJoseph Koshy * documentation and/or other materials provided with the distribution. 13ebccf1e3SJoseph Koshy * 14ebccf1e3SJoseph Koshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ebccf1e3SJoseph Koshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ebccf1e3SJoseph Koshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ebccf1e3SJoseph Koshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ebccf1e3SJoseph Koshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ebccf1e3SJoseph Koshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ebccf1e3SJoseph Koshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ebccf1e3SJoseph Koshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ebccf1e3SJoseph Koshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ebccf1e3SJoseph Koshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ebccf1e3SJoseph Koshy * SUCH DAMAGE. 25ebccf1e3SJoseph Koshy */ 26ebccf1e3SJoseph Koshy 27ebccf1e3SJoseph Koshy #include <sys/cdefs.h> 28ebccf1e3SJoseph Koshy __FBSDID("$FreeBSD$"); 29ebccf1e3SJoseph Koshy 30ebccf1e3SJoseph Koshy #include <sys/types.h> 31ebccf1e3SJoseph Koshy #include <sys/event.h> 32ebccf1e3SJoseph Koshy #include <sys/queue.h> 3315139246SJoseph Koshy #include <sys/socket.h> 3415139246SJoseph Koshy #include <sys/stat.h> 35ebccf1e3SJoseph Koshy #include <sys/time.h> 36ebccf1e3SJoseph Koshy #include <sys/ttycom.h> 37ebccf1e3SJoseph Koshy #include <sys/wait.h> 38ebccf1e3SJoseph Koshy 39ebccf1e3SJoseph Koshy #include <assert.h> 40ebccf1e3SJoseph Koshy #include <err.h> 41ebccf1e3SJoseph Koshy #include <errno.h> 42ebccf1e3SJoseph Koshy #include <fcntl.h> 43ebccf1e3SJoseph Koshy #include <limits.h> 44ebccf1e3SJoseph Koshy #include <math.h> 45ebccf1e3SJoseph Koshy #include <pmc.h> 46f263522aSJoseph Koshy #include <pmclog.h> 47ebccf1e3SJoseph Koshy #include <signal.h> 48ebccf1e3SJoseph Koshy #include <stdarg.h> 49ebccf1e3SJoseph Koshy #include <stdint.h> 5015139246SJoseph Koshy #include <stdio.h> 51ebccf1e3SJoseph Koshy #include <stdlib.h> 52ebccf1e3SJoseph Koshy #include <string.h> 53ebccf1e3SJoseph Koshy #include <sysexits.h> 54ebccf1e3SJoseph Koshy #include <unistd.h> 55ebccf1e3SJoseph Koshy 5615139246SJoseph Koshy #include "pmcstat.h" 5715139246SJoseph Koshy 58f263522aSJoseph Koshy /* 59f263522aSJoseph Koshy * A given invocation of pmcstat(8) can manage multiple PMCs of both 60f263522aSJoseph Koshy * the system-wide and per-process variety. Each of these could be in 61f263522aSJoseph Koshy * 'counting mode' or in 'sampling mode'. 62f263522aSJoseph Koshy * 63f263522aSJoseph Koshy * For 'counting mode' PMCs, pmcstat(8) will periodically issue a 64f263522aSJoseph Koshy * pmc_read() at the configured time interval and print out the value 65f263522aSJoseph Koshy * of the requested PMCs. 66f263522aSJoseph Koshy * 67f263522aSJoseph Koshy * For 'sampling mode' PMCs it can log to a file for offline analysis, 68f263522aSJoseph Koshy * or can analyse sampling data "on the fly", either by converting 69f263522aSJoseph Koshy * samples to printed textual form or by creating gprof(1) compatible 70f263522aSJoseph Koshy * profiles, one per program executed. When creating gprof(1) 71f263522aSJoseph Koshy * profiles it can optionally merge entries from multiple processes 72f263522aSJoseph Koshy * for a given executable into a single profile file. 73f263522aSJoseph Koshy */ 74f263522aSJoseph Koshy 7515139246SJoseph Koshy /* Globals */ 76ebccf1e3SJoseph Koshy 77ebccf1e3SJoseph Koshy int pmcstat_interrupt = 0; 78ebccf1e3SJoseph Koshy int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; 79ebccf1e3SJoseph Koshy int pmcstat_pipefd[NPIPEFD]; 80ebccf1e3SJoseph Koshy int pmcstat_kq; 81ebccf1e3SJoseph Koshy 82ebccf1e3SJoseph Koshy /* 83ebccf1e3SJoseph Koshy * cleanup 84ebccf1e3SJoseph Koshy */ 85ebccf1e3SJoseph Koshy 86ebccf1e3SJoseph Koshy void 87ebccf1e3SJoseph Koshy pmcstat_cleanup(struct pmcstat_args *a) 88ebccf1e3SJoseph Koshy { 89ebccf1e3SJoseph Koshy struct pmcstat_ev *ev, *tmp; 90ebccf1e3SJoseph Koshy 91ebccf1e3SJoseph Koshy /* release allocated PMCs. */ 92ebccf1e3SJoseph Koshy STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp) 93ebccf1e3SJoseph Koshy if (ev->ev_pmcid != PMC_ID_INVALID) { 94ebccf1e3SJoseph Koshy if (pmc_release(ev->ev_pmcid) < 0) 95ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot release pmc " 96f263522aSJoseph Koshy "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); 97ebccf1e3SJoseph Koshy free(ev->ev_name); 98ebccf1e3SJoseph Koshy free(ev->ev_spec); 99ebccf1e3SJoseph Koshy STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next); 100ebccf1e3SJoseph Koshy free(ev); 101ebccf1e3SJoseph Koshy } 102f263522aSJoseph Koshy 103dc1d9d2eSJoseph Koshy /* de-configure the log file if present. */ 104dc1d9d2eSJoseph Koshy if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) 105dc1d9d2eSJoseph Koshy (void) pmc_configure_logfile(-1); 106dc1d9d2eSJoseph Koshy 107f263522aSJoseph Koshy if (a->pa_logparser) { 108f263522aSJoseph Koshy pmclog_close(a->pa_logparser); 109f263522aSJoseph Koshy a->pa_logparser = NULL; 110f263522aSJoseph Koshy } 11115139246SJoseph Koshy 11215139246SJoseph Koshy if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)) 11315139246SJoseph Koshy pmcstat_shutdown_logging(); 114ebccf1e3SJoseph Koshy } 115ebccf1e3SJoseph Koshy 116ebccf1e3SJoseph Koshy void 117ebccf1e3SJoseph Koshy pmcstat_start_pmcs(struct pmcstat_args *a) 118ebccf1e3SJoseph Koshy { 119ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 120ebccf1e3SJoseph Koshy 121ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 122ebccf1e3SJoseph Koshy 123ebccf1e3SJoseph Koshy assert(ev->ev_pmcid != PMC_ID_INVALID); 124ebccf1e3SJoseph Koshy 125ebccf1e3SJoseph Koshy if (pmc_start(ev->ev_pmcid) < 0) { 126f263522aSJoseph Koshy warn("ERROR: Cannot start pmc 0x%x \"%s\"", 127ebccf1e3SJoseph Koshy ev->ev_pmcid, ev->ev_name); 128ebccf1e3SJoseph Koshy pmcstat_cleanup(a); 129f263522aSJoseph Koshy exit(EX_OSERR); 130ebccf1e3SJoseph Koshy } 131ebccf1e3SJoseph Koshy } 132ebccf1e3SJoseph Koshy 133ebccf1e3SJoseph Koshy } 134ebccf1e3SJoseph Koshy 135ebccf1e3SJoseph Koshy void 136ebccf1e3SJoseph Koshy pmcstat_print_headers(struct pmcstat_args *a) 137ebccf1e3SJoseph Koshy { 138ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 139ebccf1e3SJoseph Koshy int c; 140ebccf1e3SJoseph Koshy 14115139246SJoseph Koshy (void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX); 142ebccf1e3SJoseph Koshy 143ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 144ebccf1e3SJoseph Koshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 145ebccf1e3SJoseph Koshy continue; 146ebccf1e3SJoseph Koshy 147ebccf1e3SJoseph Koshy c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; 148ebccf1e3SJoseph Koshy 149ebccf1e3SJoseph Koshy if (ev->ev_fieldskip != 0) { 15015139246SJoseph Koshy (void) fprintf(a->pa_printfile, "%*s%c/%*s ", 151ebccf1e3SJoseph Koshy ev->ev_fieldskip, "", c, 152ebccf1e3SJoseph Koshy ev->ev_fieldwidth - ev->ev_fieldskip - 2, 153ebccf1e3SJoseph Koshy ev->ev_name); 154ebccf1e3SJoseph Koshy } else 15515139246SJoseph Koshy (void) fprintf(a->pa_printfile, "%c/%*s ", 156ebccf1e3SJoseph Koshy c, ev->ev_fieldwidth - 2, ev->ev_name); 157ebccf1e3SJoseph Koshy } 158ebccf1e3SJoseph Koshy 15915139246SJoseph Koshy (void) fflush(a->pa_printfile); 160ebccf1e3SJoseph Koshy } 161ebccf1e3SJoseph Koshy 162ebccf1e3SJoseph Koshy void 163ebccf1e3SJoseph Koshy pmcstat_print_counters(struct pmcstat_args *a) 164ebccf1e3SJoseph Koshy { 165ebccf1e3SJoseph Koshy int extra_width; 166ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 167ebccf1e3SJoseph Koshy pmc_value_t value; 168ebccf1e3SJoseph Koshy 169ebccf1e3SJoseph Koshy extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; 170ebccf1e3SJoseph Koshy 171ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 172ebccf1e3SJoseph Koshy 173ebccf1e3SJoseph Koshy /* skip sampling mode counters */ 174ebccf1e3SJoseph Koshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 175ebccf1e3SJoseph Koshy continue; 176ebccf1e3SJoseph Koshy 177ebccf1e3SJoseph Koshy if (pmc_read(ev->ev_pmcid, &value) < 0) 178ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot read pmc " 179ebccf1e3SJoseph Koshy "\"%s\"", ev->ev_name); 180ebccf1e3SJoseph Koshy 18115139246SJoseph Koshy (void) fprintf(a->pa_printfile, "%*ju ", 18215139246SJoseph Koshy ev->ev_fieldwidth + extra_width, 18315139246SJoseph Koshy (uintmax_t) ev->ev_cumulative ? value : 18415139246SJoseph Koshy (value - ev->ev_saved)); 18515139246SJoseph Koshy 186ebccf1e3SJoseph Koshy if (ev->ev_cumulative == 0) 187ebccf1e3SJoseph Koshy ev->ev_saved = value; 188ebccf1e3SJoseph Koshy extra_width = 0; 189ebccf1e3SJoseph Koshy } 190ebccf1e3SJoseph Koshy 19115139246SJoseph Koshy (void) fflush(a->pa_printfile); 192ebccf1e3SJoseph Koshy } 193ebccf1e3SJoseph Koshy 194ebccf1e3SJoseph Koshy /* 195ebccf1e3SJoseph Koshy * Print output 196ebccf1e3SJoseph Koshy */ 197ebccf1e3SJoseph Koshy 198ebccf1e3SJoseph Koshy void 199ebccf1e3SJoseph Koshy pmcstat_print_pmcs(struct pmcstat_args *a) 200ebccf1e3SJoseph Koshy { 201ebccf1e3SJoseph Koshy static int linecount = 0; 202ebccf1e3SJoseph Koshy 20315139246SJoseph Koshy /* check if we need to print a header line */ 204ebccf1e3SJoseph Koshy if (++linecount > pmcstat_displayheight) { 20515139246SJoseph Koshy (void) fprintf(a->pa_printfile, "\n"); 206ebccf1e3SJoseph Koshy linecount = 1; 207ebccf1e3SJoseph Koshy } 208ebccf1e3SJoseph Koshy if (linecount == 1) 209ebccf1e3SJoseph Koshy pmcstat_print_headers(a); 21015139246SJoseph Koshy (void) fprintf(a->pa_printfile, "\n"); 211ebccf1e3SJoseph Koshy 212ebccf1e3SJoseph Koshy pmcstat_print_counters(a); 213ebccf1e3SJoseph Koshy 214ebccf1e3SJoseph Koshy return; 215ebccf1e3SJoseph Koshy } 216ebccf1e3SJoseph Koshy 217ebccf1e3SJoseph Koshy /* 218ebccf1e3SJoseph Koshy * Do process profiling 219ebccf1e3SJoseph Koshy * 220ebccf1e3SJoseph Koshy * If a pid was specified, attach each allocated PMC to the target 221ebccf1e3SJoseph Koshy * process. Otherwise, fork a child and attach the PMCs to the child, 222ebccf1e3SJoseph Koshy * and have the child exec() the target program. 223ebccf1e3SJoseph Koshy */ 224ebccf1e3SJoseph Koshy 225ebccf1e3SJoseph Koshy void 226ebccf1e3SJoseph Koshy pmcstat_setup_process(struct pmcstat_args *a) 227ebccf1e3SJoseph Koshy { 228ebccf1e3SJoseph Koshy char token; 229ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 230ebccf1e3SJoseph Koshy struct kevent kev; 231ebccf1e3SJoseph Koshy 232ebccf1e3SJoseph Koshy if (a->pa_flags & FLAG_HAS_PID) { 233f263522aSJoseph Koshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) 234ebccf1e3SJoseph Koshy if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 235ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to " 236ebccf1e3SJoseph Koshy "process %d", ev->ev_name, (int) a->pa_pid); 237ebccf1e3SJoseph Koshy } else { 238ebccf1e3SJoseph Koshy 239ebccf1e3SJoseph Koshy /* 240ebccf1e3SJoseph Koshy * We need to fork a new process and startup the child 241ebccf1e3SJoseph Koshy * using execvp(). Before doing the exec() the child 242ebccf1e3SJoseph Koshy * process reads its pipe for a token so that the parent 243ebccf1e3SJoseph Koshy * can finish doing its pmc_attach() calls. 244ebccf1e3SJoseph Koshy */ 245ebccf1e3SJoseph Koshy if (pipe(pmcstat_pipefd) < 0) 246ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot create pipe"); 247ebccf1e3SJoseph Koshy 248ebccf1e3SJoseph Koshy switch (a->pa_pid = fork()) { 249ebccf1e3SJoseph Koshy case -1: 250ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot fork"); 251ebccf1e3SJoseph Koshy /*NOTREACHED*/ 252ebccf1e3SJoseph Koshy 253ebccf1e3SJoseph Koshy case 0: /* child */ 254ebccf1e3SJoseph Koshy 255ebccf1e3SJoseph Koshy /* wait for our parent to signal us */ 256ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[WRITEPIPEFD]); 257ebccf1e3SJoseph Koshy if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0) 258ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR (child): cannot read " 259ebccf1e3SJoseph Koshy "token"); 260ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[READPIPEFD]); 261ebccf1e3SJoseph Koshy 262ebccf1e3SJoseph Koshy /* exec() the program requested */ 263f263522aSJoseph Koshy execvp(*a->pa_argv, a->pa_argv); 264f263522aSJoseph Koshy /* and if that fails, notify the parent */ 265f263522aSJoseph Koshy kill(getppid(), SIGCHLD); 266f263522aSJoseph Koshy err(EX_OSERR, "ERROR: execvp \"%s\" failed", 267f263522aSJoseph Koshy *a->pa_argv); 268ebccf1e3SJoseph Koshy /*NOTREACHED*/ 269ebccf1e3SJoseph Koshy 270ebccf1e3SJoseph Koshy default: /* parent */ 271ebccf1e3SJoseph Koshy 272ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[READPIPEFD]); 273ebccf1e3SJoseph Koshy 274ebccf1e3SJoseph Koshy /* attach all our PMCs to the child */ 275ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) 276ebccf1e3SJoseph Koshy if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) && 277ebccf1e3SJoseph Koshy pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 278ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot attach pmc " 279ebccf1e3SJoseph Koshy "\"%s\" to process %d", ev->ev_name, 280ebccf1e3SJoseph Koshy (int) a->pa_pid); 281ebccf1e3SJoseph Koshy 282ebccf1e3SJoseph Koshy } 283ebccf1e3SJoseph Koshy } 284ebccf1e3SJoseph Koshy 285f263522aSJoseph Koshy /* Ask to be notified via a kevent when the target process exits */ 286f263522aSJoseph Koshy EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0, 287f263522aSJoseph Koshy NULL); 288ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 289f263522aSJoseph Koshy err(EX_OSERR, "ERROR: cannot monitor child process %d", 290ebccf1e3SJoseph Koshy a->pa_pid); 291ebccf1e3SJoseph Koshy return; 292ebccf1e3SJoseph Koshy } 293ebccf1e3SJoseph Koshy 294ebccf1e3SJoseph Koshy void 295ebccf1e3SJoseph Koshy pmcstat_start_process(struct pmcstat_args *a) 296ebccf1e3SJoseph Koshy { 297ebccf1e3SJoseph Koshy 298ebccf1e3SJoseph Koshy /* nothing to do: target is already running */ 299ebccf1e3SJoseph Koshy if (a->pa_flags & FLAG_HAS_PID) 300ebccf1e3SJoseph Koshy return; 301ebccf1e3SJoseph Koshy 302ebccf1e3SJoseph Koshy /* write token to child to state that we are ready */ 303ebccf1e3SJoseph Koshy if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1) 304ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: write failed"); 305ebccf1e3SJoseph Koshy 306ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[WRITEPIPEFD]); 307ebccf1e3SJoseph Koshy } 308ebccf1e3SJoseph Koshy 309ebccf1e3SJoseph Koshy void 310ebccf1e3SJoseph Koshy pmcstat_show_usage(void) 311ebccf1e3SJoseph Koshy { 312ebccf1e3SJoseph Koshy errx(EX_USAGE, 313ebccf1e3SJoseph Koshy "[options] [commandline]\n" 314ebccf1e3SJoseph Koshy "\t Measure process and/or system performance using hardware\n" 315ebccf1e3SJoseph Koshy "\t performance monitoring counters.\n" 316ebccf1e3SJoseph Koshy "\t Options include:\n" 317f263522aSJoseph Koshy "\t -C\t\t (toggle) show cumulative counts\n" 318f263522aSJoseph Koshy "\t -D path\t create profiles in directory \"path\"\n" 319f263522aSJoseph Koshy "\t -E\t\t (toggle) show counts at process exit\n" 320f263522aSJoseph Koshy "\t -O file\t send log output to \"file\"\n" 321f263522aSJoseph Koshy "\t -P spec\t allocate a process-private sampling PMC\n" 322f263522aSJoseph Koshy "\t -R file\t read events from \"file\"\n" 323f263522aSJoseph Koshy "\t -S spec\t allocate a system-wide sampling PMC\n" 324f263522aSJoseph Koshy "\t -W\t\t (toggle) show counts per context switch\n" 325f263522aSJoseph Koshy "\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n" 326f263522aSJoseph Koshy "\t -d\t\t (toggle) track descendants\n" 327f263522aSJoseph Koshy "\t -g\t\t produce gprof(1) compatible profiles\n" 328ebccf1e3SJoseph Koshy "\t -n rate\t set sampling rate\n" 329ebccf1e3SJoseph Koshy "\t -o file\t send print output to \"file\"\n" 330f263522aSJoseph Koshy "\t -p spec\t allocate a process-private counting PMC\n" 331f263522aSJoseph Koshy "\t -s spec\t allocate a system-wide counting PMC\n" 33239f4e0fcSJoseph Koshy "\t -t pid\t\t attach to running process with pid \"pid\"\n" 333ebccf1e3SJoseph Koshy "\t -w secs\t set printing time interval" 334ebccf1e3SJoseph Koshy ); 335ebccf1e3SJoseph Koshy } 336ebccf1e3SJoseph Koshy 337ebccf1e3SJoseph Koshy /* 338ebccf1e3SJoseph Koshy * Main 339ebccf1e3SJoseph Koshy */ 340ebccf1e3SJoseph Koshy 341ebccf1e3SJoseph Koshy int 342ebccf1e3SJoseph Koshy main(int argc, char **argv) 343ebccf1e3SJoseph Koshy { 344ebccf1e3SJoseph Koshy double interval; 345ebccf1e3SJoseph Koshy int option, npmc, ncpu; 346ebccf1e3SJoseph Koshy int c, current_cpu, current_sampling_count; 347f263522aSJoseph Koshy int do_print, do_descendants; 348f263522aSJoseph Koshy int do_logproccsw, do_logprocexit; 349f263522aSJoseph Koshy int pipefd[2]; 350f263522aSJoseph Koshy int use_cumulative_counts; 351ebccf1e3SJoseph Koshy pid_t pid; 352ebccf1e3SJoseph Koshy char *end; 353f263522aSJoseph Koshy const char *errmsg; 354f263522aSJoseph Koshy enum pmcstat_state runstate; 355ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 356ebccf1e3SJoseph Koshy struct sigaction sa; 357ebccf1e3SJoseph Koshy struct kevent kev; 358ebccf1e3SJoseph Koshy struct winsize ws; 35915139246SJoseph Koshy struct stat sb; 360ebccf1e3SJoseph Koshy 361ebccf1e3SJoseph Koshy current_cpu = 0; 362ebccf1e3SJoseph Koshy current_sampling_count = DEFAULT_SAMPLE_COUNT; 363ebccf1e3SJoseph Koshy do_descendants = 0; 364f263522aSJoseph Koshy do_logproccsw = 0; 365f263522aSJoseph Koshy do_logprocexit = 0; 366ebccf1e3SJoseph Koshy use_cumulative_counts = 0; 367f263522aSJoseph Koshy args.pa_required = 0; 368ebccf1e3SJoseph Koshy args.pa_flags = 0; 369ebccf1e3SJoseph Koshy args.pa_pid = (pid_t) -1; 37015139246SJoseph Koshy args.pa_logfd = -1; 37115139246SJoseph Koshy args.pa_samplesdir = "."; 37215139246SJoseph Koshy args.pa_kernel = "/boot/kernel/kernel"; 37315139246SJoseph Koshy args.pa_printfile = stderr; 374ebccf1e3SJoseph Koshy args.pa_interval = DEFAULT_WAIT_INTERVAL; 375ebccf1e3SJoseph Koshy STAILQ_INIT(&args.pa_head); 376ebccf1e3SJoseph Koshy 377ebccf1e3SJoseph Koshy ev = NULL; 378ebccf1e3SJoseph Koshy 37915139246SJoseph Koshy while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgk:n:o:p:s:t:w:")) 380f263522aSJoseph Koshy != -1) 381ebccf1e3SJoseph Koshy switch (option) { 382ebccf1e3SJoseph Koshy case 'C': /* cumulative values */ 383ebccf1e3SJoseph Koshy use_cumulative_counts = !use_cumulative_counts; 384f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_COUNTING_PMCS; 385ebccf1e3SJoseph Koshy break; 386ebccf1e3SJoseph Koshy 387ebccf1e3SJoseph Koshy case 'c': /* CPU */ 388ebccf1e3SJoseph Koshy current_cpu = strtol(optarg, &end, 0); 389ebccf1e3SJoseph Koshy if (*end != '\0' || current_cpu < 0) 390ebccf1e3SJoseph Koshy errx(EX_USAGE, 391f263522aSJoseph Koshy "ERROR: Illegal CPU number \"%s\".", 392ebccf1e3SJoseph Koshy optarg); 393f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_SYSTEM_PMCS; 394ebccf1e3SJoseph Koshy break; 395ebccf1e3SJoseph Koshy 39615139246SJoseph Koshy case 'D': 39715139246SJoseph Koshy if (stat(optarg, &sb) < 0) 39815139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot stat \"%s\"", 39915139246SJoseph Koshy optarg); 40015139246SJoseph Koshy if (!S_ISDIR(sb.st_mode)) 40115139246SJoseph Koshy errx(EX_USAGE, "ERROR: \"%s\" is not a " 40215139246SJoseph Koshy "directory", optarg); 40315139246SJoseph Koshy args.pa_samplesdir = optarg; 40415139246SJoseph Koshy args.pa_flags |= FLAG_HAS_SAMPLESDIR; 40515139246SJoseph Koshy args.pa_required |= FLAG_DO_GPROF; 40615139246SJoseph Koshy break; 40715139246SJoseph Koshy 408ebccf1e3SJoseph Koshy case 'd': /* toggle descendents */ 409ebccf1e3SJoseph Koshy do_descendants = !do_descendants; 410f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_PROCESS_PMCS; 411f263522aSJoseph Koshy break; 412f263522aSJoseph Koshy 413f263522aSJoseph Koshy case 'g': /* produce gprof compatible profiles */ 414f263522aSJoseph Koshy args.pa_flags |= FLAG_DO_GPROF; 415f263522aSJoseph Koshy break; 416f263522aSJoseph Koshy 41715139246SJoseph Koshy case 'k': /* pathname to the kernel */ 41815139246SJoseph Koshy args.pa_kernel = optarg; 41915139246SJoseph Koshy args.pa_required |= FLAG_DO_GPROF; 42015139246SJoseph Koshy args.pa_flags |= FLAG_HAS_KERNELPATH; 421f263522aSJoseph Koshy break; 422f263522aSJoseph Koshy 423f263522aSJoseph Koshy case 'E': /* log process exit */ 424f263522aSJoseph Koshy do_logprocexit = !do_logprocexit; 425f263522aSJoseph Koshy args.pa_required |= (FLAG_HAS_PROCESS_PMCS | 42615139246SJoseph Koshy FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE); 427ebccf1e3SJoseph Koshy break; 428ebccf1e3SJoseph Koshy 429ebccf1e3SJoseph Koshy case 'p': /* process virtual counting PMC */ 430ebccf1e3SJoseph Koshy case 's': /* system-wide counting PMC */ 431ebccf1e3SJoseph Koshy case 'P': /* process virtual sampling PMC */ 432ebccf1e3SJoseph Koshy case 'S': /* system-wide sampling PMC */ 433ebccf1e3SJoseph Koshy if ((ev = malloc(sizeof(*ev))) == NULL) 434f263522aSJoseph Koshy errx(EX_SOFTWARE, "ERROR: Out of memory."); 435ebccf1e3SJoseph Koshy 436ebccf1e3SJoseph Koshy switch (option) { 437ebccf1e3SJoseph Koshy case 'p': ev->ev_mode = PMC_MODE_TC; break; 438ebccf1e3SJoseph Koshy case 's': ev->ev_mode = PMC_MODE_SC; break; 439ebccf1e3SJoseph Koshy case 'P': ev->ev_mode = PMC_MODE_TS; break; 440ebccf1e3SJoseph Koshy case 'S': ev->ev_mode = PMC_MODE_SS; break; 441ebccf1e3SJoseph Koshy } 442ebccf1e3SJoseph Koshy 443f263522aSJoseph Koshy if (option == 'P' || option == 'p') { 444f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_PROCESS_PMCS; 44515139246SJoseph Koshy args.pa_required |= (FLAG_HAS_COMMANDLINE | 446f263522aSJoseph Koshy FLAG_HAS_PID); 447f263522aSJoseph Koshy } 448ebccf1e3SJoseph Koshy 449f263522aSJoseph Koshy if (option == 'P' || option == 'S') { 450f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_SAMPLING_PMCS; 45115139246SJoseph Koshy args.pa_required |= (FLAG_HAS_PIPE | 45215139246SJoseph Koshy FLAG_HAS_OUTPUT_LOGFILE); 453f263522aSJoseph Koshy } 454ebccf1e3SJoseph Koshy 455ebccf1e3SJoseph Koshy if (option == 'p' || option == 's') 456f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_COUNTING_PMCS; 457f263522aSJoseph Koshy 458f263522aSJoseph Koshy if (option == 's' || option == 'S') 459f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_SYSTEM_PMCS; 460ebccf1e3SJoseph Koshy 461ebccf1e3SJoseph Koshy ev->ev_spec = strdup(optarg); 462ebccf1e3SJoseph Koshy 463ebccf1e3SJoseph Koshy if (option == 'S' || option == 'P') 464ebccf1e3SJoseph Koshy ev->ev_count = current_sampling_count; 465ebccf1e3SJoseph Koshy else 466ebccf1e3SJoseph Koshy ev->ev_count = -1; 467ebccf1e3SJoseph Koshy 468ebccf1e3SJoseph Koshy if (option == 'S' || option == 's') 469ebccf1e3SJoseph Koshy ev->ev_cpu = current_cpu; 470ebccf1e3SJoseph Koshy else 471ebccf1e3SJoseph Koshy ev->ev_cpu = PMC_CPU_ANY; 472ebccf1e3SJoseph Koshy 473f263522aSJoseph Koshy ev->ev_flags = 0; 474f263522aSJoseph Koshy if (do_descendants) 475f263522aSJoseph Koshy ev->ev_flags |= PMC_F_DESCENDANTS; 476f263522aSJoseph Koshy if (do_logprocexit) 477f263522aSJoseph Koshy ev->ev_flags |= PMC_F_LOG_PROCEXIT; 478f263522aSJoseph Koshy if (do_logproccsw) 479f263522aSJoseph Koshy ev->ev_flags |= PMC_F_LOG_PROCCSW; 480f263522aSJoseph Koshy 481ebccf1e3SJoseph Koshy ev->ev_cumulative = use_cumulative_counts; 482ebccf1e3SJoseph Koshy 483ebccf1e3SJoseph Koshy ev->ev_saved = 0LL; 484ebccf1e3SJoseph Koshy ev->ev_pmcid = PMC_ID_INVALID; 485ebccf1e3SJoseph Koshy 486ebccf1e3SJoseph Koshy /* extract event name */ 487ebccf1e3SJoseph Koshy c = strcspn(optarg, ", \t"); 488ebccf1e3SJoseph Koshy ev->ev_name = malloc(c + 1); 489ebccf1e3SJoseph Koshy (void) strncpy(ev->ev_name, optarg, c); 490ebccf1e3SJoseph Koshy *(ev->ev_name + c) = '\0'; 491ebccf1e3SJoseph Koshy 492ebccf1e3SJoseph Koshy STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next); 493ebccf1e3SJoseph Koshy 494ebccf1e3SJoseph Koshy break; 495ebccf1e3SJoseph Koshy 496ebccf1e3SJoseph Koshy case 'n': /* sampling count */ 497ebccf1e3SJoseph Koshy current_sampling_count = strtol(optarg, &end, 0); 498ebccf1e3SJoseph Koshy if (*end != '\0' || current_sampling_count <= 0) 499ebccf1e3SJoseph Koshy errx(EX_USAGE, 500f263522aSJoseph Koshy "ERROR: Illegal count value \"%s\".", 501ebccf1e3SJoseph Koshy optarg); 502f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_SAMPLING_PMCS; 503ebccf1e3SJoseph Koshy break; 504ebccf1e3SJoseph Koshy 505ebccf1e3SJoseph Koshy case 'o': /* outputfile */ 50615139246SJoseph Koshy if (args.pa_printfile != NULL) 50715139246SJoseph Koshy (void) fclose(args.pa_printfile); 50815139246SJoseph Koshy if ((args.pa_printfile = fopen(optarg, "w")) == NULL) 509ebccf1e3SJoseph Koshy errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 510f263522aSJoseph Koshy "writing.", optarg); 51115139246SJoseph Koshy args.pa_flags |= FLAG_DO_PRINT; 512f263522aSJoseph Koshy break; 513ebccf1e3SJoseph Koshy 514ebccf1e3SJoseph Koshy case 'O': /* sampling output */ 51515139246SJoseph Koshy if (args.pa_outputpath) 51615139246SJoseph Koshy errx(EX_USAGE, "ERROR: option -O may only be " 517f263522aSJoseph Koshy "specified once."); 51815139246SJoseph Koshy args.pa_outputpath = optarg; 51915139246SJoseph Koshy args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE; 52015139246SJoseph Koshy break; 52115139246SJoseph Koshy 52215139246SJoseph Koshy case 'R': /* read an existing log file */ 52315139246SJoseph Koshy if (args.pa_logparser != NULL) 52415139246SJoseph Koshy errx(EX_USAGE, "ERROR: option -R may only be " 52515139246SJoseph Koshy "specified once."); 52615139246SJoseph Koshy args.pa_inputpath = optarg; 52715139246SJoseph Koshy if (args.pa_printfile == stderr) 52815139246SJoseph Koshy args.pa_printfile = stdout; 52915139246SJoseph Koshy args.pa_flags |= FLAG_READ_LOGFILE; 530ebccf1e3SJoseph Koshy break; 531ebccf1e3SJoseph Koshy 532ebccf1e3SJoseph Koshy case 't': /* target pid */ 533ebccf1e3SJoseph Koshy pid = strtol(optarg, &end, 0); 534ebccf1e3SJoseph Koshy if (*end != '\0' || pid <= 0) 535ebccf1e3SJoseph Koshy errx(EX_USAGE, "ERROR: Illegal pid value " 536f263522aSJoseph Koshy "\"%s\".", optarg); 537ebccf1e3SJoseph Koshy 538ebccf1e3SJoseph Koshy args.pa_flags |= FLAG_HAS_PID; 539f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_PROCESS_PMCS; 540ebccf1e3SJoseph Koshy args.pa_pid = pid; 541ebccf1e3SJoseph Koshy break; 542ebccf1e3SJoseph Koshy 543ebccf1e3SJoseph Koshy case 'w': /* wait interval */ 544ebccf1e3SJoseph Koshy interval = strtod(optarg, &end); 545ebccf1e3SJoseph Koshy if (*end != '\0' || interval <= 0) 546ebccf1e3SJoseph Koshy errx(EX_USAGE, "ERROR: Illegal wait interval " 547f263522aSJoseph Koshy "value \"%s\".", optarg); 548ebccf1e3SJoseph Koshy args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; 54915139246SJoseph Koshy args.pa_required |= FLAG_HAS_COUNTING_PMCS; 550ebccf1e3SJoseph Koshy args.pa_interval = interval; 551f263522aSJoseph Koshy break; 552ebccf1e3SJoseph Koshy 553f263522aSJoseph Koshy case 'W': /* toggle LOG_CSW */ 554f263522aSJoseph Koshy do_logproccsw = !do_logproccsw; 555f263522aSJoseph Koshy args.pa_required |= (FLAG_HAS_PROCESS_PMCS | 55615139246SJoseph Koshy FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE); 557ebccf1e3SJoseph Koshy break; 558ebccf1e3SJoseph Koshy 559ebccf1e3SJoseph Koshy case '?': 560ebccf1e3SJoseph Koshy default: 561ebccf1e3SJoseph Koshy pmcstat_show_usage(); 562ebccf1e3SJoseph Koshy break; 563ebccf1e3SJoseph Koshy 564ebccf1e3SJoseph Koshy } 565ebccf1e3SJoseph Koshy 566ebccf1e3SJoseph Koshy args.pa_argc = (argc -= optind); 567ebccf1e3SJoseph Koshy args.pa_argv = (argv += optind); 568ebccf1e3SJoseph Koshy 56915139246SJoseph Koshy if (argc) /* command line present */ 57015139246SJoseph Koshy args.pa_flags |= FLAG_HAS_COMMANDLINE; 571ebccf1e3SJoseph Koshy 572ebccf1e3SJoseph Koshy /* 573ebccf1e3SJoseph Koshy * Check invocation syntax. 574ebccf1e3SJoseph Koshy */ 575ebccf1e3SJoseph Koshy 57615139246SJoseph Koshy /* disallow -O and -R together */ 57715139246SJoseph Koshy if (args.pa_outputpath && args.pa_inputpath) 57815139246SJoseph Koshy errx(EX_USAGE, "ERROR: options -O and -R are mutually " 57915139246SJoseph Koshy "exclusive."); 58015139246SJoseph Koshy 58115139246SJoseph Koshy if (args.pa_flags & FLAG_READ_LOGFILE) { 582f263522aSJoseph Koshy errmsg = NULL; 58315139246SJoseph Koshy if (args.pa_flags & FLAG_HAS_COMMANDLINE) 584f263522aSJoseph Koshy errmsg = "a command line specification"; 585f263522aSJoseph Koshy else if (args.pa_flags & FLAG_HAS_PID) 586f263522aSJoseph Koshy errmsg = "option -t"; 587f263522aSJoseph Koshy else if (!STAILQ_EMPTY(&args.pa_head)) 588f263522aSJoseph Koshy errmsg = "a PMC event specification"; 589f263522aSJoseph Koshy if (errmsg) 590f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: option -R may not be used with " 591f263522aSJoseph Koshy "%s.", errmsg); 59215139246SJoseph Koshy } else if (STAILQ_EMPTY(&args.pa_head)) 59315139246SJoseph Koshy /* All other uses require a PMC spec. */ 594ebccf1e3SJoseph Koshy pmcstat_show_usage(); 595ebccf1e3SJoseph Koshy 596f263522aSJoseph Koshy /* check for -t pid without a process PMC spec */ 597f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_PID) && 598f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) 599f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: option -t requires a process mode PMC " 600f263522aSJoseph Koshy "to be specified."); 601f263522aSJoseph Koshy 602f263522aSJoseph Koshy /* check for process-mode options without a command or -t pid */ 603f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && 60415139246SJoseph Koshy (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0) 60515139246SJoseph Koshy errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require " 60615139246SJoseph Koshy "a command line or target process."); 607f263522aSJoseph Koshy 608f263522aSJoseph Koshy /* check for -p | -P without a target process of some sort */ 60915139246SJoseph Koshy if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) && 61015139246SJoseph Koshy (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0) 61115139246SJoseph Koshy errx(EX_USAGE, "ERROR: options -P and -p require a " 612f263522aSJoseph Koshy "target process or a command line."); 613f263522aSJoseph Koshy 614f263522aSJoseph Koshy /* check for process-mode options without a process-mode PMC */ 615f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && 616f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) 61715139246SJoseph Koshy errx(EX_USAGE, "ERROR: options -d, -E, and -W require a " 618f263522aSJoseph Koshy "process mode PMC to be specified."); 619f263522aSJoseph Koshy 620f263522aSJoseph Koshy /* check for -c cpu and not system mode PMCs */ 621f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) && 622f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0) 623f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: option -c requires at least one " 624f263522aSJoseph Koshy "system mode PMC to be specified."); 625f263522aSJoseph Koshy 626f263522aSJoseph Koshy /* check for counting mode options without a counting PMC */ 627f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) && 628f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0) 62915139246SJoseph Koshy errx(EX_USAGE, "ERROR: options -C, -o and -W require at least " 63015139246SJoseph Koshy "one counting mode PMC to be specified."); 631f263522aSJoseph Koshy 632f263522aSJoseph Koshy /* check for sampling mode options without a sampling PMC spec */ 633f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) && 634f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0) 63515139246SJoseph Koshy errx(EX_USAGE, "ERROR: options -n and -O require at least " 63615139246SJoseph Koshy "one sampling mode PMC to be specified."); 637f263522aSJoseph Koshy 63815139246SJoseph Koshy if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) == 63915139246SJoseph Koshy (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) 640ebccf1e3SJoseph Koshy errx(EX_USAGE, 641ebccf1e3SJoseph Koshy "ERROR: option -t cannot be specified with a command " 642f263522aSJoseph Koshy "line."); 643ebccf1e3SJoseph Koshy 64415139246SJoseph Koshy /* check if -g is being used correctly */ 64515139246SJoseph Koshy if ((args.pa_flags & FLAG_DO_GPROF) && 64615139246SJoseph Koshy !(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE))) 64715139246SJoseph Koshy errx(EX_USAGE, "ERROR: option -g requires sampling PMCs or -R " 64815139246SJoseph Koshy "to be specified."); 64915139246SJoseph Koshy 650f263522aSJoseph Koshy /* check if -O was spuriously specified */ 65115139246SJoseph Koshy if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) && 65215139246SJoseph Koshy (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) 653f263522aSJoseph Koshy errx(EX_USAGE, 654f263522aSJoseph Koshy "ERROR: option -O is used only with options " 655f263522aSJoseph Koshy "-E, -P, -S and -W."); 656f263522aSJoseph Koshy 65715139246SJoseph Koshy /* -D dir and -k kernel path require -g */ 65815139246SJoseph Koshy if ((args.pa_flags & FLAG_HAS_KERNELPATH) && 65915139246SJoseph Koshy ((args.pa_flags & FLAG_DO_GPROF) == 0)) 66015139246SJoseph Koshy errx(EX_USAGE, "ERROR: option -k is only used with -g."); 66115139246SJoseph Koshy 66215139246SJoseph Koshy if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) && 66315139246SJoseph Koshy ((args.pa_flags & FLAG_DO_GPROF) == 0)) 66415139246SJoseph Koshy errx(EX_USAGE, "ERROR: option -D is only used with -g."); 66515139246SJoseph Koshy 66615139246SJoseph Koshy /* 66715139246SJoseph Koshy * Disallow textual output of sampling PMCs if counting PMCs 66815139246SJoseph Koshy * have also been asked for, mostly because the combined output 66915139246SJoseph Koshy * is difficult to make sense of. 67015139246SJoseph Koshy */ 67115139246SJoseph Koshy if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && 67215139246SJoseph Koshy (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) && 67315139246SJoseph Koshy ((args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)) 67415139246SJoseph Koshy errx(EX_USAGE, "ERROR: option -O is required if counting and " 67515139246SJoseph Koshy "sampling PMCs are specified together."); 67615139246SJoseph Koshy 677f263522aSJoseph Koshy /* if we've been asked to process a log file, do that and exit */ 67815139246SJoseph Koshy if (args.pa_flags & FLAG_READ_LOGFILE) { 67915139246SJoseph Koshy /* 68015139246SJoseph Koshy * Print the log in textual form if we haven't been 68115139246SJoseph Koshy * asked to generate gmon.out files. 68215139246SJoseph Koshy */ 68315139246SJoseph Koshy if ((args.pa_flags & FLAG_DO_GPROF) == 0) 68415139246SJoseph Koshy args.pa_flags |= FLAG_DO_PRINT; 68515139246SJoseph Koshy 68615139246SJoseph Koshy pmcstat_initialize_logging(&args); 68715139246SJoseph Koshy if ((args.pa_logfd = pmcstat_open(args.pa_inputpath, 68815139246SJoseph Koshy PMCSTAT_OPEN_FOR_READ)) < 0) 68915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot open \"%s\" for " 69015139246SJoseph Koshy "reading", args.pa_inputpath); 69115139246SJoseph Koshy if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL) 69215139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot create parser"); 693f263522aSJoseph Koshy pmcstat_process_log(&args); 694f263522aSJoseph Koshy exit(EX_OK); 695f263522aSJoseph Koshy } 696f263522aSJoseph Koshy 697f263522aSJoseph Koshy /* otherwise, we've been asked to collect data */ 698ebccf1e3SJoseph Koshy if (pmc_init() < 0) 699ebccf1e3SJoseph Koshy err(EX_UNAVAILABLE, 700ebccf1e3SJoseph Koshy "ERROR: Initialization of the pmc(3) library failed"); 701ebccf1e3SJoseph Koshy 702ebccf1e3SJoseph Koshy if ((ncpu = pmc_ncpu()) < 0) 703ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine the number CPUs " 704ebccf1e3SJoseph Koshy "on the system"); 705ebccf1e3SJoseph Koshy 706ebccf1e3SJoseph Koshy if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */ 707ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine the number of PMCs " 708ebccf1e3SJoseph Koshy "on CPU %d", 0); 709ebccf1e3SJoseph Koshy 71015139246SJoseph Koshy /* Allocate a kqueue */ 71115139246SJoseph Koshy if ((pmcstat_kq = kqueue()) < 0) 71215139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate kqueue"); 71315139246SJoseph Koshy 71415139246SJoseph Koshy /* 71515139246SJoseph Koshy * Configure the specified log file or setup a default log 71615139246SJoseph Koshy * consumer via a pipe. 71715139246SJoseph Koshy */ 71815139246SJoseph Koshy if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) { 71915139246SJoseph Koshy if (args.pa_outputpath) { 72015139246SJoseph Koshy if ((args.pa_logfd = pmcstat_open(args.pa_outputpath, 72115139246SJoseph Koshy PMCSTAT_OPEN_FOR_WRITE)) < 0) 72215139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot open \"%s\" for " 72315139246SJoseph Koshy "writing", args.pa_outputpath); 72415139246SJoseph Koshy } else { 72515139246SJoseph Koshy /* 72615139246SJoseph Koshy * process the log on the fly by reading it in 72715139246SJoseph Koshy * through a pipe. 72815139246SJoseph Koshy */ 72915139246SJoseph Koshy if (pipe(pipefd) < 0) 73015139246SJoseph Koshy err(EX_OSERR, "ERROR: pipe(2) failed"); 73115139246SJoseph Koshy 73215139246SJoseph Koshy if (fcntl(pipefd[READPIPEFD], F_SETFL, O_NONBLOCK) < 0) 73315139246SJoseph Koshy err(EX_OSERR, "ERROR: fcntl(2) failed"); 73415139246SJoseph Koshy 73515139246SJoseph Koshy EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD, 73615139246SJoseph Koshy 0, 0, NULL); 73715139246SJoseph Koshy 73815139246SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 73915139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent"); 74015139246SJoseph Koshy 74115139246SJoseph Koshy args.pa_logfd = pipefd[WRITEPIPEFD]; 74215139246SJoseph Koshy 74315139246SJoseph Koshy args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT); 74415139246SJoseph Koshy args.pa_logparser = pmclog_open(pipefd[READPIPEFD]); 74515139246SJoseph Koshy } 74615139246SJoseph Koshy 74715139246SJoseph Koshy if (pmc_configure_logfile(args.pa_logfd) < 0) 74815139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot configure log file"); 74915139246SJoseph Koshy } 75015139246SJoseph Koshy 751ebccf1e3SJoseph Koshy /* 752ebccf1e3SJoseph Koshy * Allocate PMCs. 753ebccf1e3SJoseph Koshy */ 754ebccf1e3SJoseph Koshy 75515139246SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 756ebccf1e3SJoseph Koshy if (pmc_allocate(ev->ev_spec, ev->ev_mode, 757f263522aSJoseph Koshy ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0) 758ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with " 759ebccf1e3SJoseph Koshy "specification \"%s\"", 760ebccf1e3SJoseph Koshy PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process", 761ebccf1e3SJoseph Koshy ev->ev_spec); 762ebccf1e3SJoseph Koshy 76315139246SJoseph Koshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && 76415139246SJoseph Koshy pmc_set(ev->ev_pmcid, ev->ev_count) < 0) 76515139246SJoseph Koshy err(EX_OSERR, "ERROR: Cannot set sampling count " 76615139246SJoseph Koshy "for PMC \"%s\"", ev->ev_name); 76715139246SJoseph Koshy } 76815139246SJoseph Koshy 769ebccf1e3SJoseph Koshy /* compute printout widths */ 770ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 771c5153e19SJoseph Koshy int counter_width; 772c5153e19SJoseph Koshy int display_width; 773c5153e19SJoseph Koshy int header_width; 774ebccf1e3SJoseph Koshy 775c5153e19SJoseph Koshy (void) pmc_width(ev->ev_pmcid, &counter_width); 776c5153e19SJoseph Koshy header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */ 777c5153e19SJoseph Koshy display_width = (int) floor(counter_width / 3.32193) + 1; 778ebccf1e3SJoseph Koshy 779c5153e19SJoseph Koshy if (header_width > display_width) { 780ebccf1e3SJoseph Koshy ev->ev_fieldskip = 0; 781c5153e19SJoseph Koshy ev->ev_fieldwidth = header_width; 782ebccf1e3SJoseph Koshy } else { 783c5153e19SJoseph Koshy ev->ev_fieldskip = display_width - 784c5153e19SJoseph Koshy header_width; 785c5153e19SJoseph Koshy ev->ev_fieldwidth = display_width; 786ebccf1e3SJoseph Koshy } 787ebccf1e3SJoseph Koshy } 788ebccf1e3SJoseph Koshy 789ebccf1e3SJoseph Koshy /* 790ebccf1e3SJoseph Koshy * If our output is being set to a terminal, register a handler 791ebccf1e3SJoseph Koshy * for window size changes. 792ebccf1e3SJoseph Koshy */ 793ebccf1e3SJoseph Koshy 79415139246SJoseph Koshy if (isatty(fileno(args.pa_printfile))) { 795ebccf1e3SJoseph Koshy 79615139246SJoseph Koshy if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0) 797ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine window size"); 798ebccf1e3SJoseph Koshy 799ebccf1e3SJoseph Koshy pmcstat_displayheight = ws.ws_row - 1; 800ebccf1e3SJoseph Koshy 801ebccf1e3SJoseph Koshy EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 802ebccf1e3SJoseph Koshy 803ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 804ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for " 805ebccf1e3SJoseph Koshy "SIGWINCH"); 806ebccf1e3SJoseph Koshy } 807ebccf1e3SJoseph Koshy 808ebccf1e3SJoseph Koshy EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 809ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 810ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); 811ebccf1e3SJoseph Koshy 812f263522aSJoseph Koshy EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 813f263522aSJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 814f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO"); 815ebccf1e3SJoseph Koshy 816ebccf1e3SJoseph Koshy /* 817f263522aSJoseph Koshy * An exec() failure of a forked child is signalled by the 818f263522aSJoseph Koshy * child sending the parent a SIGCHLD. We don't register an 819f263522aSJoseph Koshy * actual signal handler for SIGCHLD, but instead use our 820f263522aSJoseph Koshy * kqueue to pick up the signal. 821ebccf1e3SJoseph Koshy */ 822f263522aSJoseph Koshy EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 823f263522aSJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 824f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD"); 825ebccf1e3SJoseph Koshy 82615139246SJoseph Koshy /* setup a timer if we have counting mode PMCs needing to be printed */ 82715139246SJoseph Koshy if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) && 82815139246SJoseph Koshy (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { 829ebccf1e3SJoseph Koshy EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 830ebccf1e3SJoseph Koshy args.pa_interval * 1000, NULL); 831ebccf1e3SJoseph Koshy 832ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 833ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for " 834ebccf1e3SJoseph Koshy "timer"); 835ebccf1e3SJoseph Koshy } 836ebccf1e3SJoseph Koshy 837ebccf1e3SJoseph Koshy /* attach PMCs to the target process, starting it if specified */ 83815139246SJoseph Koshy if (args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) 839ebccf1e3SJoseph Koshy pmcstat_setup_process(&args); 840ebccf1e3SJoseph Koshy 841ebccf1e3SJoseph Koshy /* start the pmcs */ 842ebccf1e3SJoseph Koshy pmcstat_start_pmcs(&args); 843ebccf1e3SJoseph Koshy 844ebccf1e3SJoseph Koshy /* start the (commandline) process if needed */ 84515139246SJoseph Koshy if (args.pa_flags & FLAG_HAS_COMMANDLINE) 846ebccf1e3SJoseph Koshy pmcstat_start_process(&args); 847ebccf1e3SJoseph Koshy 84815139246SJoseph Koshy /* initialize logging if printing the configured log */ 84915139246SJoseph Koshy if ((args.pa_flags & FLAG_DO_PRINT) && 85015139246SJoseph Koshy (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))) 85115139246SJoseph Koshy pmcstat_initialize_logging(&args); 85215139246SJoseph Koshy 853ebccf1e3SJoseph Koshy /* Handle SIGINT using the kqueue loop */ 854ebccf1e3SJoseph Koshy sa.sa_handler = SIG_IGN; 855ebccf1e3SJoseph Koshy sa.sa_flags = 0; 856ebccf1e3SJoseph Koshy (void) sigemptyset(&sa.sa_mask); 857ebccf1e3SJoseph Koshy 858ebccf1e3SJoseph Koshy if (sigaction(SIGINT, &sa, NULL) < 0) 859ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot install signal handler"); 860ebccf1e3SJoseph Koshy 861ebccf1e3SJoseph Koshy /* 862ebccf1e3SJoseph Koshy * loop till either the target process (if any) exits, or we 863ebccf1e3SJoseph Koshy * are killed by a SIGINT. 864ebccf1e3SJoseph Koshy */ 865f263522aSJoseph Koshy runstate = PMCSTAT_RUNNING; 866f263522aSJoseph Koshy do_print = 0; 867ebccf1e3SJoseph Koshy do { 868ebccf1e3SJoseph Koshy if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) { 869ebccf1e3SJoseph Koshy if (errno != EINTR) 870ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: kevent failed"); 871ebccf1e3SJoseph Koshy else 872ebccf1e3SJoseph Koshy continue; 873ebccf1e3SJoseph Koshy } 874ebccf1e3SJoseph Koshy 875ebccf1e3SJoseph Koshy if (kev.flags & EV_ERROR) 876ebccf1e3SJoseph Koshy errc(EX_OSERR, kev.data, "ERROR: kevent failed"); 877ebccf1e3SJoseph Koshy 878ebccf1e3SJoseph Koshy switch (kev.filter) { 879f263522aSJoseph Koshy case EVFILT_PROC: /* target has exited */ 88015139246SJoseph Koshy if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | 88115139246SJoseph Koshy FLAG_HAS_PIPE)) 882f263522aSJoseph Koshy runstate = pmcstat_close_log(&args); 883f263522aSJoseph Koshy break; 884ebccf1e3SJoseph Koshy 885f263522aSJoseph Koshy case EVFILT_READ: /* log file data is present */ 886dc1d9d2eSJoseph Koshy runstate = pmcstat_process_log(&args); 887ebccf1e3SJoseph Koshy break; 888ebccf1e3SJoseph Koshy 889ebccf1e3SJoseph Koshy case EVFILT_SIGNAL: 890f263522aSJoseph Koshy if (kev.ident == SIGCHLD) { 891f263522aSJoseph Koshy /* 892f263522aSJoseph Koshy * The child process sends us a 893f263522aSJoseph Koshy * SIGCHLD if its exec() failed. We 894f263522aSJoseph Koshy * wait for it to exit and then exit 895f263522aSJoseph Koshy * ourselves. 896f263522aSJoseph Koshy */ 897f263522aSJoseph Koshy (void) wait(&c); 898f263522aSJoseph Koshy runstate = PMCSTAT_FINISHED; 899f263522aSJoseph Koshy } else if (kev.ident == SIGIO) { 900f263522aSJoseph Koshy /* 901f263522aSJoseph Koshy * We get a SIGIO if a PMC loses all 902f263522aSJoseph Koshy * of its targets, or if logfile 903f263522aSJoseph Koshy * writes encounter an error. 904f263522aSJoseph Koshy */ 90515139246SJoseph Koshy if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | 90615139246SJoseph Koshy FLAG_HAS_PIPE)) { 907f263522aSJoseph Koshy runstate = pmcstat_close_log(&args); 90815139246SJoseph Koshy if (args.pa_flags & 90915139246SJoseph Koshy (FLAG_DO_PRINT|FLAG_DO_GPROF)) 91015139246SJoseph Koshy pmcstat_process_log(&args); 91115139246SJoseph Koshy } 912f263522aSJoseph Koshy do_print = 1; /* print PMCs at exit */ 913f263522aSJoseph Koshy runstate = PMCSTAT_FINISHED; 914f263522aSJoseph Koshy } else if (kev.ident == SIGINT) { 91515139246SJoseph Koshy /* Kill the child process if we started it */ 91615139246SJoseph Koshy if (args.pa_flags & FLAG_HAS_COMMANDLINE) 917ebccf1e3SJoseph Koshy if (kill(args.pa_pid, SIGINT) != 0) 918f263522aSJoseph Koshy err(EX_OSERR, "ERROR: cannot " 919f263522aSJoseph Koshy "signal child process"); 920f263522aSJoseph Koshy runstate = PMCSTAT_FINISHED; 921ebccf1e3SJoseph Koshy } else if (kev.ident == SIGWINCH) { 92215139246SJoseph Koshy if (ioctl(fileno(args.pa_printfile), 923ebccf1e3SJoseph Koshy TIOCGWINSZ, &ws) < 0) 924ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine " 925ebccf1e3SJoseph Koshy "window size"); 926ebccf1e3SJoseph Koshy pmcstat_displayheight = ws.ws_row - 1; 927ebccf1e3SJoseph Koshy } else 928ebccf1e3SJoseph Koshy assert(0); 929ebccf1e3SJoseph Koshy 930ebccf1e3SJoseph Koshy break; 931f263522aSJoseph Koshy 932f263522aSJoseph Koshy case EVFILT_TIMER: /* print out counting PMCs */ 933f263522aSJoseph Koshy do_print = 1; 934f263522aSJoseph Koshy break; 935f263522aSJoseph Koshy 936ebccf1e3SJoseph Koshy } 937ebccf1e3SJoseph Koshy 93815139246SJoseph Koshy if (do_print && 93915139246SJoseph Koshy (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) { 940f263522aSJoseph Koshy pmcstat_print_pmcs(&args); 94115139246SJoseph Koshy if (runstate == PMCSTAT_FINISHED && /* final newline */ 94215139246SJoseph Koshy (args.pa_flags & FLAG_DO_PRINT) == 0) 94315139246SJoseph Koshy (void) fprintf(args.pa_printfile, "\n"); 944f263522aSJoseph Koshy do_print = 0; 945f263522aSJoseph Koshy } 946f263522aSJoseph Koshy 947f263522aSJoseph Koshy } while (runstate != PMCSTAT_FINISHED); 948f263522aSJoseph Koshy 949f263522aSJoseph Koshy /* flush any pending log entries */ 95015139246SJoseph Koshy if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE)) 951f263522aSJoseph Koshy pmc_flush_logfile(); 952ebccf1e3SJoseph Koshy 953ebccf1e3SJoseph Koshy pmcstat_cleanup(&args); 954ebccf1e3SJoseph Koshy 955ebccf1e3SJoseph Koshy return 0; 956ebccf1e3SJoseph Koshy } 957