MFP4: Enhancements to pmcstat(8):
- Allow the "-t" option to take a regular expression naming command line processes to attach process PMCs to. - Update the manual page and add an example showing the use of the new functionality. - Update the (c) year on the affected source files.
This commit is contained in:
parent
d4fd0c015b
commit
0dfc773563
@ -6,7 +6,7 @@ PROG= pmcstat
|
||||
MAN= pmcstat.8
|
||||
|
||||
DPADD= ${LIBPMC} ${LIBM}
|
||||
LDADD= -lpmc -lm
|
||||
LDADD= -lkvm -lpmc -lm
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd April 22, 2007
|
||||
.Dd April 23, 2007
|
||||
.Os
|
||||
.Dt PMCSTAT 8
|
||||
.Sh NAME
|
||||
@ -50,7 +50,7 @@
|
||||
.Op Fl q
|
||||
.Op Fl r Ar fsroot
|
||||
.Op Fl s Ar event-spec
|
||||
.Op Fl t Ar pid
|
||||
.Op Fl t Ar process-spec
|
||||
.Op Fl v
|
||||
.Op Fl w Ar secs
|
||||
.Op Ar command Op Ar args
|
||||
@ -63,18 +63,21 @@ utility measures system performance using the facilities provided by
|
||||
The
|
||||
.Nm
|
||||
utility can measure both hardware events seen by the system as a
|
||||
whole, and those seen when a specified process is executing on the
|
||||
system's CPUs.
|
||||
If a specific process is being targeted (for example,
|
||||
whole, and those seen when a specified set of processes are executing
|
||||
on the system's CPUs.
|
||||
If a specific set of processes is being targeted (for example,
|
||||
if the
|
||||
.Fl t Ar pid
|
||||
.Fl t Ar process-spec
|
||||
option is specified, or if a command line is specified using
|
||||
.Ar command ) ,
|
||||
then measurement occurs till the target process exits or
|
||||
the
|
||||
then measurement occurs till
|
||||
.Ar command
|
||||
exits, or till all target processes specified by the
|
||||
.Fl t Ar process-spec
|
||||
options exit, or till the
|
||||
.Nm
|
||||
utility is interrupted by the user.
|
||||
If a specific process is not targeted for measurement, then
|
||||
If a specific set of processes is not targeted for measurement, then
|
||||
.Nm
|
||||
will perform system-wide measurements till interrupted by the
|
||||
user.
|
||||
@ -237,12 +240,13 @@ The default is
|
||||
Allocate a system mode counting PMC measuring hardware events
|
||||
specified in
|
||||
.Ar event-spec .
|
||||
.It Fl t Ar pid
|
||||
Attach all process mode PMCs allocated to the process with PID
|
||||
.Ar pid .
|
||||
The option is not allowed in conjunction with specifying a
|
||||
command using
|
||||
.Ar command .
|
||||
.It Fl t Ar process-spec
|
||||
Attach process mode PMCs to the processes named by argument
|
||||
.Ar process-spec .
|
||||
Argument
|
||||
.Ar process-spec
|
||||
may be a non-negative integer denoting a specific process id, or a
|
||||
regular expression for selecting processes based on their command names.
|
||||
.It Fl v
|
||||
Increase verbosity.
|
||||
.It Fl w Ar secs
|
||||
@ -273,6 +277,11 @@ and measure the number of data cache misses suffered
|
||||
by it and its children every 12 seconds on an AMD Athlon, use:
|
||||
.Dl "pmcstat -d -w 12 -p k7-dc-misses mozilla"
|
||||
.Pp
|
||||
To measure processor instructions retired for all processes named
|
||||
.Dq emacs
|
||||
use:
|
||||
.Dl "pmcstat -t '^emacs$' -p instructions"
|
||||
.Pp
|
||||
To count instruction tlb-misses on CPUs 0 and 2 on a Intel
|
||||
Pentium Pro/Pentium III SMP system use:
|
||||
.Dl "pmcstat -c 0,2 -s p6-itlb-miss"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2006, Joseph Koshy
|
||||
* Copyright (c) 2003-2007, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,23 +29,27 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ttycom.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <kvm.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <pmc.h>
|
||||
#include <pmclog.h>
|
||||
#include <regex.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
@ -100,19 +104,32 @@ int pmcstat_interrupt = 0;
|
||||
int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
|
||||
int pmcstat_sockpair[NSOCKPAIRFD];
|
||||
int pmcstat_kq;
|
||||
kvm_t *pmcstat_kvm;
|
||||
struct kinfo_proc *pmcstat_plist;
|
||||
|
||||
void
|
||||
pmcstat_attach_pmcs(struct pmcstat_args *a)
|
||||
{
|
||||
struct pmcstat_ev *ev;
|
||||
struct pmcstat_target *pt;
|
||||
int count;
|
||||
|
||||
/* Attach all process PMCs to the child process. */
|
||||
STAILQ_FOREACH(ev, &a->pa_head, ev_next)
|
||||
if (PMC_IS_VIRTUAL_MODE(ev->ev_mode) &&
|
||||
pmc_attach(ev->ev_pmcid, a->pa_pid) != 0)
|
||||
err(EX_OSERR, "ERROR: cannot attach pmc \"%s\" to "
|
||||
"process %d", ev->ev_name, (int) a->pa_pid);
|
||||
/* Attach all process PMCs to target processes. */
|
||||
count = 0;
|
||||
STAILQ_FOREACH(ev, &a->pa_events, ev_next) {
|
||||
if (PMC_IS_SYSTEM_MODE(ev->ev_mode))
|
||||
continue;
|
||||
SLIST_FOREACH(pt, &a->pa_targets, pt_next)
|
||||
if (pmc_attach(ev->ev_pmcid, pt->pt_pid) == 0)
|
||||
count++;
|
||||
else if (errno != ESRCH)
|
||||
err(EX_OSERR, "ERROR: cannot attach pmc "
|
||||
"\"%s\" to process %d", ev->ev_name,
|
||||
(int) pt->pt_pid);
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
errx(EX_DATAERR, "ERROR: No processes were attached to.");
|
||||
}
|
||||
|
||||
|
||||
@ -122,14 +139,14 @@ pmcstat_cleanup(struct pmcstat_args *a)
|
||||
struct pmcstat_ev *ev, *tmp;
|
||||
|
||||
/* release allocated PMCs. */
|
||||
STAILQ_FOREACH_SAFE(ev, &a->pa_head, ev_next, tmp)
|
||||
STAILQ_FOREACH_SAFE(ev, &a->pa_events, ev_next, tmp)
|
||||
if (ev->ev_pmcid != PMC_ID_INVALID) {
|
||||
if (pmc_release(ev->ev_pmcid) < 0)
|
||||
err(EX_OSERR, "ERROR: cannot release pmc "
|
||||
"0x%x \"%s\"", ev->ev_pmcid, ev->ev_name);
|
||||
free(ev->ev_name);
|
||||
free(ev->ev_spec);
|
||||
STAILQ_REMOVE(&a->pa_head, ev, pmcstat_ev, ev_next);
|
||||
STAILQ_REMOVE(&a->pa_events, ev, pmcstat_ev, ev_next);
|
||||
free(ev);
|
||||
}
|
||||
|
||||
@ -170,7 +187,7 @@ pmcstat_clone_event_descriptor(struct pmcstat_args *a, struct pmcstat_ev *ev,
|
||||
ev_clone->ev_saved = ev->ev_saved;
|
||||
ev_clone->ev_spec = strdup(ev->ev_spec);
|
||||
|
||||
STAILQ_INSERT_TAIL(&a->pa_head, ev_clone, ev_next);
|
||||
STAILQ_INSERT_TAIL(&a->pa_events, ev_clone, ev_next);
|
||||
|
||||
cpumask &= ~(1 << cpu);
|
||||
}
|
||||
@ -180,12 +197,14 @@ void
|
||||
pmcstat_create_process(struct pmcstat_args *a)
|
||||
{
|
||||
char token;
|
||||
pid_t pid;
|
||||
struct kevent kev;
|
||||
struct pmcstat_target *pt;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pmcstat_sockpair) < 0)
|
||||
err(EX_OSERR, "ERROR: cannot create socket pair");
|
||||
|
||||
switch (a->pa_pid = fork()) {
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
err(EX_OSERR, "ERROR: cannot fork");
|
||||
/*NOTREACHED*/
|
||||
@ -215,11 +234,16 @@ pmcstat_create_process(struct pmcstat_args *a)
|
||||
}
|
||||
|
||||
/* Ask to be notified via a kevent when the target process exits. */
|
||||
EV_SET(&kev, a->pa_pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
|
||||
EV_SET(&kev, pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
|
||||
NULL);
|
||||
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(EX_OSERR, "ERROR: cannot monitor child process %d",
|
||||
a->pa_pid);
|
||||
err(EX_OSERR, "ERROR: cannot monitor child process %d", pid);
|
||||
|
||||
if ((pt = malloc(sizeof(*pt))) == NULL)
|
||||
errx(EX_SOFTWARE, "ERROR: Out of memory.");
|
||||
|
||||
pt->pt_pid = pid;
|
||||
SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next);
|
||||
|
||||
/* Wait for the child to signal that its ready to go. */
|
||||
if (read(pmcstat_sockpair[PARENTSOCKET], &token, 1) < 0)
|
||||
@ -228,6 +252,66 @@ pmcstat_create_process(struct pmcstat_args *a)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
pmcstat_find_targets(struct pmcstat_args *a, const char *spec)
|
||||
{
|
||||
int n, nproc, pid, rv;
|
||||
struct pmcstat_target *pt;
|
||||
char errbuf[_POSIX2_LINE_MAX], *end;
|
||||
static struct kinfo_proc *kp;
|
||||
regex_t reg;
|
||||
regmatch_t regmatch;
|
||||
|
||||
/* First check if we've been given a process id. */
|
||||
pid = strtol(spec, &end, 0);
|
||||
if (end != spec && pid >= 0) {
|
||||
if ((pt = malloc(sizeof(*pt))) == NULL)
|
||||
goto outofmemory;
|
||||
pt->pt_pid = pid;
|
||||
SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise treat arg as a regular expression naming processes. */
|
||||
if (pmcstat_kvm == NULL) {
|
||||
if ((pmcstat_kvm = kvm_openfiles(NULL, "/dev/null", NULL, 0,
|
||||
errbuf)) == NULL)
|
||||
err(EX_OSERR, "ERROR: Cannot open kernel \"%s\"",
|
||||
errbuf);
|
||||
if ((pmcstat_plist = kvm_getprocs(pmcstat_kvm, KERN_PROC_PROC,
|
||||
0, &nproc)) == NULL)
|
||||
err(EX_OSERR, "ERROR: Cannot get process list: %s",
|
||||
kvm_geterr(pmcstat_kvm));
|
||||
}
|
||||
|
||||
if ((rv = regcomp(®, spec, REG_EXTENDED|REG_NOSUB)) != 0) {
|
||||
regerror(rv, ®, errbuf, sizeof(errbuf));
|
||||
err(EX_DATAERR, "ERROR: Failed to compile regex \"%s\": %s",
|
||||
spec, errbuf);
|
||||
}
|
||||
|
||||
for (n = 0, kp = pmcstat_plist; n < nproc; n++, kp++) {
|
||||
if ((rv = regexec(®, kp->ki_comm, 1, ®match, 0)) == 0) {
|
||||
if ((pt = malloc(sizeof(*pt))) == NULL)
|
||||
goto outofmemory;
|
||||
pt->pt_pid = kp->ki_pid;
|
||||
SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next);
|
||||
} else if (rv != REG_NOMATCH) {
|
||||
regerror(rv, ®, errbuf, sizeof(errbuf));
|
||||
errx(EX_SOFTWARE, "ERROR: Regex evalation failed: %s",
|
||||
errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
regfree(®);
|
||||
|
||||
return;
|
||||
|
||||
outofmemory:
|
||||
errx(EX_SOFTWARE, "Out of memory.");
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
||||
uint32_t
|
||||
pmcstat_get_cpumask(const char *cpuspec)
|
||||
{
|
||||
@ -251,12 +335,30 @@ pmcstat_get_cpumask(const char *cpuspec)
|
||||
return (cpumask);
|
||||
}
|
||||
|
||||
void
|
||||
pmcstat_kill_process(struct pmcstat_args *a)
|
||||
{
|
||||
struct pmcstat_target *pt;
|
||||
|
||||
assert(a->pa_flags & FLAG_HAS_COMMANDLINE);
|
||||
|
||||
/*
|
||||
* If a command line was specified, it would be the very first
|
||||
* in the list, before any other processes specified by -t.
|
||||
*/
|
||||
pt = SLIST_FIRST(&a->pa_targets);
|
||||
assert(pt != NULL);
|
||||
|
||||
if (kill(pt->pt_pid, SIGINT) != 0)
|
||||
err(EX_OSERR, "ERROR: cannot signal child process");
|
||||
}
|
||||
|
||||
void
|
||||
pmcstat_start_pmcs(struct pmcstat_args *a)
|
||||
{
|
||||
struct pmcstat_ev *ev;
|
||||
|
||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
|
||||
STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
|
||||
|
||||
assert(ev->ev_pmcid != PMC_ID_INVALID);
|
||||
|
||||
@ -278,7 +380,7 @@ pmcstat_print_headers(struct pmcstat_args *a)
|
||||
|
||||
(void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX);
|
||||
|
||||
STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
|
||||
STAILQ_FOREACH(ev, &a->pa_events, ev_next) {
|
||||
if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
|
||||
continue;
|
||||
|
||||
@ -309,7 +411,7 @@ pmcstat_print_counters(struct pmcstat_args *a)
|
||||
|
||||
extra_width = sizeof(PRINT_HEADER_PREFIX) - 1;
|
||||
|
||||
STAILQ_FOREACH(ev, &a->pa_head, ev_next) {
|
||||
STAILQ_FOREACH(ev, &a->pa_events, ev_next) {
|
||||
|
||||
/* skip sampling mode counters */
|
||||
if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
|
||||
@ -422,7 +524,6 @@ main(int argc, char **argv)
|
||||
int pipefd[2];
|
||||
int use_cumulative_counts;
|
||||
uint32_t cpumask;
|
||||
pid_t pid;
|
||||
char *end, *tmp;
|
||||
const char *errmsg;
|
||||
enum pmcstat_state runstate;
|
||||
@ -444,7 +545,6 @@ main(int argc, char **argv)
|
||||
args.pa_required = 0;
|
||||
args.pa_flags = 0;
|
||||
args.pa_verbosity = 1;
|
||||
args.pa_pid = (pid_t) -1;
|
||||
args.pa_logfd = -1;
|
||||
args.pa_fsroot = "";
|
||||
args.pa_kernel = strdup("/boot/kernel");
|
||||
@ -452,7 +552,8 @@ main(int argc, char **argv)
|
||||
args.pa_printfile = stderr;
|
||||
args.pa_interval = DEFAULT_WAIT_INTERVAL;
|
||||
args.pa_mapfilename = NULL;
|
||||
STAILQ_INIT(&args.pa_head);
|
||||
STAILQ_INIT(&args.pa_events);
|
||||
SLIST_INIT(&args.pa_targets);
|
||||
bzero(&ds_start, sizeof(ds_start));
|
||||
bzero(&ds_end, sizeof(ds_end));
|
||||
ev = NULL;
|
||||
@ -535,7 +636,7 @@ main(int argc, char **argv)
|
||||
if (option == 'P' || option == 'p') {
|
||||
args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
|
||||
args.pa_required |= (FLAG_HAS_COMMANDLINE |
|
||||
FLAG_HAS_PID);
|
||||
FLAG_HAS_TARGET);
|
||||
}
|
||||
|
||||
if (option == 'P' || option == 'S') {
|
||||
@ -581,7 +682,7 @@ main(int argc, char **argv)
|
||||
(void) strncpy(ev->ev_name, optarg, c);
|
||||
*(ev->ev_name + c) = '\0';
|
||||
|
||||
STAILQ_INSERT_TAIL(&args.pa_head, ev, ev_next);
|
||||
STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next);
|
||||
|
||||
if (option == 's' || option == 'S')
|
||||
pmcstat_clone_event_descriptor(&args, ev,
|
||||
@ -633,15 +734,11 @@ main(int argc, char **argv)
|
||||
args.pa_flags |= FLAG_READ_LOGFILE;
|
||||
break;
|
||||
|
||||
case 't': /* target pid */
|
||||
pid = strtol(optarg, &end, 0);
|
||||
if (*end != '\0' || pid <= 0)
|
||||
errx(EX_USAGE, "ERROR: Illegal pid value "
|
||||
"\"%s\".", optarg);
|
||||
case 't': /* target pid or process name */
|
||||
pmcstat_find_targets(&args, optarg);
|
||||
|
||||
args.pa_flags |= FLAG_HAS_PID;
|
||||
args.pa_flags |= FLAG_HAS_TARGET;
|
||||
args.pa_required |= FLAG_HAS_PROCESS_PMCS;
|
||||
args.pa_pid = pid;
|
||||
break;
|
||||
|
||||
case 'v': /* verbose */
|
||||
@ -690,32 +787,32 @@ main(int argc, char **argv)
|
||||
errmsg = NULL;
|
||||
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||
errmsg = "a command line specification";
|
||||
else if (args.pa_flags & FLAG_HAS_PID)
|
||||
else if (args.pa_flags & FLAG_HAS_TARGET)
|
||||
errmsg = "option -t";
|
||||
else if (!STAILQ_EMPTY(&args.pa_head))
|
||||
else if (!STAILQ_EMPTY(&args.pa_events))
|
||||
errmsg = "a PMC event specification";
|
||||
if (errmsg)
|
||||
errx(EX_USAGE, "ERROR: option -R may not be used with "
|
||||
"%s.", errmsg);
|
||||
} else if (STAILQ_EMPTY(&args.pa_head))
|
||||
} else if (STAILQ_EMPTY(&args.pa_events))
|
||||
/* 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) &&
|
||||
if ((args.pa_required & FLAG_HAS_TARGET) &&
|
||||
(args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
|
||||
errx(EX_USAGE, "ERROR: option -t requires a process mode PMC "
|
||||
"to be specified.");
|
||||
|
||||
/* check for process-mode options without a command or -t pid */
|
||||
if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
|
||||
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
|
||||
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) == 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_COMMANDLINE | FLAG_HAS_PID)) &&
|
||||
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_PID)) == 0)
|
||||
if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) &&
|
||||
(args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) == 0)
|
||||
errx(EX_USAGE, "ERROR: options -P and -p require a "
|
||||
"target process or a command line.");
|
||||
|
||||
@ -743,12 +840,6 @@ main(int argc, char **argv)
|
||||
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_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)))
|
||||
@ -894,7 +985,7 @@ main(int argc, char **argv)
|
||||
* Allocate PMCs.
|
||||
*/
|
||||
|
||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
|
||||
STAILQ_FOREACH(ev, &args.pa_events, 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 "
|
||||
@ -909,7 +1000,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* compute printout widths */
|
||||
STAILQ_FOREACH(ev, &args.pa_head, ev_next) {
|
||||
STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
|
||||
int counter_width;
|
||||
int display_width;
|
||||
int header_width;
|
||||
@ -987,8 +1078,18 @@ main(int argc, char **argv)
|
||||
err(EX_OSERR, "ERROR: Cannot retrieve driver statistics");
|
||||
|
||||
/* Attach process pmcs to the target process. */
|
||||
if (args.pa_pid != -1)
|
||||
pmcstat_attach_pmcs(&args);
|
||||
if (args.pa_flags & FLAG_HAS_TARGET) {
|
||||
if (SLIST_EMPTY(&args.pa_targets))
|
||||
errx(EX_DATAERR, "ERROR: No matching target "
|
||||
"processes.");
|
||||
else
|
||||
pmcstat_attach_pmcs(&args);
|
||||
|
||||
if (pmcstat_kvm) {
|
||||
kvm_close(pmcstat_kvm);
|
||||
pmcstat_kvm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* start the pmcs */
|
||||
pmcstat_start_pmcs(&args);
|
||||
@ -1069,9 +1170,7 @@ main(int argc, char **argv)
|
||||
} else if (kev.ident == SIGINT) {
|
||||
/* 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");
|
||||
pmcstat_kill_process(&args);
|
||||
runstate = PMCSTAT_FINISHED;
|
||||
} else if (kev.ident == SIGWINCH) {
|
||||
if (ioctl(fileno(args.pa_printfile),
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2005-2006, Joseph Koshy
|
||||
* Copyright (c) 2005-2007, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,7 +29,7 @@
|
||||
#ifndef _PMCSTAT_H_
|
||||
#define _PMCSTAT_H_
|
||||
|
||||
#define FLAG_HAS_PID 0x00000001 /* explicit pid */
|
||||
#define FLAG_HAS_TARGET 0x00000001 /* process target */
|
||||
#define FLAG_HAS_WAIT_INTERVAL 0x00000002 /* -w secs */
|
||||
#define FLAG_HAS_OUTPUT_LOGFILE 0x00000004 /* -O file or pipe */
|
||||
#define FLAG_HAS_COMMANDLINE 0x00000008 /* command */
|
||||
@ -94,11 +94,15 @@ struct pmcstat_ev {
|
||||
char *ev_spec; /* event specification */
|
||||
};
|
||||
|
||||
struct pmcstat_target {
|
||||
SLIST_ENTRY(pmcstat_target) pt_next;
|
||||
pid_t pt_pid;
|
||||
};
|
||||
|
||||
struct pmcstat_args {
|
||||
int pa_flags; /* argument flags */
|
||||
int pa_required; /* required features */
|
||||
int pa_verbosity; /* verbosity level */
|
||||
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 */
|
||||
@ -111,7 +115,8 @@ struct pmcstat_args {
|
||||
double pa_interval; /* printing interval in seconds */
|
||||
int pa_argc;
|
||||
char **pa_argv;
|
||||
STAILQ_HEAD(, pmcstat_ev) pa_head;
|
||||
STAILQ_HEAD(, pmcstat_ev) pa_events;
|
||||
SLIST_HEAD(, pmcstat_target) pa_targets;
|
||||
} args;
|
||||
|
||||
/* Function prototypes */
|
||||
@ -121,7 +126,9 @@ void pmcstat_clone_event_descriptor(struct pmcstat_args *_a,
|
||||
struct pmcstat_ev *_ev, uint32_t _cpumask);
|
||||
int pmcstat_close_log(struct pmcstat_args *_a);
|
||||
void pmcstat_create_process(struct pmcstat_args *_a);
|
||||
void pmcstat_find_targets(struct pmcstat_args *_a, const char *_arg);
|
||||
void pmcstat_initialize_logging(struct pmcstat_args *_a);
|
||||
void pmcstat_kill_process(struct pmcstat_args *_a);
|
||||
int pmcstat_open_log(const char *_p, int _mode);
|
||||
void pmcstat_print_counters(struct pmcstat_args *_a);
|
||||
void pmcstat_print_headers(struct pmcstat_args *_a);
|
||||
|
Loading…
Reference in New Issue
Block a user