Module Name:    src
Committed By:   maxv
Date:           Fri Mar 10 13:09:11 UTC 2017

Modified Files:
        src/sys/arch/i386/i386: pmc.c
        src/sys/arch/x86/include: sysarch.h
        src/usr.bin/pmc: pmc.1 pmc.c

Log Message:
Switch to per-CPU PMC results, and completely rewrite the pmc(1) tool. Now
the PMCs are system-wide, fine-grained and more tunable by the user.

We don't do application tracking, since it would require to store the PMC
values in mdproc and starting/stopping the counters on each context switch.
While this doesn't seem to be particularly difficult to achieve, I don't
think it is really interesting; and if someone really wants to measure
the performance of an application, they can simply schedctl it to a cpu
and look at the PMC results for this cpu.

Note that several options are implemented but not yet used.


To generate a diff of this commit:
cvs rdiff -u -r1.24 -r1.25 src/sys/arch/i386/i386/pmc.c
cvs rdiff -u -r1.10 -r1.11 src/sys/arch/x86/include/sysarch.h
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/pmc/pmc.1
cvs rdiff -u -r1.19 -r1.20 src/usr.bin/pmc/pmc.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/i386/i386/pmc.c
diff -u src/sys/arch/i386/i386/pmc.c:1.24 src/sys/arch/i386/i386/pmc.c:1.25
--- src/sys/arch/i386/i386/pmc.c:1.24	Wed Mar  8 16:42:27 2017
+++ src/sys/arch/i386/i386/pmc.c	Fri Mar 10 13:09:11 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: pmc.c,v 1.24 2017/03/08 16:42:27 maxv Exp $	*/
+/*	$NetBSD: pmc.c,v 1.25 2017/03/10 13:09:11 maxv Exp $	*/
 
 /*
  * Copyright (c) 2017 The NetBSD Foundation, Inc.
@@ -67,7 +67,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmc.c,v 1.24 2017/03/08 16:42:27 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmc.c,v 1.25 2017/03/10 13:09:11 maxv Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -88,17 +88,15 @@ typedef struct {
 	uint32_t evtmsr;	/* event selector MSR */
 	uint64_t evtval;	/* event selector value */
 	uint32_t ctrmsr;	/* counter MSR */
-	uint64_t ctrval;	/* counter value */
-	uint64_t tsc;
+	uint64_t ctrval;	/* initial counter value */
 } pmc_state_t;
 
-static uint64_t pmc_val_cpus[MAXCPUS] __aligned(CACHE_LINE_SIZE);
+static x86_pmc_cpuval_t pmc_val_cpus[MAXCPUS] __aligned(CACHE_LINE_SIZE);
 static kmutex_t pmc_lock;
 
 static pmc_state_t pmc_state[PMC_NCOUNTERS];
 static int pmc_ncounters __read_mostly;
 static int pmc_type __read_mostly;
-static int pmc_flags __read_mostly;
 
 static void
 pmc_read_cpu(void *arg1, void *arg2)
@@ -106,29 +104,25 @@ pmc_read_cpu(void *arg1, void *arg2)
 	pmc_state_t *pmc = (pmc_state_t *)arg1;
 	struct cpu_info *ci = curcpu();
 
-	pmc_val_cpus[cpu_index(ci)] = rdmsr(pmc->ctrmsr) & 0xffffffffffULL;
+	pmc_val_cpus[cpu_index(ci)].ctrval =
+	    rdmsr(pmc->ctrmsr) & 0xffffffffffULL;
+	pmc_val_cpus[cpu_index(ci)].overfl = 0;
 }
 
 static void
 pmc_read(pmc_state_t *pmc)
 {
 	uint64_t xc;
-	size_t i;
 
 	xc = xc_broadcast(0, pmc_read_cpu, pmc, NULL);
 	xc_wait(xc);
-
-	pmc->ctrval = 0;
-	for (i = 0; i < ncpu; i++) {
-		/* XXX: really shitty */
-		pmc->ctrval += pmc_val_cpus[i];
-	}
 }
 
 static void
 pmc_apply_cpu(void *arg1, void *arg2)
 {
 	pmc_state_t *pmc = (pmc_state_t *)arg1;
+	struct cpu_info *ci = curcpu();
 
 	wrmsr(pmc->ctrmsr, pmc->ctrval);
 	switch (pmc_type) {
@@ -142,6 +136,9 @@ pmc_apply_cpu(void *arg1, void *arg2)
 		wrmsr(pmc->evtmsr, pmc->evtval);
 		break;
 	}
+
+	pmc_val_cpus[cpu_index(ci)].ctrval = 0;
+	pmc_val_cpus[cpu_index(ci)].overfl = 0;
 }
 
 static void
