hwpmc: add summary command and further metadata extensions

metadata changes:
- log pmc sample rate with pmcallocate
- log proc flags with thread / process logging
  to identify user vs kernel threads

fixes:
- use log cpuid to translate event id to event name

Implement rudimentary summary command to track sample
counts by thread and process name within a pmc log.

% make -j4 buildkernel >& /dev/null &
% sudo pmcstat -S unhalted_core_cycles -S llc-misses -O foo sleep 15
% pmc summary foo
cpu_clk_unhalted.thread_p_any:
        idle: 138108207162
        clang-6.0: 105336158004
        sh: 72340108510
        make: 8642012963
        kernel: 7754011631
longest_lat_cache.miss:
        clang-6.0: 87502625
        sh: 40901227
        make: 5500165
        kernel: 3300099
        awk: 2000060

%  pmc summary -f ~/foo
idx: 278 name: cpu_clk_unhalted.thread_p_any rate: 2000003
idle: 69054
clang-6.0: 52668
sh: 36170
make: 4321
kernel: 3877
hwpmc: proc(7445): 3319
awk: 1289
xargs: 357
rand_harvestq: 181
mtree: 102
intr: 53
zfskern: 31
usb: 7
pagedaemon: 4
ntpd: 3
syslogd: 1
acpi_thermal: 1
logger: 1
syncer: 1
snmptrapd: 1
sleep: 1
idx: 17 name: longest_lat_cache.miss rate: 100003
clang-6.0: 875
sh: 409
make: 55
kernel: 33
awk: 20
hwpmc: proc(7445): 14
xargs: 9
idle: 8
intr: 3
zfskern: 2
This commit is contained in:
Matt Macy 2018-06-06 02:48:09 +00:00
parent 64fe1b5e1d
commit b2ca2e50b9
17 changed files with 278 additions and 33 deletions

View File

