From b2ca2e50b9aaccc15efaf970f9139310429abfe5 Mon Sep 17 00:00:00 2001 From: Matt Macy Date: Wed, 6 Jun 2018 02:48:09 +0000 Subject: [PATCH] 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 --- lib/libpmc/libpmc.c | 4 +- lib/libpmc/libpmc_pmu_util.c | 6 +- lib/libpmc/pmc.h | 4 +- lib/libpmc/pmclog.c | 10 +- lib/libpmc/pmclog.h | 8 +- share/examples/hwpmc/overhead.c | 2 +- sys/dev/hwpmc/hwpmc_logging.c | 12 +- sys/dev/hwpmc/hwpmc_mod.c | 6 + sys/sys/pmc.h | 5 +- sys/sys/pmclog.h | 5 +- usr.sbin/pmc/Makefile | 5 +- usr.sbin/pmc/cmd_pmc.h | 1 + usr.sbin/pmc/cmd_pmc_filter.cc | 17 +-- usr.sbin/pmc/cmd_pmc_stat.c | 2 +- usr.sbin/pmc/cmd_pmc_summary.cc | 220 ++++++++++++++++++++++++++++++++ usr.sbin/pmc/pmc.c | 1 + usr.sbin/pmcstat/pmcstat.c | 3 +- 17 files changed, 278 insertions(+), 33 deletions(-) create mode 100644 usr.sbin/pmc/cmd_pmc_summary.cc diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c index 3b3026cf086b..5286533bacdc 100644 --- a/lib/libpmc/libpmc.c +++ b/lib/libpmc/libpmc.c @@ -1007,7 +1007,8 @@ pmc_mdep_is_compatible_class(enum pmc_class pc) int 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; int retval; @@ -1030,6 +1031,7 @@ pmc_allocate(const char *ctrspec, enum pmc_mode mode, pmc_config.pm_cpu = cpu; pmc_config.pm_mode = mode; pmc_config.pm_flags = flags; + pmc_config.pm_count = count; if (PMC_IS_SAMPLING_MODE(mode)) pmc_config.pm_caps |= PMC_CAP_INTERRUPT; /* diff --git a/lib/libpmc/libpmc_pmu_util.c b/lib/libpmc/libpmc_pmu_util.c index dbcf355aa394..730954d8e23a 100644 --- a/lib/libpmc/libpmc_pmu_util.c +++ b/lib/libpmc/libpmc_pmu_util.c @@ -162,13 +162,13 @@ pmc_pmu_idx_get_by_event(const char *cpuid, const char *event) } 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_event *pe; int i; - if ((pme = pmu_events_map_get(NULL)) == NULL) + if ((pme = pmu_events_map_get(cpuid)) == NULL) return (NULL); for (i = 0, pe = pme->table; (pe->name || pe->desc || pe->event) && i < idx; pe++, i++); return (pe->name); @@ -470,7 +470,7 @@ pmc_pmu_pmcallocate(const char *e __unused, struct pmc_op_pmcallocate *p __unuse } 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); } diff --git a/lib/libpmc/pmc.h b/lib/libpmc/pmc.h index 476f86fa9961..9d12085364e5 100644 --- a/lib/libpmc/pmc.h +++ b/lib/libpmc/pmc.h @@ -75,7 +75,7 @@ struct pmc_pmcinfo { __BEGIN_DECLS 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_capabilities(pmc_id_t _pmc, uint32_t *_caps); 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 *); 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); +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_stat_mode(const char ***); __END_DECLS diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c index cfa11118c60c..2df9d97d1e6a 100644 --- a/lib/libpmc/pmclog.c +++ b/lib/libpmc/pmclog.c @@ -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_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_cpuid = strdup(ev->pl_u.pl_i.pl_cpuid); ps->ps_version = ev->pl_u.pl_i.pl_version; ps->ps_arch = ev->pl_u.pl_i.pl_arch; 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_event); PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags); - PMCLOG_READ32(le,noop); - ev->pl_u.pl_a.pl_evname = pmc_pmu_event_get_by_idx(ev->pl_u.pl_a.pl_event); + PMCLOG_READ64(le,ev->pl_u.pl_a.pl_rate); + 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) break; 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: PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_tid); 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); break; case PMCLOG_TYPE_THR_EXIT: @@ -415,6 +416,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len, break; case PMCLOG_TYPE_PROC_CREATE: 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); break; default: /* unknown record type */ @@ -553,6 +556,7 @@ pmclog_open(int fd) ps->ps_count = 0; ps->ps_offset = (off_t) 0; bzero(&ps->ps_saved, sizeof(ps->ps_saved)); + ps->ps_cpuid = NULL; ps->ps_svcount = 0; ps->ps_fd = fd; ps->ps_data = NULL; diff --git a/lib/libpmc/pmclog.h b/lib/libpmc/pmclog.h index 0f324ced6bc8..4293984ba476 100644 --- a/lib/libpmc/pmclog.h +++ b/lib/libpmc/pmclog.h @@ -89,15 +89,16 @@ struct pmclog_ev_pcsample { }; struct pmclog_ev_pmcallocate { - uint32_t pl_event; const char * pl_evname; + uint64_t pl_rate; + uint32_t pl_event; uint32_t pl_flags; pmc_id_t pl_pmcid; }; struct pmclog_ev_pmcallocatedyn { - uint32_t pl_event; char pl_evname[PMC_NAME_MAX]; + uint32_t pl_event; uint32_t pl_flags; pmc_id_t pl_pmcid; }; @@ -122,6 +123,7 @@ struct pmclog_ev_proccsw { struct pmclog_ev_proccreate { pid_t pl_pid; + uint32_t pl_flags; char pl_pcomm[MAXCOMLEN+1]; }; @@ -150,6 +152,7 @@ struct pmclog_ev_sysexit { struct pmclog_ev_threadcreate { pid_t pl_tid; pid_t pl_pid; + uint32_t pl_flags; char pl_tdname[MAXCOMLEN+1]; }; @@ -211,6 +214,7 @@ struct pmclog_parse_state { int ps_fd; /* active fd or -1 */ char *ps_buffer; /* scratch buffer if fd != -1 */ char *ps_data; /* current parse pointer */ + char *ps_cpuid; /* log cpuid */ size_t ps_len; /* length of buffered data */ }; diff --git a/share/examples/hwpmc/overhead.c b/share/examples/hwpmc/overhead.c index 14834bd27c3d..372babee35d0 100644 --- a/share/examples/hwpmc/overhead.c +++ b/share/examples/hwpmc/overhead.c @@ -65,7 +65,7 @@ main(int argc, char **argv) if (pmc_init() != 0) 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", counter_name); diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c index 49996229480b..1f314518549a 100644 --- a/sys/dev/hwpmc/hwpmc_logging.c +++ b/sys/dev/hwpmc/hwpmc_logging.c @@ -194,7 +194,7 @@ CTASSERT(sizeof(struct pmclog_map_in) == PATH_MAX + CTASSERT(offsetof(struct pmclog_map_in,pl_pathname) == 4*4 + 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(offsetof(struct pmclog_pmcattach,pl_pathname) == 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_event); PMCLOG_EMIT32(pm->pm_flags); + PMCLOG_EMIT64(pm->pm_sc.pm_reloadcount); ps = pmc_soft_ev_acquire(pm->pm_event); if (ps != NULL) 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_event); PMCLOG_EMIT32(pm->pm_flags); + PMCLOG_EMIT64(pm->pm_sc.pm_reloadcount); PMCLOG_DESPATCH_SYNC(po); } } @@ -1050,11 +1052,15 @@ pmclog_process_proccreate(struct pmc_owner *po, struct proc *p, int sync) if (sync) { PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate)); PMCLOG_EMIT32(p->p_pid); + PMCLOG_EMIT32(p->p_flag); + PMCLOG_EMIT32(0); PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1); PMCLOG_DESPATCH_SYNC(po); } else { PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate)); PMCLOG_EMIT32(p->p_pid); + PMCLOG_EMIT32(p->p_flag); + PMCLOG_EMIT32(0); PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1); 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_EMIT32(td->td_tid); PMCLOG_EMIT32(p->p_pid); - PMCLOG_EMIT32(0); + PMCLOG_EMIT32(p->p_flag); PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1); PMCLOG_DESPATCH_SYNC(po); } else { PMCLOG_RESERVE(po, THR_CREATE, sizeof(struct pmclog_threadcreate)); PMCLOG_EMIT32(td->td_tid); PMCLOG_EMIT32(p->p_pid); - PMCLOG_EMIT32(0); + PMCLOG_EMIT32(p->p_flag); PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1); PMCLOG_DESPATCH(po); } diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index 191f2554ee60..cb09592a61d1 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -3925,6 +3925,12 @@ pmc_syscall_handler(struct thread *td, void *syscall_args) pmc->pm_caps = caps; 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' */ pmc_save_cpu_binding(&pb); diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h index 35183bc8dba6..a1e24bc8d47b 100644 --- a/sys/sys/pmc.h +++ b/sys/sys/pmc.h @@ -61,8 +61,8 @@ * * The patch version is incremented for every bug fix. */ -#define PMC_VERSION_MAJOR 0x06 -#define PMC_VERSION_MINOR 0x02 +#define PMC_VERSION_MAJOR 0x07 +#define PMC_VERSION_MINOR 0x03 #define PMC_VERSION_PATCH 0x0000 #define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \ @@ -439,6 +439,7 @@ struct pmc_op_pmcallocate { uint32_t pm_flags; /* additional modifiers PMC_F_* */ enum pmc_mode pm_mode; /* desired mode */ 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 */ }; diff --git a/sys/sys/pmclog.h b/sys/sys/pmclog.h index 9e37985d93b4..c0b645292cd6 100644 --- a/sys/sys/pmclog.h +++ b/sys/sys/pmclog.h @@ -162,6 +162,7 @@ struct pmclog_pmcallocate { uint32_t pl_pmcid; uint32_t pl_event; uint32_t pl_flags; + uint64_t pl_rate; } __packed; struct pmclog_pmcattach { @@ -190,6 +191,8 @@ struct pmclog_proccsw { struct pmclog_proccreate { PMCLOG_ENTRY_HEADER uint32_t pl_pid; + uint32_t pl_flags; + uint32_t pl_pad; uint64_t pl_pcomm[MAXCOMLEN+1]; /* keep 8 byte aligned */ } __packed; @@ -226,7 +229,7 @@ struct pmclog_threadcreate { PMCLOG_ENTRY_HEADER uint32_t pl_tid; uint32_t pl_pid; - uint32_t pl_pad; + uint32_t pl_flags; uint64_t pl_tdname[MAXCOMLEN+1]; /* keep 8 byte aligned */ } __packed; diff --git a/usr.sbin/pmc/Makefile b/usr.sbin/pmc/Makefile index 481670118b30..21d727e63aeb 100644 --- a/usr.sbin/pmc/Makefile +++ b/usr.sbin/pmc/Makefile @@ -5,11 +5,12 @@ .include PROG_CXX= pmc MAN= -CXXFLAGS+= -O0 +CXXFLAGS+= LIBADD= kvm pmc m ncursesw pmcstat elf 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 diff --git a/usr.sbin/pmc/cmd_pmc.h b/usr.sbin/pmc/cmd_pmc.h index 46a253cc8713..cdec055a2036 100644 --- a/usr.sbin/pmc/cmd_pmc.h +++ b/usr.sbin/pmc/cmd_pmc.h @@ -47,6 +47,7 @@ extern "C" { int cmd_pmc_filter(int, char **); int cmd_pmc_stat_system(int, char **); int cmd_pmc_list_events(int, char **); + int cmd_pmc_summary(int, char **); #if defined(__cplusplus) }; #endif diff --git a/usr.sbin/pmc/cmd_pmc_filter.cc b/usr.sbin/pmc/cmd_pmc_filter.cc index b35bb2bee3bd..3892066c7695 100644 --- a/usr.sbin/pmc/cmd_pmc_filter.cc +++ b/usr.sbin/pmc/cmd_pmc_filter.cc @@ -70,13 +70,12 @@ __FBSDID("$FreeBSD$"); #include #include -#if _LIBCPP_STD_VER >= 11 #include + using std::unordered_map; -#else -#include -using std::tr1::unordered_map; -#endif +typedef unordered_map idmap; +typedef std::pair identry; + #define LIST_MAX 64 static struct option longopts[] = { {"lwps", required_argument, NULL, 't'}, @@ -158,10 +157,6 @@ struct pmcid_ent { (PMCLOG_TYPE_ ## T << 16) | \ ((L) & 0xFFFF)) - -typedef unordered_map < int ,std::string > idmap; -typedef std::pair < int ,std::string > identry; - static bool 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; while (pmclog_read(ps, &ev) == 0) { 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) - 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 (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len) errx(EX_OSERR, "ERROR: failed output write"); diff --git a/usr.sbin/pmc/cmd_pmc_stat.c b/usr.sbin/pmc/cmd_pmc_stat.c index 57ab6b9a54a2..7ea54c08681f 100644 --- a/usr.sbin/pmc/cmd_pmc_stat.c +++ b/usr.sbin/pmc/cmd_pmc_stat.c @@ -354,7 +354,7 @@ pmc_stat_internal(int argc, char **argv, int system_mode) STAILQ_FOREACH(ev, &pmc_args.pa_events, ev_next) { 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, "ERROR: Cannot allocate %s-mode pmc with specification \"%s\"", PMC_IS_SYSTEM_MODE(ev->ev_mode) ? diff --git a/usr.sbin/pmc/cmd_pmc_summary.cc b/usr.sbin/pmc/cmd_pmc_summary.cc new file mode 100644 index 000000000000..ea2ca6a4c7f5 --- /dev/null +++ b/usr.sbin/pmc/cmd_pmc_summary.cc @@ -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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "cmd_pmc.h" + +#include +#include +#include +#include + +using std::unordered_map; +typedef unordered_map idmap; +typedef unordered_map intmap; +typedef unordered_map strintmap; +typedef std::pair sampleid; +typedef std::pair samplename; +typedef unordered_map > eventcountmap; + +#define P_KPROC 0x00004 /* Kernel process. */ + +static void +usage(void) +{ + errx(EX_USAGE, + "\t summarize log file\n" + "\t -k , --topk 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(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)); +} diff --git a/usr.sbin/pmc/pmc.c b/usr.sbin/pmc/pmc.c index fb8268fc224a..296ed0bb0201 100644 --- a/usr.sbin/pmc/pmc.c +++ b/usr.sbin/pmc/pmc.c @@ -66,6 +66,7 @@ static struct cmd_handler disp_table[] = { {"stat-system", cmd_pmc_stat_system}, {"list-events", cmd_pmc_list_events}, {"filter", cmd_pmc_filter}, + {"summary", cmd_pmc_summary}, {NULL, NULL} }; diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c index f73de2b37385..838e8cac58b4 100644 --- a/usr.sbin/pmcstat/pmcstat.c +++ b/usr.sbin/pmcstat/pmcstat.c @@ -1129,7 +1129,8 @@ main(int argc, char **argv) STAILQ_FOREACH(ev, &args.pa_events, ev_next) { 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, "ERROR: Cannot allocate %s-mode pmc with specification \"%s\"", PMC_IS_SYSTEM_MODE(ev->ev_mode) ?