mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-06 17:18:32 +00:00
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
This commit is contained in:
parent
8d0d26767b
commit
bfb46e2ba8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=334601
|
@ -62,8 +62,8 @@ static struct pmu_alias pmu_alias_table[] = {
|
|||
{"BRANCH-MISSES-RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"},
|
||||
{"cycles", "tsc-tsc"},
|
||||
{"instructions", "inst-retired.any_p"},
|
||||
{"branch-mispredicts", "br_misp_retired.all_branches" },
|
||||
{"branches", "br_inst_retired.all_branches" },
|
||||
{"branch-mispredicts", "br_misp_retired.all_branches"},
|
||||
{"branches", "br_inst_retired.all_branches"},
|
||||
{"interrupts", "hw_interrupts.received"},
|
||||
{"ic-misses", "frontend_retired.l1i_miss"},
|
||||
{NULL, NULL},
|
||||
|
@ -106,18 +106,22 @@ struct pmu_event_desc {
|
|||
};
|
||||
|
||||
static const struct pmu_events_map *
|
||||
pmu_events_map_get(void)
|
||||
pmu_events_map_get(const char *cpuid)
|
||||
{
|
||||
size_t s;
|
||||
char buf[64];
|
||||
const struct pmu_events_map *pme;
|
||||
|
||||
if (sysctlbyname("kern.hwpmc.cpuid", (void *)NULL, &s,
|
||||
(void *)NULL, 0) == -1)
|
||||
return (NULL);
|
||||
if (sysctlbyname("kern.hwpmc.cpuid", buf, &s,
|
||||
(void *)NULL, 0) == -1)
|
||||
return (NULL);
|
||||
if (cpuid != NULL) {
|
||||
memcpy(buf, cpuid, 64);
|
||||
} else {
|
||||
if (sysctlbyname("kern.hwpmc.cpuid", (void *)NULL, &s,
|
||||
(void *)NULL, 0) == -1)
|
||||
return (NULL);
|
||||
if (sysctlbyname("kern.hwpmc.cpuid", buf, &s,
|
||||
(void *)NULL, 0) == -1)
|
||||
return (NULL);
|
||||
}
|
||||
for (pme = pmu_events_map; pme->cpuid != NULL; pme++)
|
||||
if (strcmp(buf, pme->cpuid) == 0)
|
||||
return (pme);
|
||||
|
@ -125,13 +129,13 @@ pmu_events_map_get(void)
|
|||
}
|
||||
|
||||
static const struct pmu_event *
|
||||
pmu_event_get(const char *event_name, int *idx)
|
||||
pmu_event_get(const char *cpuid, const char *event_name, int *idx)
|
||||
{
|
||||
const struct pmu_events_map *pme;
|
||||
const struct pmu_event *pe;
|
||||
int i;
|
||||
|
||||
if ((pme = pmu_events_map_get()) == NULL)
|
||||
if ((pme = pmu_events_map_get(cpuid)) == NULL)
|
||||
return (NULL);
|
||||
for (i = 0, pe = pme->table; pe->name || pe->desc || pe->event; pe++, i++) {
|
||||
if (pe->name == NULL)
|
||||
|
@ -145,6 +149,18 @@ pmu_event_get(const char *event_name, int *idx)
|
|||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
pmc_pmu_idx_get_by_event(const char *cpuid, const char *event)
|
||||
{
|
||||
int idx;
|
||||
const char *realname;
|
||||
|
||||
realname = pmu_alias_get(event);
|
||||
if (pmu_event_get(cpuid, realname, &idx) == NULL)
|
||||
return (-1);
|
||||
return (idx);
|
||||
}
|
||||
|
||||
const char *
|
||||
pmc_pmu_event_get_by_idx(int idx)
|
||||
{
|
||||
|
@ -152,7 +168,7 @@ pmc_pmu_event_get_by_idx(int idx)
|
|||
const struct pmu_event *pe;
|
||||
int i;
|
||||
|
||||
if ((pme = pmu_events_map_get()) == NULL)
|
||||
if ((pme = pmu_events_map_get(NULL)) == NULL)
|
||||
return (NULL);
|
||||
for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++);
|
||||
return (pe->name);
|
||||
|
@ -218,9 +234,9 @@ pmc_pmu_sample_rate_get(const char *event_name)
|
|||
struct pmu_event_desc ped;
|
||||
|
||||
event_name = pmu_alias_get(event_name);
|
||||
if ((pe = pmu_event_get(event_name, NULL)) == NULL)
|
||||
if ((pe = pmu_event_get(NULL, event_name, NULL)) == NULL)
|
||||
return (DEFAULT_SAMPLE_COUNT);
|
||||
if (pe->alias && (pe = pmu_event_get(pe->alias, NULL)) == NULL)
|
||||
if (pe->alias && (pe = pmu_event_get(NULL, pe->alias, NULL)) == NULL)
|
||||
return (DEFAULT_SAMPLE_COUNT);
|
||||
if (pe->event == NULL)
|
||||
return (DEFAULT_SAMPLE_COUNT);
|
||||
|
@ -233,7 +249,7 @@ int
|
|||
pmc_pmu_enabled(void)
|
||||
{
|
||||
|
||||
return (pmu_events_map_get() != NULL);
|
||||
return (pmu_events_map_get(NULL) != NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -250,7 +266,7 @@ pmc_pmu_print_counters(const char *event_name)
|
|||
|
||||
if (debug != NULL && strcmp(debug, "true") == 0)
|
||||
do_debug = 1;
|
||||
if ((pme = pmu_events_map_get()) == NULL)
|
||||
if ((pme = pmu_events_map_get(NULL)) == NULL)
|
||||
return;
|
||||
for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
|
||||
if (pe->name == NULL)
|
||||
|
@ -269,7 +285,7 @@ pmc_pmu_print_counter_desc(const char *ev)
|
|||
const struct pmu_events_map *pme;
|
||||
const struct pmu_event *pe;
|
||||
|
||||
if ((pme = pmu_events_map_get()) == NULL)
|
||||
if ((pme = pmu_events_map_get(NULL)) == NULL)
|
||||
return;
|
||||
for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
|
||||
if (pe->name == NULL)
|
||||
|
@ -286,7 +302,7 @@ pmc_pmu_print_counter_desc_long(const char *ev)
|
|||
const struct pmu_events_map *pme;
|
||||
const struct pmu_event *pe;
|
||||
|
||||
if ((pme = pmu_events_map_get()) == NULL)
|
||||
if ((pme = pmu_events_map_get(NULL)) == NULL)
|
||||
return;
|
||||
for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
|
||||
if (pe->name == NULL)
|
||||
|
@ -306,7 +322,7 @@ pmc_pmu_print_counter_full(const char *ev)
|
|||
const struct pmu_events_map *pme;
|
||||
const struct pmu_event *pe;
|
||||
|
||||
if ((pme = pmu_events_map_get()) == NULL)
|
||||
if ((pme = pmu_events_map_get(NULL)) == NULL)
|
||||
return;
|
||||
for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
|
||||
if (pe->name == NULL)
|
||||
|
@ -350,9 +366,9 @@ pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm)
|
|||
bzero(iap, sizeof(*iap));
|
||||
event_name = pmu_alias_get(event_name);
|
||||
pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE);
|
||||
if ((pe = pmu_event_get(event_name, &idx)) == NULL)
|
||||
if ((pe = pmu_event_get(NULL, event_name, &idx)) == NULL)
|
||||
return (ENOENT);
|
||||
if (pe->alias && (pe = pmu_event_get(pe->alias, &idx)) == NULL)
|
||||
if (pe->alias && (pe = pmu_event_get(NULL, pe->alias, &idx)) == NULL)
|
||||
return (ENOENT);
|
||||
if (pe->event == NULL)
|
||||
return (ENOENT);
|
||||
|
@ -361,11 +377,11 @@ pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm)
|
|||
|
||||
|
||||
if (strcasestr(event_name, "UNC_") == event_name ||
|
||||
strcasestr(event_name, "uncore") != NULL) {
|
||||
strcasestr(event_name, "uncore") != NULL) {
|
||||
pm->pm_class = PMC_CLASS_UCP;
|
||||
pm->pm_caps |= PMC_CAP_QUALIFIER;
|
||||
} else if ((ped.ped_umask == -1) ||
|
||||
(ped.ped_event == 0x0 && ped.ped_umask == 0x3)) {
|
||||
(ped.ped_event == 0x0 && ped.ped_umask == 0x3)) {
|
||||
pm->pm_class = PMC_CLASS_IAF;
|
||||
} else {
|
||||
pm->pm_class = PMC_CLASS_IAP;
|
||||
|
@ -458,10 +474,17 @@ pmc_pmu_event_get_by_idx(int idx __unused)
|
|||
{
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
pmc_pmu_stat_mode(const char ***a __unused)
|
||||
{
|
||||
return (EOPNOTSUPP);
|
||||
}
|
||||
|
||||
int
|
||||
pmc_pmu_idx_get_by_event(const char *e __unused)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -121,6 +121,7 @@ void pmc_pmu_print_counter_full(const char *);
|
|||
uint64_t pmc_pmu_sample_rate_get(const char *);
|
||||
int pmc_pmu_pmcallocate(const char *, struct pmc_op_pmcallocate *);
|
||||
const char *pmc_pmu_event_get_by_idx(int idx);
|
||||
int pmc_pmu_idx_get_by_event(const char*, const char *);
|
||||
int pmc_pmu_stat_mode(const char ***);
|
||||
__END_DECLS
|
||||
|
||||
|
|
|
@ -78,28 +78,6 @@ __FBSDID("$FreeBSD$");
|
|||
* performance critical paths.
|
||||
*/
|
||||
|
||||
enum pmclog_parser_state {
|
||||
PL_STATE_NEW_RECORD, /* in-between records */
|
||||
PL_STATE_EXPECTING_HEADER, /* header being read */
|
||||
PL_STATE_PARTIAL_RECORD, /* header present but not the record */
|
||||
PL_STATE_ERROR /* parsing error encountered */
|
||||
};
|
||||
|
||||
struct pmclog_parse_state {
|
||||
enum pmclog_parser_state ps_state;
|
||||
enum pmc_cputype ps_arch; /* log file architecture */
|
||||
uint32_t ps_version; /* hwpmc version */
|
||||
int ps_initialized; /* whether initialized */
|
||||
int ps_count; /* count of records processed */
|
||||
off_t ps_offset; /* stream byte offset */
|
||||
union pmclog_entry ps_saved; /* saved partial log entry */
|
||||
int ps_svcount; /* #bytes saved */
|
||||
int ps_fd; /* active fd or -1 */
|
||||
char *ps_buffer; /* scratch buffer if fd != -1 */
|
||||
char *ps_data; /* current parse pointer */
|
||||
size_t ps_len; /* length of buffered data */
|
||||
};
|
||||
|
||||
#define PMCLOG_HEADER_FROM_SAVED_STATE(PS) \
|
||||
(* ((uint32_t *) &(PS)->ps_saved))
|
||||
|
||||
|
@ -299,7 +277,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
|||
}
|
||||
|
||||
PMCLOG_INITIALIZE_READER(le, ps->ps_saved);
|
||||
|
||||
ev->pl_data = le;
|
||||
PMCLOG_READ32(le,h);
|
||||
|
||||
if (!PMCLOG_HEADER_CHECK_MAGIC(h)) {
|
||||
|
@ -348,8 +326,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
|||
case PMCLOG_TYPE_INITIALIZE:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_version);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch);
|
||||
PMCLOG_READ32(le, noop);
|
||||
PMCLOG_READSTRING(le, ev->pl_u.pl_i.pl_cpuid, PMC_CPUID_LEN);
|
||||
memcpy(ev->pl_u.pl_i.pl_cpuid, le, PMC_CPUID_LEN);
|
||||
ps->ps_version = ev->pl_u.pl_i.pl_version;
|
||||
ps->ps_arch = ev->pl_u.pl_i.pl_arch;
|
||||
ps->ps_initialized = 1;
|
||||
|
@ -434,6 +412,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
|||
|
||||
ev->pl_offset = (ps->ps_offset += evlen);
|
||||
ev->pl_count = (ps->ps_count += 1);
|
||||
ev->pl_len = evlen;
|
||||
ev->pl_state = PMCLOG_OK;
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -152,6 +152,8 @@ struct pmclog_ev {
|
|||
size_t pl_count; /* count of records so far */
|
||||
struct timespec pl_ts; /* log entry timestamp */
|
||||
enum pmclog_type pl_type; /* type of log entry */
|
||||
void *pl_data;
|
||||
int pl_len;
|
||||
union { /* log entry data */
|
||||
struct pmclog_ev_callchain pl_cc;
|
||||
struct pmclog_ev_closelog pl_cl;
|
||||
|
@ -172,6 +174,28 @@ struct pmclog_ev {
|
|||
} pl_u;
|
||||
};
|
||||
|
||||
enum pmclog_parser_state {
|
||||
PL_STATE_NEW_RECORD, /* in-between records */
|
||||
PL_STATE_EXPECTING_HEADER, /* header being read */
|
||||
PL_STATE_PARTIAL_RECORD, /* header present but not the record */
|
||||
PL_STATE_ERROR /* parsing error encountered */
|
||||
};
|
||||
|
||||
struct pmclog_parse_state {
|
||||
enum pmclog_parser_state ps_state;
|
||||
enum pmc_cputype ps_arch; /* log file architecture */
|
||||
uint32_t ps_version; /* hwpmc version */
|
||||
int ps_initialized; /* whether initialized */
|
||||
int ps_count; /* count of records processed */
|
||||
off_t ps_offset; /* stream byte offset */
|
||||
union pmclog_entry ps_saved; /* saved partial log entry */
|
||||
int ps_svcount; /* #bytes saved */
|
||||
int ps_fd; /* active fd or -1 */
|
||||
char *ps_buffer; /* scratch buffer if fd != -1 */
|
||||
char *ps_data; /* current parse pointer */
|
||||
size_t ps_len; /* length of buffered data */
|
||||
};
|
||||
|
||||
#define PMCLOG_FD_NONE (-1)
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 **);
|
||||
|
||||
|
|
269
usr.sbin/pmc/cmd_pmc_filter.c
Normal file
269
usr.sbin/pmc/cmd_pmc_filter.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue