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> 33ebccf1e3SJoseph Koshy #include <sys/time.h> 34ebccf1e3SJoseph Koshy #include <sys/ttycom.h> 35ebccf1e3SJoseph Koshy #include <sys/wait.h> 36ebccf1e3SJoseph Koshy 37ebccf1e3SJoseph Koshy #include <assert.h> 38ebccf1e3SJoseph Koshy #include <err.h> 39ebccf1e3SJoseph Koshy #include <errno.h> 40ebccf1e3SJoseph Koshy #include <fcntl.h> 41ebccf1e3SJoseph Koshy #include <limits.h> 42ebccf1e3SJoseph Koshy #include <math.h> 43ebccf1e3SJoseph Koshy #include <pmc.h> 44f263522aSJoseph Koshy #include <pmclog.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 54f263522aSJoseph Koshy /* 55f263522aSJoseph Koshy * A given invocation of pmcstat(8) can manage multiple PMCs of both 56f263522aSJoseph Koshy * the system-wide and per-process variety. Each of these could be in 57f263522aSJoseph Koshy * 'counting mode' or in 'sampling mode'. 58f263522aSJoseph Koshy * 59f263522aSJoseph Koshy * For 'counting mode' PMCs, pmcstat(8) will periodically issue a 60f263522aSJoseph Koshy * pmc_read() at the configured time interval and print out the value 61f263522aSJoseph Koshy * of the requested PMCs. 62f263522aSJoseph Koshy * 63f263522aSJoseph Koshy * For 'sampling mode' PMCs it can log to a file for offline analysis, 64f263522aSJoseph Koshy * or can analyse sampling data "on the fly", either by converting 65f263522aSJoseph Koshy * samples to printed textual form or by creating gprof(1) compatible 66f263522aSJoseph Koshy * profiles, one per program executed. When creating gprof(1) 67f263522aSJoseph Koshy * profiles it can optionally merge entries from multiple processes 68f263522aSJoseph Koshy * for a given executable into a single profile file. 69f263522aSJoseph Koshy */ 70f263522aSJoseph Koshy 71ebccf1e3SJoseph Koshy /* Operation modes */ 72ebccf1e3SJoseph Koshy 73ebccf1e3SJoseph Koshy #define FLAG_HAS_PID 0x00000001 74ebccf1e3SJoseph Koshy #define FLAG_HAS_WAIT_INTERVAL 0x00000002 75ebccf1e3SJoseph Koshy #define FLAG_HAS_LOG_FILE 0x00000004 76ebccf1e3SJoseph Koshy #define FLAG_HAS_PROCESS 0x00000008 77f263522aSJoseph Koshy #define FLAG_HAS_SAMPLING_PMCS 0x00000010 78f263522aSJoseph Koshy #define FLAG_HAS_COUNTING_PMCS 0x00000020 79f263522aSJoseph Koshy #define FLAG_HAS_PROCESS_PMCS 0x00000040 80f263522aSJoseph Koshy #define FLAG_HAS_SYSTEM_PMCS 0x00000080 81f263522aSJoseph Koshy #define FLAG_HAS_PIPE 0x00000100 82f263522aSJoseph Koshy #define FLAG_PROCESS_LOGFILE 0x00000200 83f263522aSJoseph Koshy #define FLAG_DO_GPROF 0x00000400 84f263522aSJoseph Koshy #define FLAG_DO_GPROF_MERGED 0x00000800 85ebccf1e3SJoseph Koshy 86ebccf1e3SJoseph Koshy #define DEFAULT_SAMPLE_COUNT 65536 87ebccf1e3SJoseph Koshy #define DEFAULT_WAIT_INTERVAL 5.0 88ebccf1e3SJoseph Koshy #define DEFAULT_DISPLAY_HEIGHT 23 89f263522aSJoseph Koshy #define DEFAULT_BUFFER_SIZE 4096 90ebccf1e3SJoseph Koshy 91f263522aSJoseph Koshy #define WRITELOG_MAGIC 0xA55AA55A 92ebccf1e3SJoseph Koshy #define PRINT_HEADER_PREFIX "# " 93ebccf1e3SJoseph Koshy #define READPIPEFD 0 94ebccf1e3SJoseph Koshy #define WRITEPIPEFD 1 95ebccf1e3SJoseph Koshy #define NPIPEFD 2 96ebccf1e3SJoseph Koshy 97f263522aSJoseph Koshy enum pmcstat_state { 98f263522aSJoseph Koshy PMCSTAT_FINISHED = 0, 99f263522aSJoseph Koshy PMCSTAT_EXITING = 1, 100f263522aSJoseph Koshy PMCSTAT_RUNNING = 2 101f263522aSJoseph Koshy }; 102f263522aSJoseph Koshy 103ebccf1e3SJoseph Koshy struct pmcstat_ev { 104ebccf1e3SJoseph Koshy STAILQ_ENTRY(pmcstat_ev) ev_next; 105ebccf1e3SJoseph Koshy char *ev_spec; /* event specification */ 106ebccf1e3SJoseph Koshy char *ev_name; /* (derived) event name */ 107ebccf1e3SJoseph Koshy enum pmc_mode ev_mode; /* desired mode */ 108ebccf1e3SJoseph Koshy int ev_count; /* associated count if in sampling mode */ 109ebccf1e3SJoseph Koshy int ev_cpu; /* specific cpu if requested */ 110f263522aSJoseph Koshy int ev_flags; /* PMC_F_* */ 111ebccf1e3SJoseph Koshy int ev_cumulative; /* show cumulative counts */ 112ebccf1e3SJoseph Koshy int ev_fieldwidth; /* print width */ 113ebccf1e3SJoseph Koshy int ev_fieldskip; /* #leading spaces */ 114ebccf1e3SJoseph Koshy pmc_value_t ev_saved; /* saved value for incremental counts */ 115ebccf1e3SJoseph Koshy pmc_id_t ev_pmcid; /* allocated ID */ 116ebccf1e3SJoseph Koshy }; 117ebccf1e3SJoseph Koshy 118ebccf1e3SJoseph Koshy struct pmcstat_args { 119f263522aSJoseph Koshy int pa_required; 120ebccf1e3SJoseph Koshy int pa_flags; 121ebccf1e3SJoseph Koshy pid_t pa_pid; 122ebccf1e3SJoseph Koshy FILE *pa_outputfile; 123ebccf1e3SJoseph Koshy FILE *pa_logfile; 124f263522aSJoseph Koshy void *pa_logparser; 125f263522aSJoseph Koshy char *pa_outputdir; 126ebccf1e3SJoseph Koshy double pa_interval; 127ebccf1e3SJoseph Koshy int pa_argc; 128ebccf1e3SJoseph Koshy char **pa_argv; 129ebccf1e3SJoseph Koshy STAILQ_HEAD(, pmcstat_ev) pa_head; 130ebccf1e3SJoseph Koshy } args; 131ebccf1e3SJoseph Koshy 132ebccf1e3SJoseph Koshy int pmcstat_interrupt = 0; 133ebccf1e3SJoseph Koshy int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; 134ebccf1e3SJoseph Koshy int pmcstat_pipefd[NPIPEFD]; 135ebccf1e3SJoseph Koshy int pmcstat_kq; 136ebccf1e3SJoseph Koshy 137ebccf1e3SJoseph Koshy /* Function prototypes */ 138ebccf1e3SJoseph Koshy void pmcstat_cleanup(struct pmcstat_args *_a); 139f263522aSJoseph Koshy int pmcstat_close_log(struct pmcstat_args *_a); 140ebccf1e3SJoseph Koshy void pmcstat_print_counters(struct pmcstat_args *_a); 141ebccf1e3SJoseph Koshy void pmcstat_print_headers(struct pmcstat_args *_a); 142ebccf1e3SJoseph Koshy void pmcstat_print_pmcs(struct pmcstat_args *_a); 143ebccf1e3SJoseph Koshy void pmcstat_setup_process(struct pmcstat_args *_a); 144ebccf1e3SJoseph Koshy void pmcstat_show_usage(void); 145ebccf1e3SJoseph Koshy void pmcstat_start_pmcs(struct pmcstat_args *_a); 146ebccf1e3SJoseph Koshy void pmcstat_start_process(struct pmcstat_args *_a); 147f263522aSJoseph Koshy void pmcstat_process_log(struct pmcstat_args *_a); 148f263522aSJoseph Koshy int pmcstat_print_log(struct pmcstat_args *_a); 149ebccf1e3SJoseph Koshy 150f263522aSJoseph Koshy #define PMCSTAT_PRINT_LOG(A,T,...) do { \ 151f263522aSJoseph Koshy fprintf((A)->pa_outputfile, T "\t" __VA_ARGS__); \ 152f263522aSJoseph Koshy fprintf((A)->pa_outputfile, "\n"); \ 153f263522aSJoseph Koshy } while (0) 154ebccf1e3SJoseph Koshy 155ebccf1e3SJoseph Koshy /* 156ebccf1e3SJoseph Koshy * cleanup 157ebccf1e3SJoseph Koshy */ 158ebccf1e3SJoseph Koshy 159ebccf1e3SJoseph Koshy void 160ebccf1e3SJoseph Koshy pmcstat_cleanup(struct pmcstat_args *a) 161ebccf1e3SJoseph Koshy { 162ebccf1e3SJoseph Koshy struct pmcstat_ev *ev, *tmp; 163ebccf1e3SJoseph Koshy 164ebccf1e3SJoseph Koshy /* de-configure the log file if present. */ 165f263522aSJoseph Koshy if (a->pa_flags & FLAG_HAS_LOG_FILE) 166ebccf1e3SJoseph Koshy (void) pmc_configure_logfile(-1); 167ebccf1e3SJoseph Koshy 168ebccf1e3SJoseph Koshy /* release allocated PMCs. */ 169ebccf1e3SJoseph Koshy STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp) 170ebccf1e3SJoseph Koshy if (ev->ev_pmcid != PMC_ID_INVALID) { 171ebccf1e3SJoseph Koshy if (pmc_release(ev->ev_pmcid) < 0) 172ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot release pmc " 173f263522aSJoseph Koshy "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name); 174ebccf1e3SJoseph Koshy free(ev->ev_name); 175ebccf1e3SJoseph Koshy free(ev->ev_spec); 176ebccf1e3SJoseph Koshy STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next); 177ebccf1e3SJoseph Koshy free(ev); 178ebccf1e3SJoseph Koshy } 179f263522aSJoseph Koshy 180f263522aSJoseph Koshy if (a->pa_logparser) { 181f263522aSJoseph Koshy pmclog_close(a->pa_logparser); 182f263522aSJoseph Koshy a->pa_logparser = NULL; 183f263522aSJoseph Koshy } 184ebccf1e3SJoseph Koshy } 185ebccf1e3SJoseph Koshy 186ebccf1e3SJoseph Koshy void 187ebccf1e3SJoseph Koshy pmcstat_start_pmcs(struct pmcstat_args *a) 188ebccf1e3SJoseph Koshy { 189ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 190ebccf1e3SJoseph Koshy 191ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 192ebccf1e3SJoseph Koshy 193ebccf1e3SJoseph Koshy assert(ev->ev_pmcid != PMC_ID_INVALID); 194ebccf1e3SJoseph Koshy 195ebccf1e3SJoseph Koshy if (pmc_start(ev->ev_pmcid) < 0) { 196f263522aSJoseph Koshy warn("ERROR: Cannot start pmc 0x%x \"%s\"", 197ebccf1e3SJoseph Koshy ev->ev_pmcid, ev->ev_name); 198ebccf1e3SJoseph Koshy pmcstat_cleanup(a); 199f263522aSJoseph Koshy exit(EX_OSERR); 200ebccf1e3SJoseph Koshy } 201ebccf1e3SJoseph Koshy } 202ebccf1e3SJoseph Koshy 203ebccf1e3SJoseph Koshy } 204ebccf1e3SJoseph Koshy 205ebccf1e3SJoseph Koshy void 206ebccf1e3SJoseph Koshy pmcstat_print_headers(struct pmcstat_args *a) 207ebccf1e3SJoseph Koshy { 208ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 209ebccf1e3SJoseph Koshy int c; 210ebccf1e3SJoseph Koshy 211ebccf1e3SJoseph Koshy (void) fprintf(a->pa_outputfile, PRINT_HEADER_PREFIX); 212ebccf1e3SJoseph Koshy 213ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 214ebccf1e3SJoseph Koshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 215ebccf1e3SJoseph Koshy continue; 216ebccf1e3SJoseph Koshy 217ebccf1e3SJoseph Koshy c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; 218ebccf1e3SJoseph Koshy 219ebccf1e3SJoseph Koshy if (ev->ev_fieldskip != 0) { 220ebccf1e3SJoseph Koshy (void) fprintf(a->pa_outputfile, "%*s%c/%*s ", 221ebccf1e3SJoseph Koshy ev->ev_fieldskip, "", c, 222ebccf1e3SJoseph Koshy ev->ev_fieldwidth - ev->ev_fieldskip - 2, 223ebccf1e3SJoseph Koshy ev->ev_name); 224ebccf1e3SJoseph Koshy } else 225ebccf1e3SJoseph Koshy (void) fprintf(a->pa_outputfile, "%c/%*s ", 226ebccf1e3SJoseph Koshy c, ev->ev_fieldwidth - 2, ev->ev_name); 227ebccf1e3SJoseph Koshy } 228ebccf1e3SJoseph Koshy 229ebccf1e3SJoseph Koshy (void) fflush(a->pa_outputfile); 230ebccf1e3SJoseph Koshy } 231ebccf1e3SJoseph Koshy 232ebccf1e3SJoseph Koshy void 233ebccf1e3SJoseph Koshy pmcstat_print_counters(struct pmcstat_args *a) 234ebccf1e3SJoseph Koshy { 235ebccf1e3SJoseph Koshy int extra_width; 236ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 237ebccf1e3SJoseph Koshy pmc_value_t value; 238ebccf1e3SJoseph Koshy 239ebccf1e3SJoseph Koshy extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; 240ebccf1e3SJoseph Koshy 241ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 242ebccf1e3SJoseph Koshy 243ebccf1e3SJoseph Koshy /* skip sampling mode counters */ 244ebccf1e3SJoseph Koshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 245ebccf1e3SJoseph Koshy continue; 246ebccf1e3SJoseph Koshy 247ebccf1e3SJoseph Koshy if (pmc_read(ev->ev_pmcid, &value) < 0) 248ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot read pmc " 249ebccf1e3SJoseph Koshy "\"%s\"", ev->ev_name); 250ebccf1e3SJoseph Koshy 251ebccf1e3SJoseph Koshy (void) fprintf(a->pa_outputfile, "%*ju ", 252ebccf1e3SJoseph Koshy ev->ev_fieldwidth + extra_width, (uintmax_t) 253ebccf1e3SJoseph Koshy ev->ev_cumulative ? value : (value - ev->ev_saved)); 254ebccf1e3SJoseph Koshy if (ev->ev_cumulative == 0) 255ebccf1e3SJoseph Koshy ev->ev_saved = value; 256ebccf1e3SJoseph Koshy extra_width = 0; 257ebccf1e3SJoseph Koshy } 258ebccf1e3SJoseph Koshy 259ebccf1e3SJoseph Koshy (void) fflush(a->pa_outputfile); 260ebccf1e3SJoseph Koshy } 261ebccf1e3SJoseph Koshy 262ebccf1e3SJoseph Koshy /* 263ebccf1e3SJoseph Koshy * Print output 264ebccf1e3SJoseph Koshy */ 265ebccf1e3SJoseph Koshy 266ebccf1e3SJoseph Koshy void 267ebccf1e3SJoseph Koshy pmcstat_print_pmcs(struct pmcstat_args *a) 268ebccf1e3SJoseph Koshy { 269ebccf1e3SJoseph Koshy static int linecount = 0; 270ebccf1e3SJoseph Koshy 271ebccf1e3SJoseph Koshy if (++linecount > pmcstat_displayheight) { 272ebccf1e3SJoseph Koshy (void) fprintf(a->pa_outputfile, "\n"); 273ebccf1e3SJoseph Koshy linecount = 1; 274ebccf1e3SJoseph Koshy } 275ebccf1e3SJoseph Koshy 276ebccf1e3SJoseph Koshy if (linecount == 1) 277ebccf1e3SJoseph Koshy pmcstat_print_headers(a); 278ebccf1e3SJoseph Koshy 279ebccf1e3SJoseph Koshy (void) fprintf(a->pa_outputfile, "\n"); 280ebccf1e3SJoseph Koshy pmcstat_print_counters(a); 281ebccf1e3SJoseph Koshy 282ebccf1e3SJoseph Koshy return; 283ebccf1e3SJoseph Koshy } 284ebccf1e3SJoseph Koshy 285ebccf1e3SJoseph Koshy /* 286ebccf1e3SJoseph Koshy * Do process profiling 287ebccf1e3SJoseph Koshy * 288ebccf1e3SJoseph Koshy * If a pid was specified, attach each allocated PMC to the target 289ebccf1e3SJoseph Koshy * process. Otherwise, fork a child and attach the PMCs to the child, 290ebccf1e3SJoseph Koshy * and have the child exec() the target program. 291ebccf1e3SJoseph Koshy */ 292ebccf1e3SJoseph Koshy 293ebccf1e3SJoseph Koshy void 294ebccf1e3SJoseph Koshy pmcstat_setup_process(struct pmcstat_args *a) 295ebccf1e3SJoseph Koshy { 296ebccf1e3SJoseph Koshy char token; 297ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 298ebccf1e3SJoseph Koshy struct kevent kev; 299ebccf1e3SJoseph Koshy 300ebccf1e3SJoseph Koshy if (a->pa_flags & FLAG_HAS_PID) { 301f263522aSJoseph Koshy STAILQ_FOREACH(ev, &a->pa_head, ev_next) 302ebccf1e3SJoseph Koshy if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 303ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to " 304ebccf1e3SJoseph Koshy "process %d", ev->ev_name, (int) a->pa_pid); 305ebccf1e3SJoseph Koshy } else { 306ebccf1e3SJoseph Koshy 307ebccf1e3SJoseph Koshy /* 308ebccf1e3SJoseph Koshy * We need to fork a new process and startup the child 309ebccf1e3SJoseph Koshy * using execvp(). Before doing the exec() the child 310ebccf1e3SJoseph Koshy * process reads its pipe for a token so that the parent 311ebccf1e3SJoseph Koshy * can finish doing its pmc_attach() calls. 312ebccf1e3SJoseph Koshy */ 313ebccf1e3SJoseph Koshy if (pipe(pmcstat_pipefd) < 0) 314ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot create pipe"); 315ebccf1e3SJoseph Koshy 316ebccf1e3SJoseph Koshy switch (a->pa_pid = fork()) { 317ebccf1e3SJoseph Koshy case -1: 318ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot fork"); 319ebccf1e3SJoseph Koshy /*NOTREACHED*/ 320ebccf1e3SJoseph Koshy 321ebccf1e3SJoseph Koshy case 0: /* child */ 322ebccf1e3SJoseph Koshy 323ebccf1e3SJoseph Koshy /* wait for our parent to signal us */ 324ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[WRITEPIPEFD]); 325ebccf1e3SJoseph Koshy if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0) 326ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR (child): cannot read " 327ebccf1e3SJoseph Koshy "token"); 328ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[READPIPEFD]); 329ebccf1e3SJoseph Koshy 330ebccf1e3SJoseph Koshy /* exec() the program requested */ 331f263522aSJoseph Koshy execvp(*a->pa_argv, a->pa_argv); 332f263522aSJoseph Koshy /* and if that fails, notify the parent */ 333f263522aSJoseph Koshy kill(getppid(), SIGCHLD); 334f263522aSJoseph Koshy err(EX_OSERR, "ERROR: execvp \"%s\" failed", 335f263522aSJoseph Koshy *a->pa_argv); 336ebccf1e3SJoseph Koshy /*NOTREACHED*/ 337ebccf1e3SJoseph Koshy 338ebccf1e3SJoseph Koshy default: /* parent */ 339ebccf1e3SJoseph Koshy 340ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[READPIPEFD]); 341ebccf1e3SJoseph Koshy 342ebccf1e3SJoseph Koshy /* attach all our PMCs to the child */ 343ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) 344ebccf1e3SJoseph Koshy if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) && 345ebccf1e3SJoseph Koshy pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 346ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: cannot attach pmc " 347ebccf1e3SJoseph Koshy "\"%s\" to process %d", ev->ev_name, 348ebccf1e3SJoseph Koshy (int) a->pa_pid); 349ebccf1e3SJoseph Koshy 350ebccf1e3SJoseph Koshy } 351ebccf1e3SJoseph Koshy } 352ebccf1e3SJoseph Koshy 353f263522aSJoseph Koshy /* Ask to be notified via a kevent when the target process exits */ 354f263522aSJoseph Koshy EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0, 355f263522aSJoseph Koshy NULL); 356ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 357f263522aSJoseph Koshy err(EX_OSERR, "ERROR: cannot monitor child process %d", 358ebccf1e3SJoseph Koshy a->pa_pid); 359ebccf1e3SJoseph Koshy return; 360ebccf1e3SJoseph Koshy } 361ebccf1e3SJoseph Koshy 362ebccf1e3SJoseph Koshy void 363ebccf1e3SJoseph Koshy pmcstat_start_process(struct pmcstat_args *a) 364ebccf1e3SJoseph Koshy { 365ebccf1e3SJoseph Koshy 366ebccf1e3SJoseph Koshy /* nothing to do: target is already running */ 367ebccf1e3SJoseph Koshy if (a->pa_flags & FLAG_HAS_PID) 368ebccf1e3SJoseph Koshy return; 369ebccf1e3SJoseph Koshy 370ebccf1e3SJoseph Koshy /* write token to child to state that we are ready */ 371ebccf1e3SJoseph Koshy if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1) 372ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: write failed"); 373ebccf1e3SJoseph Koshy 374ebccf1e3SJoseph Koshy (void) close(pmcstat_pipefd[WRITEPIPEFD]); 375ebccf1e3SJoseph Koshy } 376ebccf1e3SJoseph Koshy 377f263522aSJoseph Koshy 378f263522aSJoseph Koshy /* 379f263522aSJoseph Koshy * Process a log file in offline analysis mode. 380f263522aSJoseph Koshy */ 381f263522aSJoseph Koshy 382f263522aSJoseph Koshy void 383f263522aSJoseph Koshy pmcstat_process_log(struct pmcstat_args *a) 384f263522aSJoseph Koshy { 385f263522aSJoseph Koshy int runstate; 386f263522aSJoseph Koshy 387f263522aSJoseph Koshy /* 388f263522aSJoseph Koshy * If gprof style profiles haven't been asked for, just print the 389f263522aSJoseph Koshy * log to the current output file. 390f263522aSJoseph Koshy */ 391f263522aSJoseph Koshy if ((a->pa_flags & (FLAG_DO_GPROF_MERGED|FLAG_DO_GPROF)) == 0) { 392f263522aSJoseph Koshy while ((runstate = pmcstat_print_log(a)) == PMCSTAT_RUNNING) 393f263522aSJoseph Koshy ; 394f263522aSJoseph Koshy return; 395f263522aSJoseph Koshy } 396f263522aSJoseph Koshy 397f263522aSJoseph Koshy /* convert the log to gprof compatible profiles */ 398f263522aSJoseph Koshy assert(0); /* To be implemented */ 399f263522aSJoseph Koshy } 400f263522aSJoseph Koshy 401f263522aSJoseph Koshy /* 402f263522aSJoseph Koshy * Print log entries available in a configured parser. 403f263522aSJoseph Koshy */ 404f263522aSJoseph Koshy 405f263522aSJoseph Koshy int 406f263522aSJoseph Koshy pmcstat_print_log(struct pmcstat_args *a) 407f263522aSJoseph Koshy { 408f263522aSJoseph Koshy struct pmclog_ev ev; 409f263522aSJoseph Koshy 410f263522aSJoseph Koshy while (pmclog_read(a->pa_logparser, &ev) == 0) { 411f263522aSJoseph Koshy assert(ev.pl_state == PMCLOG_OK); 412f263522aSJoseph Koshy switch (ev.pl_type) { 413f263522aSJoseph Koshy case PMCLOG_TYPE_CLOSELOG: 414f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"close",); 415f263522aSJoseph Koshy break; 416f263522aSJoseph Koshy case PMCLOG_TYPE_DROPNOTIFY: 417f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"drop",); 418f263522aSJoseph Koshy break; 419f263522aSJoseph Koshy case PMCLOG_TYPE_INITIALIZE: 420f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"init","0x%x \"%s\"", 421f263522aSJoseph Koshy ev.pl_u.pl_i.pl_version, 422f263522aSJoseph Koshy pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch)); 423f263522aSJoseph Koshy break; 424f263522aSJoseph Koshy case PMCLOG_TYPE_MAPPINGCHANGE: 425f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"mapping","%s %d %p %p \"%s\"", 426f263522aSJoseph Koshy ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ? 427f263522aSJoseph Koshy "insert" : "delete", 428f263522aSJoseph Koshy ev.pl_u.pl_m.pl_pid, 429f263522aSJoseph Koshy (void *) ev.pl_u.pl_m.pl_start, 430f263522aSJoseph Koshy (void *) ev.pl_u.pl_m.pl_end, 431f263522aSJoseph Koshy ev.pl_u.pl_m.pl_pathname); 432f263522aSJoseph Koshy break; 433f263522aSJoseph Koshy case PMCLOG_TYPE_PCSAMPLE: 434f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"sample","0x%x %d %p", 435f263522aSJoseph Koshy ev.pl_u.pl_s.pl_pmcid, 436f263522aSJoseph Koshy ev.pl_u.pl_s.pl_pid, 437f263522aSJoseph Koshy (void *) ev.pl_u.pl_s.pl_pc); 438f263522aSJoseph Koshy break; 439f263522aSJoseph Koshy case PMCLOG_TYPE_PMCALLOCATE: 440f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"allocate","0x%x \"%s\" 0x%x", 441f263522aSJoseph Koshy ev.pl_u.pl_a.pl_pmcid, 442f263522aSJoseph Koshy ev.pl_u.pl_a.pl_evname, 443f263522aSJoseph Koshy ev.pl_u.pl_a.pl_flags); 444f263522aSJoseph Koshy break; 445f263522aSJoseph Koshy case PMCLOG_TYPE_PMCATTACH: 446f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"attach","0x%x %d \"%s\"", 447f263522aSJoseph Koshy ev.pl_u.pl_t.pl_pmcid, 448f263522aSJoseph Koshy ev.pl_u.pl_t.pl_pid, 449f263522aSJoseph Koshy ev.pl_u.pl_t.pl_pathname); 450f263522aSJoseph Koshy break; 451f263522aSJoseph Koshy case PMCLOG_TYPE_PMCDETACH: 452f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"detach","0x%x %d", 453f263522aSJoseph Koshy ev.pl_u.pl_d.pl_pmcid, 454f263522aSJoseph Koshy ev.pl_u.pl_d.pl_pid); 455f263522aSJoseph Koshy break; 456f263522aSJoseph Koshy case PMCLOG_TYPE_PROCCSW: 457f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"csw","0x%x %d %jd", 458f263522aSJoseph Koshy ev.pl_u.pl_c.pl_pmcid, 459f263522aSJoseph Koshy ev.pl_u.pl_c.pl_pid, 460f263522aSJoseph Koshy ev.pl_u.pl_c.pl_value); 461f263522aSJoseph Koshy break; 462f263522aSJoseph Koshy case PMCLOG_TYPE_PROCEXEC: 463f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"exec","%d \"%s\"", 464f263522aSJoseph Koshy ev.pl_u.pl_x.pl_pid, 465f263522aSJoseph Koshy ev.pl_u.pl_x.pl_pathname); 466f263522aSJoseph Koshy break; 467f263522aSJoseph Koshy case PMCLOG_TYPE_PROCEXIT: 468f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"exitvalue","0x%x %d %jd", 469f263522aSJoseph Koshy ev.pl_u.pl_e.pl_pmcid, 470f263522aSJoseph Koshy ev.pl_u.pl_e.pl_pid, 471f263522aSJoseph Koshy ev.pl_u.pl_e.pl_value); 472f263522aSJoseph Koshy break; 473f263522aSJoseph Koshy case PMCLOG_TYPE_PROCFORK: 474f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"fork","%d %d", 475f263522aSJoseph Koshy ev.pl_u.pl_f.pl_oldpid, 476f263522aSJoseph Koshy ev.pl_u.pl_f.pl_newpid); 477f263522aSJoseph Koshy break; 478f263522aSJoseph Koshy case PMCLOG_TYPE_USERDATA: 479f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"user","0x%x", 480f263522aSJoseph Koshy ev.pl_u.pl_u.pl_userdata); 481f263522aSJoseph Koshy break; 482f263522aSJoseph Koshy case PMCLOG_TYPE_SYSEXIT: 483f263522aSJoseph Koshy PMCSTAT_PRINT_LOG(a,"exit","%d", 484f263522aSJoseph Koshy ev.pl_u.pl_se.pl_pid); 485f263522aSJoseph Koshy break; 486f263522aSJoseph Koshy default: 487f263522aSJoseph Koshy fprintf(a->pa_outputfile, "unknown %d", 488f263522aSJoseph Koshy ev.pl_type); 489f263522aSJoseph Koshy } 490f263522aSJoseph Koshy } 491f263522aSJoseph Koshy 492f263522aSJoseph Koshy if (ev.pl_state == PMCLOG_EOF) 493f263522aSJoseph Koshy return PMCSTAT_FINISHED; 494f263522aSJoseph Koshy else if (ev.pl_state == PMCLOG_REQUIRE_DATA) 495f263522aSJoseph Koshy return PMCSTAT_RUNNING; 496f263522aSJoseph Koshy 497f263522aSJoseph Koshy err(EX_DATAERR, "ERROR: event parsing failed " 498f263522aSJoseph Koshy "(record %jd, offset 0x%jx)", 499f263522aSJoseph Koshy (uintmax_t) ev.pl_count + 1, ev.pl_offset); 500f263522aSJoseph Koshy /*NOTREACHED*/ 501f263522aSJoseph Koshy } 502f263522aSJoseph Koshy 503f263522aSJoseph Koshy /* 504f263522aSJoseph Koshy * Close a logfile, after first flushing all in-module queued data. 505f263522aSJoseph Koshy */ 506f263522aSJoseph Koshy 507f263522aSJoseph Koshy int 508f263522aSJoseph Koshy pmcstat_close_log(struct pmcstat_args *a) 509f263522aSJoseph Koshy { 510f263522aSJoseph Koshy if (pmc_flush_logfile() < 0 || 511f263522aSJoseph Koshy pmc_configure_logfile(-1) < 0) 512f263522aSJoseph Koshy err(EX_OSERR, "ERROR: logging failed"); 513f263522aSJoseph Koshy a->pa_flags &= ~FLAG_HAS_LOG_FILE; 514f263522aSJoseph Koshy return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING : 515f263522aSJoseph Koshy PMCSTAT_FINISHED; 516f263522aSJoseph Koshy } 517f263522aSJoseph Koshy 518ebccf1e3SJoseph Koshy void 519ebccf1e3SJoseph Koshy pmcstat_show_usage(void) 520ebccf1e3SJoseph Koshy { 521ebccf1e3SJoseph Koshy errx(EX_USAGE, 522ebccf1e3SJoseph Koshy "[options] [commandline]\n" 523ebccf1e3SJoseph Koshy "\t Measure process and/or system performance using hardware\n" 524ebccf1e3SJoseph Koshy "\t performance monitoring counters.\n" 525ebccf1e3SJoseph Koshy "\t Options include:\n" 526f263522aSJoseph Koshy "\t -C\t\t (toggle) show cumulative counts\n" 527f263522aSJoseph Koshy "\t -D path\t create profiles in directory \"path\"\n" 528f263522aSJoseph Koshy "\t -E\t\t (toggle) show counts at process exit\n" 529f263522aSJoseph Koshy "\t -O file\t send log output to \"file\"\n" 530f263522aSJoseph Koshy "\t -P spec\t allocate a process-private sampling PMC\n" 531f263522aSJoseph Koshy "\t -R file\t read events from \"file\"\n" 532f263522aSJoseph Koshy "\t -S spec\t allocate a system-wide sampling PMC\n" 533f263522aSJoseph Koshy "\t -W\t\t (toggle) show counts per context switch\n" 534f263522aSJoseph Koshy "\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n" 535f263522aSJoseph Koshy "\t -d\t\t (toggle) track descendants\n" 536f263522aSJoseph Koshy "\t -g\t\t produce gprof(1) compatible profiles\n" 537f263522aSJoseph Koshy "\t -m\t\t merge gprof(1) profiles for executables\n" 538ebccf1e3SJoseph Koshy "\t -n rate\t set sampling rate\n" 539ebccf1e3SJoseph Koshy "\t -o file\t send print output to \"file\"\n" 540f263522aSJoseph Koshy "\t -p spec\t allocate a process-private counting PMC\n" 541f263522aSJoseph Koshy "\t -s spec\t allocate a system-wide counting PMC\n" 54239f4e0fcSJoseph Koshy "\t -t pid\t\t attach to running process with pid \"pid\"\n" 543ebccf1e3SJoseph Koshy "\t -w secs\t set printing time interval" 544ebccf1e3SJoseph Koshy ); 545ebccf1e3SJoseph Koshy } 546ebccf1e3SJoseph Koshy 547ebccf1e3SJoseph Koshy /* 548ebccf1e3SJoseph Koshy * Main 549ebccf1e3SJoseph Koshy */ 550ebccf1e3SJoseph Koshy 551ebccf1e3SJoseph Koshy int 552ebccf1e3SJoseph Koshy main(int argc, char **argv) 553ebccf1e3SJoseph Koshy { 554ebccf1e3SJoseph Koshy double interval; 555ebccf1e3SJoseph Koshy int option, npmc, ncpu; 556ebccf1e3SJoseph Koshy int c, current_cpu, current_sampling_count; 557f263522aSJoseph Koshy int do_print, do_descendants; 558f263522aSJoseph Koshy int do_logproccsw, do_logprocexit; 559f263522aSJoseph Koshy int logfd; 560f263522aSJoseph Koshy int pipefd[2]; 561f263522aSJoseph Koshy int use_cumulative_counts; 562ebccf1e3SJoseph Koshy pid_t pid; 563ebccf1e3SJoseph Koshy char *end; 564f263522aSJoseph Koshy const char *errmsg; 565f263522aSJoseph Koshy enum pmcstat_state runstate; 566ebccf1e3SJoseph Koshy struct pmcstat_ev *ev; 567ebccf1e3SJoseph Koshy struct sigaction sa; 568ebccf1e3SJoseph Koshy struct kevent kev; 569ebccf1e3SJoseph Koshy struct winsize ws; 570ebccf1e3SJoseph Koshy 571ebccf1e3SJoseph Koshy current_cpu = 0; 572ebccf1e3SJoseph Koshy current_sampling_count = DEFAULT_SAMPLE_COUNT; 573ebccf1e3SJoseph Koshy do_descendants = 0; 574f263522aSJoseph Koshy do_logproccsw = 0; 575f263522aSJoseph Koshy do_logprocexit = 0; 576ebccf1e3SJoseph Koshy use_cumulative_counts = 0; 577f263522aSJoseph Koshy args.pa_required = 0; 578ebccf1e3SJoseph Koshy args.pa_flags = 0; 579ebccf1e3SJoseph Koshy args.pa_pid = (pid_t) -1; 580ebccf1e3SJoseph Koshy args.pa_logfile = NULL; 581f263522aSJoseph Koshy args.pa_outputdir = NULL; 582ebccf1e3SJoseph Koshy args.pa_outputfile = stderr; 583ebccf1e3SJoseph Koshy args.pa_interval = DEFAULT_WAIT_INTERVAL; 584ebccf1e3SJoseph Koshy STAILQ_INIT(&args.pa_head); 585ebccf1e3SJoseph Koshy 586ebccf1e3SJoseph Koshy ev = NULL; 587ebccf1e3SJoseph Koshy 588f263522aSJoseph Koshy while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgmn:o:p:s:t:w:")) 589f263522aSJoseph Koshy != -1) 590ebccf1e3SJoseph Koshy switch (option) { 591ebccf1e3SJoseph Koshy case 'C': /* cumulative values */ 592ebccf1e3SJoseph Koshy use_cumulative_counts = !use_cumulative_counts; 593f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_COUNTING_PMCS; 594ebccf1e3SJoseph Koshy break; 595ebccf1e3SJoseph Koshy 596ebccf1e3SJoseph Koshy case 'c': /* CPU */ 597ebccf1e3SJoseph Koshy current_cpu = strtol(optarg, &end, 0); 598ebccf1e3SJoseph Koshy if (*end != '\0' || current_cpu < 0) 599ebccf1e3SJoseph Koshy errx(EX_USAGE, 600f263522aSJoseph Koshy "ERROR: Illegal CPU number \"%s\".", 601ebccf1e3SJoseph Koshy optarg); 602f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_SYSTEM_PMCS; 603ebccf1e3SJoseph Koshy break; 604ebccf1e3SJoseph Koshy 605ebccf1e3SJoseph Koshy case 'd': /* toggle descendents */ 606ebccf1e3SJoseph Koshy do_descendants = !do_descendants; 607f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_PROCESS_PMCS; 608f263522aSJoseph Koshy break; 609f263522aSJoseph Koshy 610f263522aSJoseph Koshy case 'D': 611f263522aSJoseph Koshy args.pa_outputdir = optarg; 612f263522aSJoseph Koshy break; 613f263522aSJoseph Koshy 614f263522aSJoseph Koshy case 'g': /* produce gprof compatible profiles */ 615f263522aSJoseph Koshy args.pa_flags |= FLAG_DO_GPROF; 616f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_SAMPLING_PMCS; 617f263522aSJoseph Koshy break; 618f263522aSJoseph Koshy 619f263522aSJoseph Koshy case 'm': /* produce merged profiles */ 620f263522aSJoseph Koshy args.pa_flags |= FLAG_DO_GPROF_MERGED; 621f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_SAMPLING_PMCS; 622f263522aSJoseph Koshy break; 623f263522aSJoseph Koshy 624f263522aSJoseph Koshy case 'E': /* log process exit */ 625f263522aSJoseph Koshy do_logprocexit = !do_logprocexit; 626f263522aSJoseph Koshy args.pa_required |= (FLAG_HAS_PROCESS_PMCS | 627f263522aSJoseph Koshy FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE); 628ebccf1e3SJoseph Koshy break; 629ebccf1e3SJoseph Koshy 630ebccf1e3SJoseph Koshy case 'p': /* process virtual counting PMC */ 631ebccf1e3SJoseph Koshy case 's': /* system-wide counting PMC */ 632ebccf1e3SJoseph Koshy case 'P': /* process virtual sampling PMC */ 633ebccf1e3SJoseph Koshy case 'S': /* system-wide sampling PMC */ 634ebccf1e3SJoseph Koshy if ((ev = malloc(sizeof(*ev))) == NULL) 635f263522aSJoseph Koshy errx(EX_SOFTWARE, "ERROR: Out of memory."); 636ebccf1e3SJoseph Koshy 637ebccf1e3SJoseph Koshy switch (option) { 638ebccf1e3SJoseph Koshy case 'p': ev->ev_mode = PMC_MODE_TC; break; 639ebccf1e3SJoseph Koshy case 's': ev->ev_mode = PMC_MODE_SC; break; 640ebccf1e3SJoseph Koshy case 'P': ev->ev_mode = PMC_MODE_TS; break; 641ebccf1e3SJoseph Koshy case 'S': ev->ev_mode = PMC_MODE_SS; break; 642ebccf1e3SJoseph Koshy } 643ebccf1e3SJoseph Koshy 644f263522aSJoseph Koshy if (option == 'P' || option == 'p') { 645f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_PROCESS_PMCS; 646f263522aSJoseph Koshy args.pa_required |= (FLAG_HAS_PROCESS | 647f263522aSJoseph Koshy FLAG_HAS_PID); 648f263522aSJoseph Koshy } 649ebccf1e3SJoseph Koshy 650f263522aSJoseph Koshy if (option == 'P' || option == 'S') { 651f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_SAMPLING_PMCS; 652f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_LOG_FILE; 653f263522aSJoseph Koshy } 654ebccf1e3SJoseph Koshy 655ebccf1e3SJoseph Koshy if (option == 'p' || option == 's') 656f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_COUNTING_PMCS; 657f263522aSJoseph Koshy 658f263522aSJoseph Koshy if (option == 's' || option == 'S') 659f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_SYSTEM_PMCS; 660ebccf1e3SJoseph Koshy 661ebccf1e3SJoseph Koshy ev->ev_spec = strdup(optarg); 662ebccf1e3SJoseph Koshy 663ebccf1e3SJoseph Koshy if (option == 'S' || option == 'P') 664ebccf1e3SJoseph Koshy ev->ev_count = current_sampling_count; 665ebccf1e3SJoseph Koshy else 666ebccf1e3SJoseph Koshy ev->ev_count = -1; 667ebccf1e3SJoseph Koshy 668ebccf1e3SJoseph Koshy if (option == 'S' || option == 's') 669ebccf1e3SJoseph Koshy ev->ev_cpu = current_cpu; 670ebccf1e3SJoseph Koshy else 671ebccf1e3SJoseph Koshy ev->ev_cpu = PMC_CPU_ANY; 672ebccf1e3SJoseph Koshy 673f263522aSJoseph Koshy ev->ev_flags = 0; 674f263522aSJoseph Koshy if (do_descendants) 675f263522aSJoseph Koshy ev->ev_flags |= PMC_F_DESCENDANTS; 676f263522aSJoseph Koshy if (do_logprocexit) 677f263522aSJoseph Koshy ev->ev_flags |= PMC_F_LOG_PROCEXIT; 678f263522aSJoseph Koshy if (do_logproccsw) 679f263522aSJoseph Koshy ev->ev_flags |= PMC_F_LOG_PROCCSW; 680f263522aSJoseph Koshy 681ebccf1e3SJoseph Koshy ev->ev_cumulative = use_cumulative_counts; 682ebccf1e3SJoseph Koshy 683ebccf1e3SJoseph Koshy ev->ev_saved = 0LL; 684ebccf1e3SJoseph Koshy ev->ev_pmcid = PMC_ID_INVALID; 685ebccf1e3SJoseph Koshy 686ebccf1e3SJoseph Koshy /* extract event name */ 687ebccf1e3SJoseph Koshy c = strcspn(optarg, ", \t"); 688ebccf1e3SJoseph Koshy ev->ev_name = malloc(c + 1); 689ebccf1e3SJoseph Koshy (void) strncpy(ev->ev_name, optarg, c); 690ebccf1e3SJoseph Koshy *(ev->ev_name + c) = '\0'; 691ebccf1e3SJoseph Koshy 692ebccf1e3SJoseph Koshy STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next); 693ebccf1e3SJoseph Koshy 694ebccf1e3SJoseph Koshy break; 695ebccf1e3SJoseph Koshy 696f263522aSJoseph Koshy case 'R': /* read an existing log file */ 697f263522aSJoseph Koshy if ((logfd = open(optarg, O_RDONLY, 0)) < 0) 698f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot open \"%s\" for " 699f263522aSJoseph Koshy "reading", optarg); 700f263522aSJoseph Koshy if ((args.pa_logparser = pmclog_open(logfd)) 701f263522aSJoseph Koshy == NULL) 702f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot create parser"); 703f263522aSJoseph Koshy args.pa_flags |= FLAG_PROCESS_LOGFILE; 704f263522aSJoseph Koshy break; 705f263522aSJoseph Koshy 706ebccf1e3SJoseph Koshy case 'n': /* sampling count */ 707ebccf1e3SJoseph Koshy current_sampling_count = strtol(optarg, &end, 0); 708ebccf1e3SJoseph Koshy if (*end != '\0' || current_sampling_count <= 0) 709ebccf1e3SJoseph Koshy errx(EX_USAGE, 710f263522aSJoseph Koshy "ERROR: Illegal count value \"%s\".", 711ebccf1e3SJoseph Koshy optarg); 712f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_SAMPLING_PMCS; 713ebccf1e3SJoseph Koshy break; 714ebccf1e3SJoseph Koshy 715ebccf1e3SJoseph Koshy case 'o': /* outputfile */ 716ebccf1e3SJoseph Koshy if (args.pa_outputfile != NULL) 717ebccf1e3SJoseph Koshy (void) fclose(args.pa_outputfile); 718ebccf1e3SJoseph Koshy if ((args.pa_outputfile = fopen(optarg, "w")) == NULL) 719ebccf1e3SJoseph Koshy errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 720f263522aSJoseph Koshy "writing.", optarg); 721f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_COUNTING_PMCS; 722f263522aSJoseph Koshy break; 723ebccf1e3SJoseph Koshy 724ebccf1e3SJoseph Koshy case 'O': /* sampling output */ 725ebccf1e3SJoseph Koshy if (args.pa_logfile != NULL) 726f263522aSJoseph Koshy errx(EX_OSERR, "ERROR: option -O may only be " 727f263522aSJoseph Koshy "specified once."); 728ebccf1e3SJoseph Koshy if ((args.pa_logfile = fopen(optarg, "w")) == NULL) 729ebccf1e3SJoseph Koshy errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 730f263522aSJoseph Koshy "writing.", optarg); 731f263522aSJoseph Koshy args.pa_flags |= FLAG_HAS_LOG_FILE; 732ebccf1e3SJoseph Koshy break; 733ebccf1e3SJoseph Koshy 734ebccf1e3SJoseph Koshy case 't': /* target pid */ 735ebccf1e3SJoseph Koshy pid = strtol(optarg, &end, 0); 736ebccf1e3SJoseph Koshy if (*end != '\0' || pid <= 0) 737ebccf1e3SJoseph Koshy errx(EX_USAGE, "ERROR: Illegal pid value " 738f263522aSJoseph Koshy "\"%s\".", optarg); 739ebccf1e3SJoseph Koshy 740ebccf1e3SJoseph Koshy args.pa_flags |= FLAG_HAS_PID; 741f263522aSJoseph Koshy args.pa_required |= FLAG_HAS_PROCESS_PMCS; 742ebccf1e3SJoseph Koshy args.pa_pid = pid; 743ebccf1e3SJoseph Koshy break; 744ebccf1e3SJoseph Koshy 745ebccf1e3SJoseph Koshy case 'w': /* wait interval */ 746ebccf1e3SJoseph Koshy interval = strtod(optarg, &end); 747ebccf1e3SJoseph Koshy if (*end != '\0' || interval <= 0) 748ebccf1e3SJoseph Koshy errx(EX_USAGE, "ERROR: Illegal wait interval " 749f263522aSJoseph Koshy "value \"%s\".", optarg); 750ebccf1e3SJoseph Koshy args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; 751ebccf1e3SJoseph Koshy args.pa_interval = interval; 752f263522aSJoseph Koshy break; 753ebccf1e3SJoseph Koshy 754f263522aSJoseph Koshy case 'W': /* toggle LOG_CSW */ 755f263522aSJoseph Koshy do_logproccsw = !do_logproccsw; 756f263522aSJoseph Koshy args.pa_required |= (FLAG_HAS_PROCESS_PMCS | 757f263522aSJoseph Koshy FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE); 758ebccf1e3SJoseph Koshy break; 759ebccf1e3SJoseph Koshy 760ebccf1e3SJoseph Koshy case '?': 761ebccf1e3SJoseph Koshy default: 762ebccf1e3SJoseph Koshy pmcstat_show_usage(); 763ebccf1e3SJoseph Koshy break; 764ebccf1e3SJoseph Koshy 765ebccf1e3SJoseph Koshy } 766ebccf1e3SJoseph Koshy 767ebccf1e3SJoseph Koshy args.pa_argc = (argc -= optind); 768ebccf1e3SJoseph Koshy args.pa_argv = (argv += optind); 769ebccf1e3SJoseph Koshy 770ebccf1e3SJoseph Koshy if (argc) 771ebccf1e3SJoseph Koshy args.pa_flags |= FLAG_HAS_PROCESS; 772ebccf1e3SJoseph Koshy 773ebccf1e3SJoseph Koshy /* 774ebccf1e3SJoseph Koshy * Check invocation syntax. 775ebccf1e3SJoseph Koshy */ 776ebccf1e3SJoseph Koshy 777f263522aSJoseph Koshy if (args.pa_flags & FLAG_PROCESS_LOGFILE) { 778f263522aSJoseph Koshy errmsg = NULL; 779f263522aSJoseph Koshy if (args.pa_flags & FLAG_HAS_PROCESS) 780f263522aSJoseph Koshy errmsg = "a command line specification"; 781f263522aSJoseph Koshy else if (args.pa_flags & FLAG_HAS_PID) 782f263522aSJoseph Koshy errmsg = "option -t"; 783f263522aSJoseph Koshy else if (!STAILQ_EMPTY(&args.pa_head)) 784f263522aSJoseph Koshy errmsg = "a PMC event specification"; 785f263522aSJoseph Koshy if (errmsg) 786f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: option -R may not be used with " 787f263522aSJoseph Koshy "%s.", errmsg); 788f263522aSJoseph Koshy } else if (STAILQ_EMPTY(&args.pa_head)) { 789ebccf1e3SJoseph Koshy warnx("ERROR: At least one PMC event must be specified"); 790ebccf1e3SJoseph Koshy pmcstat_show_usage(); 791ebccf1e3SJoseph Koshy } 792ebccf1e3SJoseph Koshy 793f263522aSJoseph Koshy /* check for -t pid without a process PMC spec */ 794f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_PID) && 795f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) 796f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: option -t requires a process mode PMC " 797f263522aSJoseph Koshy "to be specified."); 798f263522aSJoseph Koshy 799f263522aSJoseph Koshy /* check for process-mode options without a command or -t pid */ 800f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && 801f263522aSJoseph Koshy (args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0) 802f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: options -d,-E,-p,-P,-W require a " 803f263522aSJoseph Koshy "command line or target process."); 804f263522aSJoseph Koshy 805f263522aSJoseph Koshy /* check for -p | -P without a target process of some sort */ 806f263522aSJoseph Koshy if ((args.pa_required & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) && 807f263522aSJoseph Koshy (args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0) 808f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: the -P or -p options require a " 809f263522aSJoseph Koshy "target process or a command line."); 810f263522aSJoseph Koshy 811f263522aSJoseph Koshy /* check for process-mode options without a process-mode PMC */ 812f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) && 813f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0) 814f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: options -d,-E,-W require a " 815f263522aSJoseph Koshy "process mode PMC to be specified."); 816f263522aSJoseph Koshy 817f263522aSJoseph Koshy /* check for -c cpu and not system mode PMCs */ 818f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) && 819f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0) 820f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: option -c requires at least one " 821f263522aSJoseph Koshy "system mode PMC to be specified."); 822f263522aSJoseph Koshy 823f263522aSJoseph Koshy /* check for counting mode options without a counting PMC */ 824f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) && 825f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0) 826f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: options -C,-o,-W require at least one " 827f263522aSJoseph Koshy "counting mode PMC to be specified."); 828f263522aSJoseph Koshy 829f263522aSJoseph Koshy /* check for sampling mode options without a sampling PMC spec */ 830f263522aSJoseph Koshy if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) && 831f263522aSJoseph Koshy (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0) 832f263522aSJoseph Koshy errx(EX_USAGE, "ERROR: options -n,-O require at least one " 833f263522aSJoseph Koshy "sampling mode PMC to be specified."); 834f263522aSJoseph Koshy 835f263522aSJoseph Koshy if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_PROCESS)) == 836f263522aSJoseph Koshy (FLAG_HAS_PID | FLAG_HAS_PROCESS)) 837ebccf1e3SJoseph Koshy errx(EX_USAGE, 838ebccf1e3SJoseph Koshy "ERROR: option -t cannot be specified with a command " 839f263522aSJoseph Koshy "line."); 840ebccf1e3SJoseph Koshy 841f263522aSJoseph Koshy /* check if -O was spuriously specified */ 842f263522aSJoseph Koshy if ((args.pa_flags & FLAG_HAS_LOG_FILE) && 843f263522aSJoseph Koshy (args.pa_required & FLAG_HAS_LOG_FILE) == 0) 844f263522aSJoseph Koshy errx(EX_USAGE, 845f263522aSJoseph Koshy "ERROR: option -O is used only with options " 846f263522aSJoseph Koshy "-E,-P,-S and -W."); 847f263522aSJoseph Koshy 848f263522aSJoseph Koshy /* if we've been asked to process a log file, do that and exit */ 849f263522aSJoseph Koshy if (args.pa_flags & FLAG_PROCESS_LOGFILE) { 850f263522aSJoseph Koshy pmcstat_process_log(&args); 851f263522aSJoseph Koshy exit(EX_OK); 852f263522aSJoseph Koshy } 853f263522aSJoseph Koshy 854f263522aSJoseph Koshy /* otherwise, we've been asked to collect data */ 855ebccf1e3SJoseph Koshy if (pmc_init() < 0) 856ebccf1e3SJoseph Koshy err(EX_UNAVAILABLE, 857ebccf1e3SJoseph Koshy "ERROR: Initialization of the pmc(3) library failed"); 858ebccf1e3SJoseph Koshy 859ebccf1e3SJoseph Koshy if ((ncpu = pmc_ncpu()) < 0) 860ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine the number CPUs " 861ebccf1e3SJoseph Koshy "on the system"); 862ebccf1e3SJoseph Koshy 863ebccf1e3SJoseph Koshy if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */ 864ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine the number of PMCs " 865ebccf1e3SJoseph Koshy "on CPU %d", 0); 866ebccf1e3SJoseph Koshy 867ebccf1e3SJoseph Koshy /* 868ebccf1e3SJoseph Koshy * Allocate PMCs. 869ebccf1e3SJoseph Koshy */ 870ebccf1e3SJoseph Koshy 871ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) 872ebccf1e3SJoseph Koshy if (pmc_allocate(ev->ev_spec, ev->ev_mode, 873f263522aSJoseph Koshy ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0) 874ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with " 875ebccf1e3SJoseph Koshy "specification \"%s\"", 876ebccf1e3SJoseph Koshy PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process", 877ebccf1e3SJoseph Koshy ev->ev_spec); 878ebccf1e3SJoseph Koshy 879ebccf1e3SJoseph Koshy /* compute printout widths */ 880ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 881c5153e19SJoseph Koshy int counter_width; 882c5153e19SJoseph Koshy int display_width; 883c5153e19SJoseph Koshy int header_width; 884ebccf1e3SJoseph Koshy 885c5153e19SJoseph Koshy (void) pmc_width(ev->ev_pmcid, &counter_width); 886c5153e19SJoseph Koshy header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */ 887c5153e19SJoseph Koshy display_width = (int) floor(counter_width / 3.32193) + 1; 888ebccf1e3SJoseph Koshy 889c5153e19SJoseph Koshy if (header_width > display_width) { 890ebccf1e3SJoseph Koshy ev->ev_fieldskip = 0; 891c5153e19SJoseph Koshy ev->ev_fieldwidth = header_width; 892ebccf1e3SJoseph Koshy } else { 893c5153e19SJoseph Koshy ev->ev_fieldskip = display_width - 894c5153e19SJoseph Koshy header_width; 895c5153e19SJoseph Koshy ev->ev_fieldwidth = display_width; 896ebccf1e3SJoseph Koshy } 897ebccf1e3SJoseph Koshy } 898ebccf1e3SJoseph Koshy 899ebccf1e3SJoseph Koshy /* Allocate a kqueue */ 900ebccf1e3SJoseph Koshy if ((pmcstat_kq = kqueue()) < 0) 901ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot allocate kqueue"); 902ebccf1e3SJoseph Koshy 903ebccf1e3SJoseph Koshy /* 904ebccf1e3SJoseph Koshy * If our output is being set to a terminal, register a handler 905ebccf1e3SJoseph Koshy * for window size changes. 906ebccf1e3SJoseph Koshy */ 907ebccf1e3SJoseph Koshy 908ebccf1e3SJoseph Koshy if (isatty(fileno(args.pa_outputfile))) { 909ebccf1e3SJoseph Koshy 910ebccf1e3SJoseph Koshy if (ioctl(fileno(args.pa_outputfile), TIOCGWINSZ, &ws) < 0) 911ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine window size"); 912ebccf1e3SJoseph Koshy 913ebccf1e3SJoseph Koshy pmcstat_displayheight = ws.ws_row - 1; 914ebccf1e3SJoseph Koshy 915ebccf1e3SJoseph Koshy EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 916ebccf1e3SJoseph Koshy 917ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 918ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for " 919ebccf1e3SJoseph Koshy "SIGWINCH"); 920ebccf1e3SJoseph Koshy } 921ebccf1e3SJoseph Koshy 922ebccf1e3SJoseph Koshy EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 923ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 924ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); 925ebccf1e3SJoseph Koshy 926f263522aSJoseph Koshy EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 927f263522aSJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 928f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO"); 929ebccf1e3SJoseph Koshy 930ebccf1e3SJoseph Koshy /* 931f263522aSJoseph Koshy * An exec() failure of a forked child is signalled by the 932f263522aSJoseph Koshy * child sending the parent a SIGCHLD. We don't register an 933f263522aSJoseph Koshy * actual signal handler for SIGCHLD, but instead use our 934f263522aSJoseph Koshy * kqueue to pick up the signal. 935ebccf1e3SJoseph Koshy */ 936f263522aSJoseph Koshy EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 937f263522aSJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 938f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD"); 939ebccf1e3SJoseph Koshy 940f263522aSJoseph Koshy /* 941f263522aSJoseph Koshy * Configure the specified log file or setup a default log 942f263522aSJoseph Koshy * consumer via a pipe. 943f263522aSJoseph Koshy */ 944f263522aSJoseph Koshy if (args.pa_required & FLAG_HAS_LOG_FILE) { 945ebccf1e3SJoseph Koshy 946f263522aSJoseph Koshy if (args.pa_logfile == NULL) { 947f263522aSJoseph Koshy if (pipe(pipefd) < 0) 948f263522aSJoseph Koshy err(EX_OSERR, "ERROR: pipe(2) failed"); 949f263522aSJoseph Koshy 950f263522aSJoseph Koshy EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD, 951f263522aSJoseph Koshy 0, 0, NULL); 952f263522aSJoseph Koshy 953f263522aSJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 954f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent"); 955f263522aSJoseph Koshy 956f263522aSJoseph Koshy logfd = pipefd[WRITEPIPEFD]; 957f263522aSJoseph Koshy 958f263522aSJoseph Koshy args.pa_flags |= (FLAG_HAS_PIPE | FLAG_HAS_LOG_FILE); 959f263522aSJoseph Koshy args.pa_logparser = pmclog_open(pipefd[READPIPEFD]); 960f263522aSJoseph Koshy } else 961f263522aSJoseph Koshy logfd = fileno(args.pa_logfile); 962f263522aSJoseph Koshy 963f263522aSJoseph Koshy if (pmc_configure_logfile(logfd) < 0) 964f263522aSJoseph Koshy err(EX_OSERR, "ERROR: Cannot configure log file"); 965ebccf1e3SJoseph Koshy 966ebccf1e3SJoseph Koshy STAILQ_FOREACH(ev, &args.pa_head, ev_next) 967ebccf1e3SJoseph Koshy if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && 968ebccf1e3SJoseph Koshy pmc_set(ev->ev_pmcid, ev->ev_count) < 0) 969ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot set sampling count " 970ebccf1e3SJoseph Koshy "for PMC \"%s\"", ev->ev_name); 971ebccf1e3SJoseph Koshy } 972ebccf1e3SJoseph Koshy 973ebccf1e3SJoseph Koshy /* setup a timer for any counting mode PMCs */ 974f263522aSJoseph Koshy if (args.pa_flags & FLAG_HAS_COUNTING_PMCS) { 975ebccf1e3SJoseph Koshy EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 976ebccf1e3SJoseph Koshy args.pa_interval * 1000, NULL); 977ebccf1e3SJoseph Koshy 978ebccf1e3SJoseph Koshy if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 979ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot register kevent for " 980ebccf1e3SJoseph Koshy "timer"); 981ebccf1e3SJoseph Koshy } 982ebccf1e3SJoseph Koshy 983ebccf1e3SJoseph Koshy /* attach PMCs to the target process, starting it if specified */ 984ebccf1e3SJoseph Koshy if (args.pa_flags & FLAG_HAS_PROCESS) 985ebccf1e3SJoseph Koshy pmcstat_setup_process(&args); 986ebccf1e3SJoseph Koshy 987ebccf1e3SJoseph Koshy /* start the pmcs */ 988ebccf1e3SJoseph Koshy pmcstat_start_pmcs(&args); 989ebccf1e3SJoseph Koshy 990ebccf1e3SJoseph Koshy /* start the (commandline) process if needed */ 991ebccf1e3SJoseph Koshy if (args.pa_flags & FLAG_HAS_PROCESS) 992ebccf1e3SJoseph Koshy pmcstat_start_process(&args); 993ebccf1e3SJoseph Koshy 994ebccf1e3SJoseph Koshy /* Handle SIGINT using the kqueue loop */ 995ebccf1e3SJoseph Koshy sa.sa_handler = SIG_IGN; 996ebccf1e3SJoseph Koshy sa.sa_flags = 0; 997ebccf1e3SJoseph Koshy (void) sigemptyset(&sa.sa_mask); 998ebccf1e3SJoseph Koshy 999ebccf1e3SJoseph Koshy if (sigaction(SIGINT, &sa, NULL) < 0) 1000ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot install signal handler"); 1001ebccf1e3SJoseph Koshy 1002ebccf1e3SJoseph Koshy /* 1003ebccf1e3SJoseph Koshy * loop till either the target process (if any) exits, or we 1004ebccf1e3SJoseph Koshy * are killed by a SIGINT. 1005ebccf1e3SJoseph Koshy */ 1006f263522aSJoseph Koshy runstate = PMCSTAT_RUNNING; 1007f263522aSJoseph Koshy do_print = 0; 1008ebccf1e3SJoseph Koshy do { 1009ebccf1e3SJoseph Koshy if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) { 1010ebccf1e3SJoseph Koshy if (errno != EINTR) 1011ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: kevent failed"); 1012ebccf1e3SJoseph Koshy else 1013ebccf1e3SJoseph Koshy continue; 1014ebccf1e3SJoseph Koshy } 1015ebccf1e3SJoseph Koshy 1016ebccf1e3SJoseph Koshy if (kev.flags & EV_ERROR) 1017ebccf1e3SJoseph Koshy errc(EX_OSERR, kev.data, "ERROR: kevent failed"); 1018ebccf1e3SJoseph Koshy 1019ebccf1e3SJoseph Koshy switch (kev.filter) { 1020f263522aSJoseph Koshy case EVFILT_PROC: /* target has exited */ 1021f263522aSJoseph Koshy if (args.pa_flags & FLAG_HAS_LOG_FILE) 1022f263522aSJoseph Koshy runstate = pmcstat_close_log(&args); 1023f263522aSJoseph Koshy break; 1024ebccf1e3SJoseph Koshy 1025f263522aSJoseph Koshy case EVFILT_READ: /* log file data is present */ 1026f263522aSJoseph Koshy runstate = pmcstat_print_log(&args); 1027ebccf1e3SJoseph Koshy break; 1028ebccf1e3SJoseph Koshy 1029ebccf1e3SJoseph Koshy case EVFILT_SIGNAL: 1030f263522aSJoseph Koshy if (kev.ident == SIGCHLD) { 1031f263522aSJoseph Koshy /* 1032f263522aSJoseph Koshy * The child process sends us a 1033f263522aSJoseph Koshy * SIGCHLD if its exec() failed. We 1034f263522aSJoseph Koshy * wait for it to exit and then exit 1035f263522aSJoseph Koshy * ourselves. 1036f263522aSJoseph Koshy */ 1037f263522aSJoseph Koshy (void) wait(&c); 1038f263522aSJoseph Koshy runstate = PMCSTAT_FINISHED; 1039f263522aSJoseph Koshy } else if (kev.ident == SIGIO) { 1040f263522aSJoseph Koshy /* 1041f263522aSJoseph Koshy * We get a SIGIO if a PMC loses all 1042f263522aSJoseph Koshy * of its targets, or if logfile 1043f263522aSJoseph Koshy * writes encounter an error. 1044f263522aSJoseph Koshy */ 1045f263522aSJoseph Koshy if (args.pa_flags & FLAG_HAS_LOG_FILE) 1046f263522aSJoseph Koshy runstate = pmcstat_close_log(&args); 1047f263522aSJoseph Koshy do_print = 1; /* print PMCs at exit */ 1048f263522aSJoseph Koshy runstate = PMCSTAT_FINISHED; 1049f263522aSJoseph Koshy } else if (kev.ident == SIGINT) { 1050ebccf1e3SJoseph Koshy /* pass the signal on to the child process */ 1051ebccf1e3SJoseph Koshy if ((args.pa_flags & FLAG_HAS_PROCESS) && 1052ebccf1e3SJoseph Koshy (args.pa_flags & FLAG_HAS_PID) == 0) 1053ebccf1e3SJoseph Koshy if (kill(args.pa_pid, SIGINT) != 0) 1054f263522aSJoseph Koshy err(EX_OSERR, "ERROR: cannot " 1055f263522aSJoseph Koshy "signal child process"); 1056f263522aSJoseph Koshy runstate = PMCSTAT_FINISHED; 1057ebccf1e3SJoseph Koshy } else if (kev.ident == SIGWINCH) { 1058ebccf1e3SJoseph Koshy if (ioctl(fileno(args.pa_outputfile), 1059ebccf1e3SJoseph Koshy TIOCGWINSZ, &ws) < 0) 1060ebccf1e3SJoseph Koshy err(EX_OSERR, "ERROR: Cannot determine " 1061ebccf1e3SJoseph Koshy "window size"); 1062ebccf1e3SJoseph Koshy pmcstat_displayheight = ws.ws_row - 1; 1063ebccf1e3SJoseph Koshy } else 1064ebccf1e3SJoseph Koshy assert(0); 1065ebccf1e3SJoseph Koshy 1066ebccf1e3SJoseph Koshy break; 1067f263522aSJoseph Koshy 1068f263522aSJoseph Koshy case EVFILT_TIMER: /* print out counting PMCs */ 1069f263522aSJoseph Koshy do_print = 1; 1070f263522aSJoseph Koshy break; 1071f263522aSJoseph Koshy 1072ebccf1e3SJoseph Koshy } 1073ebccf1e3SJoseph Koshy 1074f263522aSJoseph Koshy if (do_print) { 1075f263522aSJoseph Koshy pmcstat_print_pmcs(&args); 1076f263522aSJoseph Koshy if (runstate == PMCSTAT_FINISHED) /* final newline */ 1077f263522aSJoseph Koshy (void) fprintf(args.pa_outputfile, "\n"); 1078f263522aSJoseph Koshy do_print = 0; 1079f263522aSJoseph Koshy } 1080f263522aSJoseph Koshy 1081f263522aSJoseph Koshy } while (runstate != PMCSTAT_FINISHED); 1082f263522aSJoseph Koshy 1083f263522aSJoseph Koshy /* flush any pending log entries */ 1084f263522aSJoseph Koshy if (args.pa_flags & FLAG_HAS_LOG_FILE) 1085f263522aSJoseph Koshy pmc_flush_logfile(); 1086ebccf1e3SJoseph Koshy 1087ebccf1e3SJoseph Koshy pmcstat_cleanup(&args); 1088ebccf1e3SJoseph Koshy 1089ebccf1e3SJoseph Koshy return 0; 1090ebccf1e3SJoseph Koshy } 1091