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_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_usermode);
|
||||
break;
|
||||
case PMCLOG_TYPE_PMCALLOCATE:
|
||||
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:
|
||||
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
|
||||
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);
|
||||
break;
|
||||
case PMCLOG_TYPE_PROCEXIT:
|
||||
@ -436,8 +439,10 @@ pmclog_read(void *cookie, struct pmclog_ev *ev)
|
||||
PMCLOG_BUFFER_SIZE);
|
||||
|
||||
if (nread <= 0) {
|
||||
ev->pl_state = nread < 0 ? PMCLOG_ERROR :
|
||||
PMCLOG_EOF;
|
||||
if (nread == 0)
|
||||
ev->pl_state = PMCLOG_EOF;
|
||||
else if (errno != EAGAIN) /* not restartable */
|
||||
ev->pl_state = PMCLOG_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ struct pmclog_ev_pcsample {
|
||||
uintfptr_t pl_pc;
|
||||
pid_t pl_pid;
|
||||
pmc_id_t pl_pmcid;
|
||||
uint32_t pl_usermode;
|
||||
};
|
||||
|
||||
struct pmclog_ev_pmcallocate {
|
||||
@ -89,6 +90,8 @@ struct pmclog_ev_proccsw {
|
||||
|
||||
struct pmclog_ev_procexec {
|
||||
pid_t pl_pid;
|
||||
pmc_id_t pl_pmcid;
|
||||
uintfptr_t pl_entryaddr;
|
||||
char pl_pathname[PATH_MAX];
|
||||
};
|
||||
|
||||
|
@ -138,14 +138,16 @@ CTASSERT(sizeof(struct pmclog_mappingchange) == PATH_MAX +
|
||||
5*4 + 2*sizeof(uintfptr_t));
|
||||
CTASSERT(offsetof(struct pmclog_mappingchange,pl_pathname) ==
|
||||
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_pmcattach) == 5*4 + PATH_MAX);
|
||||
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4);
|
||||
CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4);
|
||||
CTASSERT(sizeof(struct pmclog_proccsw) == 5*4 + 8);
|
||||
CTASSERT(sizeof(struct pmclog_procexec) == 4*4 + PATH_MAX);
|
||||
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 4*4);
|
||||
CTASSERT(sizeof(struct pmclog_procexec) == 5*4 + PATH_MAX +
|
||||
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_procfork) == 5*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_EMITADDR(ps->ps_pc);
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(ps->ps_usermode);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
|
||||
@ -834,7 +837,8 @@ pmclog_process_proccsw(struct pmc *pm, struct pmc_process *pp, pmc_value_t v)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -845,6 +849,8 @@ pmclog_process_procexec(struct pmc_owner *po, pid_t pid, char *path)
|
||||
|
||||
PMCLOG_RESERVE(po, PROCEXEC, recordlen);
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMITADDR(startaddr);
|
||||
PMCLOG_EMIT32(pmid);
|
||||
PMCLOG_EMITSTRING(path,pathlen);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
@ -859,9 +865,6 @@ pmclog_process_procexit(struct pmc *pm, struct pmc_process *pp)
|
||||
int ri;
|
||||
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);
|
||||
PMCDBG(LOG,EXT,1,"pm=%p pid=%d v=%jx", pm, pp->pp_proc->p_pid,
|
||||
pp->pp_pmcs[ri].pp_pmcval);
|
||||
|
@ -661,16 +661,16 @@ pmc_force_context_switch(void)
|
||||
*/
|
||||
|
||||
static void
|
||||
pmc_getprocname(struct proc *p, char **fullpath, char **freepath)
|
||||
pmc_getfilename(struct vnode *v, char **fullpath, char **freepath)
|
||||
{
|
||||
struct thread *td;
|
||||
|
||||
td = curthread;
|
||||
*fullpath = "unknown";
|
||||
*freepath = NULL;
|
||||
vn_lock(p->p_textvp, LK_EXCLUSIVE | LK_RETRY, td);
|
||||
vn_fullpath(td, p->p_textvp, fullpath, freepath);
|
||||
VOP_UNLOCK(p->p_textvp, 0, td);
|
||||
vn_lock(v, LK_EXCLUSIVE | LK_RETRY, td);
|
||||
vn_fullpath(td, v, fullpath, freepath);
|
||||
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 */
|
||||
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);
|
||||
if (freepath)
|
||||
FREE(freepath, M_TEMP);
|
||||
@ -1442,7 +1442,6 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
|
||||
|
||||
case PMC_FN_PROCESS_EXEC:
|
||||
{
|
||||
int *credentials_changed;
|
||||
char *fullpath, *freepath;
|
||||
unsigned int ri;
|
||||
int is_using_hwpmcs;
|
||||
@ -1450,16 +1449,20 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
|
||||
struct proc *p;
|
||||
struct pmc_owner *po;
|
||||
struct pmc_process *pp;
|
||||
struct pmckern_procexec *pk;
|
||||
|
||||
sx_assert(&pmc_sx, SX_XLOCKED);
|
||||
|
||||
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. */
|
||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
|
||||
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);
|
||||
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;
|
||||
if (po->po_sscount == 0 &&
|
||||
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);
|
||||
}
|
||||
|
||||
if (freepath)
|
||||
FREE(freepath, M_TEMP);
|
||||
|
||||
credentials_changed = arg;
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
@ -3457,7 +3460,7 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
*/
|
||||
|
||||
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;
|
||||
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_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",
|
||||
cpu, pm, (int64_t) pc, usermode,
|
||||
cpu, pm, (uint64_t) pc, usermode,
|
||||
(int) (psb->ps_write - psb->ps_samples),
|
||||
(int) (psb->ps_read - psb->ps_samples));
|
||||
error = ENOMEM;
|
||||
@ -3483,7 +3486,7 @@ pmc_process_interrupt(int cpu, struct pmc *pm, intfptr_t pc, int usermode)
|
||||
|
||||
/* fill in entry */
|
||||
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_read - psb->ps_samples));
|
||||
|
||||
@ -3549,7 +3552,7 @@ pmc_process_samples(int cpu)
|
||||
goto entrydone;
|
||||
|
||||
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_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++)
|
||||
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);
|
||||
pmc_unlink_target_process(pm, pp);
|
||||
}
|
||||
|
@ -301,6 +301,9 @@ do_execve(td, args, mac_p)
|
||||
struct label *interplabel = NULL;
|
||||
int will_transition;
|
||||
#endif
|
||||
#ifdef HWPMC_HOOKS
|
||||
struct pmckern_procexec pe;
|
||||
#endif
|
||||
|
||||
vfslocked = 0;
|
||||
imgp = &image_params;
|
||||
@ -681,8 +684,10 @@ interpret:
|
||||
*/
|
||||
if (PMC_SYSTEM_SAMPLING_ACTIVE() || PMC_PROC_IS_USING_PMCS(p)) {
|
||||
PROC_UNLOCK(p);
|
||||
PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC,
|
||||
(void *) &credential_changing);
|
||||
pe.pm_credentialschanged = credential_changing;
|
||||
pe.pm_entryaddr = imgp->entry_addr;
|
||||
|
||||
PMC_CALL_HOOK_X(td, PMC_FN_PROCESS_EXEC, (void *) &pe);
|
||||
} else
|
||||
PROC_UNLOCK(p);
|
||||
#else /* !HWPMC_HOOKS */
|
||||
|
@ -995,7 +995,7 @@ MALLOC_DECLARE(M_PMC);
|
||||
|
||||
struct pmc_mdep *pmc_md_initialize(void); /* MD init function */
|
||||
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);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
@ -44,6 +44,11 @@
|
||||
#define PMC_FN_CSW_OUT 3
|
||||
#define PMC_FN_DO_SAMPLES 4
|
||||
|
||||
struct pmckern_procexec {
|
||||
int pm_credentialschanged;
|
||||
uintptr_t pm_entryaddr;
|
||||
};
|
||||
|
||||
/* hook */
|
||||
extern int (*pmc_hook)(struct thread *_td, int _function, void *_arg);
|
||||
extern int (*pmc_intr)(int _cpu, uintptr_t _pc, int _usermode);
|
||||
|
@ -113,6 +113,7 @@ struct pmclog_pcsample {
|
||||
uint32_t pl_pid;
|
||||
uintfptr_t pl_pc; /* 8 byte aligned */
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_usermode;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_pmcallocate {
|
||||
@ -145,6 +146,8 @@ struct pmclog_proccsw {
|
||||
struct pmclog_procexec {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uintfptr_t pl_start; /* keep 8 byte aligned */
|
||||
uint32_t pl_pmcid;
|
||||
char pl_pathname[PATH_MAX];
|
||||
} __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_proccsw(struct pmc *_pm, struct pmc_process *_pp,
|
||||
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_procfork(struct pmc_owner *_po, pid_t _oldpid, pid_t _newpid);
|
||||
void pmclog_process_sysexit(struct pmc_owner *_po, pid_t _pid);
|
||||
|
@ -10,6 +10,6 @@ LDADD= -lpmc -lm
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
SRCS= pmcstat.c
|
||||
SRCS= pmcstat.c pmcstat.h pmcstat_log.c
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -31,8 +31,8 @@
|
||||
.Nd "performance measurement with performance monitoring hardware"
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl D Ar pathname
|
||||
.Op Fl C
|
||||
.Op Fl D Ar pathname
|
||||
.Op Fl E
|
||||
.Op Fl O Ar logfilename
|
||||
.Op Fl P Ar event-spec
|
||||
@ -42,6 +42,7 @@
|
||||
.Op Fl c Ar cpu
|
||||
.Op Fl d
|
||||
.Op Fl g
|
||||
.Op Fl k Ar kernelfile
|
||||
.Op Fl n Ar rate
|
||||
.Op Fl o Ar outputfile
|
||||
.Op Fl p Ar event-spec
|
||||
@ -154,6 +155,11 @@ The default is to measure events for the target process alone.
|
||||
.It Fl g
|
||||
Produce execution profiles in a format compatible with
|
||||
.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
|
||||
Set the default sampling rate for subsequent sampling mode
|
||||
PMCs specified on the command line.
|
||||
|
@ -30,6 +30,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ttycom.h>
|
||||
#include <sys/wait.h>
|
||||
@ -44,13 +46,15 @@ __FBSDID("$FreeBSD$");
|
||||
#include <pmclog.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pmcstat.h"
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -68,90 +72,13 @@ __FBSDID("$FreeBSD$");
|
||||
* for a given executable into a single profile file.
|
||||
*/
|
||||
|
||||
/* Operation modes */
|
||||
|
||||
#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;
|
||||
/* Globals */
|
||||
|
||||
int pmcstat_interrupt = 0;
|
||||
int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
|
||||
int pmcstat_pipefd[NPIPEFD];
|
||||
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
|
||||
*/
|
||||
@ -162,7 +89,7 @@ pmcstat_cleanup(struct pmcstat_args *a)
|
||||
struct pmcstat_ev *ev, *tmp;
|
||||
|
||||
/* 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);
|
||||
|
||||
/* release allocated PMCs. */
|
||||
@ -181,6 +108,9 @@ pmcstat_cleanup(struct pmcstat_args *a)
|
||||
pmclog_close(a->pa_logparser);
|
||||
a->pa_logparser = NULL;
|
||||
}
|
||||
|
||||
if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
|
||||
pmcstat_shutdown_logging();
|
||||
}
|
||||
|
||||
void
|
||||
@ -208,7 +138,7 @@ pmcstat_print_headers(struct pmcstat_args *a)
|
||||
struct pmcstat_ev *ev;
|
||||
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) {
|
||||
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';
|
||||
|
||||
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_fieldwidth - ev->ev_fieldskip - 2,
|
||||
ev->ev_name);
|
||||
} else
|
||||
(void) fprintf(a->pa_outputfile, "%c/%*s ",
|
||||
(void) fprintf(a->pa_printfile, "%c/%*s ",
|
||||
c, ev->ev_fieldwidth - 2, ev->ev_name);
|
||||
}
|
||||
|
||||
(void) fflush(a->pa_outputfile);
|
||||
(void) fflush(a->pa_printfile);
|
||||
}
|
||||
|
||||
void
|
||||
@ -248,15 +178,17 @@ pmcstat_print_counters(struct pmcstat_args *a)
|
||||
err(EX_OSERR, "ERROR: Cannot read pmc "
|
||||
"\"%s\"", ev->ev_name);
|
||||
|
||||
(void) fprintf(a->pa_outputfile, "%*ju ",
|
||||
ev->ev_fieldwidth + extra_width, (uintmax_t)
|
||||
ev->ev_cumulative ? value : (value - ev->ev_saved));
|
||||
(void) fprintf(a->pa_printfile, "%*ju ",
|
||||
ev->ev_fieldwidth + extra_width,
|
||||
(uintmax_t) ev->ev_cumulative ? value :
|
||||
(value - ev->ev_saved));
|
||||
|
||||
if (ev->ev_cumulative == 0)
|
||||
ev->ev_saved = value;
|
||||
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;
|
||||
|
||||
/* check if we need to print a header line */
|
||||
if (++linecount > pmcstat_displayheight) {
|
||||
(void) fprintf(a->pa_outputfile, "\n");
|
||||
(void) fprintf(a->pa_printfile, "\n");
|
||||
linecount = 1;
|
||||
}
|
||||
|
||||
if (linecount == 1)
|
||||
pmcstat_print_headers(a);
|
||||
(void) fprintf(a->pa_printfile, "\n");
|
||||
|
||||
(void) fprintf(a->pa_outputfile, "\n");
|
||||
pmcstat_print_counters(a);
|
||||
|
||||
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
|
||||
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 -d\t\t (toggle) track descendants\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 -o file\t send print output to \"file\"\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 do_print, do_descendants;
|
||||
int do_logproccsw, do_logprocexit;
|
||||
int logfd;
|
||||
int pipefd[2];
|
||||
int use_cumulative_counts;
|
||||
pid_t pid;
|
||||
@ -567,6 +357,7 @@ main(int argc, char **argv)
|
||||
struct sigaction sa;
|
||||
struct kevent kev;
|
||||
struct winsize ws;
|
||||
struct stat sb;
|
||||
|
||||
current_cpu = 0;
|
||||
current_sampling_count = DEFAULT_SAMPLE_COUNT;
|
||||
@ -577,15 +368,16 @@ main(int argc, char **argv)
|
||||
args.pa_required = 0;
|
||||
args.pa_flags = 0;
|
||||
args.pa_pid = (pid_t) -1;
|
||||
args.pa_logfile = NULL;
|
||||
args.pa_outputdir = NULL;
|
||||
args.pa_outputfile = stderr;
|
||||
args.pa_logfd = -1;
|
||||
args.pa_samplesdir = ".";
|
||||
args.pa_kernel = "/boot/kernel/kernel";
|
||||
args.pa_printfile = stderr;
|
||||
args.pa_interval = DEFAULT_WAIT_INTERVAL;
|
||||
STAILQ_INIT(&args.pa_head);
|
||||
|
||||
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)
|
||||
switch (option) {
|
||||
case 'C': /* cumulative values */
|
||||
@ -602,29 +394,37 @@ main(int argc, char **argv)
|
||||
args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
|
||||
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 */
|
||||
do_descendants = !do_descendants;
|
||||
args.pa_required |= FLAG_HAS_PROCESS_PMCS;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
args.pa_outputdir = optarg;
|
||||
break;
|
||||
|
||||
case 'g': /* produce gprof compatible profiles */
|
||||
args.pa_flags |= FLAG_DO_GPROF;
|
||||
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
|
||||
break;
|
||||
|
||||
case 'm': /* produce merged profiles */
|
||||
args.pa_flags |= FLAG_DO_GPROF_MERGED;
|
||||
args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
|
||||
case 'k': /* pathname to the kernel */
|
||||
args.pa_kernel = optarg;
|
||||
args.pa_required |= FLAG_DO_GPROF;
|
||||
args.pa_flags |= FLAG_HAS_KERNELPATH;
|
||||
break;
|
||||
|
||||
case 'E': /* log process exit */
|
||||
do_logprocexit = !do_logprocexit;
|
||||
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
|
||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE);
|
||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
|
||||
break;
|
||||
|
||||
case 'p': /* process virtual counting PMC */
|
||||
@ -643,13 +443,14 @@ main(int argc, char **argv)
|
||||
|
||||
if (option == 'P' || option == 'p') {
|
||||
args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
|
||||
args.pa_required |= (FLAG_HAS_PROCESS |
|
||||
args.pa_required |= (FLAG_HAS_COMMANDLINE |
|
||||
FLAG_HAS_PID);
|
||||
}
|
||||
|
||||
if (option == 'P' || option == 'S') {
|
||||
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')
|
||||
@ -693,16 +494,6 @@ main(int argc, char **argv)
|
||||
|
||||
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 */
|
||||
current_sampling_count = strtol(optarg, &end, 0);
|
||||
if (*end != '\0' || current_sampling_count <= 0)
|
||||
@ -713,22 +504,30 @@ main(int argc, char **argv)
|
||||
break;
|
||||
|
||||
case 'o': /* outputfile */
|
||||
if (args.pa_outputfile != NULL)
|
||||
(void) fclose(args.pa_outputfile);
|
||||
if ((args.pa_outputfile = fopen(optarg, "w")) == NULL)
|
||||
if (args.pa_printfile != NULL)
|
||||
(void) fclose(args.pa_printfile);
|
||||
if ((args.pa_printfile = fopen(optarg, "w")) == NULL)
|
||||
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
|
||||
"writing.", optarg);
|
||||
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
|
||||
args.pa_flags |= FLAG_DO_PRINT;
|
||||
break;
|
||||
|
||||
case 'O': /* sampling output */
|
||||
if (args.pa_logfile != NULL)
|
||||
errx(EX_OSERR, "ERROR: option -O may only be "
|
||||
if (args.pa_outputpath)
|
||||
errx(EX_USAGE, "ERROR: option -O may only be "
|
||||
"specified once.");
|
||||
if ((args.pa_logfile = fopen(optarg, "w")) == NULL)
|
||||
errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
|
||||
"writing.", optarg);
|
||||
args.pa_flags |= FLAG_HAS_LOG_FILE;
|
||||
args.pa_outputpath = optarg;
|
||||
args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE;
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
case 't': /* target pid */
|
||||
@ -748,13 +547,14 @@ main(int argc, char **argv)
|
||||
errx(EX_USAGE, "ERROR: Illegal wait interval "
|
||||
"value \"%s\".", optarg);
|
||||
args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
|
||||
args.pa_required |= FLAG_HAS_COUNTING_PMCS;
|
||||
args.pa_interval = interval;
|
||||
break;
|
||||
|
||||
case 'W': /* toggle LOG_CSW */
|
||||
do_logproccsw = !do_logproccsw;
|
||||
args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
|
||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_LOG_FILE);
|
||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@ -767,16 +567,21 @@ main(int argc, char **argv)
|
||||
args.pa_argc = (argc -= optind);
|
||||
args.pa_argv = (argv += optind);
|
||||
|
||||
if (argc)
|
||||
args.pa_flags |= FLAG_HAS_PROCESS;
|
||||
if (argc) /* command line present */
|
||||
args.pa_flags |= FLAG_HAS_COMMANDLINE;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
if (args.pa_flags & FLAG_HAS_PROCESS)
|
||||
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||
errmsg = "a command line specification";
|
||||
else if (args.pa_flags & FLAG_HAS_PID)
|
||||
errmsg = "option -t";
|
||||
@ -785,10 +590,9 @@ main(int argc, char **argv)
|
||||
if (errmsg)
|
||||
errx(EX_USAGE, "ERROR: option -R may not be used with "
|
||||
"%s.", errmsg);
|
||||
} else if (STAILQ_EMPTY(&args.pa_head)) {
|
||||
warnx("ERROR: At least one PMC event must be specified");
|
||||
} else if (STAILQ_EMPTY(&args.pa_head))
|
||||
/* All other uses require a PMC spec. */
|
||||
pmcstat_show_usage();
|
||||
}
|
||||
|
||||
/* check for -t pid without a process PMC spec */
|
||||
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 */
|
||||
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
||||
(args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0)
|
||||
errx(EX_USAGE, "ERROR: options -d,-E,-p,-P,-W require a "
|
||||
"command line or target process.");
|
||||
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
|
||||
errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require "
|
||||
"a command line or target process.");
|
||||
|
||||
/* check for -p | -P without a target process of some sort */
|
||||
if ((args.pa_required & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) &&
|
||||
(args.pa_flags & (FLAG_HAS_PROCESS | FLAG_HAS_PID)) == 0)
|
||||
errx(EX_USAGE, "ERROR: the -P or -p options require a "
|
||||
if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) &&
|
||||
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
|
||||
errx(EX_USAGE, "ERROR: options -P and -p require a "
|
||||
"target process or a command line.");
|
||||
|
||||
/* check for process-mode options without a process-mode PMC */
|
||||
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
||||
(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.");
|
||||
|
||||
/* 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 */
|
||||
if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) &&
|
||||
(args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0)
|
||||
errx(EX_USAGE, "ERROR: options -C,-o,-W require at least one "
|
||||
"counting mode PMC to be specified.");
|
||||
errx(EX_USAGE, "ERROR: options -C, -o and -W require at least "
|
||||
"one counting mode PMC to be specified.");
|
||||
|
||||
/* check for sampling mode options without a sampling PMC spec */
|
||||
if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) &&
|
||||
(args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0)
|
||||
errx(EX_USAGE, "ERROR: options -n,-O require at least one "
|
||||
"sampling mode PMC to be specified.");
|
||||
errx(EX_USAGE, "ERROR: options -n and -O require at least "
|
||||
"one sampling mode PMC to be specified.");
|
||||
|
||||
if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_PROCESS)) ==
|
||||
(FLAG_HAS_PID | FLAG_HAS_PROCESS))
|
||||
if ((args.pa_flags & (FLAG_HAS_PID | FLAG_HAS_COMMANDLINE)) ==
|
||||
(FLAG_HAS_PID | FLAG_HAS_COMMANDLINE))
|
||||
errx(EX_USAGE,
|
||||
"ERROR: option -t cannot be specified with a command "
|
||||
"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 */
|
||||
if ((args.pa_flags & FLAG_HAS_LOG_FILE) &&
|
||||
(args.pa_required & FLAG_HAS_LOG_FILE) == 0)
|
||||
if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) &&
|
||||
(args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)
|
||||
errx(EX_USAGE,
|
||||
"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 (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);
|
||||
exit(EX_OK);
|
||||
}
|
||||
@ -864,11 +708,52 @@ main(int argc, char **argv)
|
||||
err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
|
||||
"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.
|
||||
*/
|
||||
|
||||
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,
|
||||
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0)
|
||||
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",
|
||||
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 */
|
||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
|
||||
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
|
||||
* 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");
|
||||
|
||||
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)
|
||||
err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD");
|
||||
|
||||
/*
|
||||
* Configure the specified log file or setup a default log
|
||||
* consumer via a pipe.
|
||||
*/
|
||||
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) {
|
||||
/* setup a timer if we have counting mode PMCs needing to be printed */
|
||||
if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
|
||||
(args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
|
||||
EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
|
||||
args.pa_interval * 1000, NULL);
|
||||
|
||||
@ -981,16 +836,21 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* start the pmcs */
|
||||
pmcstat_start_pmcs(&args);
|
||||
|
||||
/* start the (commandline) process if needed */
|
||||
if (args.pa_flags & FLAG_HAS_PROCESS)
|
||||
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||
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 */
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sa.sa_flags = 0;
|
||||
@ -1018,7 +878,8 @@ main(int argc, char **argv)
|
||||
|
||||
switch (kev.filter) {
|
||||
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);
|
||||
break;
|
||||
|
||||
@ -1042,20 +903,24 @@ main(int argc, char **argv)
|
||||
* of its targets, or if logfile
|
||||
* 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);
|
||||
if (args.pa_flags &
|
||||
(FLAG_DO_PRINT|FLAG_DO_GPROF))
|
||||
pmcstat_process_log(&args);
|
||||
}
|
||||
do_print = 1; /* print PMCs at exit */
|
||||
runstate = PMCSTAT_FINISHED;
|
||||
} else if (kev.ident == SIGINT) {
|
||||
/* pass the signal on to the child process */
|
||||
if ((args.pa_flags & FLAG_HAS_PROCESS) &&
|
||||
(args.pa_flags & FLAG_HAS_PID) == 0)
|
||||
/* Kill the child process if we started it */
|
||||
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||
if (kill(args.pa_pid, SIGINT) != 0)
|
||||
err(EX_OSERR, "ERROR: cannot "
|
||||
"signal child process");
|
||||
runstate = PMCSTAT_FINISHED;
|
||||
} else if (kev.ident == SIGWINCH) {
|
||||
if (ioctl(fileno(args.pa_outputfile),
|
||||
if (ioctl(fileno(args.pa_printfile),
|
||||
TIOCGWINSZ, &ws) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot determine "
|
||||
"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);
|
||||
if (runstate == PMCSTAT_FINISHED) /* final newline */
|
||||
(void) fprintf(args.pa_outputfile, "\n");
|
||||
if (runstate == PMCSTAT_FINISHED && /* final newline */
|
||||
(args.pa_flags & FLAG_DO_PRINT) == 0)
|
||||
(void) fprintf(args.pa_printfile, "\n");
|
||||
do_print = 0;
|
||||
}
|
||||
|
||||
} while (runstate != PMCSTAT_FINISHED);
|
||||
|
||||
/* 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();
|
||||
|
||||
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