From ebfaf69cc0a741100c3ef6ec6960d3a3ba0d1168 Mon Sep 17 00:00:00 2001 From: Matt Macy Date: Tue, 5 Jun 2018 04:26:40 +0000 Subject: [PATCH] hwpmc: log name->pid, name->tid mappings By logging all threads and processes 'pmc filter' can now filter on process or thread name, relieving the user of the burden of determining which tid or pid was which when the sample was taken. % pmc filter -T if_io_tqg -P nginx pmc.log pmc-iflib.log % pmc filter -x -T idle pmc.log pmc-noidle.log --- lib/libpmc/pmclog.c | 13 ++ lib/libpmc/pmclog.h | 18 +++ sys/dev/hwpmc/hwpmc_logging.c | 69 +++++++-- sys/dev/hwpmc/hwpmc_mod.c | 92 +++++++++++- sys/kern/kern_kthread.c | 15 +- sys/kern/kern_thr.c | 6 + sys/kern/kern_thread.c | 3 +- sys/sys/pmc.h | 2 +- sys/sys/pmckern.h | 3 + sys/sys/pmclog.h | 32 +++- usr.sbin/Makefile | 2 + usr.sbin/pmc/Makefile | 6 +- usr.sbin/pmc/cmd_pmc.h | 15 +- .../{cmd_pmc_filter.c => cmd_pmc_filter.cc} | 137 ++++++++++++++---- 14 files changed, 355 insertions(+), 58 deletions(-) rename usr.sbin/pmc/{cmd_pmc_filter.c => cmd_pmc_filter.cc} (65%) diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c index 4ed65ebad75b..cfa11118c60c 100644 --- a/lib/libpmc/pmclog.c +++ b/lib/libpmc/pmclog.c @@ -404,6 +404,19 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len, case PMCLOG_TYPE_USERDATA: PMCLOG_READ32(le,ev->pl_u.pl_u.pl_userdata); break; + 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); + memcpy(ev->pl_u.pl_tc.pl_tdname, le, MAXCOMLEN+1); + break; + case PMCLOG_TYPE_THR_EXIT: + PMCLOG_READ32(le,ev->pl_u.pl_te.pl_tid); + break; + case PMCLOG_TYPE_PROC_CREATE: + PMCLOG_READ32(le,ev->pl_u.pl_pc.pl_pid); + memcpy(ev->pl_u.pl_pc.pl_pcomm, le, MAXCOMLEN+1); + break; default: /* unknown record type */ ps->ps_state = PL_STATE_ERROR; ev->pl_state = PMCLOG_ERROR; diff --git a/lib/libpmc/pmclog.h b/lib/libpmc/pmclog.h index 0d2c7d0f8b4c..0f324ced6bc8 100644 --- a/lib/libpmc/pmclog.h +++ b/lib/libpmc/pmclog.h @@ -120,6 +120,11 @@ struct pmclog_ev_proccsw { pmc_value_t pl_value; }; +struct pmclog_ev_proccreate { + pid_t pl_pid; + char pl_pcomm[MAXCOMLEN+1]; +}; + struct pmclog_ev_procexec { pid_t pl_pid; pmc_id_t pl_pmcid; @@ -142,6 +147,16 @@ struct pmclog_ev_sysexit { pid_t pl_pid; }; +struct pmclog_ev_threadcreate { + pid_t pl_tid; + pid_t pl_pid; + char pl_tdname[MAXCOMLEN+1]; +}; + +struct pmclog_ev_threadexit { + pid_t pl_tid; +}; + struct pmclog_ev_userdata { uint32_t pl_userdata; }; @@ -166,10 +181,13 @@ struct pmclog_ev { struct pmclog_ev_pmcattach pl_t; struct pmclog_ev_pmcdetach pl_d; struct pmclog_ev_proccsw pl_c; + struct pmclog_ev_proccreate pl_pc; struct pmclog_ev_procexec pl_x; struct pmclog_ev_procexit pl_e; struct pmclog_ev_procfork pl_f; struct pmclog_ev_sysexit pl_se; + struct pmclog_ev_threadcreate pl_tc; + struct pmclog_ev_threadexit pl_te; struct pmclog_ev_userdata pl_u; } pl_u; }; diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c index 482c91e46031..49996229480b 100644 --- a/sys/dev/hwpmc/hwpmc_logging.c +++ b/sys/dev/hwpmc/hwpmc_logging.c @@ -229,7 +229,7 @@ static void pmclog_loop(void *arg); static void pmclog_release(struct pmc_owner *po); static uint32_t *pmclog_reserve(struct pmc_owner *po, int length); static void pmclog_schedule_io(struct pmc_owner *po, int wakeup); -static void pmclog_schedule_all(struct pmc_owner *po); +static void pmclog_schedule_all(struct pmc_owner *po, int force); static void pmclog_stop_kthread(struct pmc_owner *po); /* @@ -808,7 +808,7 @@ pmclog_deconfigure_log(struct pmc_owner *po) */ int -pmclog_flush(struct pmc_owner *po) +pmclog_flush(struct pmc_owner *po, int force) { int error; @@ -832,7 +832,7 @@ pmclog_flush(struct pmc_owner *po) goto error; } - pmclog_schedule_all(po); + pmclog_schedule_all(po, force); error: mtx_unlock(&pmc_kthread_mtx); @@ -840,9 +840,8 @@ pmclog_flush(struct pmc_owner *po) } static void -pmclog_schedule_one_cond(void *arg) +pmclog_schedule_one_cond(struct pmc_owner *po, int force) { - struct pmc_owner *po = arg; struct pmclog_buffer *plb; int cpu; @@ -851,7 +850,8 @@ pmclog_schedule_one_cond(void *arg) /* tell hardclock not to run again */ if (PMC_CPU_HAS_SAMPLES(cpu)) PMC_CALL_HOOK_UNLOCKED(curthread, PMC_FN_DO_SAMPLES, NULL); - pmc_flush_samples(cpu); + if (force) + pmc_flush_samples(cpu); plb = po->po_curbuf[cpu]; if (plb && plb->plb_ptr != plb->plb_base) pmclog_schedule_io(po, 1); @@ -859,7 +859,7 @@ pmclog_schedule_one_cond(void *arg) } static void -pmclog_schedule_all(struct pmc_owner *po) +pmclog_schedule_all(struct pmc_owner *po, int force) { /* * Schedule the current buffer if any and not empty. @@ -868,7 +868,7 @@ pmclog_schedule_all(struct pmc_owner *po) thread_lock(curthread); sched_bind(curthread, i); thread_unlock(curthread); - pmclog_schedule_one_cond(po); + pmclog_schedule_one_cond(po, force); } thread_lock(curthread); sched_unbind(curthread); @@ -895,7 +895,7 @@ pmclog_close(struct pmc_owner *po) /* * Schedule the current buffer. */ - pmclog_schedule_all(po); + pmclog_schedule_all(po, 0); wakeup_one(po); mtx_unlock(&pmc_kthread_mtx); @@ -1044,6 +1044,22 @@ pmclog_process_pmcdetach(struct pmc *pm, pid_t pid) PMCLOG_DESPATCH_SYNC(po); } +void +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_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_EMITSTRING(p->p_comm, MAXCOMLEN+1); + PMCLOG_DESPATCH(po); + } +} + /* * Log a context switch event to the log file. */ @@ -1079,14 +1095,13 @@ pmclog_process_procexec(struct pmc_owner *po, pmc_id_t pmid, pid_t pid, pathlen = strlen(path) + 1; /* #bytes for the path */ recordlen = offsetof(struct pmclog_procexec, pl_pathname) + pathlen; - PMCLOG_RESERVE(po, PROCEXEC, recordlen); PMCLOG_EMIT32(pid); PMCLOG_EMIT32(pmid); PMCLOG_EMIT32(0); PMCLOG_EMITADDR(startaddr); PMCLOG_EMITSTRING(path,pathlen); - PMCLOG_DESPATCH(po); + PMCLOG_DESPATCH_SYNC(po); } /* @@ -1138,6 +1153,38 @@ pmclog_process_sysexit(struct pmc_owner *po, pid_t pid) PMCLOG_DESPATCH(po); } +void +pmclog_process_threadcreate(struct pmc_owner *po, struct thread *td, int sync) +{ + struct proc *p; + + p = td->td_proc; + if (sync) { + PMCLOG_RESERVE(po, THR_CREATE, sizeof(struct pmclog_threadcreate)); + PMCLOG_EMIT32(td->td_tid); + PMCLOG_EMIT32(p->p_pid); + PMCLOG_EMIT32(0); + 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_EMITSTRING(td->td_name, MAXCOMLEN+1); + PMCLOG_DESPATCH(po); + } +} + +void +pmclog_process_threadexit(struct pmc_owner *po, struct thread *td) +{ + + PMCLOG_RESERVE(po, THR_EXIT, sizeof(struct pmclog_threadexit)); + PMCLOG_EMIT32(td->td_tid); + PMCLOG_DESPATCH(po); +} + /* * Write a user log entry. */ diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index e9c47881894e..191f2554ee60 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -269,6 +269,11 @@ static int generic_switch_out(struct pmc_cpu *pc, struct pmc_process *pp); static struct pmc_mdep *pmc_generic_cpu_initialize(void); static void pmc_generic_cpu_finalize(struct pmc_mdep *md); static void pmc_post_callchain_callback(void); +static void pmc_process_threadcreate(struct thread *td); +static void pmc_process_threadexit(struct thread *td); +static void pmc_process_proccreate(struct proc *p); +static void pmc_process_allproc(struct pmc *pm); + /* * Kernel tunables and sysctl(8) interface. */ @@ -2049,6 +2054,9 @@ const char *pmc_hooknames[] = { "THR-CREATE", "THR-EXIT", "THR-USERRET", + "THR-CREATE-LOG", + "THR-EXIT-LOG", + "PROC-CREATE-LOG" }; #endif @@ -2225,6 +2233,10 @@ pmc_hook_handler(struct thread *td, int function, void *arg) pmc_process_munmap(td, (struct pmckern_map_out *) arg); break; + case PMC_FN_PROC_CREATE_LOG: + pmc_process_proccreate((struct proc *)arg); + break; + case PMC_FN_USER_CALLCHAIN: /* * Record a call chain. @@ -2270,14 +2282,22 @@ pmc_hook_handler(struct thread *td, int function, void *arg) case PMC_FN_THR_CREATE: pmc_process_thread_add(td); + pmc_process_threadcreate(td); + break; + + case PMC_FN_THR_CREATE_LOG: + pmc_process_threadcreate(td); break; case PMC_FN_THR_EXIT: KASSERT(td == curthread, ("[pmc,%d] td != curthread", __LINE__)); pmc_process_thread_delete(td); + pmc_process_threadexit(td); + break; + case PMC_FN_THR_EXIT_LOG: + pmc_process_threadexit(td); break; - case PMC_FN_THR_USERRET: KASSERT(td == curthread, ("[pmc,%d] td != curthread", __LINE__)); @@ -2697,9 +2717,9 @@ pmc_wait_for_pmc_idle(struct pmc *pm) * Loop (with a forced context switch) till the PMC's runcount * comes down to zero. */ - pmclog_flush(pm->pm_owner); + pmclog_flush(pm->pm_owner, 1); while (counter_u64_fetch(pm->pm_runcount) > 0) { - pmclog_flush(pm->pm_owner); + pmclog_flush(pm->pm_owner, 1); #ifdef HWPMC_DEBUG maxloop--; KASSERT(maxloop > 0, @@ -3439,7 +3459,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args) break; } - error = pmclog_flush(po); + error = pmclog_flush(po, 0); } break; @@ -4015,6 +4035,8 @@ pmc_syscall_handler(struct thread *td, void *syscall_args) pmc = NULL; break; } + if (mode == PMC_MODE_SS) + pmc_process_allproc(pmc); /* * Return the allocated index. @@ -5216,8 +5238,10 @@ pmc_process_fork(void *arg __unused, struct proc *p1, struct proc *newproc, */ epoch_enter_preempt(global_epoch_preempt); CK_LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) - if (po->po_flags & PMC_PO_OWNS_LOGFILE) + if (po->po_flags & PMC_PO_OWNS_LOGFILE) { pmclog_process_procfork(po, p1->p_pid, newproc->p_pid); + pmclog_process_proccreate(po, newproc, 1); + } epoch_exit_preempt(global_epoch_preempt); if (!is_using_hwpmcs) @@ -5279,6 +5303,64 @@ pmc_process_fork(void *arg __unused, struct proc *p1, struct proc *newproc, sx_xunlock(&pmc_sx); } +static void +pmc_process_threadcreate(struct thread *td) +{ + struct pmc_owner *po; + + epoch_enter_preempt(global_epoch_preempt); + CK_LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) + if (po->po_flags & PMC_PO_OWNS_LOGFILE) + pmclog_process_threadcreate(po, td, 1); + epoch_exit_preempt(global_epoch_preempt); +} + +static void +pmc_process_threadexit(struct thread *td) +{ + struct pmc_owner *po; + + epoch_enter_preempt(global_epoch_preempt); + CK_LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) + if (po->po_flags & PMC_PO_OWNS_LOGFILE) + pmclog_process_threadexit(po, td); + epoch_exit_preempt(global_epoch_preempt); +} + +static void +pmc_process_proccreate(struct proc *p) +{ + struct pmc_owner *po; + + epoch_enter_preempt(global_epoch_preempt); + CK_LIST_FOREACH(po, &pmc_ss_owners, po_ssnext) + if (po->po_flags & PMC_PO_OWNS_LOGFILE) + pmclog_process_proccreate(po, p, 1 /* sync */); + epoch_exit_preempt(global_epoch_preempt); +} + +static void +pmc_process_allproc(struct pmc *pm) +{ + struct pmc_owner *po; + struct thread *td; + struct proc *p; + + po = pm->pm_owner; + if ((po->po_flags & PMC_PO_OWNS_LOGFILE) == 0) + return; + sx_slock(&allproc_lock); + FOREACH_PROC_IN_SYSTEM(p) { + pmclog_process_proccreate(po, p, 0 /* sync */); + PROC_LOCK(p); + FOREACH_THREAD_IN_PROC(p, td) + pmclog_process_threadcreate(po, td, 0 /* sync */); + PROC_UNLOCK(p); + } + sx_sunlock(&allproc_lock); + pmclog_flush(po, 0); +} + static void pmc_kld_load(void *arg __unused, linker_file_t lf) { diff --git a/sys/kern/kern_kthread.c b/sys/kern/kern_kthread.c index 2554572d88f8..19b7df110641 100644 --- a/sys/kern/kern_kthread.c +++ b/sys/kern/kern_kthread.c @@ -126,6 +126,12 @@ kproc_create(void (*func)(void *), void *arg, sched_clear_tdname(td); #endif TSTHREAD(td, td->td_name); +#ifdef HWPMC_HOOKS + if (PMC_SYSTEM_SAMPLING_ACTIVE()) { + PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_PROC_CREATE_LOG, p2); + PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_CREATE_LOG, NULL); + } +#endif /* call the processes' main()... */ cpu_fork_kthread_handler(td, func, arg); @@ -310,7 +316,10 @@ kthread_add(void (*func)(void *), void *arg, struct proc *p, /* Avoid inheriting affinity from a random parent. */ cpuset_kernthread(newtd); - +#ifdef HWPMC_HOOKS + if (PMC_SYSTEM_SAMPLING_ACTIVE()) + PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_CREATE_LOG, NULL); +#endif /* Delay putting it on the run queue until now. */ if (!(flags & RFSTOPPED)) { thread_lock(newtd); @@ -331,6 +340,10 @@ kthread_exit(void) td = curthread; p = td->td_proc; +#ifdef HWPMC_HOOKS + if (PMC_SYSTEM_SAMPLING_ACTIVE()) + PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT_LOG, NULL); +#endif /* A module may be waiting for us to exit. */ wakeup(td); diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index 5e817cdfaad7..915b552706a3 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -265,6 +265,8 @@ thread_create(struct thread *td, struct rtprio *rtp, #ifdef HWPMC_HOOKS if (PMC_PROC_IS_USING_PMCS(p)) PMC_CALL_HOOK(newtd, PMC_FN_THR_CREATE, NULL); + else if (PMC_SYSTEM_SAMPLING_ACTIVE()) + PMC_CALL_HOOK_UNLOCKED(newtd, PMC_FN_THR_CREATE_LOG, NULL); #endif tidhash_add(newtd); @@ -592,6 +594,10 @@ sys_thr_set_name(struct thread *td, struct thr_set_name_args *uap) if (ttd == NULL) return (ESRCH); strcpy(ttd->td_name, name); +#ifdef HWPMC_HOOKS + if (PMC_PROC_IS_USING_PMCS(p) || PMC_SYSTEM_SAMPLING_ACTIVE()) + PMC_CALL_HOOK_UNLOCKED(ttd, PMC_FN_THR_CREATE_LOG, NULL); +#endif #ifdef KTR sched_clear_tdname(ttd); #endif diff --git a/sys/kern/kern_thread.c b/sys/kern/kern_thread.c index 86bcdc90bfa0..a3a6e87d2c4a 100644 --- a/sys/kern/kern_thread.c +++ b/sys/kern/kern_thread.c @@ -589,7 +589,8 @@ thread_exit(void) if (PMC_PROC_IS_USING_PMCS(td->td_proc)) { PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT); PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT, NULL); - } + } else if (PMC_SYSTEM_SAMPLING_ACTIVE()) + PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT_LOG, NULL); #endif PROC_UNLOCK(p); PROC_STATLOCK(p); diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h index b78ee0581bae..35183bc8dba6 100644 --- a/sys/sys/pmc.h +++ b/sys/sys/pmc.h @@ -62,7 +62,7 @@ * The patch version is incremented for every bug fix. */ #define PMC_VERSION_MAJOR 0x06 -#define PMC_VERSION_MINOR 0x01 +#define PMC_VERSION_MINOR 0x02 #define PMC_VERSION_PATCH 0x0000 #define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \ diff --git a/sys/sys/pmckern.h b/sys/sys/pmckern.h index d0a19161483a..aa4961ede132 100644 --- a/sys/sys/pmckern.h +++ b/sys/sys/pmckern.h @@ -63,6 +63,9 @@ #define PMC_FN_THR_CREATE 12 #define PMC_FN_THR_EXIT 13 #define PMC_FN_THR_USERRET 14 +#define PMC_FN_THR_CREATE_LOG 15 +#define PMC_FN_THR_EXIT_LOG 16 +#define PMC_FN_PROC_CREATE_LOG 17 #define PMC_HR 0 /* Hardware ring buffer */ #define PMC_SR 1 /* Software ring buffer */ diff --git a/sys/sys/pmclog.h b/sys/sys/pmclog.h index 8d170cd3ded3..9e37985d93b4 100644 --- a/sys/sys/pmclog.h +++ b/sys/sys/pmclog.h @@ -67,7 +67,13 @@ enum pmclog_type { * * New variant of PMCLOG_TYPE_PMCALLOCATE for dynamic event. */ - PMCLOG_TYPE_PMCALLOCATEDYN = 17 + PMCLOG_TYPE_PMCALLOCATEDYN = 17, + /* + * V6 ABI + */ + PMCLOG_TYPE_THR_CREATE = 18, + PMCLOG_TYPE_THR_EXIT = 19, + PMCLOG_TYPE_PROC_CREATE = 20 }; /* @@ -181,6 +187,12 @@ struct pmclog_proccsw { uint32_t pl_tid; } __packed; +struct pmclog_proccreate { + PMCLOG_ENTRY_HEADER + uint32_t pl_pid; + uint64_t pl_pcomm[MAXCOMLEN+1]; /* keep 8 byte aligned */ +} __packed; + struct pmclog_procexec { PMCLOG_ENTRY_HEADER uint32_t pl_pid; @@ -210,6 +222,19 @@ struct pmclog_sysexit { uint32_t pl_pid; } __packed; +struct pmclog_threadcreate { + PMCLOG_ENTRY_HEADER + uint32_t pl_tid; + uint32_t pl_pid; + uint32_t pl_pad; + uint64_t pl_tdname[MAXCOMLEN+1]; /* keep 8 byte aligned */ +} __packed; + +struct pmclog_threadexit { + PMCLOG_ENTRY_HEADER + uint32_t pl_tid; +} __packed; + struct pmclog_userdata { PMCLOG_ENTRY_HEADER uint32_t pl_userdata; @@ -261,7 +286,7 @@ union pmclog_entry { /* only used to size scratch areas */ int pmclog_configure_log(struct pmc_mdep *_md, struct pmc_owner *_po, int _logfd); int pmclog_deconfigure_log(struct pmc_owner *_po); -int pmclog_flush(struct pmc_owner *_po); +int pmclog_flush(struct pmc_owner *_po, int force); int pmclog_close(struct pmc_owner *_po); void pmclog_initialize(void); int pmclog_proc_create(struct thread *td, void **handlep); @@ -283,6 +308,9 @@ void pmclog_process_procexec(struct pmc_owner *_po, pmc_id_t _pmid, pid_t _pid, void pmclog_process_procexit(struct pmc *_pm, struct pmc_process *_pp); void pmclog_process_procfork(struct pmc_owner *_po, pid_t _oldpid, pid_t _newpid); void pmclog_process_sysexit(struct pmc_owner *_po, pid_t _pid); +void pmclog_process_threadcreate(struct pmc_owner *_po, struct thread *td, int sync); +void pmclog_process_threadexit(struct pmc_owner *_po, struct thread *td); +void pmclog_process_proccreate(struct pmc_owner *_po, struct proc *p, int sync); int pmclog_process_userlog(struct pmc_owner *_po, struct pmc_op_writelog *_wl); void pmclog_shutdown(void); diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index afc6f3021d9b..af327d42e97a 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -179,7 +179,9 @@ SUBDIR.${MK_OPENSSL}+= keyserv SUBDIR.${MK_PC_SYSINSTALL}+= pc-sysinstall SUBDIR.${MK_PF}+= ftp-proxy SUBDIR.${MK_PKGBOOTSTRAP}+= pkg +.if (${COMPILER_TYPE} == "clang" || (${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 60100)) SUBDIR.${MK_PMC}+= pmc +.endif SUBDIR.${MK_PMC}+= pmcannotate SUBDIR.${MK_PMC}+= pmccontrol SUBDIR.${MK_PMC}+= pmcstat diff --git a/usr.sbin/pmc/Makefile b/usr.sbin/pmc/Makefile index 6a0438452822..481670118b30 100644 --- a/usr.sbin/pmc/Makefile +++ b/usr.sbin/pmc/Makefile @@ -2,12 +2,14 @@ # $FreeBSD$ # -PROG= pmc +.include +PROG_CXX= pmc MAN= +CXXFLAGS+= -O0 LIBADD= kvm pmc m ncursesw pmcstat elf SRCS= pmc.c pmc_util.c cmd_pmc_stat.c \ - cmd_pmc_list.c cmd_pmc_filter.c + cmd_pmc_list.c cmd_pmc_filter.cc .include diff --git a/usr.sbin/pmc/cmd_pmc.h b/usr.sbin/pmc/cmd_pmc.h index 463c30b0fb87..46a253cc8713 100644 --- a/usr.sbin/pmc/cmd_pmc.h +++ b/usr.sbin/pmc/cmd_pmc.h @@ -40,11 +40,16 @@ 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 **); - +#if defined(__cplusplus) +extern "C" { +#endif + 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 **); +#if defined(__cplusplus) +}; +#endif int pmc_util_get_pid(struct pmcstat_args *); void pmc_util_start_pmcs(struct pmcstat_args *); void pmc_util_cleanup(struct pmcstat_args *); diff --git a/usr.sbin/pmc/cmd_pmc_filter.c b/usr.sbin/pmc/cmd_pmc_filter.cc similarity index 65% rename from usr.sbin/pmc/cmd_pmc_filter.c rename to usr.sbin/pmc/cmd_pmc_filter.cc index c4dbe2a2fe7d..b35bb2bee3bd 100644 --- a/usr.sbin/pmc/cmd_pmc_filter.c +++ b/usr.sbin/pmc/cmd_pmc_filter.cc @@ -68,11 +68,22 @@ __FBSDID("$FreeBSD$"); #include #include "cmd_pmc.h" +#include +#include +#if _LIBCPP_STD_VER >= 11 +#include +using std::unordered_map; +#else +#include +using std::tr1::unordered_map; +#endif #define LIST_MAX 64 static struct option longopts[] = { - {"threads", no_argument, NULL, 't'}, - {"pids", no_argument, NULL, 'p'}, - {"events", no_argument, NULL, 'e'}, + {"lwps", required_argument, NULL, 't'}, + {"pids", required_argument, NULL, 'p'}, + {"threads", required_argument, NULL, 'T'}, + {"processes", required_argument, NULL, 'P'}, + {"events", required_argument, NULL, 'e'}, {NULL, 0, NULL, 0} }; @@ -81,15 +92,18 @@ usage(void) { errx(EX_USAGE, "\t filter log file\n" - "\t -t , --threads -- comma-delimited list of lwps to filter on\n" - "\t -p , --pids -- comma-delimited list of pids to filter on\n" "\t -e , --events -- comma-delimited list of events to filter on\n" + "\t -p , --pids -- comma-delimited list of pids to filter on\n" + "\t -P , --processes -- comma-delimited list of process names to filter on\n" + "\t -t , --lwps -- comma-delimited list of lwps to filter on\n" + "\t -T , --threads -- comma-delimited list of thread names to filter on\n" + "\t -x -- toggle inclusive filtering\n" ); } static void -parse_intlist(char *strlist, int *intlist, int *pcount, int (*fn) (const char *)) +parse_intlist(char *strlist, uint32_t *intlist, int *pcount, int (*fn) (const char *)) { char *token; int count, tokenval; @@ -105,7 +119,7 @@ parse_intlist(char *strlist, int *intlist, int *pcount, int (*fn) (const char *) } static void -parse_events(char *strlist, int *intlist, int *pcount, char *cpuid) +parse_events(char *strlist, uint32_t intlist[LIST_MAX], int *pcount, char *cpuid) { char *token; int count, tokenval; @@ -120,6 +134,21 @@ parse_events(char *strlist, int *intlist, int *pcount, char *cpuid) *pcount = count; } +static void +parse_names(char *strlist, char *namelist[LIST_MAX], int *pcount) +{ + char *token; + int count; + + count = 0; + while ((token = strsep(&strlist, ",")) != NULL && + count < LIST_MAX) { + namelist[count++] = token; + } + *pcount = count; +} + + struct pmcid_ent { uint32_t pe_pmcid; uint32_t pe_idx; @@ -129,23 +158,52 @@ 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) +{ + int i; + + auto kvpair = map.find(id); + if (kvpair == map.end()) { + printf("unknown id: %d\n", id); + return (false); + } + auto p = list; + for (i = 0; i < count; i++, p++) { + if (strstr(kvpair->second.c_str(), *p) != NULL) + return (true); + } + return (false); +} + static void pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidcount, - char *events, int infd, int outfd) + char *events, char *processes, char *threads, bool exclusive, 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; + char *proclist[LIST_MAX]; + char *threadlist[LIST_MAX]; + int i, pmccount, copies, eventcount, proccount, threadcount; + uint32_t idx; + idmap pidmap, tidmap; - if ((ps = pmclog_open(infd)) == NULL) + if ((ps = static_cast < struct pmclog_parse_state *>(pmclog_open(infd)))== NULL) errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno)); - eventcount = pmccount = 0; + proccount = eventcount = pmccount = 0; + if (processes) + parse_names(processes, proclist, &proccount); + if (threads) + parse_names(threads, threadlist, &threadcount); 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); @@ -157,9 +215,9 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco lseek(infd, 0, SEEK_SET); pmclog_close(ps); - if ((ps = pmclog_open(infd)) == NULL) + if ((ps = static_cast < struct pmclog_parse_state *>(pmclog_open(infd)))== NULL) errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno)); - if ((pe = malloc(sizeof(*pe) * pmccount)) == NULL) + if ((pe = (typeof(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) { @@ -171,12 +229,14 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco } lseek(infd, 0, SEEK_SET); pmclog_close(ps); - if ((ps = pmclog_open(infd)) == NULL) + if ((ps = static_cast < struct pmclog_parse_state *>(pmclog_open(infd)))== NULL) errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno)); - dstoff = copies = 0; + copies = 0; while (pmclog_read(ps, &ev) == 0) { - dstoff += ev.pl_len; - h = *(uint32_t *)ev.pl_data; + 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)); + 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)); 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"); @@ -186,14 +246,14 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco for (i = 0; i < pidcount; i++) if (pidlist[i] == ev.pl_u.pl_cc.pl_pid) break; - if (i == pidcount) + if ((i == pidcount) == exclusive) continue; } if (lwpcount) { for (i = 0; i < lwpcount; i++) if (lwplist[i] == ev.pl_u.pl_cc.pl_tid) break; - if (i == lwpcount) + if ((i == lwpcount) == exclusive) continue; } if (eventcount) { @@ -210,9 +270,15 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco if (idx == eventlist[i]) break; } - if (i == eventcount) + if ((i == eventcount) == exclusive) continue; } + if (proccount && + pmc_find_name(pidmap, ev.pl_u.pl_cc.pl_pid, proclist, proccount) == exclusive) + continue; + if (threadcount && + pmc_find_name(tidmap, ev.pl_u.pl_cc.pl_tid, threadlist, threadcount) == exclusive) + continue; if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len) errx(EX_OSERR, "ERROR: failed output write"); } @@ -221,24 +287,35 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco int cmd_pmc_filter(int argc, char **argv) { - char *lwps, *pids, *events; + char *lwps, *pids, *events, *processes, *threads; uint32_t lwplist[LIST_MAX]; uint32_t pidlist[LIST_MAX]; int option, lwpcount, pidcount; int prelogfd, postlogfd; + bool exclusive; - lwps = pids = events = NULL; + threads = processes = lwps = pids = events = NULL; lwpcount = pidcount = 0; - while ((option = getopt_long(argc, argv, "t:p:e:", longopts, NULL)) != -1) { + exclusive = false; + while ((option = getopt_long(argc, argv, "e:p:t:xP:T:", longopts, NULL)) != -1) { switch (option) { - case 't': - lwps = strdup(optarg); + case 'e': + events = strdup(optarg); break; case 'p': pids = strdup(optarg); break; - case 'e': - events = strdup(optarg); + case 'P': + processes = strdup(optarg); + break; + case 't': + lwps = strdup(optarg); + break; + case 'T': + threads = strdup(optarg); + break; + case 'x': + exclusive = !exclusive; break; case '?': default: @@ -264,6 +341,6 @@ cmd_pmc_filter(int argc, char **argv) strerror(errno)); pmc_filter_handler(lwplist, lwpcount, pidlist, pidcount, events, - prelogfd, postlogfd); + processes, threads, exclusive, prelogfd, postlogfd); return (0); }