@ -1007,7 +1007,8 @@ pmc_mdep_is_compatible_class(enum pmc_class pc)
int int
pmc_allocate(const char *ctrspec, enum pmc_mode mode, pmc_allocate(const char *ctrspec, enum pmc_mode mode,
uint32_t flags, int cpu, pmc_id_t *pmcid) uint32_t flags, int cpu, pmc_id_t *pmcid,
uint64_t count)
{ {
size_t n; size_t n;
int retval; int retval;
@ -1030,6 +1031,7 @@ pmc_allocate(const char *ctrspec, enum pmc_mode mode,
pmc_config.pm_cpu = cpu; pmc_config.pm_cpu = cpu;
pmc_config.pm_mode = mode; pmc_config.pm_mode = mode;
pmc_config.pm_flags = flags; pmc_config.pm_flags = flags;
pmc_config.pm_count = count;
if (PMC_IS_SAMPLING_MODE(mode)) if (PMC_IS_SAMPLING_MODE(mode))
pmc_config.pm_caps |= PMC_CAP_INTERRUPT; pmc_config.pm_caps |= PMC_CAP_INTERRUPT;
/* /*

View File

@ -162,13 +162,13 @@ pmc_pmu_idx_get_by_event(const char *cpuid, const char *event)
} }
const char * const char *
pmc_pmu_event_get_by_idx(int idx) pmc_pmu_event_get_by_idx(const char *cpuid, int idx)
{ {
const struct pmu_events_map *pme; const struct pmu_events_map *pme;
const struct pmu_event *pe; const struct pmu_event *pe;
int i; int i;
if ((pme = pmu_events_map_get(NULL)) == NULL) if ((pme = pmu_events_map_get(cpuid)) == NULL)
return (NULL); return (NULL);
for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++); for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++);
return (pe->name); return (pe->name);
@ -470,7 +470,7 @@ pmc_pmu_pmcallocate(const char *e __unused, struct pmc_op_pmcallocate *p __unuse
} }
const char * const char *
pmc_pmu_event_get_by_idx(int idx __unused) pmc_pmu_event_get_by_idx(const char *c __unused, int idx __unused)
{ {
return (NULL); return (NULL);
} }

View File

@ -75,7 +75,7 @@ struct pmc_pmcinfo {
__BEGIN_DECLS __BEGIN_DECLS
int pmc_allocate(const char *_ctrspec, enum pmc_mode _mode, uint32_t _flags, int pmc_allocate(const char *_ctrspec, enum pmc_mode _mode, uint32_t _flags,
int _cpu, pmc_id_t *_pmcid); int _cpu, pmc_id_t *_pmcid, uint64_t count);
int pmc_attach(pmc_id_t _pmcid, pid_t _pid); int pmc_attach(pmc_id_t _pmcid, pid_t _pid);
int pmc_capabilities(pmc_id_t _pmc, uint32_t *_caps); int pmc_capabilities(pmc_id_t _pmc, uint32_t *_caps);
int pmc_configure_logfile(int _fd); int pmc_configure_logfile(int _fd);
@ -120,7 +120,7 @@ void pmc_pmu_print_counter_desc_long(const char *);
void pmc_pmu_print_counter_full(const char *); void pmc_pmu_print_counter_full(const char *);
uint64_t pmc_pmu_sample_rate_get(const char *); uint64_t pmc_pmu_sample_rate_get(const char *);
int pmc_pmu_pmcallocate(const char *, struct pmc_op_pmcallocate *); int pmc_pmu_pmcallocate(const char *, struct pmc_op_pmcallocate *);
const char *pmc_pmu_event_get_by_idx(int idx); const char *pmc_pmu_event_get_by_idx(const char *, int idx);
int pmc_pmu_idx_get_by_event(const char*, const char *); int pmc_pmu_idx_get_by_event(const char*, const char *);
int pmc_pmu_stat_mode(const char ***); int pmc_pmu_stat_mode(const char ***);
__END_DECLS __END_DECLS

View File

@ -328,6 +328,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch); PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch);
PMCLOG_READSTRING(le, ev->pl_u.pl_i.pl_cpuid, PMC_CPUID_LEN); 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); memcpy(ev->pl_u.pl_i.pl_cpuid, le, PMC_CPUID_LEN);
ps->ps_cpuid = strdup(ev->pl_u.pl_i.pl_cpuid);
ps->ps_version = ev->pl_u.pl_i.pl_version; ps->ps_version = ev->pl_u.pl_i.pl_version;
ps->ps_arch = ev->pl_u.pl_i.pl_arch; ps->ps_arch = ev->pl_u.pl_i.pl_arch;
ps->ps_initialized = 1; ps->ps_initialized = 1;
@ -347,8 +348,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid); PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid);
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_event); PMCLOG_READ32(le,ev->pl_u.pl_a.pl_event);
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags); PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags);
PMCLOG_READ32(le,noop); PMCLOG_READ64(le,ev->pl_u.pl_a.pl_rate);
ev->pl_u.pl_a.pl_evname = pmc_pmu_event_get_by_idx(ev->pl_u.pl_a.pl_event); ev->pl_u.pl_a.pl_evname = pmc_pmu_event_get_by_idx(ps->ps_cpuid, ev->pl_u.pl_a.pl_event);
if (ev->pl_u.pl_a.pl_evname != NULL) if (ev->pl_u.pl_a.pl_evname != NULL)
break; break;
else if ((ev->pl_u.pl_a.pl_evname = else if ((ev->pl_u.pl_a.pl_evname =
@ -407,7 +408,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
case PMCLOG_TYPE_THR_CREATE: case PMCLOG_TYPE_THR_CREATE:
PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_tid); PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_tid);
PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_pid); PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_pid);
PMCLOG_READ32(le,noop); PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_flags);
memcpy(ev->pl_u.pl_tc.pl_tdname, le, MAXCOMLEN+1); memcpy(ev->pl_u.pl_tc.pl_tdname, le, MAXCOMLEN+1);
break; break;
case PMCLOG_TYPE_THR_EXIT: case PMCLOG_TYPE_THR_EXIT:
@ -415,6 +416,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
break; break;
case PMCLOG_TYPE_PROC_CREATE: case PMCLOG_TYPE_PROC_CREATE:
PMCLOG_READ32(le,ev->pl_u.pl_pc.pl_pid); PMCLOG_READ32(le,ev->pl_u.pl_pc.pl_pid);
PMCLOG_READ32(le,ev->pl_u.pl_pc.pl_flags);
PMCLOG_READ32(le,noop);
memcpy(ev->pl_u.pl_pc.pl_pcomm, le, MAXCOMLEN+1); memcpy(ev->pl_u.pl_pc.pl_pcomm, le, MAXCOMLEN+1);
break; break;
default: /* unknown record type */ default: /* unknown record type */
@ -553,6 +556,7 @@ pmclog_open(int fd)
ps->ps_count = 0; ps->ps_count = 0;
ps->ps_offset = (off_t) 0; ps->ps_offset = (off_t) 0;
bzero(&ps->ps_saved, sizeof(ps->ps_saved)); bzero(&ps->ps_saved, sizeof(ps->ps_saved));
ps->ps_cpuid = NULL;
ps->ps_svcount = 0; ps->ps_svcount = 0;
ps->ps_fd = fd; ps->ps_fd = fd;
ps->ps_data = NULL; ps->ps_data = NULL;

View File

@ -89,15 +89,16 @@ struct pmclog_ev_pcsample {
}; };
struct pmclog_ev_pmcallocate { struct pmclog_ev_pmcallocate {
uint32_t pl_event;
const char * pl_evname; const char * pl_evname;
uint64_t pl_rate;
uint32_t pl_event;
uint32_t pl_flags; uint32_t pl_flags;
pmc_id_t pl_pmcid; pmc_id_t pl_pmcid;
}; };
struct pmclog_ev_pmcallocatedyn { struct pmclog_ev_pmcallocatedyn {
uint32_t pl_event;
char pl_evname[PMC_NAME_MAX]; char pl_evname[PMC_NAME_MAX];
uint32_t pl_event;
uint32_t pl_flags; uint32_t pl_flags;
pmc_id_t pl_pmcid; pmc_id_t pl_pmcid;
}; };
@ -122,6 +123,7 @@ struct pmclog_ev_proccsw {
struct pmclog_ev_proccreate { struct pmclog_ev_proccreate {
pid_t pl_pid; pid_t pl_pid;
uint32_t pl_flags;
char pl_pcomm[MAXCOMLEN+1]; char pl_pcomm[MAXCOMLEN+1];
}; };
@ -150,6 +152,7 @@ struct pmclog_ev_sysexit {
struct pmclog_ev_threadcreate { struct pmclog_ev_threadcreate {
pid_t pl_tid; pid_t pl_tid;
pid_t pl_pid; pid_t pl_pid;
uint32_t pl_flags;
char pl_tdname[MAXCOMLEN+1]; char pl_tdname[MAXCOMLEN+1];
}; };
@ -211,6 +214,7 @@ struct pmclog_parse_state {
int ps_fd; /* active fd or -1 */ int ps_fd; /* active fd or -1 */
char *ps_buffer; /* scratch buffer if fd != -1 */ char *ps_buffer; /* scratch buffer if fd != -1 */
char *ps_data; /* current parse pointer */ char *ps_data; /* current parse pointer */
char *ps_cpuid; /* log cpuid */
size_t ps_len; /* length of buffered data */ size_t ps_len; /* length of buffered data */
}; };

View File

@ -65,7 +65,7 @@ main(int argc, char **argv)
if (pmc_init() != 0) if (pmc_init() != 0)
err(EX_OSERR, "hwpmc(4) not loaded, kldload or update your kernel"); err(EX_OSERR, "hwpmc(4) not loaded, kldload or update your kernel");
if (pmc_allocate(counter_name, PMC_MODE_SC, 0, 0, &pmcid) < 0) if (pmc_allocate(counter_name, PMC_MODE_SC, 0, 0, &pmcid, 64*1024) < 0)
err(EX_OSERR, "failed to allocate %s as a system counter in counting mode", err(EX_OSERR, "failed to allocate %s as a system counter in counting mode",
counter_name); counter_name);

View File

@ -194,7 +194,7 @@ CTASSERT(sizeof(struct pmclog_map_in) == PATH_MAX +
CTASSERT(offsetof(struct pmclog_map_in,pl_pathname) == CTASSERT(offsetof(struct pmclog_map_in,pl_pathname) ==
4*4 + sizeof(uintfptr_t)); 4*4 + sizeof(uintfptr_t));
CTASSERT(sizeof(struct pmclog_map_out) == 4*4 + 2*sizeof(uintfptr_t)); CTASSERT(sizeof(struct pmclog_map_out) == 4*4 + 2*sizeof(uintfptr_t));
CTASSERT(sizeof(struct pmclog_pmcallocate) == 6*4); CTASSERT(sizeof(struct pmclog_pmcallocate) == 8*4);
CTASSERT(sizeof(struct pmclog_pmcattach) == 6*4 + PATH_MAX); CTASSERT(sizeof(struct pmclog_pmcattach) == 6*4 + PATH_MAX);
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 6*4); CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 6*4);
CTASSERT(sizeof(struct pmclog_pmcdetach) == 6*4); CTASSERT(sizeof(struct pmclog_pmcdetach) == 6*4);
@ -991,6 +991,7 @@ pmclog_process_pmcallocate(struct pmc *pm)
PMCLOG_EMIT32(pm->pm_id); PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT32(pm->pm_event); PMCLOG_EMIT32(pm->pm_event);
PMCLOG_EMIT32(pm->pm_flags); PMCLOG_EMIT32(pm->pm_flags);
PMCLOG_EMIT64(pm->pm_sc.pm_reloadcount);
ps = pmc_soft_ev_acquire(pm->pm_event); ps = pmc_soft_ev_acquire(pm->pm_event);
if (ps != NULL) if (ps != NULL)
PMCLOG_EMITSTRING(ps->ps_ev.pm_ev_name,PMC_NAME_MAX); PMCLOG_EMITSTRING(ps->ps_ev.pm_ev_name,PMC_NAME_MAX);
@ -1004,6 +1005,7 @@ pmclog_process_pmcallocate(struct pmc *pm)
PMCLOG_EMIT32(pm->pm_id); PMCLOG_EMIT32(pm->pm_id);
PMCLOG_EMIT32(pm->pm_event); PMCLOG_EMIT32(pm->pm_event);
PMCLOG_EMIT32(pm->pm_flags); PMCLOG_EMIT32(pm->pm_flags);
PMCLOG_EMIT64(pm->pm_sc.pm_reloadcount);
PMCLOG_DESPATCH_SYNC(po); PMCLOG_DESPATCH_SYNC(po);
} }
} }
@ -1050,11 +1052,15 @@ pmclog_process_proccreate(struct pmc_owner *po, struct proc *p, int sync)
if (sync) { if (sync) {
PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate)); PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate));
PMCLOG_EMIT32(p->p_pid); PMCLOG_EMIT32(p->p_pid);
PMCLOG_EMIT32(p->p_flag);
PMCLOG_EMIT32(0);
PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1); PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1);
PMCLOG_DESPATCH_SYNC(po); PMCLOG_DESPATCH_SYNC(po);
} else { } else {
PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate)); PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate));
PMCLOG_EMIT32(p->p_pid); PMCLOG_EMIT32(p->p_pid);
PMCLOG_EMIT32(p->p_flag);
PMCLOG_EMIT32(0);
PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1); PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1);
PMCLOG_DESPATCH(po); PMCLOG_DESPATCH(po);
} }
@ -1163,14 +1169,14 @@ pmclog_process_threadcreate(struct pmc_owner *po, struct thread *td, int sync)
PMCLOG_RESERVE(po, THR_CREATE, sizeof(struct pmclog_threadcreate)); PMCLOG_RESERVE(po, THR_CREATE, sizeof(struct pmclog_threadcreate));
PMCLOG_EMIT32(td->td_tid); PMCLOG_EMIT32(td->td_tid);
PMCLOG_EMIT32(p->p_pid); PMCLOG_EMIT32(p->p_pid);
PMCLOG_EMIT32(0); PMCLOG_EMIT32(p->p_flag);
PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1); PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1);
PMCLOG_DESPATCH_SYNC(po); PMCLOG_DESPATCH_SYNC(po);
} else { } else {
PMCLOG_RESERVE(po, THR_CREATE, sizeof(struct pmclog_threadcreate)); PMCLOG_RESERVE(po, THR_CREATE, sizeof(struct pmclog_threadcreate));
PMCLOG_EMIT32(td->td_tid); PMCLOG_EMIT32(td->td_tid);
PMCLOG_EMIT32(p->p_pid); PMCLOG_EMIT32(p->p_pid);
PMCLOG_EMIT32(0); PMCLOG_EMIT32(p->p_flag);
PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1); PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1);
PMCLOG_DESPATCH(po); PMCLOG_DESPATCH(po);
} }

