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