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:
parent
64fe1b5e1d
commit
b2ca2e50b9
@ -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;
|
||||||
/*
|
/*
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
@ -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");
|
||||||
|
@ -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) ?
|
||||||
|
220
usr.sbin/pmc/cmd_pmc_summary.cc
Normal file
220
usr.sbin/pmc/cmd_pmc_summary.cc
Normal 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));
|
||||||
|
}
|
@ -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}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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) ?
|
||||||
|
Loading…
Reference in New Issue
Block a user