View File

@ -3925,6 +3925,12 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
pmc->pm_caps = caps; pmc->pm_caps = caps;
pmc->pm_flags = pa.pm_flags; pmc->pm_flags = pa.pm_flags;
/* XXX set lower bound on sampling for process counters */
if (PMC_IS_SAMPLING_MODE(mode))
pmc->pm_sc.pm_reloadcount = pa.pm_count;
else
pmc->pm_sc.pm_initial = pa.pm_count;
/* switch thread to CPU 'cpu' */ /* switch thread to CPU 'cpu' */
pmc_save_cpu_binding(&pb); pmc_save_cpu_binding(&pb);

View File

@ -61,8 +61,8 @@
* *
* The patch version is incremented for every bug fix. * The patch version is incremented for every bug fix.
*/ */
#define PMC_VERSION_MAJOR 0x06 #define PMC_VERSION_MAJOR 0x07
#define PMC_VERSION_MINOR 0x02 #define PMC_VERSION_MINOR 0x03
#define PMC_VERSION_PATCH 0x0000 #define PMC_VERSION_PATCH 0x0000
#define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \ #define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \
@ -439,6 +439,7 @@ struct pmc_op_pmcallocate {
uint32_t pm_flags; /* additional modifiers PMC_F_* */ uint32_t pm_flags; /* additional modifiers PMC_F_* */
enum pmc_mode pm_mode; /* desired mode */ enum pmc_mode pm_mode; /* desired mode */
pmc_id_t pm_pmcid; /* [return] process pmc id */ pmc_id_t pm_pmcid; /* [return] process pmc id */
pmc_value_t pm_count; /* initial/sample count */
union pmc_md_op_pmcallocate pm_md; /* MD layer extensions */ union pmc_md_op_pmcallocate pm_md; /* MD layer extensions */
}; };

View File

@ -162,6 +162,7 @@ struct pmclog_pmcallocate {
uint32_t pl_pmcid; uint32_t pl_pmcid;
uint32_t pl_event; uint32_t pl_event;
uint32_t pl_flags; uint32_t pl_flags;
uint64_t pl_rate;
} __packed; } __packed;
struct pmclog_pmcattach { struct pmclog_pmcattach {
@ -190,6 +191,8 @@ struct pmclog_proccsw {
struct pmclog_proccreate { struct pmclog_proccreate {
PMCLOG_ENTRY_HEADER PMCLOG_ENTRY_HEADER
uint32_t pl_pid; uint32_t pl_pid;
uint32_t pl_flags;
uint32_t pl_pad;
uint64_t pl_pcomm[MAXCOMLEN+1]; /* keep 8 byte aligned */ uint64_t pl_pcomm[MAXCOMLEN+1]; /* keep 8 byte aligned */
} __packed; } __packed;
@ -226,7 +229,7 @@ struct pmclog_threadcreate {
PMCLOG_ENTRY_HEADER PMCLOG_ENTRY_HEADER
uint32_t pl_tid; uint32_t pl_tid;
uint32_t pl_pid; uint32_t pl_pid;
uint32_t pl_pad; uint32_t pl_flags;
uint64_t pl_tdname[MAXCOMLEN+1]; /* keep 8 byte aligned */ uint64_t pl_tdname[MAXCOMLEN+1]; /* keep 8 byte aligned */
} __packed; } __packed;

View File

@ -5,11 +5,12 @@
.include <src.opts.mk> .include <src.opts.mk>
PROG_CXX= pmc PROG_CXX= pmc
MAN= MAN=
CXXFLAGS+= -O0 CXXFLAGS+=
LIBADD= kvm pmc m ncursesw pmcstat elf LIBADD= kvm pmc m ncursesw pmcstat elf
SRCS= pmc.c pmc_util.c cmd_pmc_stat.c \ SRCS= pmc.c pmc_util.c cmd_pmc_stat.c \
cmd_pmc_list.c cmd_pmc_filter.cc cmd_pmc_list.c cmd_pmc_filter.cc \
cmd_pmc_summary.cc
.include <bsd.prog.mk> .include <bsd.prog.mk>

View File

@ -47,6 +47,7 @@ extern "C" {
int cmd_pmc_filter(int, char **); int cmd_pmc_filter(int, char **);
int cmd_pmc_stat_system(int, char **); int cmd_pmc_stat_system(int, char **);
int cmd_pmc_list_events(int, char **); int cmd_pmc_list_events(int, char **);
int cmd_pmc_summary(int, char **);
#if defined(__cplusplus) #if defined(__cplusplus)
}; };
#endif #endif

View File

@ -70,13 +70,12 @@ __FBSDID("$FreeBSD$");
#include <iostream> #include <iostream>
#include <string> #include <string>
#if _LIBCPP_STD_VER >= 11
#include <unordered_map> #include <unordered_map>
using std::unordered_map; using std::unordered_map;
#else typedef unordered_map <int, std::string> idmap;
#include <tr1/unordered_map> typedef std::pair <int, std::string> identry;
using std::tr1::unordered_map;
#endif
#define LIST_MAX 64 #define LIST_MAX 64
static struct option longopts[] = { static struct option longopts[] = {
{"lwps", required_argument, NULL, 't'}, {"lwps", required_argument, NULL, 't'},
@ -158,10 +157,6 @@ struct pmcid_ent {
(PMCLOG_TYPE_ ## T << 16) | \ (PMCLOG_TYPE_ ## T << 16) | \
((L) & 0xFFFF)) ((L) & 0xFFFF))
typedef unordered_map < int ,std::string > idmap;
typedef std::pair < int ,std::string > identry;
static bool static bool
pmc_find_name(idmap & map, uint32_t id, char *list[LIST_MAX], int count) pmc_find_name(idmap & map, uint32_t id, char *list[LIST_MAX], int count)
{ {
@ -234,9 +229,9 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco
copies = 0; copies = 0;
while (pmclog_read(ps, &ev) == 0) { while (pmclog_read(ps, &ev) == 0) {
if (ev.pl_type == PMCLOG_TYPE_THR_CREATE) if (ev.pl_type == PMCLOG_TYPE_THR_CREATE)
tidmap.insert(identry(ev.pl_u.pl_tc.pl_tid, ev.pl_u.pl_tc.pl_tdname)); tidmap[ev.pl_u.pl_tc.pl_tid] = ev.pl_u.pl_tc.pl_tdname;
if (ev.pl_type == PMCLOG_TYPE_PROC_CREATE) if (ev.pl_type == PMCLOG_TYPE_PROC_CREATE)
pidmap.insert(identry(ev.pl_u.pl_pc.pl_pid, ev.pl_u.pl_pc.pl_pcomm)); pidmap[ev.pl_u.pl_pc.pl_pid] = ev.pl_u.pl_pc.pl_pcomm;
if (ev.pl_type != PMCLOG_TYPE_CALLCHAIN) { if (ev.pl_type != PMCLOG_TYPE_CALLCHAIN) {
if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len) if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len)
errx(EX_OSERR, "ERROR: failed output write"); errx(EX_OSERR, "ERROR: failed output write");

View File

@ -354,7 +354,7 @@ pmc_stat_internal(int argc, char **argv, int system_mode)
STAILQ_FOREACH(ev, &pmc_args.pa_events, ev_next) { STAILQ_FOREACH(ev, &pmc_args.pa_events, ev_next) {
if (pmc_allocate(ev->ev_spec, ev->ev_mode, if (pmc_allocate(ev->ev_spec, ev->ev_mode,
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0) ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid, ev->ev_count) < 0)
err(EX_OSERR, err(EX_OSERR,
"ERROR: Cannot allocate %s-mode pmc with specification \"%s\"", "ERROR: Cannot allocate %s-mode pmc with specification \"%s\"",
PMC_IS_SYSTEM_MODE(ev->ev_mode) ? PMC_IS_SYSTEM_MODE(ev->ev_mode) ?

View File

@ -0,0 +1,220 @@
/*-
* 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"
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using std::unordered_map;
typedef unordered_map <int, std::string> idmap;
typedef unordered_map <uint32_t, uint64_t> intmap;
typedef unordered_map <std::string, intmap> strintmap;
typedef std::pair<uint64_t, uint32_t> sampleid;
typedef std::pair<uint64_t, std::string> samplename;
typedef unordered_map <uint32_t, std::vector<samplename>> eventcountmap;
#define P_KPROC 0x00004 /* Kernel process. */
static void
usage(void)
{
errx(EX_USAGE,
"\t summarize log file\n"
"\t -k <k>, --topk <k> show topk processes for each counter\n"
);
}
static int
pmc_summary_handler(int logfd, int k, bool do_full)
{
struct pmclog_parse_state *ps;
struct pmclog_ev ev;
idmap pidmap, tidmap, eventnamemap;
strintmap tideventmap, pideventmap;
intmap eventmap, pmcidmap, ratemap;
intmap kerntidmap, kernpidmap;
eventcountmap countmap;
ps = static_cast<struct pmclog_parse_state*>(pmclog_open(logfd));
if (ps == NULL)
errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n",
strerror(errno));
while (pmclog_read(ps, &ev) == 0) {
if (ev.pl_type == PMCLOG_TYPE_PMCALLOCATE) {
pmcidmap[ev.pl_u.pl_a.pl_pmcid] = ev.pl_u.pl_a.pl_event;
ratemap[ev.pl_u.pl_a.pl_event] = ev.pl_u.pl_a.pl_rate;
eventnamemap[ev.pl_u.pl_a.pl_event] = ev.pl_u.pl_a.pl_evname;
}
if (ev.pl_type == PMCLOG_TYPE_THR_CREATE) {
tidmap[ev.pl_u.pl_tc.pl_tid] = ev.pl_u.pl_tc.pl_tdname;
kerntidmap[ev.pl_u.pl_tc.pl_tid] = !!(ev.pl_u.pl_tc.pl_flags & P_KPROC);
if (tideventmap.find(ev.pl_u.pl_tc.pl_tdname) == tideventmap.end())
tideventmap[ev.pl_u.pl_tc.pl_tdname] = intmap();
}
if (ev.pl_type == PMCLOG_TYPE_PROC_CREATE) {
pidmap[ev.pl_u.pl_pc.pl_pid] = ev.pl_u.pl_pc.pl_pcomm;
kernpidmap[ev.pl_u.pl_pc.pl_pid] = !!(ev.pl_u.pl_pc.pl_flags & P_KPROC);
if (pideventmap.find(ev.pl_u.pl_pc.pl_pcomm) == pideventmap.end())
pideventmap[ev.pl_u.pl_pc.pl_pcomm] = intmap();
}
if (ev.pl_type == PMCLOG_TYPE_CALLCHAIN) {
auto event = pmcidmap[ev.pl_u.pl_cc.pl_pmcid];
if (event == 0)
continue;
eventmap[event]++;
auto tidname = tidmap.find(ev.pl_u.pl_cc.pl_tid);
auto pidname = pidmap.find(ev.pl_u.pl_cc.pl_pid);
if (tidname != tidmap.end()) {
auto &teventmap = tideventmap[tidname->second];
teventmap[event]++;
}
if (pidname != pidmap.end()) {
auto &peventmap = pideventmap[pidname->second];
peventmap[event]++;
}
}
}
for (auto &pkv : pideventmap)
for (auto &ekv : pkv.second) {
auto &samplevec = countmap[ekv.first];
samplevec.emplace_back(ekv.second, pkv.first);
}
for (auto &kv : countmap)
std::sort(kv.second.begin(), kv.second.end(), [](auto &a, auto &b) {return (a.first < b.first);});
if (do_full) {
for (auto &kv : countmap) {
auto &name = eventnamemap[kv.first];
auto rate = ratemap[kv.first];
std::cout << "idx: " << kv.first << " name: " << name << " rate: " << rate << std::endl;
while (!kv.second.empty()) {
auto &val = kv.second.back();
kv.second.pop_back();
std::cout << val.second << ": " << val.first << std::endl;
}
}
return (0);
}
for (auto &kv : countmap) {
auto &name = eventnamemap[kv.first];
auto rate = ratemap[kv.first];
std::cout << name << ":" << std::endl;
for (auto i = 0; i < k; i++) {
auto largest = kv.second.back();
kv.second.pop_back();
std::cout << "\t" << largest.second << ": " << largest.first*rate << std::endl;
}
}
return (0);
}
static struct option longopts[] = {
{"full", no_argument, NULL, 'f'},
{"topk", required_argument, NULL, 'k'},
{NULL, 0, NULL, 0}
};
int
cmd_pmc_summary(int argc, char **argv)
{
int option, logfd, k;
bool do_full;
do_full = false;
k = 5;
while ((option = getopt_long(argc, argv, "k:f", longopts, NULL)) != -1) {
switch (option) {
case 'f':
do_full = 1;
break;
case 'k':
k = atoi(optarg);
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
printf("argc: %d\n", argc);
for (int i = 0; i < argc; i++)
printf("%s\n", argv[i]);
usage();
}
if ((logfd = 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));
return (pmc_summary_handler(logfd, k, do_full));
}

View File

@ -66,6 +66,7 @@ static struct cmd_handler disp_table[] = {
{"stat-system", cmd_pmc_stat_system}, {"stat-system", cmd_pmc_stat_system},
{"list-events", cmd_pmc_list_events}, {"list-events", cmd_pmc_list_events},
{"filter", cmd_pmc_filter}, {"filter", cmd_pmc_filter},
{"summary", cmd_pmc_summary},
{NULL, NULL} {NULL, NULL}
}; };

View File

@ -1129,7 +1129,8 @@ main(int argc, char **argv)
STAILQ_FOREACH(ev, &args.pa_events, ev_next) { STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
if (pmc_allocate(ev->ev_spec, ev->ev_mode, if (pmc_allocate(ev->ev_spec, ev->ev_mode,
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0) ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
ev->ev_count) < 0)
err(EX_OSERR, err(EX_OSERR,
"ERROR: Cannot allocate %s-mode pmc with specification \"%s\"", "ERROR: Cannot allocate %s-mode pmc with specification \"%s\"",
PMC_IS_SYSTEM_MODE(ev->ev_mode) ? PMC_IS_SYSTEM_MODE(ev->ev_mode) ?