@@ -210,6 +207,7 @@ pmc_stop(pmc_state_t *pmc, struct x86_pm
 {
 	pmc->running = false;
 	pmc->evtval = 0;
+	pmc->ctrval = 0;
 	pmc_apply(pmc);
 }
 
@@ -262,9 +260,6 @@ pmc_init(void)
 		break;
 	}
 
-	if (pmc_type != PMC_TYPE_NONE && cpu_hascounter())
-		pmc_flags |= PMC_INFO_HASTSC;
-
 	mutex_init(&pmc_lock, MUTEX_DEFAULT, IPL_NONE);
 }
 
@@ -277,7 +272,7 @@ sys_pmc_info(struct lwp *l, struct x86_p
 
 	rv.vers = PMC_VERSION;
 	rv.type = pmc_type;
-	rv.flags = pmc_flags;
+	rv.nctrs = pmc_ncounters;
 
 	return copyout(&rv, uargs, sizeof(rv));
 }
@@ -330,6 +325,7 @@ sys_pmc_read(struct lwp *l, struct x86_p
 {
 	struct x86_pmc_read_args args;
 	pmc_state_t *pmc;
+	size_t nval;
 	int error;
 
 	if (pmc_type == PMC_TYPE_NONE)
@@ -341,20 +337,27 @@ sys_pmc_read(struct lwp *l, struct x86_p
 
 	if (args.counter >= pmc_ncounters)
 		return EINVAL;
+	if (args.values == NULL)
+		return EINVAL;
+	nval = MIN(ncpu, args.nval);
+
 	pmc = &pmc_state[args.counter];
 
 	mutex_enter(&pmc_lock);
 
 	if (pmc->running) {
 		pmc_read(pmc);
-		if (pmc_flags & PMC_INFO_HASTSC)
-			pmc->tsc = cpu_counter();
+		error = copyout(&pmc_val_cpus, args.values,
+		    nval * sizeof(x86_pmc_cpuval_t));
+		args.nval = nval;
+	} else {
+		error = ENOENT;
 	}
 
-	args.val = pmc->ctrval;
-	args.time = pmc->tsc;
-
 	mutex_exit(&pmc_lock);
 
+	if (error)
+		return error;
+
 	return copyout(&args, uargs, sizeof(args));
 }

Index: src/sys/arch/x86/include/sysarch.h
diff -u src/sys/arch/x86/include/sysarch.h:1.10 src/sys/arch/x86/include/sysarch.h:1.11
--- src/sys/arch/x86/include/sysarch.h:1.10	Wed Mar  8 16:42:27 2017
+++ src/sys/arch/x86/include/sysarch.h	Fri Mar 10 13:09:11 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: sysarch.h,v 1.10 2017/03/08 16:42:27 maxv Exp $	*/
+/*	$NetBSD: sysarch.h,v 1.11 2017/03/10 13:09:11 maxv Exp $	*/
 
 /*-
  * Copyright (c) 2007 The NetBSD Foundation, Inc.
@@ -133,7 +133,7 @@ struct _X86_SYSARCH_L(set_ioperm_args) {
 struct _X86_SYSARCH_L(pmc_info_args) {
 	int vers;
 	int type;
-	int flags;
+	uint32_t nctrs;
 };
 
 #define	PMC_VERSION		1
@@ -162,10 +162,15 @@ struct _X86_SYSARCH_L(pmc_startstop_args
 #define	PMC_SETUP_EDGE		0x04
 #define	PMC_SETUP_INV		0x08
 
+typedef struct {
+	uint64_t ctrval;
+	uint32_t overfl;
+} x86_pmc_cpuval_t;
+
 struct _X86_SYSARCH_L(pmc_read_args) {
-	int counter;
-	uint64_t val;
-	uint64_t time;
+	uint32_t counter;
+	x86_pmc_cpuval_t *values;
+	uint32_t nval;
 };
 
 struct mtrr;

Index: src/usr.bin/pmc/pmc.1
diff -u src/usr.bin/pmc/pmc.1:1.9 src/usr.bin/pmc/pmc.1:1.10
--- src/usr.bin/pmc/pmc.1:1.9	Tue Mar 18 18:20:45 2014
+++ src/usr.bin/pmc/pmc.1	Fri Mar 10 13:09:11 2017
@@ -1,4 +1,31 @@
-.\"	$NetBSD: pmc.1,v 1.9 2014/03/18 18:20:45 riastradh Exp $
+.\"	$NetBSD: pmc.1,v 1.10 2017/03/10 13:09:11 maxv Exp $
+.\"
+.\" Copyright (c) 2017 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Maxime Villard.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 .\"
 .\" Copyright (c) 2000 Zembu Labs, Inc.
 .\" All rights reserved.
@@ -31,77 +58,87 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd October 24, 2000
+.Dd March 10, 2017
 .Dt PMC 1
 .Os
 .Sh NAME
 .Nm pmc
-.Nd performance-monitoring counter interface for command execution
+.Nd performance-monitoring counter interface
 .Sh SYNOPSIS
-.Nm
-.Fl h
-.Nm
-.Fl C
-.Nm
-.Fl c
-.Ar event
+.Nm pmc
 .Ar command
-.Oo
-.Ar options
-.Ar ...
-.Oc
+.Op Ar arguments
 .Sh DESCRIPTION
+The
 .Nm
-is a means of using a processor's performance-monitoring counter (PMC)
-facility to measure various aspects of a program's execution.
-It is
-meant to be used in a fashion similar to
-.Xr time 1 .
+command can be used to control and inspect the state of CPUs in the system.
 .Pp
-The arguments are as follows:
-.Bl -tag -width indent
-.It Fl h
-Display a list of performance counter events available on
-the system.
-.It Fl C
-Cancel any performance counters that are currently running.
-.It Fl c Ar event
-Count the event specified by
-.Ar event
-while running the command.
+The first argument,
+.Ar command ,
+specifies the action to take.
+Valid commands are:
+.Bl -tag -width offline
+.It list
+Display a list of performance counter events available on the system.
+.It start Ar name:option Op Ar name:option ...
+Start one or several counters.
+.Ar name
+specifies the name of the event to count; it must be taken from the list of
+available events.
+.Ar option
+specifies the source of the event; it must be a combination of
+.Ar u
+(userland) and
+.Ar k
+(kernel).
+.It stop
+Stop any performance counters that are currently running, and display the
+values of these counters.
 .El
+.Sh EXAMPLES
+.Pp
+The following command prints the available counters.
+.Dl $ pmc list
+.Pp
+The following command starts two counters.
+The former will count the 'l2cache-access' events that are triggered from
+userland, the latter will count the 'l1cache-access' events triggered from
+both userland and the kernel.
+.Dl $ pmc start l2cache-access:u l1cache-access:uk
+.Pp
+Note that the two following commands are not exactly identical.
+.Dl $ pmc start l1cache-access:u l1cache-access:k
+.Dl $ pmc start l1cache-access:uk
+The former will start two different counters that have a different source but
+track the same event.
+The latter will start one counter that tracks the event from all sources;
+it therefore does the sum of the two counters from the first command, but
+takes only one counter to do so.
+.Ed
 .Sh DIAGNOSTICS
 .Bl -diag
-.It PMC support is not compiled into the kernel
+.It PMC support not compiled into the kernel
 Performance-monitoring counter support has not been compiled into the
 kernel.
 It may be included using the
-.Em PERFCTRS
+.Em PMC
 option.
 See
 .Xr options 4
 for details.
-.It PMC counters are not supported by CPU
-Performance-monitoring counters are not available for the CPU.
+.El
+.Sh BUGS
+The
+.Nm
+command currently only supports performance-monitoring counters
+on the i386 architecture.
 .El
 .Sh SEE ALSO
-.Xr time 1 ,
 .Xr options 4
 .Sh HISTORY
 The
 .Nm
 command first appeared in
 .Nx 1.6 .
-.Sh AUTHORS
-The
-.Nm
-command was written by
-.An Frank van der Linden Aq Mt f...@wasabisystems.com .
-The kernel support for reading performance counters on the i386
-architecture was written by
-.An Jason R. Thorpe Aq Mt thor...@zembu.com .
-.Sh BUGS
-The
-.Nm
-command currently only supports performance-monitoring counters
-on the i386 architecture.
+It was revamped in 2017.
+

Index: src/usr.bin/pmc/pmc.c
diff -u src/usr.bin/pmc/pmc.c:1.19 src/usr.bin/pmc/pmc.c:1.20
--- src/usr.bin/pmc/pmc.c:1.19	Wed Mar  8 16:42:27 2017
+++ src/usr.bin/pmc/pmc.c	Fri Mar 10 13:09:11 2017
@@ -1,4 +1,33 @@
-/*	$NetBSD: pmc.c,v 1.19 2017/03/08 16:42:27 maxv Exp $	*/
+/*	$NetBSD: pmc.c,v 1.20 2017/03/10 13:09:11 maxv Exp $	*/
+
+/*
+ * Copyright (c) 2017 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Maxime Villard.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
 
 /*
  * Copyright 2000 Wasabi Systems, Inc.
@@ -37,12 +66,14 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: pmc.c,v 1.19 2017/03/08 16:42:27 maxv Exp $");
+__RCSID("$NetBSD: pmc.c,v 1.20 2017/03/10 13:09:11 maxv Exp $");
 #endif
 
 #include <sys/types.h>
+#include <sys/stdbool.h>
 #include <machine/sysarch.h>
 #include <machine/specialreg.h>
+#include <machine/param.h>
 #include <sys/wait.h>
 #include <err.h>
 #include <errno.h>
@@ -62,13 +93,19 @@ typedef struct x86_64_pmc_startstop_args
 typedef struct x86_64_pmc_read_args x86_pmc_read_args_t;
 #endif
 
-struct pmc_name2val {
+typedef struct {
 	const char *name;
-	int val;
-	int unit;
-};
+	uint32_t val;
+	uint32_t unit;
+} pmc_name2val_t;
 
-static const struct pmc_name2val i586_names[] = {
+typedef struct {
+	int type;
+	const pmc_name2val_t *pmc_names;
+	size_t size;
+} pmc_name2val_cpu_t;
+
+static const pmc_name2val_t i586_names[] = {
 	{ "tlb-data-miss",		PMC5_DATA_TLB_MISS,		0 },
 	{ "tlb-ins-miss",		PMC5_INST_TLB_MISS,		0 },
 	{ "l1cache-ins-miss",		PMC5_INST_CACHE_MISS,		0 },
@@ -109,7 +146,7 @@ static const struct pmc_name2val i586_na
 	{ "break-match3",		PMC5_BP3_MATCH,			0 },
 };
 
-static const struct pmc_name2val i686_names[] = {
+static const pmc_name2val_t i686_names[] = {
 	{ "mem-refs",			PMC6_DATA_MEM_REFS,		0 },
 	{ "l1cache-lines",		PMC6_DCU_LINES_IN,		0 },
 	{ "l1cache-mlines",		PMC6_DCU_M_LINES_IN,		0 },
@@ -236,7 +273,7 @@ static const struct pmc_name2val i686_na
 	{ "seg-rename-retire",		PMC6_RET_SEG_RENAMES,		0 },
 };
 
-static const struct pmc_name2val k7_names[] = {
+static const pmc_name2val_t k7_names[] = {
 	{ "seg-load-all",		K7_SEGMENT_REG_LOADS,		0x7f },
 	{ "seg-load-es",		K7_SEGMENT_REG_LOADS,		0x01 },
 	{ "seg-load-cs",		K7_SEGMENT_REG_LOADS,		0x02 },
@@ -318,34 +355,183 @@ static const struct pmc_name2val k7_name
 	{ "break-match3",		K7_BP3_MATCH,			0 },
 };
 
-static struct pmc_name2val_cpus {
-	int type;
-	const struct pmc_name2val *pmc_names;
-	int size;
-} pmc_cpus[] = {
+static const pmc_name2val_cpu_t pmc_cpus[] = {
 	{ PMC_TYPE_I586, i586_names,
-	  sizeof(i586_names) / sizeof(struct pmc_name2val) },
+	  sizeof(i586_names) / sizeof(pmc_name2val_t) },
 	{ PMC_TYPE_I686, i686_names,
-	  sizeof(i686_names) / sizeof(struct pmc_name2val) },
+	  sizeof(i686_names) / sizeof(pmc_name2val_t) },
 	{ PMC_TYPE_K7, k7_names,
-	  sizeof(k7_names) / sizeof(struct pmc_name2val) },
+	  sizeof(k7_names) / sizeof(pmc_name2val_t) },
 };
 
+/* -------------------------------------------------------------------------- */
+
 static void usage(void) __dead;
 
+static void pmc_list(const pmc_name2val_cpu_t *, char **);
+static void pmc_start(const pmc_name2val_cpu_t *, char **);
+static void pmc_stop(const pmc_name2val_cpu_t *, char **);
+
+static const pmc_name2val_cpu_t *pmc_lookup_cpu(int);
+static const pmc_name2val_t *pmc_find_event(const pmc_name2val_cpu_t *,
+    const char *);
+
+static int x86_pmc_info(x86_pmc_info_args_t *);
+static int x86_pmc_startstop(x86_pmc_startstop_args_t *);
+static int x86_pmc_read(x86_pmc_read_args_t *);
+
+static uint32_t pmc_ncounters;
+
+static struct cmdtab {
+	const char *label;
+	bool takesargs;
+	bool argsoptional;
+	void (*func)(const pmc_name2val_cpu_t *, char **);
+} const pmc_cmdtab[] = {
+	{ "list",	false, false, pmc_list },
+	{ "start",	true,  false, pmc_start },
+	{ "stop",	false, false, pmc_stop },
+	{ NULL,		false, false, NULL },
+};
+
 static void usage(void)
 {
-	fprintf(stderr, "usage: %s -h\n"
-	    "       %s -C\n"
-	    "       %s -c <event> command [options] ...\n",
-	    getprogname(), getprogname(), getprogname());
-	exit(1);
+	const char *progname = getprogname();
+
+	fprintf(stderr, "usage: %s list\n", progname);
+	fprintf(stderr, "       %s start [name:option ...]\n", progname);
+	fprintf(stderr, "       %s stop\n", progname);
+	exit(EXIT_FAILURE);
+	/* NOTREACHED */
+}
+
+static void
+pmc_list(const pmc_name2val_cpu_t *pncp, char **argv)
+{
+	int i, n, left, pairs;
+
+	printf("Supported performance counter events:\n");
+	n = pncp->size;
+	pairs = n / 2;
+	left = n % 2;
+
+	for (i = 0; i < pairs; i++)
+		printf("    %37s %37s\n", pncp->pmc_names[i * 2].name,
+		    pncp->pmc_names[i * 2 + 1].name);
+	if (left != 0)
+		printf("\t%37s\n", pncp->pmc_names[n - 1].name);
+}
+
+static void
+pmc_start(const pmc_name2val_cpu_t *pncp, char **argv)
+{
+	x86_pmc_startstop_args_t pmcargs[PMC_NCOUNTERS], *pmcarg;
+	char *tokens[PMC_NCOUNTERS][2], *event, *options;
+	const pmc_name2val_t *pnp;
+	uint32_t flags;
+	size_t n, i;
+
+	for (n = 0; n < pmc_ncounters; n++) {
+		if (argv[n] == NULL)
+			break;
+		tokens[n][0] = strtok(argv[n], ":");
+		tokens[n][1] = strtok(NULL, ":");
+		if (tokens[n][1] == NULL)
+			usage();
+	}
+
+	for (i = 0; i < n; i++) {
+		pmcarg = &pmcargs[i];
+		event = tokens[i][0];
+		options = tokens[i][1];
+
+		pnp = pmc_find_event(pncp, event);
+		if (pnp == NULL)
+			errx(EXIT_FAILURE, "Unable to find '%s'", event);
+		flags = 0;
+		if (strchr(options, 'u'))
+			flags |= PMC_SETUP_USER;
+		if (strchr(options, 'k'))
+			flags |= PMC_SETUP_KERNEL;
+		if (flags == 0)
+			errx(EXIT_FAILURE, "Missing counter option");
+
+		memset(pmcarg, 0, sizeof(*pmcarg));
+		pmcarg->counter = i;
+		pmcarg->event = pnp->val;
+		pmcarg->unit = pnp->unit;
+		pmcarg->flags = flags;
+	}
+
+	for (i = 0; i < n; i++) {
+		pmcarg = &pmcargs[i];
+		if (x86_pmc_startstop(pmcarg) < 0)
+			errx(EXIT_FAILURE, "Unable to start counter #%zu", i);
+		printf("Counter #%zu started\n", i);
+	}
 }
 
-static const struct pmc_name2val_cpus *
+static void
+pmc_stop(const pmc_name2val_cpu_t *pncp, char **argv)
+{
+	x86_pmc_cpuval_t cpuval[PMC_NCOUNTERS][MAXCPUS];
+	x86_pmc_startstop_args_t pmcstop;
+	x86_pmc_read_args_t pmcread;
+	size_t i, j, n, nval = 0;
+
+	/* Read the values. */
+	for (n = 0; n < pmc_ncounters; n++) {
+		memset(&pmcread, 0, sizeof(pmcread));
+		pmcread.counter = n;
+		pmcread.values = (x86_pmc_cpuval_t *)&cpuval[n];
+		pmcread.nval = MAXCPUS;
+		if (x86_pmc_read(&pmcread) < 0) {
+			if (errno == ENOENT) {
+				/*
+				 * This counter is not running. So we stop the
+				 * iteration here, since the next counters
+				 * won't be running either.
+				 */
+				break;
+			}
+			errx(EXIT_FAILURE, "Unable to read counter #%zu", n);
+		}
+		nval = pmcread.nval;
+	}
+
+	/* Display the results. Should probably be revisited. */
+	printf("Counter\t\t");
+	for (i = 0; i < nval; i++) {
+		printf("cpu%zu\t\t", i);
+	}
+	printf("\n");
+	printf("-------\t\t");
+	for (i = 0; i < nval; i++) {
+		printf("----\t\t");
+	}
+	printf("\n");
+	for (i = 0; i < n; i++) {
+		printf("%zu\t\t", i);
+		for (j = 0; j < nval; j++) {
+			printf("%llu\t\t", cpuval[i][j].ctrval);
+		}
+		printf("\n");
+	}
+
+	/* Stop the counters. */
+	for (i = 0; i < n; i++) {
+		memset(&pmcstop, 0, sizeof(pmcstop));
+		pmcstop.counter = i;
+		if (x86_pmc_startstop(&pmcstop) < 0)
+			errx(EXIT_FAILURE, "Unable to stop counter #%zu", i);
+		printf("Counter #%zu stopped\n", i);
+	}
+}
+
+static const pmc_name2val_cpu_t *
 pmc_lookup_cpu(int type)
 {
-	size_t i, n = sizeof(pmc_cpus) / sizeof(struct pmc_name2val_cpus);
+	size_t i, n = sizeof(pmc_cpus) / sizeof(pmc_name2val_cpu_t);
 
 	for (i = 0; i < n; i++) {
 		if (pmc_cpus[i].type == type)
@@ -355,10 +541,10 @@ pmc_lookup_cpu(int type)
 	return NULL;
 }
 
-static const struct pmc_name2val *
-pmc_find_event(const struct pmc_name2val_cpus *pncp, const char *name)
+static const pmc_name2val_t *
+pmc_find_event(const pmc_name2val_cpu_t *pncp, const char *name)
 {
-	int i;
+	size_t i;
 
 	for (i = 0; i < pncp->size; i++) {
 		if (strcmp(pncp->pmc_names[i].name, name) == 0)
@@ -368,23 +554,6 @@ pmc_find_event(const struct pmc_name2val
 	return NULL;
 }
 
-static void
-pmc_list_events(const struct pmc_name2val_cpus *pncp)
-{
-	int i, n, left, pairs;
-
-	printf("Supported performance counter events:\n");
-	n = pncp->size;
-	pairs = n / 2;
-	left = n % 2;
-
-	for (i = 0; i < pairs; i++)
-		printf("    %37s %37s\n", pncp->pmc_names[i * 2].name,
-		    pncp->pmc_names[i * 2 + 1].name);
-	if (left != 0)
-		printf("\t%37s\n", pncp->pmc_names[n - 1].name);
-}
-
 static int
 x86_pmc_info(x86_pmc_info_args_t *args)
 {
@@ -412,125 +581,35 @@ x86_pmc_read(x86_pmc_read_args_t *args)
 int
 main(int argc, char **argv)
 {
-	int c, status, ret0, ret1, errn0, errn1;
-	const char * volatile event = "unknown";
-	const struct pmc_name2val_cpus *pncp;
-	const struct pmc_name2val *pnp;
+	const pmc_name2val_cpu_t *pncp;
 	x86_pmc_info_args_t pmcinfo;
-	x86_pmc_startstop_args_t pss0, pss1;
-	x86_pmc_read_args_t pr0, pr1;
-	pid_t pid;
+	const struct cmdtab *ct;
 
 	setprogname(argv[0]);
-	errn0 = errn1 = 0;
+	argv += 1;
 
 	if (x86_pmc_info(&pmcinfo) < 0)
-		errx(2, "PMC support is not compiled into the kernel");
+		errx(EXIT_FAILURE, "PMC support not compiled into the kernel");
 	if (pmcinfo.vers != 1)
-		errx(2, "Wrong PMC version");
+		errx(EXIT_FAILURE, "Wrong PMC version");
+	pmc_ncounters = pmcinfo.nctrs;
 
 	pncp = pmc_lookup_cpu(pmcinfo.type);
 	if (pncp == NULL)
-		errx(3, "PMC counters are not supported for your CPU (0x%x)",
+		errx(EXIT_FAILURE, "PMC type 0x%x not recognized",
 		    pmcinfo.type);
 
-	pnp = NULL;
-	while ((c = getopt(argc, argv, "Cc:h")) != -1) {
-		switch (c) {
-		case 'C':
-			if (argc != 2)
+	for (ct = pmc_cmdtab; ct->label != NULL; ct++) {
+		if (strcmp(argv[0], ct->label) == 0) {
+			if (!ct->argsoptional &&
+			    ((ct->takesargs == 0) ^ (argv[1] == NULL)))
+			{
 				usage();
-			/*
-			 * Just clear both counters. Useful if a previous run
-			 * got killed and did not clean up.
-			 */
-			memset(&pss0, 0, sizeof(pss0));
-			x86_pmc_startstop(&pss0);
-			pss0.counter = 1;
-			x86_pmc_startstop(&pss0);
-			return 0;
-		case 'c':
-			event = optarg;
-			pnp = pmc_find_event(pncp, event);
+			}
+			(*ct->func)(pncp, argv + 1);
 			break;
-		case 'h':
-			if (argc != 2)
-				usage();
-			pmc_list_events(pncp);
-			return 0;
-		default:
-			usage();
 		}
 	}
 
-	if (pnp == NULL || argc <= optind)
-		usage();
-
-	memset(&pss0, 0, sizeof(pss0));
-	memset(&pss1, 0, sizeof(pss1));
-	pss0.event = pss1.event = pnp->val;
-	pss0.unit = pss1.unit = pnp->unit;
-	pss0.flags = PMC_SETUP_USER;
-	pss0.counter = 0;
-	pss1.flags = PMC_SETUP_KERNEL;
-	pss1.counter = 1;
-
-	/*
-	 * XXX should catch signals and tidy up in the parent.
-	 */
-	if (x86_pmc_startstop(&pss0) < 0)
-		err(4, "pmc_start user");
-	pss0.flags = 0;
-	if (x86_pmc_startstop(&pss1) < 0)
-		err(5, "pmc_start kernel");
-	pss1.flags = 0;
-
-	pid = vfork();
-
-	switch (pid) {
-	case -1:
-		err(5, "vfork");
-	case 0:
-		execvp(argv[optind], &argv[optind]);
-		err(6, "execvp");
-	}
-
-	signal(SIGINT, SIG_IGN);
-	signal(SIGQUIT, SIG_IGN);
-
-	if (waitpid(pid, &status, 0) == -1)
-		err(6, "waitpid");
-	if (!WIFEXITED(status))
-		return 0;
-
-	/*
-	 * Do not immediately exit on errors below. The counters must be
-	 * stopped first, or subsequent runs will get EBUSY.
-	 */
-	pr0.counter = 0;
-	ret0 = x86_pmc_read(&pr0);
-	if (ret0 < 0)
-		errn0 = errno;
-	pr1.counter = 1;
-	ret1 = x86_pmc_read(&pr1);
-	if (ret1 < 0)
-		errn1 = errno;
-
-	if (x86_pmc_startstop(&pss0) < 0)
-		warn("pmc_stop user");
-	if (x86_pmc_startstop(&pss1) < 0)
-		warn("pmc_stop kernel");
-
-	if (ret0 < 0) {
-		errno = errn0;
-		err(6, "pmc_read");
-	}
-	if (ret1 < 0) {
-		errno = errn1;
-		err(7, "pmc_read");
-	}
-
-	printf("%s: user %llu kernel %llu\n", event, pr0.val, pr1.val);
-
 	return 0;
 }

Reply via email to