MFP4:
- pmcstat(8) gprof output mode fixes: lib/libpmc/pmclog.{c,h}, sys/sys/pmclog.h: + Add a 'is_usermode' field to the PMCLOG_PCSAMPLE event + Add an 'entryaddr' field to the PMCLOG_PROCEXEC event, so that pmcstat(8) can determine where the runtime loader /libexec/ld-elf.so.1 is getting loaded. sys/kern/kern_exec.c: + Use a local struct to group the entry address of the image being exec()'ed and the process credential changed flag to the exec handling hook inside hwpmc(4). usr.sbin/pmcstat/*: + Support "-k kernelpath", "-D sampledir". + Implement the ELF bits of 'gmon.out' profile generation in a new file "pmcstat_log.c". Move all log related functions to this file. + Move local definitions and prototypes to "pmcstat.h" - Other bug fixes: + lib/libpmc/pmclog.c: correctly handle EOF in pmclog_read(). + sys/dev/hwpmc_mod.c: unconditionally log a PROCEXIT event to all attached PMCs when a process exits. + sys/sys/pmc.h: correct a function prototype. + Improve usage checks in pmcstat(8). Approved by: re (blanket hwpmc)
This commit is contained in:
parent
27a2197ea5
commit
151392465f
@ -334,6 +334,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
|||||||
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pid);
|
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pid);
|
||||||
PMCLOG_READADDR(le,ev->pl_u.pl_s.pl_pc);
|
PMCLOG_READADDR(le,ev->pl_u.pl_s.pl_pc);
|
||||||
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pmcid);
|
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pmcid);
|
||||||
|
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_usermode);
|
||||||
break;
|
break;
|
||||||
case PMCLOG_TYPE_PMCALLOCATE:
|
case PMCLOG_TYPE_PMCALLOCATE:
|
||||||
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid);
|
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid);
|
||||||
@ -361,6 +362,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
|||||||
case PMCLOG_TYPE_PROCEXEC:
|
case PMCLOG_TYPE_PROCEXEC:
|
||||||
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
|
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
|
||||||
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid);
|
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid);
|
||||||
|
PMCLOG_READADDR(le,ev->pl_u.pl_x.pl_entryaddr);
|
||||||
|
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pmcid);
|
||||||
PMCLOG_READSTRING(le,ev->pl_u.pl_x.pl_pathname,pathlen);
|
PMCLOG_READSTRING(le,ev->pl_u.pl_x.pl_pathname,pathlen);
|
||||||
break;
|
break;
|
||||||
case PMCLOG_TYPE_PROCEXIT:
|
case PMCLOG_TYPE_PROCEXIT:
|
||||||
@ -436,8 +439,10 @@ pmclog_read(void *cookie, struct pmclog_ev *ev)
|
|||||||
PMCLOG_BUFFER_SIZE);
|
PMCLOG_BUFFER_SIZE);
|
||||||
|
|
||||||
if (nread <= 0) {
|
if (nread <= 0) {
|
||||||
ev->pl_state = nread < 0 ? PMCLOG_ERROR :
|
if (nread == 0)
|
||||||
PMCLOG_EOF;
|
ev->pl_state = PMCLOG_EOF;
|
||||||
|
else if (errno != EAGAIN) /* not restartable */
|
||||||
|
ev->pl_state = PMCLOG_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ struct pmclog_ev_pcsample {
|
|||||||
uintfptr_t pl_pc;
|
uintfptr_t pl_pc;
|
||||||
pid_t pl_pid;
|
pid_t pl_pid;
|
||||||
pmc_id_t pl_pmcid;
|
pmc_id_t pl_pmcid;
|
||||||
|
uint32_t pl_usermode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pmclog_ev_pmcallocate {
|
struct pmclog_ev_pmcallocate {
|
||||||
@ -89,6 +90,8 @@ struct pmclog_ev_proccsw {
|
|||||||
|
|
||||||
struct pmclog_ev_procexec {
|
struct pmclog_ev_procexec {
|
||||||
pid_t pl_pid;
|
pid_t pl_pid;
|
||||||
|
pmc_id_t pl_pmcid;
|
||||||
|
uintfptr_t pl_entryaddr;
|
||||||
char pl_pathname[PATH_MAX];
|
char pl_pathname[PATH_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -138,14 +138,16 @@ CTASSERT(sizeof(struct pmclog_mappingchange) == PATH_MAX +
|
|||||||
5*4 + 2*sizeof(uintfptr_t));
|
5*4 + 2*sizeof(uintfptr_t));
|
||||||
CTASSERT(offsetof(struct pmclog_mappingchange,pl_pathname) ==
|
CTASSERT(offsetof(struct pmclog_mappingchange,pl_pathname) ==
|
||||||
5*4 + 2*sizeof(uintfptr_t));
|
5*4 + 2*sizeof(uintfptr_t));
|
||||||
CTASSERT(sizeof(struct pmclog_pcsample) == 5*4 + sizeof(uintfptr_t));
|
CTASSERT(sizeof(struct pmclog_pcsample) == 6*4 + sizeof(uintfptr_t));
|
||||||
CTASSERT(sizeof(struct pmclog_pmcallocate) == 6*4);
|
CTASSERT(sizeof(struct pmclog_pmcallocate) == 6*4);
|
||||||
CTASSERT(sizeof(struct pmclog_pmcattach) == 5*4 + PATH_MAX);
|
CTASSERT(sizeof(struct pmclog_pmcattach) == 5*4 + PATH_MAX);
|
||||||
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4);
|
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4);
|
||||||
CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4);
|
CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4);
|
||||||
CTASSERT(sizeof(struct pmclog_proccsw) == 5*4 + 8);
|
CTASSERT(sizeof(struct pmclog_proccsw) == 5*4 + 8);
|
||||||
CTASSERT(sizeof(struct pmclog_procexec) == 4*4 + PATH_MAX);
|
CTASSERT(sizeof(struct pmclog_procexec) == 5*4 + PATH_MAX +
|
||||||
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 4*4);
|
sizeof(uintfptr_t));
|
||||||
|
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 5*4 +
|
||||||
|
sizeof(uintfptr_t));
|
||||||
CTASSERT(sizeof(struct pmclog_procexit) == 5*4 + 8);
|
CTASSERT(sizeof(struct pmclog_procexit) == 5*4 + 8);
|
||||||
CTASSERT(sizeof(struct pmclog_procfork) == 5*4);
|
CTASSERT(sizeof(struct pmclog_procfork) == 5*4);
|
||||||
CTASSERT(sizeof(struct pmclog_sysexit) == 4*4);
|
CTASSERT(sizeof(struct pmclog_sysexit) == 4*4);
|
||||||
@ -755,6 +757,7 @@ pmclog_process_pcsample(struct pmc *pm, struct pmc_sample *ps)
|
|||||||
PMCLOG_EMIT32(ps->ps_pid);
|
PMCLOG_EMIT32(ps->ps_pid);
|
||||||
PMCLOG_EMITADDR(ps->ps_pc);
|
PMCLOG_EMITADDR(ps->ps_pc);
|
||||||
PMCLOG_EMIT32(pm->pm_id);
|
PMCLOG_EMIT32(pm->pm_id);
|
||||||
|
PMCLOG_EMIT32(ps->ps_usermode);
|
||||||
PMCLOG_DESPATCH(po);
|
PMCLOG_DESPATCH(po);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,7 +837,8 @@ pmclog_process_proccsw(struct pmc *pm, struct pmc_process *pp, pmc_value_t v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
pmclog_process_procexec(struct pmc_owner *po, pid_t pid, char *path)
|
pmclog_process_procexec(struct pmc_owner *po, pmc_id_t pmid, pid_t pid,
|
||||||
|
uintfptr_t startaddr, char *path)
|
||||||
{
|
{
|
||||||
int pathlen, recordlen;
|
int pathlen, recordlen;
|
||||||
|
|
||||||
@ -845,6 +849,8 @@ pmclog_process_procexec(struct pmc_owner *po, pid_t pid, char *path)
|
|||||||
|
|
||||||
PMCLOG_RESERVE(po, PROCEXEC, recordlen);
|
PMCLOG_RESERVE(po, PROCEXEC, recordlen);
|
||||||
PMCLOG_EMIT32(pid);
|
PMCLOG_EMIT32(pid);
|
||||||
|
PMCLOG_EMITADDR(startaddr);
|
||||||
|
PMCLOG_EMIT32(pmid);
|
||||||
PMCLOG_EMITSTRING(path,pathlen);
|
PMCLOG_EMITSTRING(path,pathlen);
|
||||||
PMCLOG_DESPATCH(po);
|
PMCLOG_DESPATCH(po);
|
||||||
}
|
}
|
||||||
@ -859,9 +865,6 @@ pmclog_process_procexit(struct pmc *pm, struct pmc_process *pp)
|
|||||||
int ri;
|
int ri;
|
||||||
struct pmc_owner *po;
|
struct pmc_owner *po;
|
||||||
|
|
||||||
KASSERT(pm->pm_flags & PMC_F_LOG_PROCEXIT,
|
|
||||||
("[pmc,%d] log-process-exit called gratuitously", __LINE__));
|
|
||||||
|
|
||||||
ri = PMC_TO_ROWINDEX(pm);
|
ri = PMC_TO_ROWINDEX(pm);
|
||||||
PMCDBG(LOG,EXT,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
|
PMCDBG(LOG,EXT,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
|
||||||
pp->pp_pmcs[ri].pp_pmcval);
|
pp->pp_pmcs[ri].pp_pmcval);
|
||||||
|
@ -661,16 +661,16 @@ pmc_force_context_switch(void)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pmc_getprocname(struct proc *p, char **fullpath, char **freepath)
|
pmc_getfilename(struct vnode *v, char **fullpath, char **freepath)
|
||||||
{
|
{
|
||||||
struct thread *td;
|
struct thread *td;
|
||||||
|
|
||||||
td = curthread;
|
td = curthread;
|
||||||
*fullpath = "unknown";
|
*fullpath = "unknown";
|
||||||
*freepath = NULL;
|
*freepath = NULL;
|
||||||
vn_lock(p->p_textvp, LK_EXCLUSIVE | LK_RETRY, td);
|
vn_lock(v, LK_EXCLUSIVE | LK_RETRY, td);
|
||||||
vn_fullpath(td, p->p_textvp, fullpath, freepath);
|
vn_fullpath(td, v, fullpath, freepath);
|
||||||
VOP_UNLOCK(p->p_textvp, 0, td);
|
VOP_UNLOCK(v, 0, td);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -951,7 +951,7 @@ pmc_attach_one_process(struct proc *p, struct pmc *pm)
|
|||||||
|
|
||||||
/* issue an attach event to a configured log file */
|
/* issue an attach event to a configured log file */
|
||||||
if (pm->pm_owner->po_flags & PMC_PO_OWNS_LOGFILE) {
|
if (pm->pm_owner->po_flags & PMC_PO_OWNS_LOGFILE) {
|
||||||
pmc_getprocname(p, &fullpath, &freepath);
|
pmc_getfilename(p->p_textvp, &fullpath, &freepath);
|
||||||
pmclog_process_pmcattach(pm, p->p_pid, fullpath);
|
pmclog_process_pmcattach(pm, p->p_pid, fullpath);
|
||||||
if (freepath)
|
if (freepath)
|
||||||
FREE(freepath, M_TEMP);
|
FREE(freepath, M_TEMP);
|
||||||
@ -1442,7 +1442,6 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
|
|||||||
|
|
||||||
case PMC_FN_PROCESS_EXEC:
|
case PMC_FN_PROCESS_EXEC:
|
||||||
{
|
{
|
||||||
int *credentials_changed;
|
|
||||||
char *fullpath, *freepath;
|
char *fullpath, *freepath;
|
||||||
unsigned int ri;
|
unsigned int ri;
|
||||||
int is_using_hwpmcs;
|
int is_using_hwpmcs;
|
||||||
@ -1450,16 +1449,20 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
|
|||||||
struct proc *p;
|
struct proc *p;
|
||||||
struct pmc_owner *po;
|
struct pmc_owner *po;
|
||||||
struct pmc_process *pp;
|
struct pmc_process *pp;
|
||||||
|
struct pmckern_procexec *pk;
|
||||||
|
|
||||||
sx_assert(&pmc_sx, SX_XLOCKED);
|
sx_assert(&pmc_sx, SX_XLOCKED);
|
||||||
|
|
||||||
p = td->td_proc;
|
p = td->td_proc;
|
||||||
pmc_getprocname(p, &fullpath, &freepath);
|
pmc_getfilename(p->p_textvp, &fullpath, &freepath);
|
||||||
|
|
||||||
|
pk = (struct pmckern_procexec *) arg;
|
||||||
|
|
||||||
/* Inform owners of SS mode PMCs of the exec event. */
|
/* Inform owners of SS mode PMCs of the exec event. */
|
||||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
|
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_procexec(po, p->p_pid, fullpath);
|
pmclog_process_procexec(po, PMC_ID_INVALID,
|
||||||
|
p->p_pid, pk->pm_entryaddr, fullpath);
|
||||||
|
|
||||||
PROC_LOCK(p);
|
PROC_LOCK(p);
|
||||||
is_using_hwpmcs = p->p_flag & P_HWPMC;
|
is_using_hwpmcs = p->p_flag & P_HWPMC;
|
||||||
@ -1499,19 +1502,19 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
|
|||||||
po = pm->pm_owner;
|
po = pm->pm_owner;
|
||||||
if (po->po_sscount == 0 &&
|
if (po->po_sscount == 0 &&
|
||||||
po->po_flags & PMC_PO_OWNS_LOGFILE)
|
po->po_flags & PMC_PO_OWNS_LOGFILE)
|
||||||
pmclog_process_procexec(po, p->p_pid,
|
pmclog_process_procexec(po, pm->pm_id,
|
||||||
|
p->p_pid, pk->pm_entryaddr,
|
||||||
fullpath);
|
fullpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freepath)
|
if (freepath)
|
||||||
FREE(freepath, M_TEMP);
|
FREE(freepath, M_TEMP);
|
||||||
|
|
||||||
credentials_changed = arg;
|
|
||||||
|
|
||||||
PMCDBG(PRC,EXC,1, "exec proc=%p (%d, %s) cred-changed=%d",
|
PMCDBG(PRC,EXC,1, "exec proc=%p (%d, %s) cred-changed=%d",
|
||||||
p, p->p_pid, p->p_comm, *credentials_changed);
|
p, p->p_pid, p->p_comm, pk->pm_credentialschanged);
|
||||||
|
|
||||||
if (*credentials_changed == 0) /* credentials didn't change */
|
if (pk->pm_credentialschanged == 0) /* no change */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3457,7 +3460,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
int
|
||||||
pmc_process_interrupt(int cpu, struct pmc *pm, intfptr_t pc, int usermode)
|
pmc_process_interrupt(int cpu, struct pmc *pm, uintfptr_t pc, int usermode)
|
||||||
{
|
{
|
||||||
int error, ri;
|
int error, ri;
|
||||||
struct thread *td;
|
struct thread *td;
|
||||||
@ -3474,7 +3477,7 @@ pmc_process_interrupt(int cpu, struct pmc *pm, intfptr_t pc, int usermode)
|
|||||||
atomic_add_int(&pmc_stats.pm_intr_bufferfull, 1);
|
atomic_add_int(&pmc_stats.pm_intr_bufferfull, 1);
|
||||||
atomic_set_int(&pm->pm_flags, PMC_F_IS_STALLED);
|
atomic_set_int(&pm->pm_flags, PMC_F_IS_STALLED);
|
||||||
PMCDBG(SAM,INT,1,"(spc) cpu=%d pm=%p pc=%jx um=%d wr=%d rd=%d",
|
PMCDBG(SAM,INT,1,"(spc) cpu=%d pm=%p pc=%jx um=%d wr=%d rd=%d",
|
||||||
cpu, pm, (int64_t) pc, usermode,
|
cpu, pm, (uint64_t) pc, usermode,
|
||||||
(int) (psb->ps_write - psb->ps_samples),
|
(int) (psb->ps_write - psb->ps_samples),
|
||||||
(int) (psb->ps_read - psb->ps_samples));
|
(int) (psb->ps_read - psb->ps_samples));
|
||||||
error = ENOMEM;
|
error = ENOMEM;
|
||||||
@ -3483,7 +3486,7 @@ pmc_process_interrupt(int cpu, struct pmc *pm, intfptr_t pc, int usermode)
|
|||||||
|
|
||||||
/* fill in entry */
|
/* fill in entry */
|
||||||
PMCDBG(SAM,INT,1,"cpu=%d pm=%p pc=%jx um=%d wr=%d rd=%d", cpu, pm,
|
PMCDBG(SAM,INT,1,"cpu=%d pm=%p pc=%jx um=%d wr=%d rd=%d", cpu, pm,
|
||||||
(int64_t) pc, usermode,
|
(uint64_t) pc, usermode,
|
||||||
(int) (psb->ps_write - psb->ps_samples),
|
(int) (psb->ps_write - psb->ps_samples),
|
||||||
(int) (psb->ps_read - psb->ps_samples));
|
(int) (psb->ps_read - psb->ps_samples));
|
||||||
|
|
||||||
@ -3549,7 +3552,7 @@ pmc_process_samples(int cpu)
|
|||||||
goto entrydone;
|
goto entrydone;
|
||||||
|
|
||||||
PMCDBG(SAM,OPS,1,"cpu=%d pm=%p pc=%jx um=%d wr=%d rd=%d", cpu,
|
PMCDBG(SAM,OPS,1,"cpu=%d pm=%p pc=%jx um=%d wr=%d rd=%d", cpu,
|
||||||
pm, (int64_t) ps->ps_pc, ps->ps_usermode,
|
pm, (uint64_t) ps->ps_pc, ps->ps_usermode,
|
||||||
(int) (psb->ps_write - psb->ps_samples),
|
(int) (psb->ps_write - psb->ps_samples),
|
||||||
(int) (psb->ps_read - psb->ps_samples));
|
(int) (psb->ps_read - psb->ps_samples));
|
||||||
|
|
||||||
@ -3768,7 +3771,7 @@ pmc_process_exit(void *arg __unused, struct proc *p)
|
|||||||
*/
|
*/
|
||||||
for (ri = 0; ri < md->pmd_npmc; ri++)
|
for (ri = 0; ri < md->pmd_npmc; ri++)
|
||||||
if ((pm = pp->pp_pmcs[ri].pp_pmc) != NULL) {
|
if ((pm = pp->pp_pmcs[ri].pp_pmc) != NULL) {
|
||||||
if (pm->pm_flags & PMC_F_LOG_PROCEXIT)
|
if (pm->pm_flags & PMC_F_NEEDS_LOGFILE)
|
||||||
pmclog_process_procexit(pm, pp);
|
pmclog_process_procexit(pm, pp);
|
||||||
pmc_unlink_target_process(pm, pp);
|
pmc_unlink_target_process(pm, pp);
|
||||||
}
|
}
|
||||||
|
@ -301,6 +301,9 @@ do_execve(td, args, mac_p)
|
|||||||
struct label *interplabel = NULL;
|
struct label *interplabel = NULL;
|
||||||
int will_transition;
|
int will_transition;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HWPMC_HOOKS
|
||||||
|
struct pmckern_procexec pe;
|
||||||
|
#endif
|
||||||
|
|
||||||
vfslocked = 0;
|
vfslocked = 0;
|
||||||
imgp = &image_params;
|
imgp = &image_params;
|
||||||
@ -681,8 +684,10 @@ interpret:
|
|||||||
*/
|
*/
|
||||||
if (PMC_SYSTEM_SAMPLING_ACTIVE() || PMC_PROC_IS_USING_PMCS(p)) {
|
if (PMC_SYSTEM_SAMPLING_ACTIVE() || PMC_PROC_IS_USING_PMCS(p)) {
|
||||||
PROC_UNLOCK(p);
|
PROC_UNLOCK(p);
|
||||||
PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC,
|
pe.pm_credentialschanged = credential_changing;
|
||||||
(void *) &credential_changing);
|
pe.pm_entryaddr = imgp->entry_addr;
|
||||||
|
|
||||||
|
PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC, (void *) &pe);
|
||||||
} else
|
} else
|
||||||
PROC_UNLOCK(p);
|
PROC_UNLOCK(p);
|
||||||
#else /* !HWPMC_HOOKS */
|
#else /* !HWPMC_HOOKS */
|
||||||
|
@ -995,7 +995,7 @@ MALLOC_DECLARE(M_PMC);
|
|||||||
|
|
||||||
struct pmc_mdep *pmc_md_initialize(void); /* MD init function */
|
struct pmc_mdep *pmc_md_initialize(void); /* MD init function */
|
||||||
int pmc_getrowdisp(int _ri);
|
int pmc_getrowdisp(int _ri);
|
||||||
int pmc_process_interrupt(int _cpu, struct pmc *_pm, intfptr_t _pc,
|
int pmc_process_interrupt(int _cpu, struct pmc *_pm, uintfptr_t _pc,
|
||||||
int _usermode);
|
int _usermode);
|
||||||
|
|
||||||
#endif /* _KERNEL */
|
#endif /* _KERNEL */
|
||||||
|
@ -44,6 +44,11 @@
|
|||||||
#define PMC_FN_CSW_OUT 3
|
#define PMC_FN_CSW_OUT 3
|
||||||
#define PMC_FN_DO_SAMPLES 4
|
#define PMC_FN_DO_SAMPLES 4
|
||||||
|
|
||||||
|
struct pmckern_procexec {
|
||||||
|
int pm_credentialschanged;
|
||||||
|
uintptr_t pm_entryaddr;
|
||||||
|
};
|
||||||
|
|
||||||
/* hook */
|
/* hook */
|
||||||
extern int (*pmc_hook)(struct thread *_td, int _function, void *_arg);
|
extern int (*pmc_hook)(struct thread *_td, int _function, void *_arg);
|
||||||
extern int (*pmc_intr)(int _cpu, uintptr_t _pc, int _usermode);
|
extern int (*pmc_intr)(int _cpu, uintptr_t _pc, int _usermode);
|
||||||
|
@ -113,6 +113,7 @@ struct pmclog_pcsample {
|
|||||||
uint32_t pl_pid;
|
uint32_t pl_pid;
|
||||||
uintfptr_t pl_pc; /* 8 byte aligned */
|
uintfptr_t pl_pc; /* 8 byte aligned */
|
||||||
uint32_t pl_pmcid;
|
uint32_t pl_pmcid;
|
||||||
|
uint32_t pl_usermode;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct pmclog_pmcallocate {
|
struct pmclog_pmcallocate {
|
||||||
@ -145,6 +146,8 @@ struct pmclog_proccsw {
|
|||||||
struct pmclog_procexec {
|
struct pmclog_procexec {
|
||||||
PMCLOG_ENTRY_HEADER
|
PMCLOG_ENTRY_HEADER
|
||||||
uint32_t pl_pid;
|
uint32_t pl_pid;
|
||||||
|
uintfptr_t pl_start; /* keep 8 byte aligned */
|
||||||
|
uint32_t pl_pmcid;
|
||||||
char pl_pathname[PATH_MAX];
|
char pl_pathname[PATH_MAX];
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
@ -217,7 +220,8 @@ void pmclog_process_pmcattach(struct pmc *_pm, pid_t _pid, char *_path);
|
|||||||
void pmclog_process_pmcdetach(struct pmc *_pm, pid_t _pid);
|
void pmclog_process_pmcdetach(struct pmc *_pm, pid_t _pid);
|
||||||
void pmclog_process_proccsw(struct pmc *_pm, struct pmc_process *_pp,
|
void pmclog_process_proccsw(struct pmc *_pm, struct pmc_process *_pp,
|
||||||
pmc_value_t _v);
|
pmc_value_t _v);
|
||||||
void pmclog_process_procexec(struct pmc_owner *_po, pid_t _pid, char *_path);
|
void pmclog_process_procexec(struct pmc_owner *_po, pmc_id_t _pmid, pid_t _pid,
|
||||||
|
uintfptr_t _startaddr, char *_path);
|
||||||
void pmclog_process_procexit(struct pmc *_pm, struct pmc_process *_pp);
|
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_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_sysexit(struct pmc_owner *_po, pid_t _pid);
|
||||||
|
@ -10,6 +10,6 @@ LDADD= -lpmc -lm
|
|||||||
|
|
||||||
WARNS?= 6
|
WARNS?= 6
|
||||||
|
|
||||||
SRCS= pmcstat.c
|
SRCS= pmcstat.c pmcstat.h pmcstat_log.c
|
||||||
|
|
||||||
.include <bsd.prog.mk>
|
.include <bsd.prog.mk>
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
.Nd "performance measurement with performance monitoring hardware"
|
.Nd "performance measurement with performance monitoring hardware"
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl D Ar pathname
|
|
||||||
.Op Fl C
|
.Op Fl C
|
||||||
|
.Op Fl D Ar pathname
|
||||||
.Op Fl E
|
.Op Fl E
|
||||||
.Op Fl O Ar logfilename
|
.Op Fl O Ar logfilename
|
||||||
.Op Fl P Ar event-spec
|
.Op Fl P Ar event-spec
|
||||||
@ -42,6 +42,7 @@
|
|||||||
.Op Fl c Ar cpu
|
.Op Fl c Ar cpu
|
||||||
.Op Fl d
|
.Op Fl d
|
||||||
.Op Fl g
|
.Op Fl g
|
||||||
|
.Op Fl k Ar kernelfile
|
||||||
.Op Fl n Ar rate
|
.Op Fl n Ar rate
|
||||||
.Op Fl o Ar outputfile
|
.Op Fl o Ar outputfile
|
||||||
.Op Fl p Ar event-spec
|
.Op Fl p Ar event-spec
|
||||||
@ -154,6 +155,11 @@ The default is to measure events for the target process alone.
|
|||||||
.It Fl g
|
.It Fl g
|
||||||
Produce execution profiles in a format compatible with
|
Produce execution profiles in a format compatible with
|
||||||
.Xr gprof 1 .
|
.Xr gprof 1 .
|
||||||
|
.It Fl k Ar kernelfile
|
||||||
|
Set the pathname of the kernel to argument
|
||||||
|
.Ar kernelfile .
|
||||||
|
The default is
|
||||||
|
.Pa "/boot/kernel/kernel" .
|
||||||
.It Fl n Ar rate
|
.It Fl n Ar rate
|
||||||
Set the default sampling rate for subsequent sampling mode
|
Set the default sampling rate for subsequent sampling mode
|
||||||
PMCs specified on the command line.
|
PMCs specified on the command line.
|
||||||
|
@ -30,6 +30,8 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#include <sys/queue.h>
|
#include <sys/queue.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/ttycom.h>
|
#include <sys/ttycom.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@ -44,13 +46,15 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <pmclog.h>
|
#include <pmclog.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "pmcstat.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A given invocation of pmcstat(8) can manage multiple PMCs of both
|
* A given invocation of pmcstat(8) can manage multiple PMCs of both
|
||||||
* the system-wide and per-process variety. Each of these could be in
|
* the system-wide and per-process variety. Each of these could be in
|
||||||
@ -68,90 +72,13 @@ __FBSDID("$FreeBSD$");
|
|||||||
* for a given executable into a single profile file.
|
* for a given executable into a single profile file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Operation modes */
|
/* Globals */
|
||||||
|
|
||||||
#define FLAG_HAS_PID 0x00000001
|
|
||||||
#define FLAG_HAS_WAIT_INTERVAL 0x00000002
|
|
||||||
#define FLAG_HAS_LOG_FILE 0x00000004
|
|
||||||
#define FLAG_HAS_PROCESS 0x00000008
|
|
||||||
#define FLAG_HAS_SAMPLING_PMCS 0x00000010
|
|
||||||
#define FLAG_HAS_COUNTING_PMCS 0x00000020
|
|
||||||
#define FLAG_HAS_PROCESS_PMCS 0x00000040
|
|
||||||
#define FLAG_HAS_SYSTEM_PMCS 0x00000080
|
|
||||||
#define FLAG_HAS_PIPE 0x00000100
|
|
||||||
#define FLAG_PROCESS_LOGFILE 0x00000200
|
|
||||||
#define FLAG_DO_GPROF 0x00000400
|
|
||||||
#define FLAG_DO_GPROF_MERGED 0x00000800
|
|
||||||
|
|
||||||
#define DEFAULT_SAMPLE_COUNT 65536
|
|
||||||
#define DEFAULT_WAIT_INTERVAL 5.0
|
|
||||||
#define DEFAULT_DISPLAY_HEIGHT 23
|
|
||||||
#define DEFAULT_BUFFER_SIZE 4096
|
|
||||||
|
|
||||||
#define WRITELOG_MAGIC 0xA55AA55A
|
|
||||||
#define PRINT_HEADER_PREFIX "# "
|
|
||||||
#define READPIPEFD 0
|
|
||||||
#define WRITEPIPEFD 1
|
|
||||||
#define NPIPEFD 2
|
|
||||||
|
|
||||||
enum pmcstat_state {
|
|
||||||
PMCSTAT_FINISHED = 0,
|
|
||||||
PMCSTAT_EXITING = 1,
|
|
||||||
PMCSTAT_RUNNING = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pmcstat_ev {
|
|
||||||
STAILQ_ENTRY(pmcstat_ev) ev_next;
|
|
||||||
char *ev_spec; /* event specification */
|
|
||||||
char *ev_name; /* (derived) event name */
|
|
||||||
enum pmc_mode ev_mode; /* desired mode */
|
|
||||||
int ev_count; /* associated count if in sampling mode */
|
|
||||||
int ev_cpu; /* specific cpu if requested */
|
|
||||||
int ev_flags; /* PMC_F_* */
|
|
||||||
int ev_cumulative; /* show cumulative counts */
|
|
||||||
int ev_fieldwidth; /* print width */
|
|
||||||
int ev_fieldskip; /* #leading spaces */
|
|
||||||
pmc_value_t ev_saved; /* saved value for incremental counts */
|
|
||||||
pmc_id_t ev_pmcid; /* allocated ID */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pmcstat_args {
|
|
||||||
int pa_required;
|
|
||||||
int pa_flags;
|
|
||||||
pid_t pa_pid;
|
|
||||||
FILE *pa_outputfile;
|
|
||||||
FILE *pa_logfile;
|
|
||||||
void *pa_logparser;
|
|
||||||
char *pa_outputdir;
|
|
||||||
double pa_interval;
|
|
||||||
int pa_argc;
|
|
||||||
char **pa_argv;
|
|
||||||
STAILQ_HEAD(, pmcstat_ev) pa_head;
|
|
||||||
} args;
|
|
||||||
|
|
||||||
int pmcstat_interrupt = 0;
|
int pmcstat_interrupt = 0;
|
||||||
int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
|
int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
|
||||||
int pmcstat_pipefd[NPIPEFD];
|
int pmcstat_pipefd[NPIPEFD];
|
||||||
int pmcstat_kq;
|
int pmcstat_kq;
|
||||||
|
|
||||||
/* Function prototypes */
|
|
||||||
void pmcstat_cleanup(struct pmcstat_args *_a);
|
|
||||||
int pmcstat_close_log(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_print_counters(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_print_headers(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_print_pmcs(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_setup_process(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_show_usage(void);
|
|
||||||
void pmcstat_start_pmcs(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_start_process(struct pmcstat_args *_a);
|
|
||||||
void pmcstat_process_log(struct pmcstat_args *_a);
|
|
||||||
int pmcstat_print_log(struct pmcstat_args *_a);
|
|
||||||
|
|
||||||
#define PMCSTAT_PRINT_LOG(A,T,...) do { \
|
|
||||||
fprintf((A)->pa_outputfile, T "\t" __VA_ARGS__); \
|
|
||||||
fprintf((A)->pa_outputfile, "\n"); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cleanup
|
* cleanup
|
||||||
*/
|
*/
|
||||||
@ -162,7 +89,7 @@ pmcstat_cleanup(struct pmcstat_args *a)
|
|||||||
struct pmcstat_ev *ev, *tmp;
|
struct pmcstat_ev *ev, *tmp;
|
||||||
|
|
||||||
/* de-configure the log file if present. */
|
/* de-configure the log file if present. */
|
||||||
if (a->pa_flags & FLAG_HAS_LOG_FILE)
|
if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
|
||||||
(void) pmc_configure_logfile(-1);
|
(void) pmc_configure_logfile(-1);
|
||||||
|
|
||||||
/* release allocated PMCs. */
|
/* release allocated PMCs. */
|
||||||
@ -181,6 +108,9 @@ pmcstat_cleanup(struct pmcstat_args *a)
|
|||||||
pmclog_close(a->pa_logparser);
|
pmclog_close(a->pa_logparser);
|
||||||
a->pa_logparser = NULL;
|
a->pa_logparser = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
|
||||||
|
pmcstat_shutdown_logging();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -208,7 +138,7 @@ pmcstat_print_headers(struct pmcstat_args *a)
|
|||||||
struct pmcstat_ev *ev;
|
struct pmcstat_ev *ev;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
(void) fprintf(a->pa_outputfile, PRINT_HEADER_PREFIX);
|
(void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX);
|
||||||
|
|
||||||
STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
|
STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
|
||||||
if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
|
if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
|
||||||
@ -217,16 +147,16 @@ pmcstat_print_headers(struct pmcstat_args *a)
|
|||||||
c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p';
|
c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p';
|
||||||
|
|
||||||
if (ev->ev_fieldskip != 0) {
|
if (ev->ev_fieldskip != 0) {
|
||||||
(void) fprintf(a->pa_outputfile, "%*s%c/%*s ",
|
(void) fprintf(a->pa_printfile, "%*s%c/%*s ",
|
||||||
ev->ev_fieldskip, "", c,
|
ev->ev_fieldskip, "", c,
|
||||||
ev->ev_fieldwidth - ev->ev_fieldskip - 2,
|
ev->ev_fieldwidth - ev->ev_fieldskip - 2,
|
||||||
ev->ev_name);
|
ev->ev_name);
|
||||||
} else
|
} else
|
||||||
(void) fprintf(a->pa_outputfile, "%c/%*s ",
|
(void) fprintf(a->pa_printfile, "%c/%*s ",
|
||||||
c, ev->ev_fieldwidth - 2, ev->ev_name);
|
c, ev->ev_fieldwidth - 2, ev->ev_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) fflush(a->pa_outputfile);
|
(void) fflush(a->pa_printfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -248,15 +178,17 @@ pmcstat_print_counters(struct pmcstat_args *a)
|
|||||||
err(EX_OSERR, "ERROR: Cannot read pmc "
|
err(EX_OSERR, "ERROR: Cannot read pmc "
|
||||||
"\"%s\"", ev->ev_name);
|
"\"%s\"", ev->ev_name);
|
||||||
|
|
||||||
(void) fprintf(a->pa_outputfile, "%*ju ",
|
(void) fprintf(a->pa_printfile, "%*ju ",
|
||||||
ev->ev_fieldwidth + extra_width, (uintmax_t)
|
ev->ev_fieldwidth + extra_width,
|
||||||
ev->ev_cumulative ? value : (value - ev->ev_saved));
|
(uintmax_t) ev->ev_cumulative ? value :
|
||||||
|
(value - ev->ev_saved));
|
||||||
|
|
||||||
if (ev->ev_cumulative == 0)
|
if (ev->ev_cumulative == 0)
|
||||||
ev->ev_saved = value;
|
ev->ev_saved = value;
|
||||||
extra_width = 0;
|
extra_width = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) fflush(a->pa_outputfile);
|
(void) fflush(a->pa_printfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -268,15 +200,15 @@ pmcstat_print_pmcs(struct pmcstat_args *a)
|
|||||||
{
|
{
|
||||||
static int linecount = 0;
|
static int linecount = 0;
|
||||||
|
|
||||||
|
/* check if we need to print a header line */
|
||||||
if (++linecount > pmcstat_displayheight) {
|
if (++linecount > pmcstat_displayheight) {
|
||||||
(void) fprintf(a->pa_outputfile, "\n");
|
(void) fprintf(a->pa_printfile, "\n");
|
||||||
linecount = 1;
|
linecount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (linecount == 1)
|
if (linecount == 1)
|
||||||
pmcstat_print_headers(a);
|
pmcstat_print_headers(a);
|
||||||
|
(void) fprintf(a->pa_printfile, "\n");
|
||||||
|
|
||||||
(void) fprintf(a->pa_outputfile, "\n");
|
|
||||||
pmcstat_print_counters(a);
|
pmcstat_print_counters(a);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -375,146 +307,6 @@ pmcstat_start_process(struct pmcstat_args *a)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Process a log file in offline analysis mode.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
pmcstat_process_log(struct pmcstat_args *a)
|
|
||||||
{
|
|
||||||
int runstate;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If gprof style profiles haven't been asked for, just print the
|
|
||||||
* log to the current output file.
|
|
||||||
*/
|
|
||||||
if ((a->pa_flags & (FLAG_DO_GPROF_MERGED|FLAG_DO_GPROF)) == 0) {
|
|
||||||
while ((runstate = pmcstat_print_log(a)) == PMCSTAT_RUNNING)
|
|
||||||
;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* convert the log to gprof compatible profiles */
|
|
||||||
assert(0); /* To be implemented */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Print log entries available in a configured parser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int
|
|
||||||
pmcstat_print_log(struct pmcstat_args *a)
|
|
||||||
{
|
|
||||||
struct pmclog_ev ev;
|
|
||||||
|
|
||||||
while (pmclog_read(a->pa_logparser, &ev) == 0) {
|
|
||||||
assert(ev.pl_state == PMCLOG_OK);
|
|
||||||
switch (ev.pl_type) {
|
|
||||||
case PMCLOG_TYPE_CLOSELOG:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"close",);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_DROPNOTIFY:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"drop",);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_INITIALIZE:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"init","0x%x \"%s\"",
|
|
||||||
ev.pl_u.pl_i.pl_version,
|
|
||||||
pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch));
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_MAPPINGCHANGE:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"mapping","%s %d %p %p \"%s\"",
|
|
||||||
ev.pl_u.pl_m.pl_type == PMCLOG_MAPPING_INSERT ?
|
|
||||||
"insert" : "delete",
|
|
||||||
ev.pl_u.pl_m.pl_pid,
|
|
||||||
(void *) ev.pl_u.pl_m.pl_start,
|
|
||||||
(void *) ev.pl_u.pl_m.pl_end,
|
|
||||||
ev.pl_u.pl_m.pl_pathname);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PCSAMPLE:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"sample","0x%x %d %p",
|
|
||||||
ev.pl_u.pl_s.pl_pmcid,
|
|
||||||
ev.pl_u.pl_s.pl_pid,
|
|
||||||
(void *) ev.pl_u.pl_s.pl_pc);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PMCALLOCATE:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"allocate","0x%x \"%s\" 0x%x",
|
|
||||||
ev.pl_u.pl_a.pl_pmcid,
|
|
||||||
ev.pl_u.pl_a.pl_evname,
|
|
||||||
ev.pl_u.pl_a.pl_flags);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PMCATTACH:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"attach","0x%x %d \"%s\"",
|
|
||||||
ev.pl_u.pl_t.pl_pmcid,
|
|
||||||
ev.pl_u.pl_t.pl_pid,
|
|
||||||
ev.pl_u.pl_t.pl_pathname);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PMCDETACH:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"detach","0x%x %d",
|
|
||||||
ev.pl_u.pl_d.pl_pmcid,
|
|
||||||
ev.pl_u.pl_d.pl_pid);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PROCCSW:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"csw","0x%x %d %jd",
|
|
||||||
ev.pl_u.pl_c.pl_pmcid,
|
|
||||||
ev.pl_u.pl_c.pl_pid,
|
|
||||||
ev.pl_u.pl_c.pl_value);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PROCEXEC:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"exec","%d \"%s\"",
|
|
||||||
ev.pl_u.pl_x.pl_pid,
|
|
||||||
ev.pl_u.pl_x.pl_pathname);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PROCEXIT:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"exitvalue","0x%x %d %jd",
|
|
||||||
ev.pl_u.pl_e.pl_pmcid,
|
|
||||||
ev.pl_u.pl_e.pl_pid,
|
|
||||||
ev.pl_u.pl_e.pl_value);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_PROCFORK:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"fork","%d %d",
|
|
||||||
ev.pl_u.pl_f.pl_oldpid,
|
|
||||||
ev.pl_u.pl_f.pl_newpid);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_USERDATA:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"user","0x%x",
|
|
||||||
ev.pl_u.pl_u.pl_userdata);
|
|
||||||
break;
|
|
||||||
case PMCLOG_TYPE_SYSEXIT:
|
|
||||||
PMCSTAT_PRINT_LOG(a,"exit","%d",
|
|
||||||
ev.pl_u.pl_se.pl_pid);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(a->pa_outputfile, "unknown %d",
|
|
||||||
ev.pl_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ev.pl_state == PMCLOG_EOF)
|
|
||||||
return PMCSTAT_FINISHED;
|
|
||||||
else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
|
|
||||||
return PMCSTAT_RUNNING;
|
|
||||||
|
|
||||||
err(EX_DATAERR, "ERROR: event parsing failed "
|
|
||||||
"(record %jd, offset 0x%jx)",
|
|
||||||
(uintmax_t) ev.pl_count + 1, ev.pl_offset);
|
|
||||||
/*NOTREACHED*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Close a logfile, after first flushing all in-module queued data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int
|
|
||||||
pmcstat_close_log(struct pmcstat_args *a)
|
|
||||||
{
|
|
||||||
if (pmc_flush_logfile() < 0 ||
|
|
||||||
pmc_configure_logfile(-1) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: logging failed");
|
|
||||||
a->pa_flags &= ~FLAG_HAS_LOG_FILE;
|
|
||||||
return a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
|
|
||||||
PMCSTAT_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
pmcstat_show_usage(void)
|
pmcstat_show_usage(void)
|
||||||
{
|
{
|
||||||
@ -534,7 +326,6 @@ pmcstat_show_usage(void)
|
|||||||
"\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n"
|
"\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n"
|
||||||
"\t -d\t\t (toggle) track descendants\n"
|
"\t -d\t\t (toggle) track descendants\n"
|
||||||
"\t -g\t\t produce gprof(1) compatible profiles\n"
|
"\t -g\t\t produce gprof(1) compatible profiles\n"
|
||||||
"\t -m\t\t merge gprof(1) profiles for executables\n"
|
|
||||||
"\t -n rate\t set sampling rate\n"
|
"\t -n rate\t set sampling rate\n"
|
||||||
"\t -o file\t send print output to \"file\"\n"
|
"\t -o file\t send print output to \"file\"\n"
|
||||||
"\t -p spec\t allocate a process-private counting PMC\n"
|
"\t -p spec\t allocate a process-private counting PMC\n"
|
||||||
@ -556,7 +347,6 @@ main(int argc, char **argv)
|
|||||||
int c, current_cpu, current_sampling_count;
|
int c, current_cpu, current_sampling_count;
|
||||||
int do_print, do_descendants;
|
int do_print, do_descendants;
|
||||||
int do_logproccsw, do_logprocexit;
|
int do_logproccsw, do_logprocexit;
|
||||||
int logfd;
|
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
int use_cumulative_counts;
|
int use_cumulative_counts;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
@ -567,6 +357,7 @@ main(int argc, char **argv)
|
|||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
struct kevent kev;
|
struct kevent kev;
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
current_cpu = 0;
|
current_cpu = 0;
|
||||||
current_sampling_count = DEFAULT_SAMPLE_COUNT;
|
current_sampling_count = DEFAULT_SAMPLE_COUNT;
|
||||||
@ -577,15 +368,16 @@ main(int argc, char **argv)
|
|||||||
args.pa_required = 0;
|
args.pa_required = 0;
|
||||||
args.pa_flags = 0;
|
args.pa_flags = 0;
|
||||||
args.pa_pid = (pid_t) -1;
|
args.pa_pid = (pid_t) -1;
|
||||||
args.pa_logfile = NULL;
|
args.pa_logfd = -1;
|
||||||
args.pa_outputdir = NULL;
|
args.pa_samplesdir = ".";
|
||||||
args.pa_outputfile = stderr;
|
args.pa_kernel = "/boot/kernel/kernel";
|
||||||
|
args.pa_printfile = stderr;
|
||||||
args.pa_interval = DEFAULT_WAIT_INTERVAL;
|
args.pa_interval = DEFAULT_WAIT_INTERVAL;
|
||||||
STAILQ_INIT(&args.pa_head);
|
STAILQ_INIT(&args.pa_head);
|
||||||
|
|
||||||
ev = NULL;
|
ev = NULL;
|
||||||
|
|
||||||
while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgmn:o:p:s:t:w:"))
|
while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgk:n:o:p:s:t:w:"))
|
||||||
!= -1)
|
!= -1)
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'C': /* cumulative values */
|
case 'C': /* cumulative values */
|
||||||
@ -602,29 +394,37 @@ main(int argc, char **argv)
|
|||||||
args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
|
args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
if (stat(optarg, &sb) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot stat \"%s\"",
|
||||||
|
optarg);
|
||||||
|
if (!S_ISDIR(sb.st_mode))
|
||||||
|
errx(EX_USAGE, "ERROR: \"%s\" is not a "
|
||||||
|
"directory", optarg);
|
||||||
|
args.pa_samplesdir = optarg;
|
||||||
|
args.pa_flags |= FLAG_HAS_SAMPLESDIR;
|
||||||
|
args.pa_required |= FLAG_DO_GPROF;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'd': /* toggle descendents */
|
case 'd': /* toggle descendents */
|
||||||
do_descendants = !do_descendants;
|
do_descendants = !do_descendants;
|
||||||
args.pa_required |= FLAG_HAS_PROCESS_PMCS;
|
args.pa_required |= FLAG_HAS_PROCESS_PMCS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'D':
|
|
||||||
args.pa_outputdir = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'g': /* produce gprof compatible profiles */
|
case 'g': /* produce gprof compatible profiles */
|
||||||
args.pa_flags |= FLAG_DO_GPROF;
|
args.pa_flags |= FLAG_DO_GPROF;
|
||||||
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm': /* produce merged profiles */
|
case 'k': /* pathname to the kernel */
|
||||||
args.pa_flags |= FLAG_DO_GPROF_MERGED;
|
args.pa_kernel = optarg;
|
||||||
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
|
args.pa_required |= FLAG_DO_GPROF;
|
||||||
|
args.pa_flags |= FLAG_HAS_KERNELPATH;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'E': /* log process exit */
|
case 'E': /* log process exit */
|
||||||
do_logprocexit = !do_logprocexit;
|
do_logprocexit = !do_logprocexit;
|
||||||
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
|
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
|
||||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE);
|
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'p': /* process virtual counting PMC */
|
case 'p': /* process virtual counting PMC */
|
||||||
@ -643,13 +443,14 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
if (option == 'P' || option == 'p') {
|
if (option == 'P' || option == 'p') {
|
||||||
args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
|
args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
|
||||||
args.pa_required |= (FLAG_HAS_PROCESS |
|
args.pa_required |= (FLAG_HAS_COMMANDLINE |
|
||||||
FLAG_HAS_PID);
|
FLAG_HAS_PID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option == 'P' || option == 'S') {
|
if (option == 'P' || option == 'S') {
|
||||||
args.pa_flags |= FLAG_HAS_SAMPLING_PMCS;
|
args.pa_flags |= FLAG_HAS_SAMPLING_PMCS;
|
||||||
args.pa_required |= FLAG_HAS_LOG_FILE;
|
args.pa_required |= (FLAG_HAS_PIPE |
|
||||||
|
FLAG_HAS_OUTPUT_LOGFILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (option == 'p' || option == 's')
|
if (option == 'p' || option == 's')
|
||||||
@ -693,16 +494,6 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'R': /* read an existing log file */
|
|
||||||
if ((logfd = open(optarg, O_RDONLY, 0)) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
|
|
||||||
"reading", optarg);
|
|
||||||
if ((args.pa_logparser = pmclog_open(logfd))
|
|
||||||
== NULL)
|
|
||||||
err(EX_OSERR, "ERROR: Cannot create parser");
|
|
||||||
args.pa_flags |= FLAG_PROCESS_LOGFILE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'n': /* sampling count */
|
case 'n': /* sampling count */
|
||||||
current_sampling_count = strtol(optarg, &end, 0);
|
current_sampling_count = strtol(optarg, &end, 0);
|
||||||
if (*end != '\0' || current_sampling_count <= 0)
|
if (*end != '\0' || current_sampling_count <= 0)
|
||||||
@ -713,22 +504,30 @@ main(int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o': /* outputfile */
|
case 'o': /* outputfile */
|
||||||
if (args.pa_outputfile != NULL)
|
if (args.pa_printfile != NULL)
|
||||||
(void) fclose(args.pa_outputfile);
|
(void) fclose(args.pa_printfile);
|
||||||
if ((args.pa_outputfile = fopen(optarg, "w")) == NULL)
|
if ((args.pa_printfile = fopen(optarg, "w")) == NULL)
|
||||||
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
|
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
|
||||||
"writing.", optarg);
|
"writing.", optarg);
|
||||||
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
|
args.pa_flags |= FLAG_DO_PRINT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'O': /* sampling output */
|
case 'O': /* sampling output */
|
||||||
if (args.pa_logfile != NULL)
|
if (args.pa_outputpath)
|
||||||
errx(EX_OSERR, "ERROR: option -O may only be "
|
errx(EX_USAGE, "ERROR: option -O may only be "
|
||||||
"specified once.");
|
"specified once.");
|
||||||
if ((args.pa_logfile = fopen(optarg, "w")) == NULL)
|
args.pa_outputpath = optarg;
|
||||||
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
|
args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE;
|
||||||
"writing.", optarg);
|
break;
|
||||||
args.pa_flags |= FLAG_HAS_LOG_FILE;
|
|
||||||
|
case 'R': /* read an existing log file */
|
||||||
|
if (args.pa_logparser != NULL)
|
||||||
|
errx(EX_USAGE, "ERROR: option -R may only be "
|
||||||
|
"specified once.");
|
||||||
|
args.pa_inputpath = optarg;
|
||||||
|
if (args.pa_printfile == stderr)
|
||||||
|
args.pa_printfile = stdout;
|
||||||
|
args.pa_flags |= FLAG_READ_LOGFILE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 't': /* target pid */
|
case 't': /* target pid */
|
||||||
@ -748,13 +547,14 @@ main(int argc, char **argv)
|
|||||||
errx(EX_USAGE, "ERROR: Illegal wait interval "
|
errx(EX_USAGE, "ERROR: Illegal wait interval "
|
||||||
"value \"%s\".", optarg);
|
"value \"%s\".", optarg);
|
||||||
args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
|
args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
|
||||||
|
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
|
||||||
args.pa_interval = interval;
|
args.pa_interval = interval;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'W': /* toggle LOG_CSW */
|
case 'W': /* toggle LOG_CSW */
|
||||||
do_logproccsw = !do_logproccsw;
|
do_logproccsw = !do_logproccsw;
|
||||||
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
|
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
|
||||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE);
|
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
@ -767,16 +567,21 @@ main(int argc, char **argv)
|
|||||||
args.pa_argc = (argc -= optind);
|
args.pa_argc = (argc -= optind);
|
||||||
args.pa_argv = (argv += optind);
|
args.pa_argv = (argv += optind);
|
||||||
|
|
||||||
if (argc)
|
if (argc) /* command line present */
|
||||||
args.pa_flags |= FLAG_HAS_PROCESS;
|
args.pa_flags |= FLAG_HAS_COMMANDLINE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check invocation syntax.
|
* Check invocation syntax.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (args.pa_flags & FLAG_PROCESS_LOGFILE) {
|
/* disallow -O and -R together */
|
||||||
|
if (args.pa_outputpath && args.pa_inputpath)
|
||||||
|
errx(EX_USAGE, "ERROR: options -O and -R are mutually "
|
||||||
|
"exclusive.");
|
||||||
|
|
||||||
|
if (args.pa_flags & FLAG_READ_LOGFILE) {
|
||||||
errmsg = NULL;
|
errmsg = NULL;
|
||||||
if (args.pa_flags & FLAG_HAS_PROCESS)
|
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||||
errmsg = "a command line specification";
|
errmsg = "a command line specification";
|
||||||
else if (args.pa_flags & FLAG_HAS_PID)
|
else if (args.pa_flags & FLAG_HAS_PID)
|
||||||
errmsg = "option -t";
|
errmsg = "option -t";
|
||||||
@ -785,10 +590,9 @@ main(int argc, char **argv)
|
|||||||
if (errmsg)
|
if (errmsg)
|
||||||
errx(EX_USAGE, "ERROR: option -R may not be used with "
|
errx(EX_USAGE, "ERROR: option -R may not be used with "
|
||||||
"%s.", errmsg);
|
"%s.", errmsg);
|
||||||
} else if (STAILQ_EMPTY(&args.pa_head)) {
|
} else if (STAILQ_EMPTY(&args.pa_head))
|
||||||
warnx("ERROR: At least one PMC event must be specified");
|
/* All other uses require a PMC spec. */
|
||||||
pmcstat_show_usage();
|
pmcstat_show_usage();
|
||||||
}
|
|
||||||
|
|
||||||
/* check for -t pid without a process PMC spec */
|
/* check for -t pid without a process PMC spec */
|
||||||
if ((args.pa_required & FLAG_HAS_PID) &&
|
if ((args.pa_required & FLAG_HAS_PID) &&
|
||||||
@ -798,20 +602,20 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
/* check for process-mode options without a command or -t pid */
|
/* check for process-mode options without a command or -t pid */
|
||||||
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
||||||
(args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0)
|
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
|
||||||
errx(EX_USAGE, "ERROR: options -d,-E,-p,-P,-W require a "
|
errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require "
|
||||||
"command line or target process.");
|
"a command line or target process.");
|
||||||
|
|
||||||
/* check for -p | -P without a target process of some sort */
|
/* check for -p | -P without a target process of some sort */
|
||||||
if ((args.pa_required & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) &&
|
if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) &&
|
||||||
(args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0)
|
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
|
||||||
errx(EX_USAGE, "ERROR: the -P or -p options require a "
|
errx(EX_USAGE, "ERROR: options -P and -p require a "
|
||||||
"target process or a command line.");
|
"target process or a command line.");
|
||||||
|
|
||||||
/* check for process-mode options without a process-mode PMC */
|
/* check for process-mode options without a process-mode PMC */
|
||||||
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
||||||
(args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
|
(args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
|
||||||
errx(EX_USAGE, "ERROR: options -d,-E,-W require a "
|
errx(EX_USAGE, "ERROR: options -d, -E, and -W require a "
|
||||||
"process mode PMC to be specified.");
|
"process mode PMC to be specified.");
|
||||||
|
|
||||||
/* check for -c cpu and not system mode PMCs */
|
/* check for -c cpu and not system mode PMCs */
|
||||||
@ -823,30 +627,70 @@ main(int argc, char **argv)
|
|||||||
/* check for counting mode options without a counting PMC */
|
/* check for counting mode options without a counting PMC */
|
||||||
if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) &&
|
if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) &&
|
||||||
(args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0)
|
(args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0)
|
||||||
errx(EX_USAGE, "ERROR: options -C,-o,-W require at least one "
|
errx(EX_USAGE, "ERROR: options -C, -o and -W require at least "
|
||||||
"counting mode PMC to be specified.");
|
"one counting mode PMC to be specified.");
|
||||||
|
|
||||||
/* check for sampling mode options without a sampling PMC spec */
|
/* check for sampling mode options without a sampling PMC spec */
|
||||||
if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) &&
|
if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) &&
|
||||||
(args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0)
|
(args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0)
|
||||||
errx(EX_USAGE, "ERROR: options -n,-O require at least one "
|
errx(EX_USAGE, "ERROR: options -n and -O require at least "
|
||||||
"sampling mode PMC to be specified.");
|
"one sampling mode PMC to be specified.");
|
||||||
|
|
||||||
if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_PROCESS)) ==
|
if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) ==
|
||||||
(FLAG_HAS_PID | FLAG_HAS_PROCESS))
|
(FLAG_HAS_PID | FLAG_HAS_COMMANDLINE))
|
||||||
errx(EX_USAGE,
|
errx(EX_USAGE,
|
||||||
"ERROR: option -t cannot be specified with a command "
|
"ERROR: option -t cannot be specified with a command "
|
||||||
"line.");
|
"line.");
|
||||||
|
|
||||||
|
/* check if -g is being used correctly */
|
||||||
|
if ((args.pa_flags & FLAG_DO_GPROF) &&
|
||||||
|
!(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE)))
|
||||||
|
errx(EX_USAGE, "ERROR: option -g requires sampling PMCs or -R "
|
||||||
|
"to be specified.");
|
||||||
|
|
||||||
/* check if -O was spuriously specified */
|
/* check if -O was spuriously specified */
|
||||||
if ((args.pa_flags & FLAG_HAS_LOG_FILE) &&
|
if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) &&
|
||||||
(args.pa_required & FLAG_HAS_LOG_FILE) == 0)
|
(args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)
|
||||||
errx(EX_USAGE,
|
errx(EX_USAGE,
|
||||||
"ERROR: option -O is used only with options "
|
"ERROR: option -O is used only with options "
|
||||||
"-E,-P,-S and -W.");
|
"-E, -P, -S and -W.");
|
||||||
|
|
||||||
|
/* -D dir and -k kernel path require -g */
|
||||||
|
if ((args.pa_flags & FLAG_HAS_KERNELPATH) &&
|
||||||
|
((args.pa_flags & FLAG_DO_GPROF) == 0))
|
||||||
|
errx(EX_USAGE, "ERROR: option -k is only used with -g.");
|
||||||
|
|
||||||
|
if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) &&
|
||||||
|
((args.pa_flags & FLAG_DO_GPROF) == 0))
|
||||||
|
errx(EX_USAGE, "ERROR: option -D is only used with -g.");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disallow textual output of sampling PMCs if counting PMCs
|
||||||
|
* have also been asked for, mostly because the combined output
|
||||||
|
* is difficult to make sense of.
|
||||||
|
*/
|
||||||
|
if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
|
||||||
|
(args.pa_flags & FLAG_HAS_SAMPLING_PMCS) &&
|
||||||
|
((args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0))
|
||||||
|
errx(EX_USAGE, "ERROR: option -O is required if counting and "
|
||||||
|
"sampling PMCs are specified together.");
|
||||||
|
|
||||||
/* if we've been asked to process a log file, do that and exit */
|
/* if we've been asked to process a log file, do that and exit */
|
||||||
if (args.pa_flags & FLAG_PROCESS_LOGFILE) {
|
if (args.pa_flags & FLAG_READ_LOGFILE) {
|
||||||
|
/*
|
||||||
|
* Print the log in textual form if we haven't been
|
||||||
|
* asked to generate gmon.out files.
|
||||||
|
*/
|
||||||
|
if ((args.pa_flags & FLAG_DO_GPROF) == 0)
|
||||||
|
args.pa_flags |= FLAG_DO_PRINT;
|
||||||
|
|
||||||
|
pmcstat_initialize_logging(&args);
|
||||||
|
if ((args.pa_logfd = pmcstat_open(args.pa_inputpath,
|
||||||
|
PMCSTAT_OPEN_FOR_READ)) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
|
||||||
|
"reading", args.pa_inputpath);
|
||||||
|
if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot create parser");
|
||||||
pmcstat_process_log(&args);
|
pmcstat_process_log(&args);
|
||||||
exit(EX_OK);
|
exit(EX_OK);
|
||||||
}
|
}
|
||||||
@ -864,11 +708,52 @@ main(int argc, char **argv)
|
|||||||
err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
|
err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
|
||||||
"on CPU %d", 0);
|
"on CPU %d", 0);
|
||||||
|
|
||||||
|
/* Allocate a kqueue */
|
||||||
|
if ((pmcstat_kq = kqueue()) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot allocate kqueue");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the specified log file or setup a default log
|
||||||
|
* consumer via a pipe.
|
||||||
|
*/
|
||||||
|
if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) {
|
||||||
|
if (args.pa_outputpath) {
|
||||||
|
if ((args.pa_logfd = pmcstat_open(args.pa_outputpath,
|
||||||
|
PMCSTAT_OPEN_FOR_WRITE)) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
|
||||||
|
"writing", args.pa_outputpath);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* process the log on the fly by reading it in
|
||||||
|
* through a pipe.
|
||||||
|
*/
|
||||||
|
if (pipe(pipefd) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: pipe(2) failed");
|
||||||
|
|
||||||
|
if (fcntl(pipefd[READPIPEFD], F_SETFL, O_NONBLOCK) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: fcntl(2) failed");
|
||||||
|
|
||||||
|
EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD,
|
||||||
|
0, 0, NULL);
|
||||||
|
|
||||||
|
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot register kevent");
|
||||||
|
|
||||||
|
args.pa_logfd = pipefd[WRITEPIPEFD];
|
||||||
|
|
||||||
|
args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT);
|
||||||
|
args.pa_logparser = pmclog_open(pipefd[READPIPEFD]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pmc_configure_logfile(args.pa_logfd) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot configure log file");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate PMCs.
|
* Allocate PMCs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next)
|
STAILQ_FOREACH(ev, &args.pa_head, 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) < 0)
|
||||||
err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with "
|
err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with "
|
||||||
@ -876,6 +761,12 @@ main(int argc, char **argv)
|
|||||||
PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process",
|
PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process",
|
||||||
ev->ev_spec);
|
ev->ev_spec);
|
||||||
|
|
||||||
|
if (PMC_IS_SAMPLING_MODE(ev->ev_mode) &&
|
||||||
|
pmc_set(ev->ev_pmcid, ev->ev_count) < 0)
|
||||||
|
err(EX_OSERR, "ERROR: Cannot set sampling count "
|
||||||
|
"for PMC \"%s\"", ev->ev_name);
|
||||||
|
}
|
||||||
|
|
||||||
/* compute printout widths */
|
/* compute printout widths */
|
||||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
|
STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
|
||||||
int counter_width;
|
int counter_width;
|
||||||
@ -896,18 +787,14 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a kqueue */
|
|
||||||
if ((pmcstat_kq = kqueue()) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: Cannot allocate kqueue");
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If our output is being set to a terminal, register a handler
|
* If our output is being set to a terminal, register a handler
|
||||||
* for window size changes.
|
* for window size changes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (isatty(fileno(args.pa_outputfile))) {
|
if (isatty(fileno(args.pa_printfile))) {
|
||||||
|
|
||||||
if (ioctl(fileno(args.pa_outputfile), TIOCGWINSZ, &ws) < 0)
|
if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0)
|
||||||
err(EX_OSERR, "ERROR: Cannot determine window size");
|
err(EX_OSERR, "ERROR: Cannot determine window size");
|
||||||
|
|
||||||
pmcstat_displayheight = ws.ws_row - 1;
|
pmcstat_displayheight = ws.ws_row - 1;
|
||||||
@ -937,41 +824,9 @@ main(int argc, char **argv)
|
|||||||
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
|
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
|
||||||
err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD");
|
err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD");
|
||||||
|
|
||||||
/*
|
/* setup a timer if we have counting mode PMCs needing to be printed */
|
||||||
* Configure the specified log file or setup a default log
|
if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
|
||||||
* consumer via a pipe.
|
(args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
|
||||||
*/
|
|
||||||
if (args.pa_required & FLAG_HAS_LOG_FILE) {
|
|
||||||
|
|
||||||
if (args.pa_logfile == NULL) {
|
|
||||||
if (pipe(pipefd) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: pipe(2) failed");
|
|
||||||
|
|
||||||
EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD,
|
|
||||||
0, 0, NULL);
|
|
||||||
|
|
||||||
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: Cannot register kevent");
|
|
||||||
|
|
||||||
logfd = pipefd[WRITEPIPEFD];
|
|
||||||
|
|
||||||
args.pa_flags |= (FLAG_HAS_PIPE | FLAG_HAS_LOG_FILE);
|
|
||||||
args.pa_logparser = pmclog_open(pipefd[READPIPEFD]);
|
|
||||||
} else
|
|
||||||
logfd = fileno(args.pa_logfile);
|
|
||||||
|
|
||||||
if (pmc_configure_logfile(logfd) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: Cannot configure log file");
|
|
||||||
|
|
||||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next)
|
|
||||||
if (PMC_IS_SAMPLING_MODE(ev->ev_mode) &&
|
|
||||||
pmc_set(ev->ev_pmcid, ev->ev_count) < 0)
|
|
||||||
err(EX_OSERR, "ERROR: Cannot set sampling count "
|
|
||||||
"for PMC \"%s\"", ev->ev_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup a timer for any counting mode PMCs */
|
|
||||||
if (args.pa_flags & FLAG_HAS_COUNTING_PMCS) {
|
|
||||||
EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
|
EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
|
||||||
args.pa_interval * 1000, NULL);
|
args.pa_interval * 1000, NULL);
|
||||||
|
|
||||||
@ -981,16 +836,21 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* attach PMCs to the target process, starting it if specified */
|
/* attach PMCs to the target process, starting it if specified */
|
||||||
if (args.pa_flags & FLAG_HAS_PROCESS)
|
if (args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE))
|
||||||
pmcstat_setup_process(&args);
|
pmcstat_setup_process(&args);
|
||||||
|
|
||||||
/* start the pmcs */
|
/* start the pmcs */
|
||||||
pmcstat_start_pmcs(&args);
|
pmcstat_start_pmcs(&args);
|
||||||
|
|
||||||
/* start the (commandline) process if needed */
|
/* start the (commandline) process if needed */
|
||||||
if (args.pa_flags & FLAG_HAS_PROCESS)
|
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||||
pmcstat_start_process(&args);
|
pmcstat_start_process(&args);
|
||||||
|
|
||||||
|
/* initialize logging if printing the configured log */
|
||||||
|
if ((args.pa_flags & FLAG_DO_PRINT) &&
|
||||||
|
(args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)))
|
||||||
|
pmcstat_initialize_logging(&args);
|
||||||
|
|
||||||
/* Handle SIGINT using the kqueue loop */
|
/* Handle SIGINT using the kqueue loop */
|
||||||
sa.sa_handler = SIG_IGN;
|
sa.sa_handler = SIG_IGN;
|
||||||
sa.sa_flags = 0;
|
sa.sa_flags = 0;
|
||||||
@ -1018,7 +878,8 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
switch (kev.filter) {
|
switch (kev.filter) {
|
||||||
case EVFILT_PROC: /* target has exited */
|
case EVFILT_PROC: /* target has exited */
|
||||||
if (args.pa_flags & FLAG_HAS_LOG_FILE)
|
if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
|
||||||
|
FLAG_HAS_PIPE))
|
||||||
runstate = pmcstat_close_log(&args);
|
runstate = pmcstat_close_log(&args);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1042,20 +903,24 @@ main(int argc, char **argv)
|
|||||||
* of its targets, or if logfile
|
* of its targets, or if logfile
|
||||||
* writes encounter an error.
|
* writes encounter an error.
|
||||||
*/
|
*/
|
||||||
if (args.pa_flags & FLAG_HAS_LOG_FILE)
|
if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
|
||||||
|
FLAG_HAS_PIPE)) {
|
||||||
runstate = pmcstat_close_log(&args);
|
runstate = pmcstat_close_log(&args);
|
||||||
|
if (args.pa_flags &
|
||||||
|
(FLAG_DO_PRINT|FLAG_DO_GPROF))
|
||||||
|
pmcstat_process_log(&args);
|
||||||
|
}
|
||||||
do_print = 1; /* print PMCs at exit */
|
do_print = 1; /* print PMCs at exit */
|
||||||
runstate = PMCSTAT_FINISHED;
|
runstate = PMCSTAT_FINISHED;
|
||||||
} else if (kev.ident == SIGINT) {
|
} else if (kev.ident == SIGINT) {
|
||||||
/* pass the signal on to the child process */
|
/* Kill the child process if we started it */
|
||||||
if ((args.pa_flags & FLAG_HAS_PROCESS) &&
|
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||||
(args.pa_flags & FLAG_HAS_PID) == 0)
|
|
||||||
if (kill(args.pa_pid, SIGINT) != 0)
|
if (kill(args.pa_pid, SIGINT) != 0)
|
||||||
err(EX_OSERR, "ERROR: cannot "
|
err(EX_OSERR, "ERROR: cannot "
|
||||||
"signal child process");
|
"signal child process");
|
||||||
runstate = PMCSTAT_FINISHED;
|
runstate = PMCSTAT_FINISHED;
|
||||||
} else if (kev.ident == SIGWINCH) {
|
} else if (kev.ident == SIGWINCH) {
|
||||||
if (ioctl(fileno(args.pa_outputfile),
|
if (ioctl(fileno(args.pa_printfile),
|
||||||
TIOCGWINSZ, &ws) < 0)
|
TIOCGWINSZ, &ws) < 0)
|
||||||
err(EX_OSERR, "ERROR: Cannot determine "
|
err(EX_OSERR, "ERROR: Cannot determine "
|
||||||
"window size");
|
"window size");
|
||||||
@ -1071,17 +936,19 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_print) {
|
if (do_print &&
|
||||||
|
(args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
|
||||||
pmcstat_print_pmcs(&args);
|
pmcstat_print_pmcs(&args);
|
||||||
if (runstate == PMCSTAT_FINISHED) /* final newline */
|
if (runstate == PMCSTAT_FINISHED && /* final newline */
|
||||||
(void) fprintf(args.pa_outputfile, "\n");
|
(args.pa_flags & FLAG_DO_PRINT) == 0)
|
||||||
|
(void) fprintf(args.pa_printfile, "\n");
|
||||||
do_print = 0;
|
do_print = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (runstate != PMCSTAT_FINISHED);
|
} while (runstate != PMCSTAT_FINISHED);
|
||||||
|
|
||||||
/* flush any pending log entries */
|
/* flush any pending log entries */
|
||||||
if (args.pa_flags & FLAG_HAS_LOG_FILE)
|
if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE))
|
||||||
pmc_flush_logfile();
|
pmc_flush_logfile();
|
||||||
|
|
||||||
pmcstat_cleanup(&args);
|
pmcstat_cleanup(&args);
|
||||||
|
127
usr.sbin/pmcstat/pmcstat.h
Normal file
127
usr.sbin/pmcstat/pmcstat.h
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*-
|
||||||
|
* Copyright (c) 2005, Joseph Koshy
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PMCSTAT_H_
|
||||||
|
#define _PMCSTAT_H_
|
||||||
|
|
||||||
|
#define FLAG_HAS_PID 0x00000001 /* explicit pid */
|
||||||
|
#define FLAG_HAS_WAIT_INTERVAL 0x00000002 /* -w secs */
|
||||||
|
#define FLAG_HAS_OUTPUT_LOGFILE 0x00000004 /* -O file or pipe */
|
||||||
|
#define FLAG_HAS_COMMANDLINE 0x00000008 /* command */
|
||||||
|
#define FLAG_HAS_SAMPLING_PMCS 0x00000010 /* -S or -P */
|
||||||
|
#define FLAG_HAS_COUNTING_PMCS 0x00000020 /* -s or -p */
|
||||||
|
#define FLAG_HAS_PROCESS_PMCS 0x00000040 /* -P or -p */
|
||||||
|
#define FLAG_HAS_SYSTEM_PMCS 0x00000080 /* -S or -s */
|
||||||
|
#define FLAG_HAS_PIPE 0x00000100 /* implicit log */
|
||||||
|
#define FLAG_READ_LOGFILE 0x00000200 /* -R file */
|
||||||
|
#define FLAG_DO_GPROF 0x00000400 /* -g */
|
||||||
|
#define FLAG_HAS_SAMPLESDIR 0x00000800 /* -D dir */
|
||||||
|
#define FLAG_HAS_KERNELPATH 0x00001000 /* -k kernel */
|
||||||
|
#define FLAG_DO_PRINT 0x00002000 /* -o */
|
||||||
|
|
||||||
|
#define DEFAULT_SAMPLE_COUNT 65536
|
||||||
|
#define DEFAULT_WAIT_INTERVAL 5.0
|
||||||
|
#define DEFAULT_DISPLAY_HEIGHT 23
|
||||||
|
#define DEFAULT_BUFFER_SIZE 4096
|
||||||
|
|
||||||
|
#define PRINT_HEADER_PREFIX "# "
|
||||||
|
#define READPIPEFD 0
|
||||||
|
#define WRITEPIPEFD 1
|
||||||
|
#define NPIPEFD 2
|
||||||
|
|
||||||
|
#define PMCSTAT_OPEN_FOR_READ 0
|
||||||
|
#define PMCSTAT_OPEN_FOR_WRITE 1
|
||||||
|
#define PMCSTAT_DEFAULT_NW_HOST "localhost"
|
||||||
|
#define PMCSTAT_DEFAULT_NW_PORT "9000"
|
||||||
|
#define PMCSTAT_NHASH 256
|
||||||
|
#define PMCSTAT_HASH_MASK 0xFF
|
||||||
|
|
||||||
|
#define PMCSTAT_LDD_COMMAND "/usr/bin/ldd"
|
||||||
|
|
||||||
|
#define PMCSTAT_PRINT_ENTRY(A,T,...) do { \
|
||||||
|
fprintf((A)->pa_printfile, "%-8s", T); \
|
||||||
|
fprintf((A)->pa_printfile, " " __VA_ARGS__); \
|
||||||
|
fprintf((A)->pa_printfile, "\n"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
enum pmcstat_state {
|
||||||
|
PMCSTAT_FINISHED = 0,
|
||||||
|
PMCSTAT_EXITING = 1,
|
||||||
|
PMCSTAT_RUNNING = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmcstat_ev {
|
||||||
|
STAILQ_ENTRY(pmcstat_ev) ev_next;
|
||||||
|
char *ev_spec; /* event specification */
|
||||||
|
char *ev_name; /* (derived) event name */
|
||||||
|
enum pmc_mode ev_mode; /* desired mode */
|
||||||
|
int ev_count; /* associated count if in sampling mode */
|
||||||
|
int ev_cpu; /* specific cpu if requested */
|
||||||
|
int ev_flags; /* PMC_F_* */
|
||||||
|
int ev_cumulative; /* show cumulative counts */
|
||||||
|
int ev_fieldwidth; /* print width */
|
||||||
|
int ev_fieldskip; /* #leading spaces */
|
||||||
|
pmc_value_t ev_saved; /* saved value for incremental counts */
|
||||||
|
pmc_id_t ev_pmcid; /* allocated ID */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pmcstat_args {
|
||||||
|
int pa_flags; /* argument flags */
|
||||||
|
int pa_required; /* required features */
|
||||||
|
pid_t pa_pid; /* attached to pid */
|
||||||
|
FILE *pa_printfile; /* where to send printed output */
|
||||||
|
int pa_logfd; /* output log file */
|
||||||
|
char *pa_inputpath; /* path to input log */
|
||||||
|
char *pa_outputpath; /* path to output log */
|
||||||
|
void *pa_logparser; /* log file parser */
|
||||||
|
const char *pa_kernel; /* pathname of the kernel */
|
||||||
|
const char *pa_samplesdir; /* directory for profile files */
|
||||||
|
double pa_interval; /* printing interval in seconds */
|
||||||
|
int pa_argc;
|
||||||
|
char **pa_argv;
|
||||||
|
STAILQ_HEAD(, pmcstat_ev) pa_head;
|
||||||
|
} args;
|
||||||
|
|
||||||
|
/* Function prototypes */
|
||||||
|
void pmcstat_cleanup(struct pmcstat_args *_a);
|
||||||
|
int pmcstat_close_log(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_initialize_logging(struct pmcstat_args *_a);
|
||||||
|
int pmcstat_open(const char *_p, int _mode);
|
||||||
|
void pmcstat_print_counters(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_print_headers(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_print_pmcs(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_setup_process(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_show_usage(void);
|
||||||
|
void pmcstat_shutdown_logging(void);
|
||||||
|
void pmcstat_start_pmcs(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_start_process(struct pmcstat_args *_a);
|
||||||
|
void pmcstat_process_log(struct pmcstat_args *_a);
|
||||||
|
int pmcstat_print_log(struct pmcstat_args *_a);
|
||||||
|
int pmcstat_convert_log(struct pmcstat_args *_a);
|
||||||
|
|
||||||
|
#endif /* _PMCSTAT_H_ */
|
1255
usr.sbin/pmcstat/pmcstat_log.c
Normal file
1255
usr.sbin/pmcstat/pmcstat_log.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user