pmcstat.c revision 145256
168583Smarcel/*- 268583Smarcel * Copyright (c) 2003,2004 Joseph Koshy 368583Smarcel * All rights reserved. 468583Smarcel * 568583Smarcel * Redistribution and use in source and binary forms, with or without 6293593Sdchagin * modification, are permitted provided that the following conditions 768583Smarcel * are met: 868583Smarcel * 1. Redistributions of source code must retain the above copyright 968583Smarcel * notice, this list of conditions and the following disclaimer. 1068583Smarcel * 2. Redistributions in binary form must reproduce the above copyright 1168583Smarcel * notice, this list of conditions and the following disclaimer in the 1268583Smarcel * documentation and/or other materials provided with the distribution. 1368583Smarcel * 14177999Skib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15227776Slstewart * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16164184Strhodes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17122256Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18255673Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19122256Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20161330Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21161330Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2268583Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2368583Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2483366Sjulian * SUCH DAMAGE. 2583366Sjulian * 2668583Smarcel */ 2768583Smarcel 2868583Smarcel#include <sys/cdefs.h> 2983221Smarcel__FBSDID("$FreeBSD: head/usr.sbin/pmcstat/pmcstat.c 145256 2005-04-19 04:01:25Z jkoshy $"); 3083221Smarcel 3183221Smarcel#include <sys/types.h> 3283221Smarcel#include <sys/event.h> 3383221Smarcel#include <sys/queue.h> 3483221Smarcel#include <sys/time.h> 3583221Smarcel#include <sys/ttycom.h> 3683221Smarcel#include <sys/wait.h> 37143198Ssobomax 38293479Sdchagin#include <assert.h> 39293479Sdchagin#include <err.h> 40293479Sdchagin#include <errno.h> 4183221Smarcel#include <fcntl.h> 4268583Smarcel#include <limits.h> 4368583Smarcel#include <math.h> 4483221Smarcel#include <pmc.h> 4583221Smarcel#include <signal.h> 4683221Smarcel#include <stdarg.h> 4783221Smarcel#include <stdio.h> 4868583Smarcel#include <stdint.h> 4983221Smarcel#include <stdlib.h> 5083221Smarcel#include <string.h> 5183221Smarcel#include <sysexits.h> 5283221Smarcel#include <unistd.h> 5368583Smarcel 5483221Smarcel/* Operation modes */ 5583221Smarcel 5683221Smarcel#define FLAG_HAS_PID 0x00000001 5768583Smarcel#define FLAG_HAS_WAIT_INTERVAL 0x00000002 5883221Smarcel#define FLAG_HAS_LOG_FILE 0x00000004 5983221Smarcel#define FLAG_HAS_PROCESS 0x00000008 6083221Smarcel#define FLAG_USING_SAMPLING 0x00000010 6168583Smarcel#define FLAG_USING_COUNTING 0x00000020 6283221Smarcel#define FLAG_USING_PROCESS_PMC 0x00000040 6383221Smarcel 6468583Smarcel#define DEFAULT_SAMPLE_COUNT 65536 6583221Smarcel#define DEFAULT_WAIT_INTERVAL 5.0 6683221Smarcel#define DEFAULT_DISPLAY_HEIGHT 23 6783221Smarcel#define DEFAULT_LOGFILE_NAME "pmcstat.out" 6883221Smarcel 6968583Smarcel#define PRINT_HEADER_PREFIX "# " 7083221Smarcel#define READPIPEFD 0 7183221Smarcel#define WRITEPIPEFD 1 7268583Smarcel#define NPIPEFD 2 7383221Smarcel 7483221Smarcelstruct pmcstat_ev { 7568583Smarcel STAILQ_ENTRY(pmcstat_ev) ev_next; 7683221Smarcel char *ev_spec; /* event specification */ 7783221Smarcel char *ev_name; /* (derived) event name */ 7883221Smarcel enum pmc_mode ev_mode; /* desired mode */ 7983221Smarcel int ev_count; /* associated count if in sampling mode */ 8068583Smarcel int ev_cpu; /* specific cpu if requested */ 8183221Smarcel int ev_descendants; /* attach to descendants */ 8283221Smarcel int ev_cumulative; /* show cumulative counts */ 8383221Smarcel int ev_fieldwidth; /* print width */ 8468583Smarcel int ev_fieldskip; /* #leading spaces */ 8583221Smarcel pmc_value_t ev_saved; /* saved value for incremental counts */ 8683221Smarcel pmc_id_t ev_pmcid; /* allocated ID */ 8783221Smarcel}; 8883221Smarcel 8968583Smarcelstruct pmcstat_args { 9083221Smarcel int pa_flags; 9183221Smarcel pid_t pa_pid; 92156843Snetchild FILE *pa_outputfile; 9368583Smarcel FILE *pa_logfile; 9483221Smarcel double pa_interval; 9583221Smarcel int pa_argc; 9683221Smarcel char **pa_argv; 9783221Smarcel STAILQ_HEAD(, pmcstat_ev) pa_head; 9868583Smarcel} args; 9983221Smarcel 10083221Smarcelint pmcstat_interrupt = 0; 10168583Smarcelint pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT; 10283221Smarcelint pmcstat_pipefd[NPIPEFD]; 10383221Smarcelint pmcstat_kq; 10483221Smarcel 10583221Smarcel/* Function prototypes */ 10683221Smarcelvoid pmcstat_cleanup(struct pmcstat_args *_a); 10783221Smarcelvoid pmcstat_print_counters(struct pmcstat_args *_a); 10868583Smarcelvoid pmcstat_print_headers(struct pmcstat_args *_a); 10983221Smarcelvoid pmcstat_print_pmcs(struct pmcstat_args *_a); 11083221Smarcelvoid pmcstat_setup_process(struct pmcstat_args *_a); 11168583Smarcelvoid pmcstat_show_usage(void); 11283221Smarcelvoid pmcstat_start_pmcs(struct pmcstat_args *_a); 11383221Smarcelvoid pmcstat_start_process(struct pmcstat_args *_a); 11468583Smarcel 11583221Smarcel 11668583Smarcel/* 11768583Smarcel * cleanup 11883221Smarcel */ 11968583Smarcel 12068583Smarcelvoid 12183221Smarcelpmcstat_cleanup(struct pmcstat_args *a) 12296889Smarcel{ 12396889Smarcel struct pmcstat_ev *ev, *tmp; 12496889Smarcel 12596889Smarcel /* de-configure the log file if present. */ 12668583Smarcel if (a->pa_flags & FLAG_USING_SAMPLING) { 12783221Smarcel (void) pmc_configure_logfile(-1); 12883221Smarcel (void) fclose(a->pa_logfile); 12968583Smarcel } 13083221Smarcel 13183221Smarcel /* release allocated PMCs. */ 132156843Snetchild STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp) 13368583Smarcel if (ev->ev_pmcid != PMC_ID_INVALID) { 13483221Smarcel if (pmc_release(ev->ev_pmcid) < 0) 13568583Smarcel err(EX_OSERR, "ERROR: cannot release pmc " 13668583Smarcel "%d \"%s\"", ev->ev_pmcid, ev->ev_name); 13783221Smarcel free(ev->ev_name); 13883221Smarcel free(ev->ev_spec); 13983221Smarcel STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next); 14068583Smarcel free(ev); 14183221Smarcel } 14283221Smarcel} 143227692Sed 14468583Smarcelvoid 14583221Smarcelpmcstat_start_pmcs(struct pmcstat_args *a) 14683221Smarcel{ 14768583Smarcel struct pmcstat_ev *ev; 14883221Smarcel 14983221Smarcel STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 15083221Smarcel 15168583Smarcel assert(ev->ev_pmcid != PMC_ID_INVALID); 15283221Smarcel 15383221Smarcel if (pmc_start(ev->ev_pmcid) < 0) { 15483221Smarcel warn("ERROR: Cannot start pmc %d \"%s\"", 15568583Smarcel ev->ev_pmcid, ev->ev_name); 15683221Smarcel pmcstat_cleanup(a); 15783221Smarcel } 15883221Smarcel } 15968583Smarcel 16083221Smarcel} 16183221Smarcel 16268583Smarcelvoid 16383221Smarcelpmcstat_print_headers(struct pmcstat_args *a) 164234354Sjkim{ 16568583Smarcel struct pmcstat_ev *ev; 16683221Smarcel int c; 16783221Smarcel 16868583Smarcel (void) fprintf(a->pa_outputfile, PRINT_HEADER_PREFIX); 16983221Smarcel 17083221Smarcel STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 17168583Smarcel if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 17283221Smarcel continue; 17383221Smarcel 17468583Smarcel c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p'; 17583221Smarcel 17683221Smarcel if (ev->ev_fieldskip != 0) { 17768583Smarcel (void) fprintf(a->pa_outputfile, "%*s%c/%*s ", 17883221Smarcel ev->ev_fieldskip, "", c, 17983221Smarcel ev->ev_fieldwidth - ev->ev_fieldskip - 2, 180219560Savg ev->ev_name); 18168583Smarcel } else 18283221Smarcel (void) fprintf(a->pa_outputfile, "%c/%*s ", 18368583Smarcel c, ev->ev_fieldwidth - 2, ev->ev_name); 18468583Smarcel } 18583221Smarcel 18683221Smarcel (void) fflush(a->pa_outputfile); 18768583Smarcel} 18883221Smarcel 18983221Smarcelvoid 19083221Smarcelpmcstat_print_counters(struct pmcstat_args *a) 19168583Smarcel{ 19283221Smarcel int extra_width; 19383221Smarcel struct pmcstat_ev *ev; 19483221Smarcel pmc_value_t value; 19583221Smarcel 19668583Smarcel extra_width = sizeof(PRINT_HEADER_PREFIX) - 1; 19783221Smarcel 19883221Smarcel STAILQ_FOREACH(ev, &a->pa_head, ev_next) { 19983221Smarcel 20083221Smarcel /* skip sampling mode counters */ 20168583Smarcel if (PMC_IS_SAMPLING_MODE(ev->ev_mode)) 20283221Smarcel continue; 20368583Smarcel 20468583Smarcel if (pmc_read(ev->ev_pmcid, &value) < 0) 20583221Smarcel err(EX_OSERR, "ERROR: Cannot read pmc " 20683221Smarcel "\"%s\"", ev->ev_name); 20783221Smarcel 20868583Smarcel (void) fprintf(a->pa_outputfile, "%*ju ", 209161309Snetchild ev->ev_fieldwidth + extra_width, (uintmax_t) 210161309Snetchild ev->ev_cumulative ? value : (value - ev->ev_saved)); 211161309Snetchild if (ev->ev_cumulative == 0) 21283221Smarcel ev->ev_saved = value; 21383221Smarcel extra_width = 0; 21483221Smarcel } 21583221Smarcel 21668583Smarcel (void) fflush(a->pa_outputfile); 21783221Smarcel} 21868583Smarcel 21968583Smarcel/* 22083221Smarcel * Print output 22183221Smarcel */ 22268583Smarcel 22383221Smarcelvoid 22483221Smarcelpmcstat_print_pmcs(struct pmcstat_args *a) 22583221Smarcel{ 22668583Smarcel static int linecount = 0; 22783221Smarcel 22883221Smarcel if (++linecount > pmcstat_displayheight) { 22983221Smarcel (void) fprintf(a->pa_outputfile, "\n"); 23068583Smarcel linecount = 1; 23183221Smarcel } 23283221Smarcel 23383221Smarcel if (linecount == 1) 23483221Smarcel pmcstat_print_headers(a); 23568583Smarcel 23683221Smarcel (void) fprintf(a->pa_outputfile, "\n"); 23783221Smarcel pmcstat_print_counters(a); 23868583Smarcel 239156851Snetchild return; 240156851Snetchild} 241156851Snetchild 242156851Snetchild/* 24383221Smarcel * Do process profiling 24483221Smarcel * 24583221Smarcel * If a pid was specified, attach each allocated PMC to the target 24668583Smarcel * process. Otherwise, fork a child and attach the PMCs to the child, 24783221Smarcel * and have the child exec() the target program. 24883221Smarcel */ 24983221Smarcel 25068583Smarcelvoid 25183221Smarcelpmcstat_setup_process(struct pmcstat_args *a) 25283221Smarcel{ 25383221Smarcel char token; 25468583Smarcel struct pmcstat_ev *ev; 25583221Smarcel struct kevent kev; 25683221Smarcel 25783221Smarcel if (a->pa_flags & FLAG_HAS_PID) { 25868583Smarcel 25983221Smarcel STAILQ_FOREACH(ev, &args.pa_head, ev_next) 26083221Smarcel if (pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 26168583Smarcel err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to " 26283221Smarcel "process %d", ev->ev_name, (int) a->pa_pid); 26383221Smarcel 26483221Smarcel } else { 26568583Smarcel 266156851Snetchild /* 267156851Snetchild * We need to fork a new process and startup the child 268238918Sjhb * using execvp(). Before doing the exec() the child 269156851Snetchild * process reads its pipe for a token so that the parent 27083221Smarcel * can finish doing its pmc_attach() calls. 27183221Smarcel */ 27283221Smarcel 27383221Smarcel if (pipe(pmcstat_pipefd) < 0) 27472540Sjlemon err(EX_OSERR, "ERROR: cannot create pipe"); 27583221Smarcel 27683221Smarcel switch (a->pa_pid = fork()) { 27768583Smarcel case -1: 27883221Smarcel err(EX_OSERR, "ERROR: cannot fork"); 27983221Smarcel /*NOTREACHED*/ 28083221Smarcel 28183221Smarcel case 0: /* child */ 28283221Smarcel 28368583Smarcel /* wait for our parent to signal us */ 28483221Smarcel (void) close(pmcstat_pipefd[WRITEPIPEFD]); 28583221Smarcel if (read(pmcstat_pipefd[READPIPEFD], &token, 1) < 0) 28683221Smarcel err(EX_OSERR, "ERROR (child): cannot read " 28783221Smarcel "token"); 28868583Smarcel (void) close(pmcstat_pipefd[READPIPEFD]); 28983221Smarcel 29083221Smarcel /* exec() the program requested */ 29168583Smarcel execvp(*args.pa_argv, args.pa_argv); 29283221Smarcel err(EX_OSERR, "ERROR (child): execvp failed"); 29383221Smarcel /*NOTREACHED*/ 29483221Smarcel 29568583Smarcel default: /* parent */ 296156851Snetchild 297156851Snetchild (void) close(pmcstat_pipefd[READPIPEFD]); 298156851Snetchild 299156851Snetchild /* attach all our PMCs to the child */ 300147142Ssobomax STAILQ_FOREACH(ev, &args.pa_head, ev_next) 301147142Ssobomax if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) && 302147142Ssobomax pmc_attach(ev->ev_pmcid, a->pa_pid) != 0) 303147142Ssobomax err(EX_OSERR, "ERROR: cannot attach pmc " 30483221Smarcel "\"%s\" to process %d", ev->ev_name, 30583221Smarcel (int) a->pa_pid); 30683221Smarcel 30768583Smarcel } 30883221Smarcel } 30983221Smarcel 31083221Smarcel /* Ask to be notified via a kevent when the child exits */ 31168583Smarcel EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0); 31283221Smarcel 31383221Smarcel if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 31483221Smarcel err(EX_OSERR, "ERROR: cannot monitor process %d", 31583221Smarcel a->pa_pid); 31668583Smarcel 31783221Smarcel return; 31883221Smarcel} 31983221Smarcel 32068583Smarcelvoid 32183221Smarcelpmcstat_start_process(struct pmcstat_args *a) 32283221Smarcel{ 32383221Smarcel 32483221Smarcel /* nothing to do: target is already running */ 32568583Smarcel if (a->pa_flags & FLAG_HAS_PID) 32683221Smarcel return; 32783221Smarcel 32883221Smarcel /* write token to child to state that we are ready */ 32983221Smarcel if (write(pmcstat_pipefd[WRITEPIPEFD], "+", 1) != 1) 33068583Smarcel err(EX_OSERR, "ERROR: write failed"); 33183221Smarcel 33283221Smarcel (void) close(pmcstat_pipefd[WRITEPIPEFD]); 33383221Smarcel} 33468583Smarcel 33583221Smarcelvoid 33683221Smarcelpmcstat_show_usage(void) 33783221Smarcel{ 33868583Smarcel errx(EX_USAGE, 33983221Smarcel "[options] [commandline]\n" 34083221Smarcel "\t Measure process and/or system performance using hardware\n" 34183221Smarcel "\t performance monitoring counters.\n" 34268583Smarcel "\t Options include:\n" 34383221Smarcel "\t -C\t\t toggle showing cumulative counts\n" 34483221Smarcel "\t -O file\t set sampling log file to \"file\"\n" 34583221Smarcel "\t -P spec\t allocate process-private sampling PMC\n" 34668583Smarcel "\t -S spec\t allocate system-wide sampling PMC\n" 34783221Smarcel "\t -c cpu\t\t set default cpu\n" 34868583Smarcel "\t -d\t\t toggle tracking descendants\n" 34968583Smarcel "\t -n rate\t set sampling rate\n" 35083221Smarcel "\t -o file\t send print output to \"file\"\n" 351234360Sjkim "\t -p spec\t allocate process-private counting PMC\n" 35283221Smarcel "\t -s spec\t allocate system-wide counting PMC\n" 35383221Smarcel "\t -t pid\t attach to running process with pid \"pid\"\n" 35468583Smarcel "\t -w secs\t set printing time interval" 35568583Smarcel ); 35683221Smarcel} 35768583Smarcel 35868583Smarcel/* 35983221Smarcel * Main 36083221Smarcel */ 361276811Sdchagin 36283221Smarcelint 363293502Sdchaginmain(int argc, char **argv) 36468583Smarcel{ 36583221Smarcel double interval; 36668583Smarcel int option, npmc, ncpu; 36768583Smarcel int c, current_cpu, current_sampling_count; 36883221Smarcel int running; 36983221Smarcel int do_descendants, use_cumulative_counts; 37068583Smarcel pid_t pid; 37183221Smarcel char *end; 37283221Smarcel struct pmcstat_ev *ev; 37383221Smarcel struct pmc_op_getpmcinfo *ppmci; 37483221Smarcel struct sigaction sa; 37583221Smarcel struct kevent kev; 37683221Smarcel struct winsize ws; 37783221Smarcel 37868583Smarcel current_cpu = 0; 37983221Smarcel current_sampling_count = DEFAULT_SAMPLE_COUNT; 38083221Smarcel do_descendants = 0; 38168583Smarcel use_cumulative_counts = 0; 38283221Smarcel args.pa_flags = 0; 38383221Smarcel args.pa_pid = (pid_t) -1; 38483221Smarcel args.pa_logfile = NULL; 385161309Snetchild args.pa_outputfile = stderr; 386218611Sdchagin args.pa_interval = DEFAULT_WAIT_INTERVAL; 387161309Snetchild STAILQ_INIT(&args.pa_head); 38868583Smarcel 389184790Sed ev = NULL; 390184790Sed 391184790Sed while ((option = getopt(argc, argv, "CO:P:S:c:dn:o:p:s:t:w:")) != -1) 392184790Sed switch (option) { 39383221Smarcel case 'C': /* cumulative values */ 39483221Smarcel use_cumulative_counts = !use_cumulative_counts; 39568583Smarcel break; 39683221Smarcel 39783221Smarcel case 'c': /* CPU */ 39883221Smarcel current_cpu = strtol(optarg, &end, 0); 39983221Smarcel if (*end != '\0' || current_cpu < 0) 40068583Smarcel errx(EX_USAGE, 40183221Smarcel "ERROR: Illegal CPU number \"%s\"", 40268583Smarcel optarg); 40368583Smarcel 404166728Sjkim break; 405166728Sjkim 406166728Sjkim case 'd': /* toggle descendents */ 407166728Sjkim do_descendants = !do_descendants; 408166728Sjkim break; 40983221Smarcel 41083221Smarcel case 'p': /* process virtual counting PMC */ 41183221Smarcel case 's': /* system-wide counting PMC */ 41283221Smarcel case 'P': /* process virtual sampling PMC */ 41368583Smarcel case 'S': /* system-wide sampling PMC */ 41483221Smarcel if ((ev = malloc(sizeof(*ev))) == NULL) 41568583Smarcel errx(EX_SOFTWARE, "ERROR: Out of memory"); 41668583Smarcel 41783221Smarcel switch (option) { 41868583Smarcel case 'p': ev->ev_mode = PMC_MODE_TC; break; 41968583Smarcel case 's': ev->ev_mode = PMC_MODE_SC; break; 42083221Smarcel case 'P': ev->ev_mode = PMC_MODE_TS; break; 42168583Smarcel case 'S': ev->ev_mode = PMC_MODE_SS; break; 42268583Smarcel } 42383221Smarcel 42468583Smarcel if (option == 'P' || option == 'p') 42568583Smarcel args.pa_flags |= FLAG_USING_PROCESS_PMC; 42683221Smarcel 42768583Smarcel if (option == 'P' || option == 'S') 42868583Smarcel args.pa_flags |= FLAG_USING_SAMPLING; 42983221Smarcel 43068583Smarcel if (option == 'p' || option == 's') 43168583Smarcel args.pa_flags |= FLAG_USING_COUNTING; 43283221Smarcel 43383221Smarcel ev->ev_spec = strdup(optarg); 43483221Smarcel 43583221Smarcel if (option == 'S' || option == 'P') 43668583Smarcel ev->ev_count = current_sampling_count; 43783221Smarcel else 43883221Smarcel ev->ev_count = -1; 43968583Smarcel 44083221Smarcel if (option == 'S' || option == 's') 44183221Smarcel ev->ev_cpu = current_cpu; 44268583Smarcel else 44383221Smarcel ev->ev_cpu = PMC_CPU_ANY; 44483221Smarcel 44568583Smarcel ev->ev_descendants = do_descendants; 44683221Smarcel ev->ev_cumulative = use_cumulative_counts; 44783221Smarcel 44883221Smarcel ev->ev_saved = 0LL; 44983221Smarcel ev->ev_pmcid = PMC_ID_INVALID; 45083221Smarcel 45183221Smarcel /* extract event name */ 45268583Smarcel c = strcspn(optarg, ", \t"); 45383221Smarcel ev->ev_name = malloc(c + 1); 45483221Smarcel (void) strncpy(ev->ev_name, optarg, c); 45583221Smarcel *(ev->ev_name + c) = '\0'; 45683221Smarcel 45768583Smarcel STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next); 45883221Smarcel 45983221Smarcel break; 46083221Smarcel 46183221Smarcel case 'n': /* sampling count */ 46283221Smarcel current_sampling_count = strtol(optarg, &end, 0); 46383221Smarcel if (*end != '\0' || current_sampling_count <= 0) 46468583Smarcel errx(EX_USAGE, 46583221Smarcel "ERROR: Illegal count value \"%s\"", 46683221Smarcel optarg); 46783221Smarcel break; 46883221Smarcel 46968583Smarcel case 'o': /* outputfile */ 47083221Smarcel if (args.pa_outputfile != NULL) 47183221Smarcel (void) fclose(args.pa_outputfile); 47268583Smarcel 47383221Smarcel if ((args.pa_outputfile = fopen(optarg, "w")) == NULL) 47483221Smarcel errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 47568583Smarcel "writing", optarg); 47683221Smarcel 47783221Smarcel case 'O': /* sampling output */ 47868583Smarcel if (args.pa_logfile != NULL) 479293488Sdchagin (void) fclose(args.pa_logfile); 480293488Sdchagin 481293488Sdchagin if ((args.pa_logfile = fopen(optarg, "w")) == NULL) 482293488Sdchagin errx(EX_OSERR, "ERROR: cannot open \"%s\" for " 483293488Sdchagin "writing", optarg); 484293488Sdchagin break; 485293488Sdchagin 486293488Sdchagin case 't': /* target pid */ 48783221Smarcel pid = strtol(optarg, &end, 0); 48883221Smarcel if (*end != '\0' || pid <= 0) 48983221Smarcel errx(EX_USAGE, "ERROR: Illegal pid value " 49083221Smarcel "\"%s\"", optarg); 49168583Smarcel 49283221Smarcel args.pa_flags |= FLAG_HAS_PID; 49383221Smarcel args.pa_pid = pid; 49468583Smarcel 49583221Smarcel break; 49683221Smarcel 49768583Smarcel case 'w': /* wait interval */ 49883221Smarcel interval = strtod(optarg, &end); 49983221Smarcel if (*end != '\0' || interval <= 0) 50075057Salc errx(EX_USAGE, "ERROR: Illegal wait interval " 501293484Sdchagin "value \"%s\"", optarg); 502293484Sdchagin args.pa_flags |= FLAG_HAS_WAIT_INTERVAL; 503293484Sdchagin args.pa_interval = interval; 504293484Sdchagin 505165410Sjkim break; 506165410Sjkim 507165410Sjkim case '?': 508165410Sjkim default: 50983221Smarcel pmcstat_show_usage(); 51083221Smarcel break; 51183221Smarcel 51283221Smarcel } 51383221Smarcel 51483221Smarcel args.pa_argc = (argc -= optind); 51575057Salc args.pa_argv = (argv += optind); 51683221Smarcel 51783221Smarcel if (argc) 51883221Smarcel args.pa_flags |= FLAG_HAS_PROCESS; 51983221Smarcel 52068583Smarcel /* 52183221Smarcel * Check invocation syntax. 52283221Smarcel */ 52383221Smarcel 52483221Smarcel if (STAILQ_EMPTY(&args.pa_head)) { 52568583Smarcel warnx("ERROR: At least one PMC event must be specified"); 52683221Smarcel pmcstat_show_usage(); 52768583Smarcel } 52868583Smarcel 52983221Smarcel if (argc == 0) { 53068583Smarcel if (args.pa_pid == -1) { 53168583Smarcel if (args.pa_flags & FLAG_USING_PROCESS_PMC) 53283221Smarcel errx(EX_USAGE, "ERROR: the -P or -p options " 53368583Smarcel "require a target process"); 53468583Smarcel } else if ((args.pa_flags & FLAG_USING_PROCESS_PMC) == 0) 53583221Smarcel errx(EX_USAGE, 53683221Smarcel "ERROR: option -t requires a process-mode pmc " 53783221Smarcel "specification"); 53883221Smarcel } else if (args.pa_pid != -1) 53968583Smarcel errx(EX_USAGE, 54083221Smarcel "ERROR: option -t cannot be specified with a command " 54183221Smarcel "name"); 54283221Smarcel 54383221Smarcel if (pmc_init() < 0) 54483221Smarcel err(EX_UNAVAILABLE, 54583221Smarcel "ERROR: Initialization of the pmc(3) library failed"); 546163736Snetchild 547163736Snetchild if ((ncpu = pmc_ncpu()) < 0) 548163736Snetchild err(EX_OSERR, "ERROR: Cannot determine the number CPUs " 549163736Snetchild "on the system"); 550163736Snetchild 55168583Smarcel if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */ 55283221Smarcel err(EX_OSERR, "ERROR: Cannot determine the number of PMCs " 55383221Smarcel "on CPU %d", 0); 55468583Smarcel 55583221Smarcel /* 55683221Smarcel * Allocate PMCs. 55783221Smarcel */ 55883221Smarcel 55983221Smarcel if (pmc_pmcinfo(0, &ppmci) < 0) 56068583Smarcel err(EX_OSERR, "ERROR: cannot retrieve pmc information"); 56183221Smarcel 56283221Smarcel assert(ppmci != NULL); 56383221Smarcel 56483221Smarcel STAILQ_FOREACH(ev, &args.pa_head, ev_next) 56583221Smarcel if (pmc_allocate(ev->ev_spec, ev->ev_mode, 56668583Smarcel (ev->ev_descendants ? PMC_F_DESCENDANTS : 0), 56783221Smarcel ev->ev_cpu, &ev->ev_pmcid) < 0) 568158407Snetchild err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with " 569158407Snetchild "specification \"%s\"", 57068583Smarcel PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process", 57183221Smarcel ev->ev_spec); 572165690Snetchild 573165690Snetchild /* compute printout widths */ 574165690Snetchild STAILQ_FOREACH(ev, &args.pa_head, ev_next) { 575165690Snetchild int pmc_width; 57668583Smarcel int pmc_display_width; 57783221Smarcel int pmc_header_width; 578293506Sdchagin 579293506Sdchagin pmc_width = ppmci->pm_pmcs[ev->ev_pmcid].pm_width; 580293506Sdchagin pmc_header_width = strlen(ev->ev_name) + 2; /* prefix '%c|' */ 58168583Smarcel pmc_display_width = (int) floor(pmc_width / 3.32193) + 1; 58283221Smarcel 58383221Smarcel if (pmc_header_width > pmc_display_width) { 58483221Smarcel ev->ev_fieldskip = 0; 58568583Smarcel ev->ev_fieldwidth = pmc_header_width; 58683221Smarcel } else { 58783221Smarcel ev->ev_fieldskip = pmc_display_width - 58883221Smarcel pmc_header_width; 58983221Smarcel ev->ev_fieldwidth = pmc_display_width; 59083221Smarcel } 59168583Smarcel } 59283221Smarcel 59383221Smarcel /* Allocate a kqueue */ 59483221Smarcel if ((pmcstat_kq = kqueue()) < 0) 59583221Smarcel err(EX_OSERR, "ERROR: Cannot allocate kqueue"); 59683221Smarcel 59768583Smarcel /* 59883221Smarcel * If our output is being set to a terminal, register a handler 59983221Smarcel * for window size changes. 60083221Smarcel */ 60183221Smarcel 60268583Smarcel if (isatty(fileno(args.pa_outputfile))) { 60383221Smarcel 60483221Smarcel if (ioctl(fileno(args.pa_outputfile), TIOCGWINSZ, &ws) < 0) 60583221Smarcel err(EX_OSERR, "ERROR: Cannot determine window size"); 60668583Smarcel 60783221Smarcel pmcstat_displayheight = ws.ws_row - 1; 608220030Savg 609220030Savg EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 61068583Smarcel 61183221Smarcel if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 612220030Savg err(EX_OSERR, "ERROR: Cannot register kevent for " 613220030Savg "SIGWINCH"); 61468583Smarcel } 61583221Smarcel 61683221Smarcel EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 61783221Smarcel 61868583Smarcel if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 61983221Smarcel err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); 62068583Smarcel 62168583Smarcel if (args.pa_flags & FLAG_USING_SAMPLING) { 62283221Smarcel 62368583Smarcel /* 62468583Smarcel * configure log file 62583221Smarcel */ 62683221Smarcel 62783221Smarcel if (args.pa_logfile == NULL) 62868583Smarcel if ((args.pa_logfile = 62983221Smarcel fopen(DEFAULT_LOGFILE_NAME, "w")) == NULL) 63083221Smarcel err(EX_CANTCREAT, "ERROR: Cannot open sampling " 63183221Smarcel "log file \"%s\"", DEFAULT_LOGFILE_NAME); 63283221Smarcel 63383221Smarcel if (pmc_configure_logfile(fileno(args.pa_logfile)) < 0) 63483221Smarcel err(EX_OSERR, "ERROR: Cannot configure sampling " 63583221Smarcel "log"); 63668583Smarcel 63783221Smarcel STAILQ_FOREACH(ev, &args.pa_head, ev_next) 63883221Smarcel if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && 63983221Smarcel pmc_set(ev->ev_pmcid, ev->ev_count) < 0) 64068583Smarcel err(EX_OSERR, "ERROR: Cannot set sampling count " 64183221Smarcel "for PMC \"%s\"", ev->ev_name); 64283221Smarcel } 64383221Smarcel 64468583Smarcel /* setup a timer for any counting mode PMCs */ 64583221Smarcel if (args.pa_flags & FLAG_USING_COUNTING) { 646234358Sjkim EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 64783221Smarcel args.pa_interval * 1000, NULL); 64868583Smarcel 64983221Smarcel if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0) 650234358Sjkim err(EX_OSERR, "ERROR: Cannot register kevent for " 65183221Smarcel "timer"); 65268583Smarcel } 65383221Smarcel 654234358Sjkim /* attach PMCs to the target process, starting it if specified */ 65583221Smarcel if (args.pa_flags & FLAG_HAS_PROCESS) 65683221Smarcel pmcstat_setup_process(&args); 65783221Smarcel 65885022Smarcel /* start the pmcs */ 65985022Smarcel pmcstat_start_pmcs(&args); 66085022Smarcel 66183221Smarcel /* start the (commandline) process if needed */ 66283221Smarcel if (args.pa_flags & FLAG_HAS_PROCESS) 66368583Smarcel pmcstat_start_process(&args); 66468583Smarcel 66583221Smarcel /* Handle SIGINT using the kqueue loop */ 66668583Smarcel sa.sa_handler = SIG_IGN; 66768583Smarcel sa.sa_flags = 0; 66883221Smarcel (void) sigemptyset(&sa.sa_mask); 66983221Smarcel 67083221Smarcel if (sigaction(SIGINT, &sa, NULL) < 0) 67183221Smarcel err(EX_OSERR, "ERROR: Cannot install signal handler"); 67283221Smarcel 67383221Smarcel /* 67483221Smarcel * loop till either the target process (if any) exits, or we 67583221Smarcel * are killed by a SIGINT. 67683221Smarcel */ 67785022Smarcel 67885022Smarcel running = 1; 67985022Smarcel do { 68083221Smarcel if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) { 68183221Smarcel if (errno != EINTR) 68283221Smarcel err(EX_OSERR, "ERROR: kevent failed"); 68383221Smarcel else 68483221Smarcel continue; 68583221Smarcel } 68683221Smarcel 68783221Smarcel if (kev.flags & EV_ERROR) 68883221Smarcel errc(EX_OSERR, kev.data, "ERROR: kevent failed"); 68983221Smarcel 69083221Smarcel switch (kev.filter) { 69183221Smarcel case EVFILT_PROC: /* target process exited */ 69283221Smarcel running = 0; 69383221Smarcel /* FALLTHROUGH */ 69483221Smarcel 69583221Smarcel case EVFILT_TIMER: /* print out counting PMCs */ 69683221Smarcel pmcstat_print_pmcs(&args); 69783221Smarcel 69883221Smarcel if (running == 0) /* final newline */ 69983221Smarcel (void) fprintf(args.pa_outputfile, "\n"); 70083221Smarcel break; 70183221Smarcel 70283221Smarcel case EVFILT_SIGNAL: 70383221Smarcel if (kev.ident == SIGINT) { 70483221Smarcel /* pass the signal on to the child process */ 70583221Smarcel if ((args.pa_flags & FLAG_HAS_PROCESS) && 706134839Sdfr (args.pa_flags & FLAG_HAS_PID) == 0) 707134839Sdfr if (kill(args.pa_pid, SIGINT) != 0) 708134839Sdfr err(EX_OSERR, "cannot kill " 709122802Ssobomax "child"); 710122802Ssobomax running = 0; 711122802Ssobomax } else if (kev.ident == SIGWINCH) { 712122802Ssobomax if (ioctl(fileno(args.pa_outputfile), 713122802Ssobomax TIOCGWINSZ, &ws) < 0) 714122802Ssobomax err(EX_OSERR, "ERROR: Cannot determine " 715122802Ssobomax "window size"); 716122802Ssobomax pmcstat_displayheight = ws.ws_row - 1; 717122802Ssobomax } else 718122802Ssobomax assert(0); 719122802Ssobomax 720122802Ssobomax break; 721122802Ssobomax } 722122802Ssobomax 723122802Ssobomax } while (running); 724122802Ssobomax 725122802Ssobomax pmcstat_cleanup(&args); 726122802Ssobomax 727122802Ssobomax return 0; 728122802Ssobomax} 729122802Ssobomax