- 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
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=147708
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_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;
}

View File

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

View File

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

View File

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

View File

@ -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 @@ do_execve(td, args, mac_p)
*/
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 */

View File

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

View File

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

View File

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

View File

@ -10,6 +10,6 @@ LDADD= -lpmc -lm
WARNS?= 6
SRCS= pmcstat.c
SRCS= pmcstat.c pmcstat.h pmcstat_log.c
.include <bsd.prog.mk>

View File

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

View File

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