diff options
author | Matt Macy <mmacy@FreeBSD.org> | 2018-06-04 04:59:48 +0000 |
---|---|---|
committer | Matt Macy <mmacy@FreeBSD.org> | 2018-06-04 04:59:48 +0000 |
commit | bfb46e2ba8bda92b9f94fabea65ae23d489ef787 (patch) | |
tree | 2e211c1131937311d9c538dadf1e4bbdb788fae5 /usr.sbin/pmc | |
parent | 8d0d26767b5606a7c65dc22daba9bf48a2ebd2b1 (diff) | |
download | src-bfb46e2ba8bda92b9f94fabea65ae23d489ef787.tar.gz src-bfb46e2ba8bda92b9f94fabea65ae23d489ef787.zip |
pmc: add filter command
pmc filter allows the user to select event types, threads, and processes from
a sample.
% pmcstat -S unhalted_core_cycles -S llc-misses -S -S resource_stalls.any -O pmc.log
% pmc filter -e llc-misses pmc.log pmc-llc-misses.log
% pmc filter -e unhalted_core_cycles -t 100339 pmc.log pmc-core-cycles.log
etc...
% pmcstat -R pmc-core-cycles.log -G pmc-core-cycles.stacks
Notes
Notes:
svn path=/head/; revision=334601
Diffstat (limited to 'usr.sbin/pmc')
-rw-r--r-- | usr.sbin/pmc/Makefile | 2 | ||||
-rw-r--r-- | usr.sbin/pmc/cmd_pmc.h | 1 | ||||
-rw-r--r-- | usr.sbin/pmc/cmd_pmc_filter.c | 269 | ||||
-rw-r--r-- | usr.sbin/pmc/pmc.c | 2 |
4 files changed, 273 insertions, 1 deletions
diff --git a/usr.sbin/pmc/Makefile b/usr.sbin/pmc/Makefile index 6a7d530aab38..6a0438452822 100644 --- a/usr.sbin/pmc/Makefile +++ b/usr.sbin/pmc/Makefile @@ -8,6 +8,6 @@ MAN= LIBADD= kvm pmc m ncursesw pmcstat elf SRCS= pmc.c pmc_util.c cmd_pmc_stat.c \ - cmd_pmc_list.c + cmd_pmc_list.c cmd_pmc_filter.c .include <bsd.prog.mk> diff --git a/usr.sbin/pmc/cmd_pmc.h b/usr.sbin/pmc/cmd_pmc.h index 1ed4393a0363..463c30b0fb87 100644 --- a/usr.sbin/pmc/cmd_pmc.h +++ b/usr.sbin/pmc/cmd_pmc.h @@ -41,6 +41,7 @@ extern struct pmcstat_args pmc_args; typedef int (*cmd_disp_t)(int, char **); int cmd_pmc_stat(int, char **); +int cmd_pmc_filter(int, char **); int cmd_pmc_stat_system(int, char **); int cmd_pmc_list_events(int, char **); diff --git a/usr.sbin/pmc/cmd_pmc_filter.c b/usr.sbin/pmc/cmd_pmc_filter.c new file mode 100644 index 000000000000..6d0bc36f1401 --- /dev/null +++ b/usr.sbin/pmc/cmd_pmc_filter.c @@ -0,0 +1,269 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018, Matthew Macy + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/cpuset.h> +#include <sys/event.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/ttycom.h> +#include <sys/user.h> +#include <sys/wait.h> + +#include <assert.h> +#include <curses.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <kvm.h> +#include <libgen.h> +#include <limits.h> +#include <locale.h> +#include <math.h> +#include <pmc.h> +#include <pmclog.h> +#include <regex.h> +#include <signal.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include <libpmcstat.h> +#include "cmd_pmc.h" + +#define LIST_MAX 64 +static struct option longopts[] = { + {"threads", no_argument, NULL, 't'}, + {"pids", no_argument, NULL, 'p'}, + {"events", no_argument, NULL, 'e'}, + {NULL, 0, NULL, 0} +}; + +static void +usage(void) +{ + errx(EX_USAGE, + "\t filter log file\n" + "\t -t <lwps>, --threads <lwps> -- comma-delimited list of lwps to filter on\n" + "\t -p <pids>, --pids <pids> -- comma-delimited list of pids to filter on\n" + "\t -e <events>, --events <events> -- comma-delimited list of events to filter on\n" + ); +} + + +static void +parse_intlist(char *strlist, int *intlist, int *pcount, int (*fn) (const char *)) +{ + char *token; + int count, tokenval; + + count = 0; + while ((token = strsep(&strlist, ",")) != NULL && + count < LIST_MAX) { + if ((tokenval = fn(token)) < 0) + errx(EX_USAGE, "ERROR: %s not usable value", token); + intlist[count++] = tokenval; + } + *pcount = count; +} + +static void +parse_events(char *strlist, int *intlist, int *pcount, char *cpuid) +{ + char *token; + int count, tokenval; + + count = 0; + while ((token = strsep(&strlist, ",")) != NULL && + count < LIST_MAX) { + if ((tokenval = pmc_pmu_idx_get_by_event(cpuid, token)) < 0) + errx(EX_USAGE, "ERROR: %s not usable value", token); + intlist[count++] = tokenval; + } + *pcount = count; +} + +struct pmcid_ent { + uint32_t pe_pmcid; + uint32_t pe_idx; +}; +#define _PMCLOG_TO_HEADER(T,L) \ + ((PMCLOG_HEADER_MAGIC << 24) | \ + (PMCLOG_TYPE_ ## T << 16) | \ + ((L) & 0xFFFF)) + +static void +pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidcount, + char *events, int infd, int outfd) +{ + struct pmclog_ev ev; + struct pmclog_parse_state *ps; + struct pmcid_ent *pe; + uint32_t eventlist[LIST_MAX]; + char cpuid[PMC_CPUID_LEN]; + int i, pmccount, copies, eventcount; + uint32_t idx, h; + off_t dstoff; + + if ((ps = pmclog_open(infd)) == NULL) + errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno)); + + pmccount = 0; + while (pmclog_read(ps, &ev) == 0) { + if (ev.pl_type == PMCLOG_TYPE_INITIALIZE) + memcpy(cpuid, ev.pl_u.pl_i.pl_cpuid, PMC_CPUID_LEN); + if (ev.pl_type == PMCLOG_TYPE_PMCALLOCATE) + pmccount++; + } + if (events) + parse_events(events, eventlist, &eventcount, cpuid); + + lseek(infd, 0, SEEK_SET); + pmclog_close(ps); + if ((ps = pmclog_open(infd)) == NULL) + errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno)); + if ((pe = malloc(sizeof(*pe) * pmccount)) == NULL) + errx(EX_OSERR, "ERROR: failed to allocate pmcid map"); + i = 0; + while (pmclog_read(ps, &ev) == 0 && i < pmccount) { + if (ev.pl_type == PMCLOG_TYPE_PMCALLOCATE) { + pe[i].pe_pmcid = ev.pl_u.pl_a.pl_pmcid; + pe[i].pe_idx = ev.pl_u.pl_a.pl_event; + i++; + } + } + lseek(infd, 0, SEEK_SET); + pmclog_close(ps); + if ((ps = pmclog_open(infd)) == NULL) + errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno)); + dstoff = copies = 0; + while (pmclog_read(ps, &ev) == 0) { + dstoff += ev.pl_len; + h = *(uint32_t *)ev.pl_data; + if (ev.pl_type != PMCLOG_TYPE_CALLCHAIN) { + if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len) + errx(EX_OSERR, "ERROR: failed output write"); + continue; + } + if (pidcount) { + for (i = 0; i < pidcount; i++) + if (pidlist[i] == ev.pl_u.pl_cc.pl_pid) + break; + if (i == pidcount) + continue; + } + if (lwpcount) { + for (i = 0; i < lwpcount; i++) + if (lwplist[i] == ev.pl_u.pl_cc.pl_tid) + break; + if (i == lwpcount) + continue; + } + if (eventcount) { + for (i = 0; i < pmccount; i++) { + if (pe[i].pe_pmcid == ev.pl_u.pl_cc.pl_pmcid) + break; + } + if (i == pmccount) + errx(EX_USAGE, "ERROR: unallocated pmcid: %d\n", + ev.pl_u.pl_cc.pl_pmcid); + + idx = pe[i].pe_idx; + for (i = 0; i < eventcount; i++) { + if (idx == eventlist[i]) + break; + } + if (i == eventcount) + continue; + } + if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len) + errx(EX_OSERR, "ERROR: failed output write"); + } +} + +int +cmd_pmc_filter(int argc, char **argv) +{ + char *lwps, *pids, *events; + uint32_t lwplist[LIST_MAX]; + uint32_t pidlist[LIST_MAX]; + int option, lwpcount, pidcount; + int prelogfd, postlogfd; + + lwps = pids = events = NULL; + lwpcount = pidcount = 0; + while ((option = getopt_long(argc, argv, "t:p:e:", longopts, NULL)) != -1) { + switch (option) { + case 't': + lwps = strdup(optarg); + break; + case 'p': + pids = strdup(optarg); + break; + case 'e': + events = strdup(optarg); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc != 2) + usage(); + + if (lwps) + parse_intlist(lwps, lwplist, &lwpcount, atoi); + if (pids) + parse_intlist(pids, pidlist, &pidcount, atoi); + if ((prelogfd = open(argv[0], O_RDONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) + errx(EX_OSERR, "ERROR: Cannot open \"%s\" for reading: %s.", argv[0], + strerror(errno)); + if ((postlogfd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) + errx(EX_OSERR, "ERROR: Cannot open \"%s\" for writing: %s.", argv[1], + strerror(errno)); + + pmc_filter_handler(lwplist, lwpcount, pidlist, pidcount, events, + prelogfd, postlogfd); + return (0); +} diff --git a/usr.sbin/pmc/pmc.c b/usr.sbin/pmc/pmc.c index a8a16e7bdfca..fb8268fc224a 100644 --- a/usr.sbin/pmc/pmc.c +++ b/usr.sbin/pmc/pmc.c @@ -65,6 +65,7 @@ static struct cmd_handler disp_table[] = { {"stat", cmd_pmc_stat}, {"stat-system", cmd_pmc_stat_system}, {"list-events", cmd_pmc_list_events}, + {"filter", cmd_pmc_filter}, {NULL, NULL} }; @@ -76,6 +77,7 @@ usage(void) "\t stat <program> run program and print stats\n" "\t stat-system <program> run program and print system wide stats for duration of execution\n" "\t list-events list PMC events available on host\n" + "\t filter filter records by lwp, pid, or event\n" ); } |