- 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:
Joseph Koshy 2005-06-30 19:01:26 +00:00
parent 27a2197ea5
commit 151392465f
13 changed files with 1662 additions and 379 deletions

View File

@ -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;
} }

View File

@ -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];
}; };

View File

@ -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);

View File

@ -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);
} }

View File

@ -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 */

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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.

View File

@ -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
View 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_ */

File diff suppressed because it is too large Load Diff