13554f22eSMatt Macy /*- 23554f22eSMatt Macy * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 33554f22eSMatt Macy * 43554f22eSMatt Macy * Copyright (c) 2018, Matthew Macy 53554f22eSMatt Macy * 63554f22eSMatt Macy * Redistribution and use in source and binary forms, with or without 73554f22eSMatt Macy * modification, are permitted provided that the following conditions 83554f22eSMatt Macy * are met: 93554f22eSMatt Macy * 1. Redistributions of source code must retain the above copyright 103554f22eSMatt Macy * notice, this list of conditions and the following disclaimer. 113554f22eSMatt Macy * 2. Redistributions in binary form must reproduce the above copyright 123554f22eSMatt Macy * notice, this list of conditions and the following disclaimer in the 133554f22eSMatt Macy * documentation and/or other materials provided with the distribution. 143554f22eSMatt Macy * 153554f22eSMatt Macy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 163554f22eSMatt Macy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 173554f22eSMatt Macy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 183554f22eSMatt Macy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 193554f22eSMatt Macy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 203554f22eSMatt Macy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 213554f22eSMatt Macy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 223554f22eSMatt Macy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 233554f22eSMatt Macy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 243554f22eSMatt Macy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 253554f22eSMatt Macy * SUCH DAMAGE. 263554f22eSMatt Macy * 273554f22eSMatt Macy */ 283554f22eSMatt Macy 293554f22eSMatt Macy #include <sys/cdefs.h> 303554f22eSMatt Macy __FBSDID("$FreeBSD$"); 313554f22eSMatt Macy 323554f22eSMatt Macy #include <sys/param.h> 333554f22eSMatt Macy #include <sys/cpuset.h> 343554f22eSMatt Macy #include <sys/event.h> 353554f22eSMatt Macy #include <sys/queue.h> 363554f22eSMatt Macy #include <sys/socket.h> 373554f22eSMatt Macy #include <sys/stat.h> 383554f22eSMatt Macy #include <sys/sysctl.h> 393554f22eSMatt Macy #include <sys/time.h> 403554f22eSMatt Macy #include <sys/ttycom.h> 413554f22eSMatt Macy #include <sys/user.h> 423554f22eSMatt Macy #include <sys/wait.h> 433554f22eSMatt Macy 443554f22eSMatt Macy #include <assert.h> 453554f22eSMatt Macy #include <curses.h> 463554f22eSMatt Macy #include <err.h> 473554f22eSMatt Macy #include <errno.h> 483554f22eSMatt Macy #include <fcntl.h> 493554f22eSMatt Macy #include <getopt.h> 503554f22eSMatt Macy #include <kvm.h> 513554f22eSMatt Macy #include <libgen.h> 523554f22eSMatt Macy #include <limits.h> 533554f22eSMatt Macy #include <locale.h> 543554f22eSMatt Macy #include <math.h> 553554f22eSMatt Macy #include <pmc.h> 563554f22eSMatt Macy #include <pmclog.h> 573554f22eSMatt Macy #include <regex.h> 583554f22eSMatt Macy #include <signal.h> 593554f22eSMatt Macy #include <stdarg.h> 603554f22eSMatt Macy #include <stdint.h> 613554f22eSMatt Macy #include <stdio.h> 623554f22eSMatt Macy #include <stdlib.h> 633554f22eSMatt Macy #include <string.h> 643554f22eSMatt Macy #include <sysexits.h> 653554f22eSMatt Macy #include <unistd.h> 663554f22eSMatt Macy 673554f22eSMatt Macy #include <libpmcstat.h> 683554f22eSMatt Macy #include "cmd_pmc.h" 693554f22eSMatt Macy 703554f22eSMatt Macy /* 713554f22eSMatt Macy * Return the frequency of the kernel's statistics clock. 723554f22eSMatt Macy */ 733554f22eSMatt Macy static int 743554f22eSMatt Macy getstathz(void) 753554f22eSMatt Macy { 763554f22eSMatt Macy int mib[2]; 773554f22eSMatt Macy size_t size; 783554f22eSMatt Macy struct clockinfo clockrate; 793554f22eSMatt Macy 803554f22eSMatt Macy mib[0] = CTL_KERN; 813554f22eSMatt Macy mib[1] = KERN_CLOCKRATE; 823554f22eSMatt Macy size = sizeof clockrate; 833554f22eSMatt Macy if (sysctl(mib, 2, &clockrate, &size, NULL, 0) == -1) 843554f22eSMatt Macy err(1, "sysctl kern.clockrate"); 853554f22eSMatt Macy return clockrate.stathz; 863554f22eSMatt Macy } 873554f22eSMatt Macy 883554f22eSMatt Macy #define STAT_MODE_NPMCS 6 896e69774aSMatt Macy #define FIXED_MODE_NPMCS 2 903554f22eSMatt Macy static struct timespec before_ts; 913554f22eSMatt Macy #define CYCLES 0 923554f22eSMatt Macy #define INST 1 933554f22eSMatt Macy #define BR 2 943554f22eSMatt Macy #define IAP_START BR 953554f22eSMatt Macy #define BR_MISS 3 963554f22eSMatt Macy #define CACHE 4 973554f22eSMatt Macy #define CACHE_MISS 5 983554f22eSMatt Macy static const char *pmc_stat_mode_names[] = { 993554f22eSMatt Macy "cycles", 1003554f22eSMatt Macy "instructions", 1013554f22eSMatt Macy "branches", 1023554f22eSMatt Macy "branch-misses", 1033554f22eSMatt Macy "cache-references", 1043554f22eSMatt Macy "cache-misses", 1053554f22eSMatt Macy }; 1063554f22eSMatt Macy 1073554f22eSMatt Macy static int pmcstat_sockpair[NSOCKPAIRFD]; 1083554f22eSMatt Macy 10932b68c46SEitan Adler static void __dead2 1103554f22eSMatt Macy usage(void) 1113554f22eSMatt Macy { 1123554f22eSMatt Macy errx(EX_USAGE, 1133554f22eSMatt Macy "\t get basic stats from command line program\n" 1143554f22eSMatt Macy "\t -j <eventlist>, --events <eventlist> comma-delimited list of event specifiers\n" 1153554f22eSMatt Macy ); 1163554f22eSMatt Macy } 1173554f22eSMatt Macy 1183554f22eSMatt Macy static void 1193554f22eSMatt Macy showtime(FILE *out, struct timespec *before, struct timespec *after, 1203554f22eSMatt Macy struct rusage *ru) 1213554f22eSMatt Macy { 1223554f22eSMatt Macy char decimal_point; 1233554f22eSMatt Macy uint64_t real, user, sys; 1243554f22eSMatt Macy 1253554f22eSMatt Macy (void)setlocale(LC_NUMERIC, ""); 1263554f22eSMatt Macy decimal_point = localeconv()->decimal_point[0]; 1273554f22eSMatt Macy 1283554f22eSMatt Macy after->tv_sec -= before->tv_sec; 1293554f22eSMatt Macy after->tv_nsec -= before->tv_nsec; 13032b68c46SEitan Adler if (after->tv_nsec < 0) { 13132b68c46SEitan Adler after->tv_sec--; 13232b68c46SEitan Adler after->tv_nsec += 1000000000; 13332b68c46SEitan Adler } 1343554f22eSMatt Macy 1353554f22eSMatt Macy real = (after->tv_sec * 1000000000 + after->tv_nsec) / 1000; 1363554f22eSMatt Macy user = ru->ru_utime.tv_sec * 1000000 + ru->ru_utime.tv_usec; 1373554f22eSMatt Macy sys = ru->ru_stime.tv_sec * 1000000 + ru->ru_stime.tv_usec; 1383554f22eSMatt Macy fprintf(out, "%13jd%c%02ld real\t\t\t#\t%2.02f%% cpu\n", 1393554f22eSMatt Macy (intmax_t)after->tv_sec, decimal_point, 1403554f22eSMatt Macy after->tv_nsec / 10000000, 100 * (double)(sys + user + 1) / (double)(real + 1)); 1413554f22eSMatt Macy fprintf(out, "%13jd%c%02ld user\t\t\t#\t%2.2f%% cpu\n", 1423554f22eSMatt Macy (intmax_t)ru->ru_utime.tv_sec, decimal_point, 1433554f22eSMatt Macy ru->ru_utime.tv_usec / 10000, 100 * (double)(user + 1) / (double)(real + 1)); 1443554f22eSMatt Macy fprintf(out, "%13jd%c%02ld sys\t\t\t#\t%2.02f%% cpu\n", 1453554f22eSMatt Macy (intmax_t)ru->ru_stime.tv_sec, decimal_point, 1463554f22eSMatt Macy ru->ru_stime.tv_usec / 10000, 100 * (double)(sys + 1) / (double)(real + 1)); 1473554f22eSMatt Macy } 1483554f22eSMatt Macy 1493554f22eSMatt Macy static const char *stat_mode_cntrs[STAT_MODE_NPMCS]; 1503554f22eSMatt Macy static const char *stat_mode_names[STAT_MODE_NPMCS]; 1513554f22eSMatt Macy 1523554f22eSMatt Macy static void 1533554f22eSMatt Macy pmc_stat_setup_stat(int system_mode, const char *arg) 1543554f22eSMatt Macy { 1553554f22eSMatt Macy const char *new_cntrs[STAT_MODE_NPMCS]; 1563554f22eSMatt Macy static const char **pmc_stat_mode_cntrs; 1573554f22eSMatt Macy struct pmcstat_ev *ev; 1583554f22eSMatt Macy char *counters, *counter; 1593554f22eSMatt Macy int i, c, start, newcnt; 1603554f22eSMatt Macy cpuset_t cpumask, rootmask; 1613554f22eSMatt Macy 1623554f22eSMatt Macy if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, 1633554f22eSMatt Macy sizeof(rootmask), &rootmask) == -1) 1643554f22eSMatt Macy err(EX_OSERR, "ERROR: Cannot determine the root set of CPUs"); 1653554f22eSMatt Macy CPU_COPY(&rootmask, &cpumask); 1663554f22eSMatt Macy 1673554f22eSMatt Macy if (pmc_pmu_stat_mode(&pmc_stat_mode_cntrs) != 0) 1683554f22eSMatt Macy errx(EX_USAGE, "ERROR: hwmpc.ko not loaded or stat not supported on host."); 1693554f22eSMatt Macy if (system_mode && geteuid() != 0) 1703554f22eSMatt Macy errx(EX_USAGE, "ERROR: system mode counters can only be used as root"); 1713554f22eSMatt Macy counters = NULL; 1723554f22eSMatt Macy for (i = 0; i < STAT_MODE_NPMCS; i++) { 1733554f22eSMatt Macy stat_mode_cntrs[i] = pmc_stat_mode_cntrs[i]; 1743554f22eSMatt Macy stat_mode_names[i] = pmc_stat_mode_names[i]; 1753554f22eSMatt Macy } 1763554f22eSMatt Macy if (arg) { 1773554f22eSMatt Macy counters = strdup(arg); 1783554f22eSMatt Macy newcnt = 0; 1793554f22eSMatt Macy while ((counter = strsep(&counters, ",")) != NULL && 1803554f22eSMatt Macy newcnt < STAT_MODE_NPMCS - IAP_START) { 1813554f22eSMatt Macy new_cntrs[newcnt++] = counter; 1823554f22eSMatt Macy if (pmc_pmu_sample_rate_get(counter) == DEFAULT_SAMPLE_COUNT) 1833554f22eSMatt Macy errx(EX_USAGE, "ERROR: %s not recognized on host", counter); 1843554f22eSMatt Macy } 1856e69774aSMatt Macy start = IAP_START + STAT_MODE_NPMCS - FIXED_MODE_NPMCS - newcnt; 1863554f22eSMatt Macy for (i = 0; i < newcnt; i++) { 1873554f22eSMatt Macy stat_mode_cntrs[start + i] = new_cntrs[i]; 1883554f22eSMatt Macy stat_mode_names[start + i] = new_cntrs[i]; 1893554f22eSMatt Macy } 1903554f22eSMatt Macy } 1913554f22eSMatt Macy if (system_mode) 1923554f22eSMatt Macy pmc_args.pa_flags |= FLAG_HAS_SYSTEM_PMCS; 1933554f22eSMatt Macy else 1943554f22eSMatt Macy pmc_args.pa_flags |= FLAG_HAS_PROCESS_PMCS; 1953554f22eSMatt Macy pmc_args.pa_flags |= FLAG_HAS_COUNTING_PMCS; 1963554f22eSMatt Macy pmc_args.pa_flags |= FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET; 1973554f22eSMatt Macy pmc_args.pa_flags |= FLAG_HAS_PIPE; 1983554f22eSMatt Macy pmc_args.pa_required |= FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET | FLAG_HAS_OUTPUT_LOGFILE; 1993554f22eSMatt Macy pmc_args.pa_outputpath = strdup("/dev/null"); 2003554f22eSMatt Macy pmc_args.pa_logfd = pmcstat_open_log(pmc_args.pa_outputpath, 2013554f22eSMatt Macy PMCSTAT_OPEN_FOR_WRITE); 2023554f22eSMatt Macy for (i = 0; i < STAT_MODE_NPMCS; i++) { 2033554f22eSMatt Macy if ((ev = malloc(sizeof(*ev))) == NULL) 2043554f22eSMatt Macy errx(EX_SOFTWARE, "ERROR: Out of memory."); 2053554f22eSMatt Macy if (system_mode) 2063554f22eSMatt Macy ev->ev_mode = PMC_MODE_SC; 2073554f22eSMatt Macy else 2083554f22eSMatt Macy ev->ev_mode = PMC_MODE_TC; 2093554f22eSMatt Macy ev->ev_spec = strdup(stat_mode_cntrs[i]); 2103554f22eSMatt Macy if (ev->ev_spec == NULL) 2113554f22eSMatt Macy errx(EX_SOFTWARE, "ERROR: Out of memory."); 2123554f22eSMatt Macy c = strcspn(strdup(stat_mode_cntrs[i]), ", \t"); 2133554f22eSMatt Macy ev->ev_name = malloc(c + 1); 2143554f22eSMatt Macy if (ev->ev_name == NULL) 2153554f22eSMatt Macy errx(EX_SOFTWARE, "ERROR: Out of memory."); 2163554f22eSMatt Macy (void)strncpy(ev->ev_name, stat_mode_cntrs[i], c); 2173554f22eSMatt Macy *(ev->ev_name + c) = '\0'; 2183554f22eSMatt Macy 2193554f22eSMatt Macy ev->ev_count = -1; 2203554f22eSMatt Macy ev->ev_flags = 0; 2213554f22eSMatt Macy ev->ev_flags |= PMC_F_DESCENDANTS; 2223554f22eSMatt Macy ev->ev_cumulative = 1; 2233554f22eSMatt Macy 2243554f22eSMatt Macy ev->ev_saved = 0LL; 2253554f22eSMatt Macy ev->ev_pmcid = PMC_ID_INVALID; 2263554f22eSMatt Macy STAILQ_INSERT_TAIL(&pmc_args.pa_events, ev, ev_next); 2273554f22eSMatt Macy if (system_mode) { 2283554f22eSMatt Macy ev->ev_cpu = CPU_FFS(&cpumask) - 1; 2293554f22eSMatt Macy CPU_CLR(ev->ev_cpu, &cpumask); 2303554f22eSMatt Macy pmcstat_clone_event_descriptor(ev, &cpumask, &pmc_args); 2313554f22eSMatt Macy CPU_SET(ev->ev_cpu, &cpumask); 2323554f22eSMatt Macy } else 2333554f22eSMatt Macy ev->ev_cpu = PMC_CPU_ANY; 2343554f22eSMatt Macy 2353554f22eSMatt Macy } 2363554f22eSMatt Macy if (clock_gettime(CLOCK_MONOTONIC, &before_ts)) 2373554f22eSMatt Macy err(1, "clock_gettime"); 2383554f22eSMatt Macy } 2393554f22eSMatt Macy 2403554f22eSMatt Macy static void 2413554f22eSMatt Macy pmc_stat_print_stat(struct rusage *ru) 2423554f22eSMatt Macy { 2433554f22eSMatt Macy struct pmcstat_ev *ev; 2443554f22eSMatt Macy struct timespec after; 2453554f22eSMatt Macy uint64_t cvals[STAT_MODE_NPMCS]; 2463554f22eSMatt Macy uint64_t ticks, value; 2473554f22eSMatt Macy int hz, i; 2483554f22eSMatt Macy 249fae71589SMatt Macy if (ru) { 2503554f22eSMatt Macy hz = getstathz(); 2513554f22eSMatt Macy ticks = hz * (ru->ru_utime.tv_sec + ru->ru_stime.tv_sec) + 2523554f22eSMatt Macy hz * (ru->ru_utime.tv_usec + ru->ru_stime.tv_usec) / 1000000; 2533554f22eSMatt Macy if (clock_gettime(CLOCK_MONOTONIC, &after)) 2543554f22eSMatt Macy err(1, "clock_gettime"); 2553554f22eSMatt Macy /* 2563554f22eSMatt Macy * If our round-off on the tick calculation still puts us at 0, 2573554f22eSMatt Macy * then always assume at least one tick. 2583554f22eSMatt Macy */ 2593554f22eSMatt Macy if (ticks == 0) 2603554f22eSMatt Macy ticks = 1; 2613554f22eSMatt Macy fprintf(pmc_args.pa_printfile, "%16ld %s\t\t#\t%02.03f M/sec\n", 2623554f22eSMatt Macy ru->ru_minflt, "page faults", ((double)ru->ru_minflt / (double)ticks) / hz); 2633554f22eSMatt Macy fprintf(pmc_args.pa_printfile, "%16ld %s\t\t#\t%02.03f M/sec\n", 2643554f22eSMatt Macy ru->ru_nvcsw, "voluntary csw", ((double)ru->ru_nvcsw / (double)ticks) / hz); 2653554f22eSMatt Macy fprintf(pmc_args.pa_printfile, "%16ld %s\t#\t%02.03f M/sec\n", 2663554f22eSMatt Macy ru->ru_nivcsw, "involuntary csw", ((double)ru->ru_nivcsw / (double)ticks) / hz); 267fae71589SMatt Macy } 268fae71589SMatt Macy 269fae71589SMatt Macy bzero(&cvals, sizeof(cvals)); 270fae71589SMatt Macy STAILQ_FOREACH(ev, &pmc_args.pa_events, ev_next) { 271fae71589SMatt Macy if (pmc_read(ev->ev_pmcid, &value) < 0) 272fae71589SMatt Macy err(EX_OSERR, "ERROR: Cannot read pmc \"%s\"", 273fae71589SMatt Macy ev->ev_name); 274fae71589SMatt Macy for (i = 0; i < STAT_MODE_NPMCS; i++) 275fae71589SMatt Macy if (strcmp(ev->ev_name, stat_mode_cntrs[i]) == 0) 276fae71589SMatt Macy cvals[i] += value; 277fae71589SMatt Macy } 2783554f22eSMatt Macy 2792708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s\n", (uintmax_t)cvals[CYCLES], stat_mode_names[CYCLES]); 2802708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s\t\t#\t%01.03f inst/cycle\n", (uintmax_t)cvals[INST], stat_mode_names[INST], 2813554f22eSMatt Macy (double)cvals[INST] / cvals[CYCLES]); 2822708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s\n", (uintmax_t)cvals[BR], stat_mode_names[BR]); 2833554f22eSMatt Macy if (stat_mode_names[BR_MISS] == pmc_stat_mode_names[BR_MISS]) 2842708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s\t\t#\t%.03f%%\n", 2852708737dSMatt Macy (uintmax_t)cvals[BR_MISS], stat_mode_names[BR_MISS], 2863554f22eSMatt Macy 100 * ((double)cvals[BR_MISS] / cvals[BR])); 2873554f22eSMatt Macy else 2882708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s\n", 2892708737dSMatt Macy (uintmax_t)cvals[BR_MISS], stat_mode_names[BR_MISS]); 2902708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s%s", (uintmax_t)cvals[CACHE], stat_mode_names[CACHE], 2913554f22eSMatt Macy stat_mode_names[CACHE] != pmc_stat_mode_names[CACHE] ? "\n" : ""); 2923554f22eSMatt Macy if (stat_mode_names[CACHE] == pmc_stat_mode_names[CACHE]) 2933554f22eSMatt Macy fprintf(pmc_args.pa_printfile, "\t#\t%.03f refs/inst\n", 2943554f22eSMatt Macy ((double)cvals[CACHE] / cvals[INST])); 2952708737dSMatt Macy fprintf(pmc_args.pa_printfile, "%16jd %s%s", (uintmax_t)cvals[CACHE_MISS], stat_mode_names[CACHE_MISS], 2963554f22eSMatt Macy stat_mode_names[CACHE_MISS] != pmc_stat_mode_names[CACHE_MISS] ? "\n" : ""); 2973554f22eSMatt Macy if (stat_mode_names[CACHE_MISS] == pmc_stat_mode_names[CACHE_MISS]) 2983554f22eSMatt Macy fprintf(pmc_args.pa_printfile, "\t\t#\t%.03f%%\n", 2993554f22eSMatt Macy 100 * ((double)cvals[CACHE_MISS] / cvals[CACHE])); 3003554f22eSMatt Macy 301fae71589SMatt Macy if (ru) 3023554f22eSMatt Macy showtime(pmc_args.pa_printfile, &before_ts, &after, ru); 3033554f22eSMatt Macy } 3043554f22eSMatt Macy 3053554f22eSMatt Macy static struct option longopts[] = { 3063554f22eSMatt Macy {"events", required_argument, NULL, 'j'}, 3073554f22eSMatt Macy {NULL, 0, NULL, 0} 3083554f22eSMatt Macy }; 3093554f22eSMatt Macy 3103554f22eSMatt Macy static int 3113554f22eSMatt Macy pmc_stat_internal(int argc, char **argv, int system_mode) 3123554f22eSMatt Macy { 3135244f3c6SMatt Macy char *event, *r; 3143554f22eSMatt Macy struct sigaction sa; 3153554f22eSMatt Macy struct kevent kev; 3163554f22eSMatt Macy struct rusage ru; 3173554f22eSMatt Macy struct winsize ws; 3183554f22eSMatt Macy struct pmcstat_ev *ev; 3195244f3c6SMatt Macy int c, option, runstate; 320fae71589SMatt Macy int waitstatus, ru_valid, do_debug; 3213554f22eSMatt Macy 322fae71589SMatt Macy do_debug = ru_valid = 0; 3235244f3c6SMatt Macy r = event = NULL; 324fae71589SMatt Macy while ((option = getopt_long(argc, argv, "dj:", longopts, NULL)) != -1) { 3253554f22eSMatt Macy switch (option) { 3263554f22eSMatt Macy case 'j': 3275244f3c6SMatt Macy r = event = strdup(optarg); 3283554f22eSMatt Macy break; 329fae71589SMatt Macy case 'd': 330fae71589SMatt Macy do_debug = 1; 331fae71589SMatt Macy break; 3323554f22eSMatt Macy case '?': 3333554f22eSMatt Macy default: 3343554f22eSMatt Macy usage(); 3353554f22eSMatt Macy } 3363554f22eSMatt Macy } 3373554f22eSMatt Macy pmc_args.pa_argc = (argc -= optind); 3383554f22eSMatt Macy pmc_args.pa_argv = (argv += optind); 3393554f22eSMatt Macy if (argc == 0) 3403554f22eSMatt Macy usage(); 3413554f22eSMatt Macy pmc_args.pa_flags |= FLAG_HAS_COMMANDLINE; 3423554f22eSMatt Macy pmc_stat_setup_stat(system_mode, event); 3435244f3c6SMatt Macy free(r); 3445244f3c6SMatt Macy bzero(&ru, sizeof(ru)); 3453554f22eSMatt Macy EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3463554f22eSMatt Macy if (kevent(pmc_kq, &kev, 1, NULL, 0, NULL) < 0) 3473554f22eSMatt Macy err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT"); 3483554f22eSMatt Macy 3493554f22eSMatt Macy EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3503554f22eSMatt Macy if (kevent(pmc_kq, &kev, 1, NULL, 0, NULL) < 0) 3513554f22eSMatt Macy err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO"); 352fae71589SMatt Macy EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); 353fae71589SMatt Macy if (kevent(pmc_kq, &kev, 1, NULL, 0, NULL) < 0) 354fae71589SMatt Macy err(EX_OSERR, 355fae71589SMatt Macy "ERROR: Cannot register kevent for timer"); 3563554f22eSMatt Macy 3573554f22eSMatt Macy STAILQ_FOREACH(ev, &pmc_args.pa_events, ev_next) { 3583554f22eSMatt Macy if (pmc_allocate(ev->ev_spec, ev->ev_mode, 359b2ca2e50SMatt Macy ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid, ev->ev_count) < 0) 3603554f22eSMatt Macy err(EX_OSERR, 3613554f22eSMatt Macy "ERROR: Cannot allocate %s-mode pmc with specification \"%s\"", 3623554f22eSMatt Macy PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 3633554f22eSMatt Macy "system" : "process", ev->ev_spec); 3643554f22eSMatt Macy 3653554f22eSMatt Macy if (PMC_IS_SAMPLING_MODE(ev->ev_mode) && 3663554f22eSMatt Macy pmc_set(ev->ev_pmcid, ev->ev_count) < 0) 3673554f22eSMatt Macy err(EX_OSERR, 3683554f22eSMatt Macy "ERROR: Cannot set sampling count for PMC \"%s\"", 3693554f22eSMatt Macy ev->ev_name); 3703554f22eSMatt Macy } 3713554f22eSMatt Macy 3723554f22eSMatt Macy /* 3733554f22eSMatt Macy * An exec() failure of a forked child is signalled by the 3743554f22eSMatt Macy * child sending the parent a SIGCHLD. We don't register an 3753554f22eSMatt Macy * actual signal handler for SIGCHLD, but instead use our 3763554f22eSMatt Macy * kqueue to pick up the signal. 3773554f22eSMatt Macy */ 3783554f22eSMatt Macy EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 3793554f22eSMatt Macy if (kevent(pmc_kq, &kev, 1, NULL, 0, NULL) < 0) 3803554f22eSMatt Macy err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD"); 3813554f22eSMatt Macy 3823554f22eSMatt Macy pmcstat_create_process(pmcstat_sockpair, &pmc_args, pmc_kq); 3833554f22eSMatt Macy 3843554f22eSMatt Macy if (SLIST_EMPTY(&pmc_args.pa_targets)) 3853554f22eSMatt Macy errx(EX_DATAERR, 3863554f22eSMatt Macy "ERROR: No matching target processes."); 3873554f22eSMatt Macy if (pmc_args.pa_flags & FLAG_HAS_PROCESS_PMCS) 3883554f22eSMatt Macy pmcstat_attach_pmcs(&pmc_args); 3893554f22eSMatt Macy 3903554f22eSMatt Macy /* start the pmcs */ 3913554f22eSMatt Macy pmc_util_start_pmcs(&pmc_args); 3923554f22eSMatt Macy 3933554f22eSMatt Macy /* start the (commandline) process if needed */ 3943554f22eSMatt Macy pmcstat_start_process(pmcstat_sockpair); 3953554f22eSMatt Macy 3963554f22eSMatt Macy /* Handle SIGINT using the kqueue loop */ 3973554f22eSMatt Macy sa.sa_handler = SIG_IGN; 3983554f22eSMatt Macy sa.sa_flags = 0; 3993554f22eSMatt Macy (void)sigemptyset(&sa.sa_mask); 4003554f22eSMatt Macy 4013554f22eSMatt Macy if (sigaction(SIGINT, &sa, NULL) < 0) 4023554f22eSMatt Macy err(EX_OSERR, "ERROR: Cannot install signal handler"); 4033554f22eSMatt Macy 4043554f22eSMatt Macy /* 4053554f22eSMatt Macy * loop till either the target process (if any) exits, or we 4063554f22eSMatt Macy * are killed by a SIGINT or we reached the time duration. 4073554f22eSMatt Macy */ 4083554f22eSMatt Macy runstate = PMCSTAT_RUNNING; 4093554f22eSMatt Macy do { 4103554f22eSMatt Macy if ((c = kevent(pmc_kq, NULL, 0, &kev, 1, NULL)) <= 0) { 4113554f22eSMatt Macy if (errno != EINTR) 4123554f22eSMatt Macy err(EX_OSERR, "ERROR: kevent failed"); 4133554f22eSMatt Macy else 4143554f22eSMatt Macy continue; 4153554f22eSMatt Macy } 4163554f22eSMatt Macy if (kev.flags & EV_ERROR) 4173554f22eSMatt Macy errc(EX_OSERR, kev.data, "ERROR: kevent failed"); 4183554f22eSMatt Macy 4193554f22eSMatt Macy switch (kev.filter) { 4203554f22eSMatt Macy case EVFILT_PROC: /* target has exited */ 4213554f22eSMatt Macy if (wait4(pmc_util_get_pid(&pmc_args), &waitstatus, 0, &ru) > 0) { 4223554f22eSMatt Macy getrusage(RUSAGE_CHILDREN, &ru); 4233554f22eSMatt Macy ru_valid = 1; 4243554f22eSMatt Macy } 4253554f22eSMatt Macy break; 4263554f22eSMatt Macy 4273554f22eSMatt Macy case EVFILT_READ: /* log file data is present */ 4283554f22eSMatt Macy break; 429fae71589SMatt Macy case EVFILT_TIMER: 430fae71589SMatt Macy if (do_debug) 431fae71589SMatt Macy pmc_stat_print_stat(NULL); 432fae71589SMatt Macy break; 4333554f22eSMatt Macy case EVFILT_SIGNAL: 4343554f22eSMatt Macy if (kev.ident == SIGCHLD) { 4353554f22eSMatt Macy /* 4363554f22eSMatt Macy * The child process sends us a 4373554f22eSMatt Macy * SIGCHLD if its exec() failed. We 4383554f22eSMatt Macy * wait for it to exit and then exit 4393554f22eSMatt Macy * ourselves. 4403554f22eSMatt Macy */ 4413554f22eSMatt Macy (void)wait(&c); 4423554f22eSMatt Macy runstate = PMCSTAT_FINISHED; 4433554f22eSMatt Macy } else if (kev.ident == SIGIO) { 4443554f22eSMatt Macy /* 4453554f22eSMatt Macy * We get a SIGIO if a PMC loses all 4463554f22eSMatt Macy * of its targets, or if logfile 4473554f22eSMatt Macy * writes encounter an error. 4483554f22eSMatt Macy */ 4493554f22eSMatt Macy if (wait4(pmc_util_get_pid(&pmc_args), &waitstatus, 0, &ru) > 0) { 4503554f22eSMatt Macy getrusage(RUSAGE_CHILDREN, &ru); 4513554f22eSMatt Macy ru_valid = 1; 4523554f22eSMatt Macy } 4533554f22eSMatt Macy runstate = pmcstat_close_log(&pmc_args); 4543554f22eSMatt Macy } else if (kev.ident == SIGINT) { 4553554f22eSMatt Macy /* Kill the child process if we started it */ 4563554f22eSMatt Macy if (pmc_args.pa_flags & FLAG_HAS_COMMANDLINE) 4573554f22eSMatt Macy pmc_util_kill_process(&pmc_args); 4583554f22eSMatt Macy runstate = pmcstat_close_log(&pmc_args); 4593554f22eSMatt Macy } else if (kev.ident == SIGWINCH) { 4603554f22eSMatt Macy if (ioctl(fileno(pmc_args.pa_printfile), 4613554f22eSMatt Macy TIOCGWINSZ, &ws) < 0) 4623554f22eSMatt Macy err(EX_OSERR, 4633554f22eSMatt Macy "ERROR: Cannot determine window size"); 4643554f22eSMatt Macy pmc_displayheight = ws.ws_row - 1; 4653554f22eSMatt Macy pmc_displaywidth = ws.ws_col - 1; 4663554f22eSMatt Macy } else 4673554f22eSMatt Macy assert(0); 4683554f22eSMatt Macy 4693554f22eSMatt Macy break; 4703554f22eSMatt Macy } 4713554f22eSMatt Macy } while (runstate != PMCSTAT_FINISHED); 4723554f22eSMatt Macy if (!ru_valid) 4733554f22eSMatt Macy warnx("couldn't get rusage"); 4743554f22eSMatt Macy pmc_stat_print_stat(&ru); 4753554f22eSMatt Macy pmc_util_cleanup(&pmc_args); 4763554f22eSMatt Macy return (0); 4773554f22eSMatt Macy } 4783554f22eSMatt Macy 4793554f22eSMatt Macy int 4803554f22eSMatt Macy cmd_pmc_stat(int argc, char **argv) 4813554f22eSMatt Macy { 4823554f22eSMatt Macy return (pmc_stat_internal(argc, argv, 0)); 4833554f22eSMatt Macy } 4843554f22eSMatt Macy 4853554f22eSMatt Macy int 4863554f22eSMatt Macy cmd_pmc_stat_system(int argc, char **argv) 4873554f22eSMatt Macy { 4883554f22eSMatt Macy return (pmc_stat_internal(argc, argv, 1)); 4893554f22eSMatt Macy } 490