summaryrefslogtreecommitdiff
path: root/usr/src/lib/libcpc
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libcpc
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libcpc')
-rw-r--r--usr/src/lib/libcpc/Makefile69
-rw-r--r--usr/src/lib/libcpc/Makefile.com69
-rw-r--r--usr/src/lib/libcpc/amd64/Makefile41
-rw-r--r--usr/src/lib/libcpc/common/libcpc.c1091
-rw-r--r--usr/src/lib/libcpc/common/libcpc.h212
-rw-r--r--usr/src/lib/libcpc/common/libcpc_impl.h293
-rw-r--r--usr/src/lib/libcpc/common/llib-lcpc32
-rw-r--r--usr/src/lib/libcpc/common/obsoleted.c410
-rw-r--r--usr/src/lib/libcpc/common/subr.c221
-rw-r--r--usr/src/lib/libcpc/i386/Makefile34
-rw-r--r--usr/src/lib/libcpc/i386/conf_pentium.c558
-rw-r--r--usr/src/lib/libcpc/i386/event_pentium.c873
-rw-r--r--usr/src/lib/libcpc/i386/getcpuid.h45
-rw-r--r--usr/src/lib/libcpc/i386/getcpuid.s64
-rw-r--r--usr/src/lib/libcpc/sparc/Makefile35
-rw-r--r--usr/src/lib/libcpc/sparc/conf_ultra.c548
-rw-r--r--usr/src/lib/libcpc/sparc/event_ultra.c509
-rw-r--r--usr/src/lib/libcpc/sparcv9/Makefile36
-rw-r--r--usr/src/lib/libcpc/spec/Makefile30
-rw-r--r--usr/src/lib/libcpc/spec/Makefile.targ33
-rw-r--r--usr/src/lib/libcpc/spec/amd64/Makefile44
-rw-r--r--usr/src/lib/libcpc/spec/cpc.spec448
-rw-r--r--usr/src/lib/libcpc/spec/i386/Makefile44
-rw-r--r--usr/src/lib/libcpc/spec/sparc/Makefile44
-rw-r--r--usr/src/lib/libcpc/spec/sparcv9/Makefile45
-rw-r--r--usr/src/lib/libcpc/spec/versions46
26 files changed, 5874 insertions, 0 deletions
diff --git a/usr/src/lib/libcpc/Makefile b/usr/src/lib/libcpc/Makefile
new file mode 100644
index 0000000000..e0e3d7f0c7
--- /dev/null
+++ b/usr/src/lib/libcpc/Makefile
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/Makefile
+
+include ../Makefile.lib
+
+HDRS = libcpc.h
+HDRDIR = common
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+LIBRARY= libcpc.a
+POFILE= $(LIBRARY:.a=.po)
+MSGFILES = common/libcpc.c common/obsoleted.c common/subr.c
+XGETFLAGS= -a
+
+.KEEP_STATE:
+
+all clean clobber install: spec .WAIT $(SUBDIRS)
+
+lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+spec $(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libcpc/Makefile.com b/usr/src/lib/libcpc/Makefile.com
new file mode 100644
index 0000000000..5cdc5f7b9d
--- /dev/null
+++ b/usr/src/lib/libcpc/Makefile.com
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/Makefile.com
+
+LIBRARY = libcpc.a
+VERS = .1
+COBJS = libcpc.o subr.o
+V1_OBJS = obsoleted.o
+OBJECTS = $(ASOBJS) $(MACHCOBJS) $(COBJS) $(V1_OBJS)
+
+include ../../Makefile.lib
+
+SRCS = $(ASOBJS:%.o=../$(MACH)/%.s) \
+ $(MACHCOBJS:%.o=../$(MACH)/%.c) \
+ $(V1_OBJS:%.o=../common/%.c) \
+ $(COBJS:%.o=../common/%.c)
+
+LIBS = $(DYNLIB) $(LINTLIB)
+$(LINTLIB) := SRCS = ../common/llib-lcpc
+LDLIBS += -lpctx -lnvpair -lc
+
+SRCDIR = ../common
+MAPDIR = ../spec/$(TRANSMACH)
+SPECMAPFILE = $(MAPDIR)/mapfile
+
+ASFLAGS += -P -D_ASM -I../common
+CPPFLAGS += -D_REENTRANT -I../common
+CFLAGS += $(CCVERBOSE)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
+
+pics/%.o: ../$(MACH)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../$(MACH)/%.s
+ $(COMPILE.s) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/libcpc/amd64/Makefile b/usr/src/lib/libcpc/amd64/Makefile
new file mode 100644
index 0000000000..f049307b31
--- /dev/null
+++ b/usr/src/lib/libcpc/amd64/Makefile
@@ -0,0 +1,41 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+MAPDIR= ../spec/amd64
+
+include ../Makefile.com
+
+OBJECTS = $(COBJS)
+
+SRCS = $(ASOBJS:%.o=../$(MACH)/%.s) \
+ $(MACHCOBJS:%.o=../$(MACH)/%.c) \
+ $(COBJS:%.o=../common/%.c)
+
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libcpc/common/libcpc.c b/usr/src/lib/libcpc/common/libcpc.c
new file mode 100644
index 0000000000..8b0f3b48fd
--- /dev/null
+++ b/usr/src/lib/libcpc/common/libcpc.c
@@ -0,0 +1,1091 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libcpc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/processor.h>
+#include <sys/procset.h>
+
+#include "libcpc_impl.h"
+
+#define MASK32 0xFFFFFFFF
+
+/*
+ * The library uses the cpc_lock field of the cpc_t struct to protect access to
+ * the linked lists inside the cpc_t, and only the linked lists. It is NOT used
+ * to protect against a user shooting his/herself in the foot (such as, for
+ * instance, destroying the same set at the same time from different threads.).
+ *
+ * SIGEMT needs to be blocked while holding the lock, to prevent deadlock among
+ * an app holding the lock and a signal handler attempting to sample or bind.
+ */
+
+static char *cpc_get_list(int which, int arg);
+static void cpc_err(cpc_t *cpc, const char *fn, int subcode, ...);
+static int cpc_set_valid(cpc_t *cpc, cpc_set_t *set);
+static int cpc_lock(cpc_t *cpc);
+static void cpc_unlock(cpc_t *cpc, int blocked);
+static int cpc_valid_event(cpc_t *cpc, uint_t pic, const char *ev);
+static int cpc_valid_attr(cpc_t *cpc, char *attr);
+static void cpc_invalidate_pctx(cpc_t *cpc, pctx_t *pctx);
+
+cpc_t *
+cpc_open(int ver)
+{
+ cpc_t *cpc;
+ void (*sigsaved)();
+ int error = 0;
+ int i;
+ int j;
+
+ if (ver != CPC_VER_CURRENT) {
+ /*
+ * v1 clients must stick to the v1 interface: cpc_version()
+ */
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * Call the syscall with invalid parameters. If we get ENOSYS this CPU
+ * has no CPC support. We need to block SIGSYS because the syscall code
+ * will send the signal if the system call fails to load.
+ */
+ sigsaved = signal(SIGSYS, SIG_IGN);
+ if (syscall(SYS_cpc, -1, -1, -1, -1, -1) != -1) {
+ (void) signal(SIGSYS, sigsaved);
+ errno = EINVAL;
+ return (NULL);
+ }
+ error = errno;
+ (void) signal(SIGSYS, sigsaved);
+
+ if (error != EINVAL) {
+ errno = error;
+ return (NULL);
+ }
+
+ if ((cpc = malloc(sizeof (cpc_t))) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ cpc->cpc_npic = syscall(SYS_cpc, CPC_NPIC, -1, 0, 0, 0);
+ cpc->cpc_caps = syscall(SYS_cpc, CPC_CAPS, -1, 0, 0, 0);
+
+ if (syscall(SYS_cpc, CPC_IMPL_NAME, -1, &cpc->cpc_cciname, 0, 0) != 0)
+ return (NULL);
+ if (syscall(SYS_cpc, CPC_CPUREF, -1, &cpc->cpc_cpuref, 0, 0) != 0)
+ return (NULL);
+
+
+ if ((cpc->cpc_attrlist = cpc_get_list(CPC_LIST_ATTRS, 0)) == NULL) {
+ free(cpc);
+ return (NULL);
+ }
+
+ if ((cpc->cpc_evlist = malloc(cpc->cpc_npic * sizeof (char *))) ==
+ NULL) {
+ free(cpc->cpc_attrlist);
+ free(cpc);
+ return (NULL);
+ }
+
+ for (i = 0; i < cpc->cpc_npic; i++) {
+ if ((cpc->cpc_evlist[i] = cpc_get_list(CPC_LIST_EVENTS, i)) ==
+ NULL)
+ break;
+ }
+ if (i != cpc->cpc_npic) {
+ for (j = 0; j < i; j++)
+ free(cpc->cpc_evlist[j]);
+ free(cpc->cpc_evlist);
+ free(cpc->cpc_attrlist);
+ free(cpc);
+ return (NULL);
+ }
+
+ cpc->cpc_sets = NULL;
+ cpc->cpc_bufs = NULL;
+ cpc->cpc_errfn = NULL;
+ (void) mutex_init(&cpc->cpc_lock, USYNC_THREAD, NULL);
+ __pctx_cpc_register_callback(cpc_invalidate_pctx);
+
+ return (cpc);
+}
+
+/*
+ * Ensure state is cleaned up:
+ *
+ * - Hardware is unbound
+ * - Sets are all destroyed
+ * - Bufs are all freed
+ */
+int
+cpc_close(cpc_t *cpc)
+{
+ while (cpc->cpc_sets != NULL) {
+ if (cpc->cpc_sets->cs_state != CS_UNBOUND)
+ (void) cpc_unbind(cpc, cpc->cpc_sets);
+ (void) cpc_set_destroy(cpc, cpc->cpc_sets);
+ }
+
+ while (cpc->cpc_bufs != NULL)
+ (void) cpc_buf_destroy(cpc, cpc->cpc_bufs);
+
+ free(cpc);
+ return (0);
+}
+
+cpc_set_t *
+cpc_set_create(cpc_t *cpc)
+{
+ cpc_set_t *set;
+ int sigblocked;
+
+ if ((set = malloc(sizeof (*set))) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ set->cs_request = NULL;
+ set->cs_nreqs = 0;
+ set->cs_state = CS_UNBOUND;
+ set->cs_fd = -1;
+ set->cs_pctx = NULL;
+ set->cs_id = -1;
+ set->cs_thr = NULL;
+
+ sigblocked = cpc_lock(cpc);
+ set->cs_next = cpc->cpc_sets;
+ cpc->cpc_sets = set;
+ cpc_unlock(cpc, sigblocked);
+
+ return (set);
+}
+
+int
+cpc_set_destroy(cpc_t *cpc, cpc_set_t *set)
+{
+ cpc_set_t *csp, *prev;
+ cpc_request_t *req, *next;
+ int sigblocked;
+
+ /*
+ * Remove this set from the cpc handle's list of sets.
+ */
+ sigblocked = cpc_lock(cpc);
+ for (csp = prev = cpc->cpc_sets; csp != NULL; csp = csp->cs_next) {
+ if (csp == set)
+ break;
+ prev = csp;
+ }
+ if (csp == NULL) {
+ cpc_unlock(cpc, sigblocked);
+ errno = EINVAL;
+ return (-1);
+ }
+ if (csp == cpc->cpc_sets)
+ cpc->cpc_sets = csp->cs_next;
+ prev->cs_next = csp->cs_next;
+ cpc_unlock(cpc, sigblocked);
+
+ if (csp->cs_state != CS_UNBOUND)
+ (void) cpc_unbind(cpc, csp);
+
+ for (req = csp->cs_request; req != NULL; req = next) {
+ next = req->cr_next;
+
+ if (req->cr_nattrs != 0)
+ free(req->cr_attr);
+
+ free(req);
+ }
+
+
+ free(set);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cpc_set_add_request(cpc_t *cpc, cpc_set_t *set, const char *event,
+ uint64_t preset, uint_t flags, uint_t nattrs, const cpc_attr_t *attrs)
+{
+ cpc_request_t *req;
+ const char *fn = "cpc_set_add_request";
+ int i;
+ int npics = cpc_npic(cpc);
+
+ if (cpc_set_valid(cpc, set) != 0 || set->cs_state != CS_UNBOUND) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ for (i = 0; i < npics; i++)
+ if (cpc_valid_event(cpc, i, event))
+ break;
+ if (i == npics) {
+ cpc_err(cpc, fn, CPC_INVALID_EVENT);
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((req = malloc(sizeof (*req))) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ (void) strncpy(req->cr_event, event, CPC_MAX_EVENT_LEN);
+ req->cr_preset = preset;
+ req->cr_flags = flags;
+ req->cr_nattrs = nattrs;
+ req->cr_index = set->cs_nreqs;
+ req->cr_attr = NULL;
+
+ if (nattrs != 0) {
+ for (i = 0; i < nattrs; i++) {
+ /*
+ * Verify that each attribute name is legal and valid.
+ */
+ if (attrs[i].ca_name[0] == '\0' ||
+ cpc_valid_attr(cpc, attrs[i].ca_name) == 0) {
+ cpc_err(cpc, fn, CPC_INVALID_ATTRIBUTE);
+ goto inval;
+ }
+
+ /*
+ * If the user requested a specific picnum, ensure that
+ * the pic can count the requested event.
+ */
+ if (strncmp("picnum", attrs[i].ca_name, 8) == 0) {
+ if (attrs[i].ca_val >= npics) {
+ cpc_err(cpc, fn, CPC_INVALID_PICNUM);
+ goto inval;
+ }
+
+ if (cpc_valid_event(cpc, attrs[i].ca_val,
+ req->cr_event) == 0) {
+ cpc_err(cpc, fn, CPC_PIC_NOT_CAPABLE);
+ goto inval;
+ }
+ }
+ }
+
+ if ((req->cr_attr = malloc(nattrs * sizeof (kcpc_attr_t)))
+ == NULL) {
+ free(req);
+ return (-1);
+ }
+
+ for (i = 0; i < nattrs; i++) {
+ req->cr_attr[i].ka_val = attrs[i].ca_val;
+ (void) strncpy(req->cr_attr[i].ka_name,
+ attrs[i].ca_name, CPC_MAX_ATTR_LEN);
+ }
+ } else
+ req->cr_attr = NULL;
+
+ req->cr_next = set->cs_request;
+ set->cs_request = req;
+ set->cs_nreqs++;
+
+ return (req->cr_index);
+
+inval:
+ free(req);
+ errno = EINVAL;
+ return (-1);
+}
+
+cpc_buf_t *
+cpc_buf_create(cpc_t *cpc, cpc_set_t *set)
+{
+ cpc_buf_t *buf;
+ int sigblocked;
+
+ if (cpc_set_valid(cpc, set) != 0) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((buf = malloc(sizeof (*buf))) == NULL)
+ return (NULL);
+
+ buf->cb_size = set->cs_nreqs * sizeof (uint64_t);
+ if ((buf->cb_data = malloc(buf->cb_size)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+
+ bzero(buf->cb_data, buf->cb_size);
+
+ buf->cb_hrtime = 0;
+ buf->cb_tick = 0;
+
+ sigblocked = cpc_lock(cpc);
+ buf->cb_next = cpc->cpc_bufs;
+ cpc->cpc_bufs = buf;
+ cpc_unlock(cpc, sigblocked);
+
+ return (buf);
+}
+
+int
+cpc_buf_destroy(cpc_t *cpc, cpc_buf_t *buf)
+{
+ cpc_buf_t *cbp, *prev;
+ int sigblocked;
+
+ /*
+ * Remove this buf from the cpc handle's list of bufs.
+ */
+ sigblocked = cpc_lock(cpc);
+ for (cbp = prev = cpc->cpc_bufs; cbp != NULL; cbp = cbp->cb_next) {
+ if (cbp == buf)
+ break;
+ prev = cbp;
+ }
+ if (cbp == NULL) {
+ cpc_unlock(cpc, sigblocked);
+ errno = EINVAL;
+ return (-1);
+ }
+ if (cbp == cpc->cpc_bufs)
+ cpc->cpc_bufs = cbp->cb_next;
+ prev->cb_next = cbp->cb_next;
+
+ cpc_unlock(cpc, sigblocked);
+ free(cbp->cb_data);
+ free(cbp);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cpc_bind_curlwp(cpc_t *cpc, cpc_set_t *set, uint_t flags)
+{
+ char *packed_set;
+ size_t packsize;
+ int ret;
+ int subcode = -1;
+
+ /*
+ * We don't bother checking cpc_set_valid() here, because this is in the
+ * fast path of an app doing SIGEMT-based profiling as they restart the
+ * counters from their signal handler.
+ */
+ if (CPC_SET_VALID_FLAGS(flags) == 0 || set->cs_nreqs <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ ret = syscall(SYS_cpc, CPC_BIND, -1, packed_set, packsize, &subcode);
+ free(packed_set);
+
+ if (ret != 0) {
+ if (subcode != -1)
+ cpc_err(cpc, "cpc_bind_curlwp", subcode);
+ return (-1);
+ }
+
+ set->cs_thr = thr_self();
+ set->cs_state = CS_BOUND_CURLWP;
+ return (ret);
+}
+
+/*ARGSUSED*/
+int
+cpc_bind_pctx(cpc_t *cpc, pctx_t *pctx, id_t id, cpc_set_t *set, uint_t flags)
+{
+ char *packed_set;
+ size_t packsize;
+ int ret;
+ int subcode = -1;
+
+ /*
+ * cpc_bind_pctx() currently has no valid flags.
+ */
+ if (flags != 0 || cpc_set_valid(cpc, set) != 0 || set->cs_nreqs <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ ret = __pctx_cpc(pctx, cpc, CPC_BIND, id, packed_set, (void *)packsize,
+ (void *)&subcode, -1);
+
+ free(packed_set);
+
+ if (ret == 0) {
+ set->cs_pctx = pctx;
+ set->cs_id = id;
+ set->cs_state = CS_BOUND_PCTX;
+ } else if (subcode != -1)
+ cpc_err(cpc, "cpc_bind_pctx", subcode);
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+int
+cpc_bind_cpu(cpc_t *cpc, processorid_t id, cpc_set_t *set, uint_t flags)
+{
+ int fd;
+ char *packed_set;
+ size_t packsize;
+ __cpc_args_t cpc_args;
+ int error;
+ const char *fn = "cpc_bind_cpu";
+ int subcode = -1;
+
+ /*
+ * cpc_bind_cpu() currently has no valid flags.
+ */
+ if (flags != 0 || cpc_set_valid(cpc, set) != 0 || set->cs_nreqs <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (processor_bind(P_LWPID, P_MYID, id, &set->cs_obind) == -1) {
+ cpc_err(cpc, fn, CPC_PBIND_FAILED);
+ return (-1);
+ }
+
+ if ((fd = open(CPUDRV_SHARED, O_RDWR)) < 0) {
+ error = errno;
+ (void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
+ errno = error;
+ return (-1);
+ }
+
+ /*
+ * To avoid leaking file descriptors, if we find an existing fd here we
+ * just close it. This is only a problem if a user attempts to bind the
+ * same set to different CPUs without first unbinding it.
+ */
+ if (set->cs_fd != -1)
+ (void) close(set->cs_fd);
+ set->cs_fd = fd;
+
+ if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
+ (void) close(fd);
+ (void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ cpc_args.udata1 = packed_set;
+ cpc_args.udata2 = (void *)packsize;
+ cpc_args.udata3 = (void *)&subcode;
+
+ if (ioctl(fd, CPCIO_BIND, &cpc_args) != 0) {
+ error = errno;
+ free(packed_set);
+ (void) close(fd);
+ (void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
+ if (subcode != -1)
+ cpc_err(cpc, fn, subcode);
+ errno = error;
+ return (-1);
+ }
+
+ free(packed_set);
+
+ set->cs_thr = thr_self();
+ set->cs_state = CS_BOUND_CPU;
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cpc_request_preset(cpc_t *cpc, int index, uint64_t preset)
+{
+ return (syscall(SYS_cpc, CPC_PRESET, -1, index,
+ (uint32_t)(preset >> 32), (uint32_t)(preset & MASK32)));
+}
+
+/*ARGSUSED*/
+int
+cpc_set_restart(cpc_t *cpc, cpc_set_t *set)
+{
+ return (syscall(SYS_cpc, CPC_RESTART, -1, 0, 0, 0));
+}
+
+/*ARGSUSED*/
+int
+cpc_unbind(cpc_t *cpc, cpc_set_t *set)
+{
+ int ret = 0;
+ int error;
+
+ if (cpc_set_valid(cpc, set) != 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ switch (set->cs_state) {
+ case CS_UNBOUND:
+ errno = EINVAL;
+ return (-1);
+ case CS_BOUND_CURLWP:
+ ret = syscall(SYS_cpc, CPC_RELE, -1, 0, 0, 0);
+ error = errno;
+ break;
+ case CS_BOUND_CPU:
+ ret = ioctl(set->cs_fd, CPCIO_RELE, NULL);
+ error = errno;
+ (void) close(set->cs_fd);
+ set->cs_fd = -1;
+ (void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
+ break;
+ case CS_BOUND_PCTX:
+ if (set->cs_pctx != NULL) {
+ ret = __pctx_cpc(set->cs_pctx, cpc, CPC_RELE,
+ set->cs_id, 0, 0, 0, 0);
+ error = errno;
+ }
+ break;
+ }
+
+ set->cs_thr = NULL;
+ set->cs_id = -1;
+ set->cs_state = CS_UNBOUND;
+ if (ret != 0)
+ errno = error;
+ return (ret);
+}
+
+/*ARGSUSED*/
+int
+cpc_set_sample(cpc_t *cpc, cpc_set_t *set, cpc_buf_t *buf)
+{
+ __cpc_args_t args;
+
+ /*
+ * The following check ensures that only the most recently bound set
+ * can be sampled, as binding a set invalidates all other sets in the
+ * cpc_t.
+ */
+ if (set->cs_state == CS_UNBOUND ||
+ buf->cb_size != set->cs_nreqs * sizeof (uint64_t)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ switch (set->cs_state) {
+ case CS_BOUND_CURLWP:
+ return (syscall(SYS_cpc, CPC_SAMPLE, -1, buf->cb_data,
+ &buf->cb_hrtime, &buf->cb_tick));
+ case CS_BOUND_CPU:
+ args.udata1 = buf->cb_data;
+ args.udata2 = &buf->cb_hrtime;
+ args.udata3 = &buf->cb_tick;
+ return (ioctl(set->cs_fd, CPCIO_SAMPLE, &args));
+ case CS_BOUND_PCTX:
+ return (__pctx_cpc(set->cs_pctx, cpc, CPC_SAMPLE, set->cs_id,
+ buf->cb_data, &buf->cb_hrtime, &buf->cb_tick,
+ buf->cb_size));
+ }
+
+ errno = EINVAL;
+ return (-1);
+}
+
+/*ARGSUSED*/
+void
+cpc_buf_sub(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, cpc_buf_t *b)
+{
+ int i;
+
+ if (a->cb_size != ds->cb_size || b->cb_size != ds->cb_size)
+ return;
+
+ ds->cb_hrtime = (a->cb_hrtime > b->cb_hrtime) ?
+ a->cb_hrtime : b->cb_hrtime;
+ ds->cb_tick = a->cb_tick - b->cb_tick;
+
+ for (i = 0; i < ds->cb_size / sizeof (uint64_t); i++)
+ ds->cb_data[i] = a->cb_data[i] - b->cb_data[i];
+}
+
+/*ARGSUSED*/
+void
+cpc_buf_add(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, cpc_buf_t *b)
+{
+ int i;
+
+ if (a->cb_size != ds->cb_size || b->cb_size != ds->cb_size)
+ return;
+
+ ds->cb_hrtime = (a->cb_hrtime > b->cb_hrtime) ?
+ a->cb_hrtime : b->cb_hrtime;
+ ds->cb_tick = a->cb_tick + b->cb_tick;
+
+ for (i = 0; i < ds->cb_size / sizeof (uint64_t); i++)
+ ds->cb_data[i] = a->cb_data[i] + b->cb_data[i];
+}
+
+/*ARGSUSED*/
+void
+cpc_buf_copy(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *src)
+{
+ if (ds->cb_size != src->cb_size)
+ return;
+
+ bcopy(src->cb_data, ds->cb_data, ds->cb_size);
+ ds->cb_hrtime = src->cb_hrtime;
+ ds->cb_tick = src->cb_tick;
+}
+
+/*ARGSUSED*/
+void
+cpc_buf_zero(cpc_t *cpc, cpc_buf_t *buf)
+{
+ bzero(buf->cb_data, buf->cb_size);
+ buf->cb_hrtime = 0;
+ buf->cb_tick = 0;
+}
+
+/*
+ * Gets or sets the value of the request specified by index.
+ */
+/*ARGSUSED*/
+int
+cpc_buf_get(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val)
+{
+ *val = buf->cb_data[index];
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+cpc_buf_set(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t val)
+{
+ buf->cb_data[index] = val;
+
+ return (0);
+}
+
+/*ARGSUSED*/
+hrtime_t
+cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf)
+{
+ return (buf->cb_hrtime);
+}
+
+/*ARGSUSED*/
+uint64_t
+cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf)
+{
+ return (buf->cb_tick);
+}
+
+static char *
+cpc_get_list(int which, int arg)
+{
+ int szcmd;
+ int size;
+ char *list;
+
+ if (which == CPC_LIST_ATTRS)
+ szcmd = CPC_ATTRLIST_SIZE;
+ else
+ szcmd = CPC_EVLIST_SIZE;
+
+ if (syscall(SYS_cpc, szcmd, -1, &size, arg, 0) != 0)
+ return (NULL);
+
+ if ((list = malloc(size)) == NULL)
+ return (NULL);
+
+ if (syscall(SYS_cpc, which, -1, list, arg, 0) != 0) {
+ free(list);
+ return (NULL);
+ }
+
+ return (list);
+}
+
+/*ARGSUSED*/
+void
+cpc_walk_requests(cpc_t *cpc, cpc_set_t *set, void *arg,
+ void (*action)(void *arg, int index, const char *event, uint64_t preset,
+ uint_t flags, int nattrs, const cpc_attr_t *attrs))
+{
+ cpc_request_t *rp;
+ cpc_attr_t *attrs = NULL;
+ int i;
+
+ for (rp = set->cs_request; rp != NULL; rp = rp->cr_next) {
+ /*
+ * Need to reconstruct a temporary cpc_attr_t array for req.
+ */
+ if (rp->cr_nattrs != 0)
+ if ((attrs = malloc(rp->cr_nattrs *
+ sizeof (cpc_attr_t))) == NULL)
+ return;
+ for (i = 0; i < rp->cr_nattrs; i++) {
+ attrs[i].ca_name = rp->cr_attr[i].ka_name;
+ attrs[i].ca_val = rp->cr_attr[i].ka_val;
+ }
+
+ action(arg, rp->cr_index, rp->cr_event, rp->cr_preset,
+ rp->cr_flags, rp->cr_nattrs, attrs);
+
+ if (rp->cr_nattrs != 0)
+ free(attrs);
+ }
+}
+
+/*ARGSUSED*/
+void
+cpc_walk_events_all(cpc_t *cpc, void *arg,
+ void (*action)(void *arg, const char *event))
+{
+ char **list;
+ char *p, *e;
+ int i;
+ int ncounters = cpc_npic(cpc);
+ cpc_strhash_t *hash;
+
+ if ((list = malloc(ncounters * sizeof (char *))) == NULL)
+ return;
+
+ if ((hash = __cpc_strhash_alloc()) == NULL) {
+ free(list);
+ return;
+ }
+
+ for (i = 0; i < ncounters; i++) {
+ if ((list[i] = strdup(cpc->cpc_evlist[i])) == NULL)
+ goto err;
+ p = list[i];
+ while ((e = strchr(p, ',')) != NULL) {
+ *e = '\0';
+ if (__cpc_strhash_add(hash, p) == -1)
+ goto err;
+ p = e + 1;
+ }
+ if (__cpc_strhash_add(hash, p) == -1)
+ goto err;
+ }
+
+ while ((p = __cpc_strhash_next(hash)) != NULL)
+ action(arg, p);
+
+err:
+ __cpc_strhash_free(hash);
+ for (i = 0; i < ncounters; i++)
+ free(list[i]);
+ free(list);
+}
+
+/*ARGSUSED*/
+void
+cpc_walk_events_pic(cpc_t *cpc, uint_t picno, void *arg,
+ void (*action)(void *arg, uint_t picno, const char *event))
+{
+ char *p;
+ char *e;
+ char *list;
+
+ if (picno >= cpc->cpc_npic) {
+ errno = EINVAL;
+ return;
+ }
+
+ if ((list = strdup(cpc->cpc_evlist[picno])) == NULL)
+ return;
+
+ /*
+ * List now points to a comma-separated list of events supported by
+ * the designated pic.
+ */
+ p = list;
+ while ((e = strchr(p, ',')) != NULL) {
+ *e = '\0';
+ action(arg, picno, p);
+ p = e + 1;
+ }
+ action(arg, picno, p);
+
+ free(list);
+}
+
+/*ARGSUSED*/
+void
+cpc_walk_attrs(cpc_t *cpc, void *arg,
+ void (*action)(void *arg, const char *attr))
+{
+ char *p;
+ char *e;
+ char *list;
+
+ if ((list = strdup(cpc->cpc_attrlist)) == NULL)
+ return;
+
+ /*
+ * Platforms with no attributes will return an empty string.
+ */
+ if (*list == '\0')
+ return;
+
+ /*
+ * List now points to a comma-separated list of attributes supported by
+ * the underlying platform.
+ */
+ p = list;
+ while ((e = strchr(p, ',')) != NULL) {
+ *e = '\0';
+ action(arg, p);
+ p = e + 1;
+ }
+ action(arg, p);
+
+ free(list);
+}
+
+/*ARGSUSED*/
+int
+cpc_enable(cpc_t *cpc)
+{
+ return (syscall(SYS_cpc, CPC_ENABLE, -1, 0, 0, 0));
+}
+
+/*ARGSUSED*/
+int
+cpc_disable(cpc_t *cpc)
+{
+ return (syscall(SYS_cpc, CPC_DISABLE, -1, 0, 0, 0));
+}
+
+/*ARGSUSED*/
+uint_t
+cpc_npic(cpc_t *cpc)
+{
+ return (cpc->cpc_npic);
+}
+
+/*ARGSUSED*/
+uint_t
+cpc_caps(cpc_t *cpc)
+{
+ return (cpc->cpc_caps);
+}
+
+const char *
+cpc_cciname(cpc_t *cpc)
+{
+ return (cpc->cpc_cciname);
+}
+
+const char *
+cpc_cpuref(cpc_t *cpc)
+{
+ return (cpc->cpc_cpuref);
+}
+
+int
+cpc_seterrhndlr(cpc_t *cpc, cpc_errhndlr_t *fn)
+{
+ cpc->cpc_errfn = fn;
+ return (0);
+}
+
+/*
+ * These strings may contain printf() conversion specifiers.
+ */
+static const char *errstr[] = {
+"", /* zero slot filler */
+"Unknown event\n", /* CPC_INVALID_EVENT */
+"Invalid counter number\n", /* CPC_INVALID_PICNUM */
+"Unknown attribute\n", /* CPC_INVALID_ATTRIBUTE */
+"Attribute out of range\n", /* CPC_ATTRIBUTE_OUT_OF_RANGE */
+"Hardware resource unavailable\n", /* CPC_RESOURCE_UNAVAIL */
+"Counter cannot count requested event\n", /* CPC_PIC_NOT_CAPABLE */
+"Invalid flags in a request\n", /* CPC_REQ_INVALID_FLAGS */
+"Requests conflict with each other\n", /* CPC_CONFLICTING_REQS */
+"Attribute requires the cpc_cpu privilege\n", /* CPC_ATTR_REQUIRES_PRIVILEGE */
+"Couldn't bind LWP to requested processor\n" /* CPC_PBIND_FAILED */
+};
+
+/*VARARGS3*/
+static void
+cpc_err(cpc_t *cpc, const char *fn, int subcode, ...)
+{
+ va_list ap;
+ const char *str;
+ int error;
+
+ /*
+ * If subcode is -1, there is no specific description for this error.
+ */
+ if (subcode == -1)
+ return;
+
+ /*
+ * We need to preserve errno across calls to this function to prevent it
+ * from being clobbered while here, or in the user's error handler.
+ */
+ error = errno;
+
+ str = dgettext(TEXT_DOMAIN, errstr[subcode]);
+
+ va_start(ap, subcode);
+ if (cpc->cpc_errfn != NULL)
+ cpc->cpc_errfn(fn, subcode, str, ap);
+ else {
+ /*
+ * If printf() conversion specifiers are added to the errstr[]
+ * table, this call needs to be changed to vfprintf().
+ */
+ (void) fprintf(stderr, "libcpc: %s: %s", fn, str);
+ }
+ va_end(ap);
+
+ errno = error;
+}
+
+/*
+ * Hook used by libpctx to alert libcpc when a pctx handle is going away.
+ * This is necessary to prevent libcpc from attempting a libpctx operation on a
+ * stale and invalid pctx_t handle. Since pctx_t's are cached by libcpc, we need
+ * to be notified when they go away.
+ */
+static void
+cpc_invalidate_pctx(cpc_t *cpc, pctx_t *pctx)
+{
+ cpc_set_t *set;
+ int sigblocked;
+
+ sigblocked = cpc_lock(cpc);
+ for (set = cpc->cpc_sets; set != NULL; set = set->cs_next)
+ if (set->cs_pctx == pctx)
+ set->cs_pctx = NULL;
+ cpc_unlock(cpc, sigblocked);
+}
+
+/*
+ * Check that the set is valid; if so it will be in the cpc handle's
+ * list of sets. The lock protects the list of sets, but not the set
+ * itself.
+ */
+static int
+cpc_set_valid(cpc_t *cpc, cpc_set_t *set)
+{
+ cpc_set_t *csp;
+ int sigblocked;
+
+ sigblocked = cpc_lock(cpc);
+ for (csp = cpc->cpc_sets; csp != NULL; csp = csp->cs_next)
+ if (csp == set)
+ break;
+ cpc_unlock(cpc, sigblocked);
+ if (csp == NULL)
+ return (-1);
+ return (0);
+}
+
+static int
+cpc_lock(cpc_t *cpc)
+{
+ int ret = (sigset(SIGEMT, SIG_HOLD) == SIG_HOLD);
+ (void) mutex_lock(&cpc->cpc_lock);
+ return (ret);
+}
+
+static void
+cpc_unlock(cpc_t *cpc, int sigblocked)
+{
+ (void) mutex_unlock(&cpc->cpc_lock);
+ if (sigblocked == 0)
+ (void) sigrelse(SIGEMT);
+}
+
+struct priv {
+ const char *name;
+ int found;
+};
+
+/*ARGSUSED*/
+static void
+ev_walker(void *arg, uint_t picno, const char *ev)
+{
+ if (strcmp(((struct priv *)arg)->name, ev) == 0)
+ ((struct priv *)arg)->found = 1;
+}
+
+static void
+at_walker(void *arg, const char *at)
+{
+ if (strcmp(((struct priv *)arg)->name, at) == 0)
+ ((struct priv *)arg)->found = 1;
+}
+
+static int
+cpc_valid_event(cpc_t *cpc, uint_t pic, const char *ev)
+{
+ struct priv pr = { NULL, 0 };
+
+ pr.name = ev;
+ cpc_walk_events_pic(cpc, pic, &pr, ev_walker);
+ return (pr.found);
+}
+
+static int
+cpc_valid_attr(cpc_t *cpc, char *attr)
+{
+ struct priv pr = { NULL, 0 };
+
+ pr.name = attr;
+ cpc_walk_attrs(cpc, &pr, at_walker);
+ return (pr.found);
+}
diff --git a/usr/src/lib/libcpc/common/libcpc.h b/usr/src/lib/libcpc/common/libcpc.h
new file mode 100644
index 0000000000..547cb62319
--- /dev/null
+++ b/usr/src/lib/libcpc/common/libcpc.h
@@ -0,0 +1,212 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBCPC_H
+#define _LIBCPC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/cpc_impl.h>
+#include <inttypes.h>
+#include <libpctx.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <string.h>
+#include <ucontext.h>
+#include <sys/processor.h>
+
+/*
+ * This library allows hardware performance counters present in
+ * certain processors to be used by applications to monitor their
+ * own statistics, the statistics of others, or the statistics of a given CPU.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct __cpc cpc_t;
+typedef struct __cpc_set cpc_set_t;
+typedef struct __cpc_buf cpc_buf_t;
+
+/*
+ * Current library version must be passed to cpc_open().
+ */
+#define CPC_VER_CURRENT 2
+
+/*
+ * Initializes the library for use and returns a pointer to an identifier that
+ * must be used as the cpc argument in subsequent libcpc calls.
+ */
+extern cpc_t *cpc_open(int ver);
+extern int cpc_close(cpc_t *cpc);
+
+/*
+ * Query information about the underlying processor.
+ */
+extern uint_t cpc_npic(cpc_t *cpc);
+extern uint_t cpc_caps(cpc_t *cpc);
+extern const char *cpc_cciname(cpc_t *cpc);
+extern const char *cpc_cpuref(cpc_t *cpc);
+
+/*
+ * A vprintf-like error handling routine can be passed to the
+ * library for use by more sophisticated callers.
+ * If specified as NULL, errors are written to stderr.
+ */
+typedef void (cpc_errhndlr_t)(const char *fn, int subcode, const char *fmt,
+ va_list ap);
+extern int cpc_seterrhndlr(cpc_t *cpc, cpc_errhndlr_t *fn);
+
+extern cpc_set_t *cpc_set_create(cpc_t *cpc);
+extern int cpc_set_destroy(cpc_t *cpc, cpc_set_t *set);
+
+/*
+ * If successful, returns an index for the new request within the set which is
+ * needed later to retrieve the request's data.
+ * Returns -1 if unsuccessful and sets errno to indicate the error.
+ */
+extern int cpc_set_add_request(cpc_t *cpc, cpc_set_t *set, const char *event,
+ uint64_t preset, uint_t flags, uint_t nattrs, const cpc_attr_t *attrs);
+
+extern cpc_buf_t *cpc_buf_create(cpc_t *cpc, cpc_set_t *set);
+extern int cpc_buf_destroy(cpc_t *cpc, cpc_buf_t *buf);
+
+/*
+ * Binds the set to the current LWP.
+ */
+extern int cpc_bind_curlwp(cpc_t *cpc, cpc_set_t *set, uint_t flags);
+
+/*
+ * Binds the set to the specified LWP in a process controlled via libpctx.
+ */
+extern int cpc_bind_pctx(cpc_t *cpc, pctx_t *pctx, id_t id, cpc_set_t *set,
+ uint_t flags);
+
+/*
+ * Binds the set to the specified CPU. The process must have sufficient
+ * privileges to bind to the CPU via processor_bind(2). An LWP can only
+ * bind to one CPU at a time. To measure more than one CPU simultaneously,
+ * one LWP must be created for each CPU.
+ */
+extern int cpc_bind_cpu(cpc_t *cpc, processorid_t id, cpc_set_t *set,
+ uint_t flags);
+
+/*
+ * Set the starting value for the indexed counter, and restart counting for a
+ * set that was frozen by a counter overflow.
+ */
+extern int cpc_request_preset(cpc_t *cpc, int index, uint64_t preset);
+extern int cpc_set_restart(cpc_t *cpc, cpc_set_t *set);
+
+/*
+ * Unbinds the set and frees up associated resources. cpc_buf_t's must be
+ * explicitly freed via cpc_buf_destroy().
+ */
+extern int cpc_unbind(cpc_t *cpc, cpc_set_t *set);
+
+/*
+ * Samples a set into a cpc_buf_t. The provided set must be bound, and the
+ * buf must have been created with the set being sampled.
+ */
+extern int cpc_set_sample(cpc_t *cpc, cpc_set_t *set, cpc_buf_t *buf);
+
+extern void cpc_buf_sub(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, cpc_buf_t *b);
+extern void cpc_buf_add(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, cpc_buf_t *b);
+extern void cpc_buf_copy(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *src);
+extern void cpc_buf_zero(cpc_t *cpc, cpc_buf_t *buf);
+
+/*
+ * Gets or sets the value of the request specified by index.
+ */
+extern int cpc_buf_get(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val);
+extern int cpc_buf_set(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t val);
+extern hrtime_t cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf);
+extern uint64_t cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf);
+
+extern void cpc_walk_requests(cpc_t *cpc, cpc_set_t *set, void *arg,
+ void (*action)(void *arg, int index, const char *event, uint64_t preset,
+ uint_t flags, int nattrs, const cpc_attr_t *attrs));
+
+extern void cpc_walk_events_all(cpc_t *cpc, void *arg,
+ void (*action)(void *arg, const char *event));
+extern void cpc_walk_events_pic(cpc_t *cpc, uint_t picno, void *arg,
+ void (*action)(void *arg, uint_t picno, const char *event));
+extern void cpc_walk_attrs(cpc_t *cpc, void *arg,
+ void (*action)(void *arg, const char *attr));
+
+extern int cpc_enable(cpc_t *cpc);
+extern int cpc_disable(cpc_t *cpc);
+
+#if defined(__sparc) || defined(__i386)
+
+/*
+ * Obsolete libcpc interfaces.
+ */
+#define CPC_VER_NONE 0
+
+typedef struct _cpc_event cpc_event_t;
+
+extern uint_t cpc_version(uint_t ver);
+extern int cpc_access();
+extern int cpc_getcpuver(void);
+extern const char *cpc_getcciname(int cpuver);
+extern const char *cpc_getcpuref(int cpuver);
+extern uint_t cpc_getnpic(int cpuver);
+typedef void (cpc_errfn_t)(const char *fn, const char *fmt, va_list ap);
+extern void cpc_seterrfn(cpc_errfn_t *errfn);
+extern const char *cpc_getusage(int cpuver);
+extern void cpc_walk_names(int cpuver, int regno, void *arg,
+ void (*action)(void *arg, int regno, const char *name, uint8_t bits));
+extern int cpc_strtoevent(int cpuver, const char *spec, cpc_event_t *event);
+extern char *cpc_eventtostr(cpc_event_t *event);
+extern void cpc_event_accum(cpc_event_t *accum, cpc_event_t *event);
+extern void cpc_event_diff(cpc_event_t *diff,
+ cpc_event_t *left, cpc_event_t *right);
+extern int cpc_bind_event(cpc_event_t *event, int flags);
+extern int cpc_take_sample(cpc_event_t *event);
+extern int cpc_count_usr_events(int enable);
+extern int cpc_count_sys_events(int enable);
+extern int cpc_rele(void);
+extern int cpc_pctx_bind_event(pctx_t *pctx,
+ id_t lwpid, cpc_event_t *event, int flags);
+extern int cpc_pctx_take_sample(pctx_t *pctx, id_t lwpid, cpc_event_t *event);
+extern int cpc_pctx_rele(pctx_t *pctx, id_t lwpid);
+extern int cpc_pctx_invalidate(pctx_t *pctx, id_t lwpid);
+extern int cpc_shared_open(void);
+extern void cpc_shared_close(int fd);
+extern int cpc_shared_bind_event(int fd, cpc_event_t *event, int flags);
+extern int cpc_shared_take_sample(int fd, cpc_event_t *event);
+extern int cpc_shared_rele(int fd);
+
+#endif /* __sparc || __i386 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBCPC_H */
diff --git a/usr/src/lib/libcpc/common/libcpc_impl.h b/usr/src/lib/libcpc/common/libcpc_impl.h
new file mode 100644
index 0000000000..401b7e327a
--- /dev/null
+++ b/usr/src/lib/libcpc/common/libcpc_impl.h
@@ -0,0 +1,293 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBCPC_IMPL_H
+#define _LIBCPC_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libcpc.h>
+#include <inttypes.h>
+#include <thread.h>
+#include <synch.h>
+#include <sys/types.h>
+#include <sys/cpc_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CPC_VER_1 1
+#define CPC1_BUFSIZE (2 * sizeof (uint64_t))
+
+struct _cpc_attr {
+ char ca_name[CPC_MAX_ATTR_LEN];
+ uint64_t ca_val;
+};
+
+typedef struct __cpc_request cpc_request_t;
+
+struct __cpc_request {
+ char cr_event[CPC_MAX_EVENT_LEN];
+ uint64_t cr_preset; /* Initial value */
+ uint16_t cr_index; /* Index of request in data */
+ uint_t cr_flags;
+ uint_t cr_nattrs; /* # CPU-specific attrs */
+ kcpc_attr_t *cr_attr;
+ cpc_request_t *cr_next; /* next request in set */
+};
+
+struct __cpc_buf {
+ uint64_t *cb_data; /* Pointer to data store */
+ hrtime_t cb_hrtime; /* hrtime at last sample */
+ uint64_t cb_tick; /* virtualized tsc/tick */
+ size_t cb_size; /* Size of data store, bytes */
+ cpc_buf_t *cb_next; /* List of all bufs */
+};
+
+/*
+ * Possible cpc_set_t states:
+ */
+typedef enum {
+ CS_UNBOUND, /* Set is not currently bound */
+ CS_BOUND_CURLWP, /* Set has been bound to curlwp */
+ CS_BOUND_PCTX, /* Set has been bound via libpctx */
+ CS_BOUND_CPU /* Set has been bound to a CPU */
+} __cpc_state_t;
+
+struct __cpc_set {
+ cpc_request_t *cs_request; /* linked list of requests */
+ __cpc_state_t cs_state; /* State of this set */
+ int cs_nreqs; /* Number of requests in set */
+ int cs_fd; /* file descriptor of cpc dev */
+ processorid_t cs_obind; /* previous proc binding */
+ pctx_t *cs_pctx; /* pctx of process bound to */
+ id_t cs_id; /* lwp ID of pctx binding */
+ thread_t cs_thr; /* thread ID which bound set */
+ cpc_set_t *cs_next; /* Linked list of all sets */
+};
+
+struct __cpc {
+ cpc_set_t *cpc_sets; /* List of existing sets */
+ cpc_buf_t *cpc_bufs; /* List of existing bufs */
+ cpc_errhndlr_t *cpc_errfn; /* Handles library errors */
+ mutex_t cpc_lock; /* Protect various ops */
+ char *cpc_attrlist; /* List of supported attrs */
+ char **cpc_evlist; /* List of events per pic */
+ char cpc_cpuref[CPC_MAX_CPUREF];
+ char cpc_cciname[CPC_MAX_IMPL_NAME];
+ uint_t cpc_caps;
+ uint_t cpc_npic;
+};
+
+/*
+ * cpc_t handle for CPCv1 clients.
+ */
+extern cpc_t *__cpc;
+
+/*PRINTFLIKE2*/
+extern void __cpc_error(const char *fn, const char *fmt, ...);
+
+extern const char *__cpc_reg_to_name(int cpuver, int regno, uint8_t bits);
+extern int __cpc_name_to_reg(int cpuver, int regno,
+ const char *name, uint8_t *bits);
+
+extern uint_t __cpc_workver;
+extern int __cpc_v1_cpuver;
+#ifdef __sparc
+extern uint64_t __cpc_v1_pcr;
+#else
+extern uint32_t __cpc_v1_pes[2];
+#endif /* __sparc */
+
+extern char *__cpc_pack_set(cpc_set_t *set, uint_t flags, size_t *buflen);
+
+typedef struct __cpc_strhash cpc_strhash_t;
+
+struct __cpc_strhash {
+ char *str;
+ struct __cpc_strhash *cur;
+ struct __cpc_strhash *next;
+};
+
+extern cpc_strhash_t *__cpc_strhash_alloc(void);
+extern void __cpc_strhash_free(cpc_strhash_t *hash);
+extern int __cpc_strhash_add(cpc_strhash_t *hash, char *key);
+extern char *__cpc_strhash_next(cpc_strhash_t *hash);
+
+/*
+ * Implementation-private system call used by libcpc
+ */
+struct __cpc;
+extern int __pctx_cpc(pctx_t *pctx, struct __cpc *cpc, int cmd, id_t lwpid,
+ void *data1, void *data2, void *data3, int bufsize);
+
+#define CPUDRV "/devices/pseudo/cpc@0"
+#define CPUDRV_SHARED CPUDRV":shared"
+
+#if defined(__sparc) || defined(__i386)
+/*
+ * These two are only used for backwards compatibility to the Obsolete CPCv1.
+ */
+extern int __cpc_init(void);
+extern cpc_set_t *__cpc_eventtoset(cpc_t *cpc, cpc_event_t *event, int flags);
+
+/*
+ * ce_cpuver values
+ */
+#define CPC_ULTRA1 1000
+#define CPC_ULTRA2 1001 /* same as ultra1 for these purposes */
+#define CPC_ULTRA3 1002
+#define CPC_ULTRA3_PLUS 1003
+#define CPC_ULTRA3_I 1004
+
+#define CPC_PENTIUM 2000
+#define CPC_PENTIUM_MMX 2001
+#define CPC_PENTIUM_PRO 2002
+#define CPC_PENTIUM_PRO_MMX 2003
+
+#define CPC_SPARC64_III 3000
+#define CPC_SPARC64_V 3002
+
+#endif /* __sparc || __i386 */
+
+#if defined(__i386) || defined(__amd64)
+/*
+ * This is common between i386 and amd64, because amd64 implements %tick.
+ * Currently only used by the cpc tools to print the label atop the CPU ticks
+ * column on amd64.
+ */
+#define CPC_TICKREG_NAME "tsc"
+#endif /* __i386 || __amd64 */
+
+#if defined(__sparc)
+
+/*
+ * UltraSPARC I, II and III processors
+ *
+ * The performance counters on these processors allow up to two 32-bit
+ * performance events to be captured simultaneously from a selection
+ * of metrics. The metrics are selected by writing to the performance
+ * control register, and subsequent values collected by reading from the
+ * performance instrumentation counter registers. Both registers are
+ * priviliged by default, and implemented as ASRs.
+ */
+
+struct _cpc_event {
+ int ce_cpuver;
+ hrtime_t ce_hrt; /* gethrtime() */
+ uint64_t ce_tick; /* virtualized %tick */
+ uint64_t ce_pic[2]; /* virtualized %pic */
+ uint64_t ce_pcr; /* %pcr */
+};
+
+#define CPC_TICKREG(ev) ((ev)->ce_tick)
+#define CPC_TICKREG_NAME "%tick"
+
+/*
+ * "Well known" bitfields in the UltraSPARC %pcr register
+ * The interfaces in libcpc should make these #defines uninteresting.
+ */
+#define CPC_ULTRA_PCR_USR 2
+#define CPC_ULTRA_PCR_SYS 1
+#define CPC_ULTRA_PCR_PRIVPIC 0
+
+#define CPC_ULTRA_PCR_PIC0_SHIFT 4
+#define CPC_ULTRA2_PCR_PIC0_MASK UINT64_C(0xf)
+#define CPC_ULTRA3_PCR_PIC0_MASK UINT64_C(0x3f)
+#define CPC_ULTRA_PCR_PIC1_SHIFT 11
+#define CPC_ULTRA2_PCR_PIC1_MASK UINT64_C(0xf)
+#define CPC_ULTRA3_PCR_PIC1_MASK UINT64_C(0x3f)
+
+#elif defined(__i386)
+
+/*
+ * Pentium I, II and III processors
+ *
+ * These CPUs allow pairs of events to captured.
+ * The hardware counters count up to 40-bits of significance, but
+ * only allow 32 (signed) bits to be programmed into them.
+ * Pentium I and Pentium II processors are programmed differently, but
+ * the resulting counters and timestamps can be handled portably.
+ */
+
+struct _cpc_event {
+ int ce_cpuver;
+ hrtime_t ce_hrt; /* gethrtime() */
+ uint64_t ce_tsc; /* virtualized rdtsc value */
+ uint64_t ce_pic[2]; /* virtualized PerfCtr[01] */
+ uint32_t ce_pes[2]; /* Pentium II */
+#define ce_cesr ce_pes[0] /* Pentium I */
+};
+
+#define CPC_TICKREG(ev) ((ev)->ce_tsc)
+
+/*
+ * "Well known" bit fields in the Pentium CES register
+ * The interfaces in libcpc should make these #defines uninteresting.
+ */
+#define CPC_P5_CESR_ES0_SHIFT 0
+#define CPC_P5_CESR_ES0_MASK 0x3f
+#define CPC_P5_CESR_ES1_SHIFT 16
+#define CPC_P5_CESR_ES1_MASK 0x3f
+
+#define CPC_P5_CESR_OS0 6
+#define CPC_P5_CESR_USR0 7
+#define CPC_P5_CESR_CLK0 8
+#define CPC_P5_CESR_PC0 9
+#define CPC_P5_CESR_OS1 (CPC_P5_CESR_OS0 + 16)
+#define CPC_P5_CESR_USR1 (CPC_P5_CESR_USR0 + 16)
+#define CPC_P5_CESR_CLK1 (CPC_P5_CESR_CLK0 + 16)
+#define CPC_P5_CESR_PC1 (CPC_P5_CESR_PC0 + 16)
+
+/*
+ * "Well known" bit fields in the Pentium Pro PerfEvtSel registers
+ * The interfaces in libcpc should make these #defines uninteresting.
+ */
+#define CPC_P6_PES_INV 23
+#define CPC_P6_PES_EN 22
+#define CPC_P6_PES_INT 20
+#define CPC_P6_PES_PC 19
+#define CPC_P6_PES_E 18
+#define CPC_P6_PES_OS 17
+#define CPC_P6_PES_USR 16
+
+#define CPC_P6_PES_UMASK_SHIFT 8
+#define CPC_P6_PES_UMASK_MASK (0xffu)
+
+#define CPC_P6_PES_CMASK_SHIFT 24
+#define CPC_P6_PES_CMASK_MASK (0xffu)
+
+#define CPC_P6_PES_PIC0_MASK (0xffu)
+#define CPC_P6_PES_PIC1_MASK (0xffu)
+
+#endif /* __i386 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBCPC_IMPL_H */
diff --git a/usr/src/lib/libcpc/common/llib-lcpc b/usr/src/lib/libcpc/common/llib-lcpc
new file mode 100644
index 0000000000..1650d06f7e
--- /dev/null
+++ b/usr/src/lib/libcpc/common/llib-lcpc
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libcpc.h"
diff --git a/usr/src/lib/libcpc/common/obsoleted.c b/usr/src/lib/libcpc/common/obsoleted.c
new file mode 100644
index 0000000000..776945dd23
--- /dev/null
+++ b/usr/src/lib/libcpc/common/obsoleted.c
@@ -0,0 +1,410 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <libintl.h>
+#include <dirent.h>
+#include <sys/cpc_impl.h>
+
+#include "libcpc.h"
+#include "libcpc_impl.h"
+
+/*
+ * CPC library handle for use by CPCv1 implementation.
+ */
+cpc_t *__cpc = NULL;
+mutex_t __cpc_lock; /* protects __cpc handle */
+int __cpc_v1_cpuver; /* CPU version in use by CPCv1 client */
+
+#ifdef __sparc
+uint64_t __cpc_v1_pcr; /* last bound %pcr value */
+#else
+uint32_t __cpc_v1_pes[2]; /* last bound %pes values */
+#endif /* __sparc */
+
+int
+__cpc_init(void)
+{
+ const char *fn = "__cpc_init";
+ extern cpc_t *__cpc; /* CPC handle for obsolete clients to share */
+
+ (void) mutex_lock(&__cpc_lock);
+ if (__cpc == NULL && (__cpc = cpc_open(CPC_VER_CURRENT)) == NULL) {
+ __cpc_error(fn, dgettext(TEXT_DOMAIN,
+ "Couldn't open CPC library handle\n"));
+ (void) mutex_unlock(&__cpc_lock);
+ return (-1);
+ }
+ (void) mutex_unlock(&__cpc_lock);
+
+ return (0);
+}
+
+int
+cpc_bind_event(cpc_event_t *this, int flags)
+{
+ cpc_set_t *set;
+ cpc_request_t *rp;
+ int ret;
+
+ if (this == NULL) {
+ (void) cpc_rele();
+ return (0);
+ }
+
+ if (__cpc_init() != 0) {
+ errno = ENXIO;
+ return (-1);
+ }
+
+ /*
+ * The cpuver and control fields of the cpc_event_t must be saved off
+ * for later. The user may call cpc_take_sample(), expecting these to
+ * be copied into a different cpc_event_t struct by the kernel. We have
+ * to fake that behavior for CPCv1 clients.
+ */
+ __cpc_v1_cpuver = this->ce_cpuver;
+#ifdef __sparc
+ __cpc_v1_pcr = this->ce_pcr;
+#else
+ __cpc_v1_pes[0] = this->ce_pes[0];
+ __cpc_v1_pes[1] = this->ce_pes[1];
+#endif /* __sparc */
+
+ if ((set = __cpc_eventtoset(__cpc, this, flags)) == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /*
+ * Convert flags to CPC2.
+ */
+ if (flags & CPC_BIND_EMT_OVF) {
+ for (rp = set->cs_request; rp != NULL; rp = rp->cr_next)
+ rp->cr_flags |= CPC_OVF_NOTIFY_EMT;
+ flags &= ~CPC_BIND_EMT_OVF;
+ }
+
+ ret = cpc_bind_curlwp(__cpc, set, flags);
+
+ (void) cpc_set_destroy(__cpc, set);
+
+ return (ret);
+}
+
+int
+cpc_take_sample(cpc_event_t *this)
+{
+ this->ce_cpuver = __cpc_v1_cpuver;
+#ifdef __sparc
+ this->ce_pcr = __cpc_v1_pcr;
+#else
+ this->ce_pes[0] = __cpc_v1_pes[0];
+ this->ce_pes[1] = __cpc_v1_pes[1];
+#endif /* __sparc */
+
+ return (syscall(SYS_cpc, CPC_SAMPLE, -1, this->ce_pic, &this->ce_hrt,
+ &CPC_TICKREG(this), 0));
+}
+
+int
+cpc_count_usr_events(int enable)
+{
+ return (syscall(SYS_cpc, CPC_USR_EVENTS, -1, (void *)enable, 0));
+}
+
+int
+cpc_count_sys_events(int enable)
+{
+ return (syscall(SYS_cpc, CPC_SYS_EVENTS, -1, (void *)enable, 0));
+}
+
+int
+cpc_rele(void)
+{
+ return (syscall(SYS_cpc, CPC_RELE, -1, NULL, 0));
+}
+
+/*
+ * See if the system call is working and installed.
+ *
+ * We invoke the system call with nonsense arguments - if it's
+ * there and working correctly, it will return EINVAL.
+ *
+ * (This avoids the user getting a SIGSYS core dump when they attempt
+ * to bind on older hardware)
+ */
+int
+cpc_access(void)
+{
+ void (*handler)(int);
+ int error = 0;
+ const char fn[] = "access";
+
+ handler = signal(SIGSYS, SIG_IGN);
+ if (syscall(SYS_cpc, -1, -1, NULL, 0) == -1 &&
+ errno != EINVAL)
+ error = errno;
+ (void) signal(SIGSYS, handler);
+
+ switch (error) {
+ case EAGAIN:
+ __cpc_error(fn, dgettext(TEXT_DOMAIN, "Another process may be "
+ "sampling system-wide CPU statistics\n"));
+ break;
+ case ENOSYS:
+ __cpc_error(fn,
+ dgettext(TEXT_DOMAIN, "CPU performance counters "
+ "are inaccessible on this machine\n"));
+ break;
+ default:
+ __cpc_error(fn, "%s\n", strerror(errno));
+ break;
+ case 0:
+ return (0);
+ }
+
+ errno = error;
+ return (-1);
+}
+
+/*
+ * To look at the system-wide counters, we have to open the
+ * 'shared' device. Once that device is open, no further contexts
+ * can be installed (though one open is needed per CPU)
+ */
+int
+cpc_shared_open(void)
+{
+ const char driver[] = CPUDRV_SHARED;
+
+ return (open(driver, O_RDWR));
+}
+
+void
+cpc_shared_close(int fd)
+{
+ (void) cpc_shared_rele(fd);
+ (void) close(fd);
+}
+
+int
+cpc_shared_bind_event(int fd, cpc_event_t *this, int flags)
+{
+ extern cpc_t *__cpc;
+ cpc_set_t *set;
+ int ret;
+ char *packed_set;
+ size_t packsize;
+ int subcode;
+ __cpc_args_t cpc_args;
+
+ if (this == NULL) {
+ (void) cpc_shared_rele(fd);
+ return (0);
+ } else if (flags != 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (__cpc_init() != 0) {
+ errno = ENXIO;
+ return (-1);
+ }
+
+ if ((set = __cpc_eventtoset(__cpc, this, flags)) == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ __cpc_v1_cpuver = this->ce_cpuver;
+
+ if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ cpc_args.udata1 = packed_set;
+ cpc_args.udata2 = (void *)packsize;
+ cpc_args.udata3 = (void *)&subcode;
+
+ ret = ioctl(fd, CPCIO_BIND, &cpc_args);
+
+ free(packed_set);
+ (void) cpc_set_destroy(__cpc, set);
+
+ return (ret);
+}
+
+int
+cpc_shared_take_sample(int fd, cpc_event_t *this)
+{
+ __cpc_args_t args;
+
+ args.udata1 = this->ce_pic;
+ args.udata2 = &this->ce_hrt;
+ args.udata3 = &CPC_TICKREG(this);
+
+ this->ce_cpuver = __cpc_v1_cpuver;
+
+ return (ioctl(fd, CPCIO_SAMPLE, &args));
+}
+
+int
+cpc_shared_rele(int fd)
+{
+ return (ioctl(fd, CPCIO_RELE, 0));
+}
+
+int
+cpc_pctx_bind_event(pctx_t *pctx, id_t lwpid, cpc_event_t *event, int flags)
+{
+ cpc_set_t *set;
+ int ret;
+
+ if (event == NULL)
+ return (cpc_pctx_rele(pctx, lwpid));
+
+ if (__cpc_init() != 0) {
+ errno = ENXIO;
+ return (-1);
+ }
+
+ else if (flags != 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((set = __cpc_eventtoset(__cpc, event, flags)) == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /*
+ * The cpuver and control fields of the cpc_event_t must be saved off
+ * for later. The user may call cpc_take_sample(), expecting these to
+ * be copied into a different cpc_event_t struct by the kernel. We have
+ * to fake that behavior for CPCv1 clients.
+ */
+ __cpc_v1_cpuver = event->ce_cpuver;
+
+ ret = cpc_bind_pctx(__cpc, pctx, lwpid, set, 0);
+
+ (void) cpc_set_destroy(__cpc, set);
+
+ return (ret);
+}
+
+int
+cpc_pctx_take_sample(pctx_t *pctx, id_t lwpid, cpc_event_t *event)
+{
+ event->ce_cpuver = __cpc_v1_cpuver;
+
+ return (__pctx_cpc(pctx, __cpc, CPC_SAMPLE, lwpid, event->ce_pic,
+ &event->ce_hrt, &CPC_TICKREG(event), CPC1_BUFSIZE));
+}
+
+/*
+ * Given a process context and an lwpid, mark the CPU performance
+ * counter context as invalid.
+ */
+int
+cpc_pctx_invalidate(pctx_t *pctx, id_t lwpid)
+{
+ return (__pctx_cpc(pctx, __cpc, CPC_INVALIDATE, lwpid, 0, 0, 0, 0));
+}
+
+/*
+ * Given a process context and an lwpid, remove all our
+ * hardware context from it.
+ */
+int
+cpc_pctx_rele(pctx_t *pctx, id_t lwpid)
+{
+ return (__pctx_cpc(pctx, __cpc, CPC_RELE, lwpid, 0, 0, 0, 0));
+}
+
+static cpc_errfn_t *__cpc_uerrfn;
+
+/*PRINTFLIKE2*/
+void
+__cpc_error(const char *fn, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (__cpc_uerrfn)
+ __cpc_uerrfn(fn, fmt, ap);
+ else {
+ (void) fprintf(stderr, "libcpc: %s: ", fn);
+ (void) vfprintf(stderr, fmt, ap);
+ }
+ va_end(ap);
+}
+
+void
+cpc_seterrfn(cpc_errfn_t *errfn)
+{
+ __cpc_uerrfn = errfn;
+}
+
+/*
+ * cpc_version() is only for CPC1 clients.
+ */
+uint_t __cpc_workver = CPC_VER_1;
+
+uint_t
+cpc_version(uint_t ver)
+{
+ __cpc_workver = CPC_VER_1;
+
+ switch (ver) {
+ case CPC_VER_NONE:
+ case CPC_VER_CURRENT:
+ return (CPC_VER_CURRENT);
+ case CPC_VER_1:
+ /*
+ * As long as the client is using cpc_version() at all, it is
+ * a CPCv1 client. We still allow CPCv1 clients to compile on
+ * CPCv2 systems.
+ */
+ return (CPC_VER_1);
+ }
+
+ return (CPC_VER_NONE);
+}
diff --git a/usr/src/lib/libcpc/common/subr.c b/usr/src/lib/libcpc/common/subr.c
new file mode 100644
index 0000000000..68a94c4c1d
--- /dev/null
+++ b/usr/src/lib/libcpc/common/subr.c
@@ -0,0 +1,221 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libintl.h>
+#include <libnvpair.h>
+#include <thread.h>
+#include <synch.h>
+
+#include "libcpc.h"
+#include "libcpc_impl.h"
+
+/*
+ * Pack a request set into a buffer using libnvpair. Size of buffer is returned
+ * in buflen.
+ */
+char *
+__cpc_pack_set(cpc_set_t *set, uint_t flags, size_t *buflen)
+{
+ cpc_request_t *req;
+ nvlist_t *setlist, **reqlist;
+ size_t packsize = 0;
+ char *buf = NULL;
+ int i;
+ int j;
+
+ if (nvlist_alloc(&setlist, 0, 0) == ENOMEM) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ if ((reqlist = (nvlist_t **)malloc(set->cs_nreqs * sizeof (*reqlist)))
+ == NULL) {
+ nvlist_free(setlist);
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ bzero((void *)reqlist, set->cs_nreqs * sizeof (*reqlist));
+
+ i = 0;
+ for (req = set->cs_request; req != NULL; req = req->cr_next) {
+ if (nvlist_alloc(&reqlist[i], 0, 0) == ENOMEM)
+ goto nomem;
+
+ if (nvlist_add_string(reqlist[i], "cr_event",
+ req->cr_event) != 0)
+ goto nomem;
+ if (nvlist_add_uint64(reqlist[i], "cr_preset",
+ req->cr_preset) != 0)
+ goto nomem;
+ if (nvlist_add_uint32(reqlist[i], "cr_flags",
+ req->cr_flags) != 0)
+ goto nomem;
+ if (nvlist_add_uint32(reqlist[i], "cr_index",
+ req->cr_index) != 0)
+ goto nomem;
+
+ if (req->cr_nattrs != 0) {
+ nvlist_t *attrs;
+
+ if (nvlist_alloc(&attrs, NV_UNIQUE_NAME, 0) == ENOMEM)
+ goto nomem;
+
+ for (j = 0; j < req->cr_nattrs; j++) {
+ if (nvlist_add_uint64(attrs,
+ req->cr_attr[j].ka_name,
+ req->cr_attr[j].ka_val) != 0) {
+ nvlist_free(attrs);
+ goto nomem;
+ }
+ }
+
+ if (nvlist_add_nvlist(reqlist[i], "cr_attr",
+ attrs) != 0) {
+ nvlist_free(attrs);
+ goto nomem;
+ }
+
+ nvlist_free(attrs);
+ }
+ i++;
+ }
+
+ if (nvlist_add_nvlist_array(setlist, "reqs", reqlist,
+ set->cs_nreqs) != 0)
+ goto nomem;
+
+ if (nvlist_add_uint32(setlist, "flags", flags) != 0)
+ goto nomem;
+
+ if (nvlist_pack(setlist, &buf, &packsize, NV_ENCODE_NATIVE,
+ 0) != 0)
+ goto nomem;
+
+ for (i = 0; i < set->cs_nreqs; i++)
+ nvlist_free(reqlist[i]);
+
+ nvlist_free(setlist);
+ free(reqlist);
+
+ *buflen = packsize;
+ return (buf);
+
+nomem:
+ for (i = 0; i < set->cs_nreqs; i++) {
+ if (reqlist[i] != 0)
+ nvlist_free(reqlist[i]);
+ }
+ nvlist_free(setlist);
+ free(reqlist);
+ errno = ENOMEM;
+ return (NULL);
+}
+
+cpc_strhash_t *
+__cpc_strhash_alloc(void)
+{
+ cpc_strhash_t *p;
+
+ if ((p = malloc(sizeof (cpc_strhash_t))) == NULL)
+ return (NULL);
+
+ p->str = "";
+ p->cur = NULL;
+ p->next = NULL;
+
+ return (p);
+}
+
+void
+__cpc_strhash_free(cpc_strhash_t *hash)
+{
+ cpc_strhash_t *p = hash, *f;
+
+ while (p != NULL) {
+ f = p;
+ p = p->next;
+ free(f);
+ }
+}
+
+/*
+ * Insert a new key into the hash table.
+ *
+ * Returns 0 if key was unique and insert successful.
+ *
+ * Returns 1 if key was already in table and no insert took place.
+ *
+ * Returns -1 if out of memory.
+ */
+int
+__cpc_strhash_add(cpc_strhash_t *hash, char *key)
+{
+ cpc_strhash_t *p, *tmp;
+
+ for (p = hash; p != NULL; p = p->next) {
+ if (strcmp(p->str, key) == 0)
+ return (1);
+ }
+
+ if ((p = malloc(sizeof (*p))) == NULL)
+ return (-1);
+
+ p->str = key;
+ tmp = hash->next;
+ hash->next = p;
+ p->next = tmp;
+ /*
+ * The head node's current pointer must stay pointed at the first
+ * real node. We just inserted at the head.
+ */
+ hash->cur = p;
+
+ return (0);
+}
+
+char *
+__cpc_strhash_next(cpc_strhash_t *hash)
+{
+ cpc_strhash_t *p;
+
+ if (hash->cur != NULL) {
+ p = hash->cur;
+ hash->cur = hash->cur->next;
+ return (p->str);
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/lib/libcpc/i386/Makefile b/usr/src/lib/libcpc/i386/Makefile
new file mode 100644
index 0000000000..3dacd61ab9
--- /dev/null
+++ b/usr/src/lib/libcpc/i386/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+MACHCOBJS = conf_pentium.o event_pentium.o
+ASOBJS = getcpuid.o
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libcpc/i386/conf_pentium.c b/usr/src/lib/libcpc/i386/conf_pentium.c
new file mode 100644
index 0000000000..d8bb18570c
--- /dev/null
+++ b/usr/src/lib/libcpc/i386/conf_pentium.c
@@ -0,0 +1,558 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <alloca.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libintl.h>
+
+#include "libcpc.h"
+#include "libcpc_impl.h"
+
+/*
+ * Configuration data for Pentium Pro performance counters.
+ *
+ * Definitions taken from [3]. See the reference to
+ * understand what any of these settings actually means.
+ *
+ * [3] "Pentium Pro Family Developer's Manual, Volume 3:
+ * Operating Systems Writer's Manual," January 1996
+ */
+
+#define V_P5 (1u << 0) /* specific to Pentium cpus */
+#define V_P5mmx (1u << 1) /* " MMX instructions */
+#define V_P6 (1u << 2) /* specific to Pentium II cpus */
+#define V_P6mmx (1u << 3) /* " MMX instructions */
+#define V_END 0
+
+/*
+ * map from "cpu version" to flag bits
+ */
+static const uint_t cpuvermap[] = {
+ V_P5, /* CPC_PENTIUM */
+ V_P5 | V_P5mmx, /* CPC_PENTIUM_MMX */
+ V_P6, /* CPC_PENTIUM_PRO */
+ V_P6 | V_P6mmx, /* CPC_PENTIUM_PRO_MMX */
+};
+
+struct nametable {
+ const uint_t ver;
+ const uint8_t bits;
+ const char *name;
+};
+
+/*
+ * Basic Pentium events
+ */
+#define P5_EVENTS(v) \
+ {v, 0x0, "data_read"}, \
+ {v, 0x1, "data_write"}, \
+ {v, 0x2, "data_tlb_miss"}, \
+ {v, 0x3, "data_read_miss"}, \
+ {v, 0x4, "data_write_miss"}, \
+ {v, 0x5, "write_hit_to_M_or_E"}, \
+ {v, 0x6, "dcache_lines_wrback"}, \
+ {v, 0x7, "external_snoops"}, \
+ {v, 0x8, "external_dcache_snoop_hits"}, \
+ {v, 0x9, "memory_access_in_both_pipes"}, \
+ {v, 0xa, "bank_conflicts"}, \
+ {v, 0xb, "misaligned_ref"}, \
+ {v, 0xc, "code_read"}, \
+ {v, 0xd, "code_tlb_miss"}, \
+ {v, 0xe, "code_cache_miss"}, \
+ {v, 0xf, "any_segreg_loaded"}, \
+ {v, 0x12, "branches"}, \
+ {v, 0x13, "btb_hits"}, \
+ {v, 0x14, "taken_or_btb_hit"}, \
+ {v, 0x15, "pipeline_flushes"}, \
+ {v, 0x16, "instr_exec"}, \
+ {v, 0x17, "instr_exec_V_pipe"}, \
+ {v, 0x18, "clks_bus_cycle"}, \
+ {v, 0x19, "clks_full_wbufs"}, \
+ {v, 0x1a, "pipe_stall_read"}, \
+ {v, 0x1b, "stall_on_write_ME"}, \
+ {v, 0x1c, "locked_bus_cycle"}, \
+ {v, 0x1d, "io_rw_cycles"}, \
+ {v, 0x1e, "reads_noncache_mem"}, \
+ {v, 0x1f, "pipeline_agi_stalls"}, \
+ {v, 0x22, "flops"}, \
+ {v, 0x23, "bp_match_dr0"}, \
+ {v, 0x24, "bp_match_dr1"}, \
+ {v, 0x25, "bp_match_dr2"}, \
+ {v, 0x26, "bp_match_dr3"}, \
+ {v, 0x27, "hw_intrs"}, \
+ {v, 0x28, "data_rw"}, \
+ {v, 0x29, "data_rw_miss"}
+
+static const struct nametable P5mmx_names0[] = {
+ P5_EVENTS(V_P5),
+ {V_P5mmx, 0x2a, "bus_ownership_latency"},
+ {V_P5mmx, 0x2b, "mmx_instr_upipe"},
+ {V_P5mmx, 0x2c, "cache_M_line_sharing"},
+ {V_P5mmx, 0x2d, "emms_instr"},
+ {V_P5mmx, 0x2e, "bus_util_processor"},
+ {V_P5mmx, 0x2f, "sat_mmx_instr"},
+ {V_P5mmx, 0x30, "clks_not_HLT"},
+ {V_P5mmx, 0x31, "mmx_data_read"},
+ {V_P5mmx, 0x32, "clks_fp_stall"},
+ {V_P5mmx, 0x33, "d1_starv_fifo_0"},
+ {V_P5mmx, 0x34, "mmx_data_write"},
+ {V_P5mmx, 0x35, "pipe_flush_wbp"},
+ {V_P5mmx, 0x36, "mmx_misalign_data_refs"},
+ {V_P5mmx, 0x37, "rets_pred_incorrect"},
+ {V_P5mmx, 0x38, "mmx_multiply_unit_interlock"},
+ {V_P5mmx, 0x39, "rets"},
+ {V_P5mmx, 0x3a, "btb_false_entries"},
+ {V_P5mmx, 0x3b, "clocks_stall_full_wb"},
+ {V_END}
+};
+
+static const struct nametable P5mmx_names1[] = {
+ P5_EVENTS(V_P5),
+ {V_P5mmx, 0x2a, "bus_ownership_transfers"},
+ {V_P5mmx, 0x2b, "mmx_instr_vpipe"},
+ {V_P5mmx, 0x2c, "cache_lint_sharing"},
+ {V_P5mmx, 0x2d, "mmx_fp_transitions"},
+ {V_P5mmx, 0x2e, "writes_noncache_mem"},
+ {V_P5mmx, 0x2f, "sats_performed"},
+ {V_P5mmx, 0x30, "clks_dcache_tlb_miss"},
+ {V_P5mmx, 0x31, "mmx_data_read_miss"},
+ {V_P5mmx, 0x32, "taken_br"},
+ {V_P5mmx, 0x33, "d1_starv_fifo_1"},
+ {V_P5mmx, 0x34, "mmx_data_write_miss"},
+ {V_P5mmx, 0x35, "pipe_flush_wbp_wb"},
+ {V_P5mmx, 0x36, "mmx_pipe_stall_data_read"},
+ {V_P5mmx, 0x37, "rets_pred"},
+ {V_P5mmx, 0x38, "movd_movq_stall"},
+ {V_P5mmx, 0x39, "rsb_overflow"},
+ {V_P5mmx, 0x3a, "btb_mispred_nt"},
+ {V_P5mmx, 0x3b, "mmx_stall_write_ME"},
+ {V_END}
+};
+
+static const struct nametable *P5mmx_names[2] = {
+ P5mmx_names0,
+ P5mmx_names1
+};
+
+/*
+ * Pentium Pro and Pentium II events
+ */
+static const struct nametable P6_names[] = {
+ /*
+ * Data cache unit
+ */
+ {V_P6, 0x43, "data_mem_refs"},
+ {V_P6, 0x45, "dcu_lines_in"},
+ {V_P6, 0x46, "dcu_m_lines_in"},
+ {V_P6, 0x47, "dcu_m_lines_out"},
+ {V_P6, 0x48, "dcu_miss_outstanding"},
+
+ /*
+ * Instruction fetch unit
+ */
+ {V_P6, 0x80, "ifu_ifetch"},
+ {V_P6, 0x81, "ifu_ifetch_miss"},
+ {V_P6, 0x85, "itlb_miss"},
+ {V_P6, 0x86, "ifu_mem_stall"},
+ {V_P6, 0x87, "ild_stall"},
+
+ /*
+ * L2 cache
+ */
+ {V_P6, 0x28, "l2_ifetch"},
+ {V_P6, 0x29, "l2_ld"},
+ {V_P6, 0x2a, "l2_st"},
+ {V_P6, 0x24, "l2_lines_in"},
+ {V_P6, 0x26, "l2_lines_out"},
+ {V_P6, 0x25, "l2_m_lines_inm"},
+ {V_P6, 0x27, "l2_m_lines_outm"},
+ {V_P6, 0x2e, "l2_rqsts"},
+ {V_P6, 0x21, "l2_ads"},
+ {V_P6, 0x22, "l2_dbus_busy"},
+ {V_P6, 0x23, "l2_dbus_busy_rd"},
+
+ /*
+ * External bus logic
+ */
+ {V_P6, 0x62, "bus_drdy_clocks"},
+ {V_P6, 0x63, "bus_lock_clocks"},
+ {V_P6, 0x60, "bus_req_outstanding"},
+ {V_P6, 0x65, "bus_tran_brd"},
+ {V_P6, 0x66, "bus_tran_rfo"},
+ {V_P6, 0x67, "bus_trans_wb"},
+ {V_P6, 0x68, "bus_tran_ifetch"},
+ {V_P6, 0x69, "bus_tran_inval"},
+ {V_P6, 0x6a, "bus_tran_pwr"},
+ {V_P6, 0x6b, "bus_trans_p"},
+ {V_P6, 0x6c, "bus_trans_io"},
+ {V_P6, 0x6d, "bus_tran_def"},
+ {V_P6, 0x6e, "bus_tran_burst"},
+ {V_P6, 0x70, "bus_tran_any"},
+ {V_P6, 0x6f, "bus_tran_mem"},
+ {V_P6, 0x64, "bus_data_rcv"},
+ {V_P6, 0x61, "bus_bnr_drv"},
+ {V_P6, 0x7a, "bus_hit_drv"},
+ {V_P6, 0x7b, "bus_hitm_drv"},
+ {V_P6, 0x7e, "bus_snoop_stall"},
+
+ /*
+ * Floating point unit
+ */
+ {V_P6, 0xc1, "flops"}, /* 0 only */
+ {V_P6, 0x10, "fp_comp_ops_exe"}, /* 0 only */
+ {V_P6, 0x11, "fp_assist"}, /* 1 only */
+ {V_P6, 0x12, "mul"}, /* 1 only */
+ {V_P6, 0x13, "div"}, /* 1 only */
+ {V_P6, 0x14, "cycles_div_busy"}, /* 0 only */
+
+ /*
+ * Memory ordering
+ */
+ {V_P6, 0x3, "ld_blocks"},
+ {V_P6, 0x4, "sb_drains"},
+ {V_P6, 0x5, "misalign_mem_ref"},
+
+ /*
+ * Instruction decoding and retirement
+ */
+ {V_P6, 0xc0, "inst_retired"},
+ {V_P6, 0xc2, "uops_retired"},
+ {V_P6, 0xd0, "inst_decoder"},
+
+ /*
+ * Interrupts
+ */
+ {V_P6, 0xc8, "hw_int_rx"},
+ {V_P6, 0xc6, "cycles_int_masked"},
+ {V_P6, 0xc7, "cycles_int_pending_and_masked"},
+
+ /*
+ * Branches
+ */
+ {V_P6, 0xc4, "br_inst_retired"},
+ {V_P6, 0xc5, "br_miss_pred_retired"},
+ {V_P6, 0xc9, "br_taken_retired"},
+ {V_P6, 0xca, "br_miss_pred_taken_ret"},
+ {V_P6, 0xe0, "br_inst_decoded"},
+ {V_P6, 0xe2, "btb_misses"},
+ {V_P6, 0xe4, "br_bogus"},
+ {V_P6, 0xe6, "baclears"},
+
+ /*
+ * Stalls
+ */
+ {V_P6, 0xa2, "resource_stalls"},
+ {V_P6, 0xd2, "partial_rat_stalls"},
+
+ /*
+ * Segment register loads
+ */
+ {V_P6, 0x6, "segment_reg_loads"},
+
+ /*
+ * Clocks
+ */
+ {V_P6, 0x79, "cpu_clk_unhalted"},
+
+ /*
+ * MMX
+ */
+ {V_P6mmx, 0xb0, "mmx_instr_exec"},
+ {V_P6mmx, 0xb1, "mmx_sat_instr_exec"},
+ {V_P6mmx, 0xb2, "mmx_uops_exec"},
+ {V_P6mmx, 0xb3, "mmx_instr_type_exec"},
+ {V_P6mmx, 0xcc, "fp_mmx_trans"},
+ {V_P6mmx, 0xcd, "mmx_assists"},
+ {V_P6mmx, 0xce, "mmx_instr_ret"},
+ {V_P6mmx, 0xd4, "seg_rename_stalls"},
+ {V_P6mmx, 0xd5, "seg_reg_renames"},
+ {V_P6mmx, 0xd6, "ret_seg_renames"},
+
+ {V_END}
+};
+
+#define MAPCPUVER(cpuver) (cpuvermap[(cpuver) - CPC_PENTIUM])
+
+static int
+validargs(int cpuver, int regno)
+{
+ if (regno < 0 || regno > 1)
+ return (0);
+ cpuver -= CPC_PENTIUM;
+ if (cpuver < 0 ||
+ cpuver >= sizeof (cpuvermap) / sizeof (cpuvermap[0]))
+ return (0);
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+versionmatch(int cpuver, int regno, const struct nametable *n)
+{
+ if (!validargs(cpuver, regno) || (n->ver & MAPCPUVER(cpuver)) == 0)
+ return (0);
+
+ switch (MAPCPUVER(cpuver)) {
+ case V_P5:
+ case V_P5 | V_P5mmx:
+ break;
+ case V_P6:
+ case V_P6 | V_P6mmx:
+ switch (n->bits) {
+ case 0xc1: /* flops */
+ case 0x10: /* fp_comp_ops_exe */
+ case 0x14: /* cycles_div_busy */
+ /* only reg0 counts these */
+ if (regno == 1)
+ return (0);
+ break;
+ case 0x11: /* fp_assist */
+ case 0x12: /* mul */
+ case 0x13: /* div */
+ /* only 1 can count these */
+ if (regno == 0)
+ return (0);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return (0);
+ }
+
+ return (1);
+}
+
+static const struct nametable *
+getnametable(int cpuver, int regno)
+{
+ const struct nametable *n;
+
+ if (!validargs(cpuver, regno))
+ return (NULL);
+
+ switch (MAPCPUVER(cpuver)) {
+ case V_P5:
+ case V_P5 | V_P5mmx:
+ n = P5mmx_names[regno];
+ break;
+ case V_P6:
+ case V_P6 | V_P6mmx:
+ n = P6_names;
+ break;
+ default:
+ n = NULL;
+ break;
+ }
+
+ return (n);
+}
+
+void
+cpc_walk_names(int cpuver, int regno, void *arg,
+ void (*action)(void *, int, const char *, uint8_t))
+{
+ const struct nametable *n;
+
+ if ((n = getnametable(cpuver, regno)) == NULL)
+ return;
+ for (; n->ver != V_END; n++)
+ if (versionmatch(cpuver, regno, n))
+ action(arg, regno, n->name, n->bits);
+}
+
+const char *
+__cpc_reg_to_name(int cpuver, int regno, uint8_t bits)
+{
+ const struct nametable *n;
+
+ if ((n = getnametable(cpuver, regno)) == NULL)
+ return (NULL);
+ for (; n->ver != V_END; n++)
+ if (bits == n->bits && versionmatch(cpuver, regno, n))
+ return (n->name);
+ return (NULL);
+}
+
+/*
+ * Register names can be specified as strings or even as numbers
+ */
+int
+__cpc_name_to_reg(int cpuver, int regno, const char *name, uint8_t *bits)
+{
+ const struct nametable *n;
+ char *eptr = NULL;
+ long value;
+
+ if ((n = getnametable(cpuver, regno)) == NULL || name == NULL)
+ return (-1);
+ for (; n->ver != V_END; n++)
+ if (strcmp(name, n->name) == 0 &&
+ versionmatch(cpuver, regno, n)) {
+ *bits = n->bits;
+ return (0);
+ }
+
+ value = strtol(name, &eptr, 0);
+ if (name != eptr && value >= 0 && value <= UINT8_MAX) {
+ *bits = (uint8_t)value;
+ return (0);
+ }
+
+ return (-1);
+}
+
+const char *
+cpc_getcciname(int cpuver)
+{
+ if (validargs(cpuver, 0))
+ switch (MAPCPUVER(cpuver)) {
+ case V_P5:
+ return ("Pentium");
+ case V_P5 | V_P5mmx:
+ return ("Pentium with MMX");
+ case V_P6:
+ return ("Pentium Pro, Pentium II");
+ case V_P6 | V_P6mmx:
+ return ("Pentium Pro with MMX, Pentium II");
+ default:
+ break;
+ }
+ return (NULL);
+}
+
+const char *
+cpc_getcpuref(int cpuver)
+{
+ if (validargs(cpuver, 0))
+ switch (MAPCPUVER(cpuver)) {
+ case V_P5:
+ case V_P5 | V_P5mmx:
+ return (gettext(
+ "See Appendix A.2 of the \"Intel Architecture "
+ "Software Developer's Manual,\" 243192, 1997"));
+ case V_P6:
+ case V_P6 | V_P6mmx:
+ return (gettext(
+ "See Appendix A.1 of the \"Intel Architecture "
+ "Software Developer's Manual,\" 243192, 1997"));
+ default:
+ break;
+ }
+ return (NULL);
+}
+
+/*
+ * This is a functional interface to allow CPUs with fewer %pic registers
+ * to share the same data structure as those with more %pic registers
+ * within the same instruction set family.
+ */
+uint_t
+cpc_getnpic(int cpuver)
+{
+ switch (cpuver) {
+ case CPC_PENTIUM:
+ case CPC_PENTIUM_MMX:
+ case CPC_PENTIUM_PRO:
+ case CPC_PENTIUM_PRO_MMX:
+#define EVENT ((cpc_event_t *)0)
+ return (sizeof (EVENT->ce_pic) / sizeof (EVENT->ce_pic[0]));
+#undef EVENT
+ default:
+ return (0);
+ }
+}
+
+#define BITS(v, u, l) \
+ (((v) >> (l)) & ((1 << (1 + (u) - (l))) - 1))
+
+#include "getcpuid.h"
+
+/*
+ * Return the version of the current processor.
+ *
+ * Version -1 is defined as 'not performance counter capable'
+ */
+int
+cpc_getcpuver(void)
+{
+ static int ver = -1;
+ uint32_t maxeax;
+ uint32_t vbuf[4];
+
+ if (ver != -1)
+ return (ver);
+
+ maxeax = cpc_getcpuid(0, &vbuf[0], &vbuf[2], &vbuf[1]);
+ {
+ char *vendor = (char *)vbuf;
+ vendor[12] = '\0';
+
+ if (strcmp(vendor, "GenuineIntel") != 0)
+ return (ver);
+ }
+
+ if (maxeax >= 1) {
+ int family, model;
+ uint32_t eax, ebx, ecx, edx;
+
+ eax = cpc_getcpuid(1, &ebx, &ecx, &edx);
+
+ if ((family = BITS(eax, 11, 8)) == 0xf)
+ family = BITS(eax, 27, 20);
+ if ((model = BITS(eax, 7, 4)) == 0xf)
+ model = BITS(eax, 19, 16);
+
+ /*
+ * map family and model into the performance
+ * counter architectures we currently understand.
+ *
+ * See application note AP485 (from developer.intel.com)
+ * for further explanation.
+ */
+ switch (family) {
+ case 5: /* Pentium and Pentium with MMX */
+ ver = model < 4 ?
+ CPC_PENTIUM : CPC_PENTIUM_MMX;
+ break;
+ case 6: /* Pentium Pro and Pentium II and III */
+ ver = BITS(edx, 23, 23) ? /* mmx check */
+ CPC_PENTIUM_PRO_MMX : CPC_PENTIUM_PRO;
+ break;
+ default:
+ case 0xf: /* Pentium IV */
+ break;
+ }
+ }
+
+ return (ver);
+}
diff --git a/usr/src/lib/libcpc/i386/event_pentium.c b/usr/src/lib/libcpc/i386/event_pentium.c
new file mode 100644
index 0000000000..423de87694
--- /dev/null
+++ b/usr/src/lib/libcpc/i386/event_pentium.c
@@ -0,0 +1,873 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to capture processor-dependencies in event specification.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <alloca.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libintl.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "libcpc.h"
+#include "libcpc_impl.h"
+
+/*
+ * Event specifications for Pentium performance counters are based
+ * on the content of a getsubopt-like string.
+ * The string should contain something that looks like this:
+ *
+ * pic0=<eventspec>,pic1=<eventspec>
+ * [,cmask0=<maskspec>][,cmask1=<maskspec>]
+ * [,umask0=<maskspec>][,umask1=<maskspec>]
+ * [,inv[0|1]][,noedge[0|1]]
+ * [,sys[0|1]][,nouser[0|1]]
+ *
+ * For example:
+ * pic0=data_mem_refs,pic1=l2_ld,sys
+ * or
+ * pic0=l2_ld,pic1=bus_drdy_clocks,umask1=0x20,nouser1
+ *
+ * By default, user event counting is enabled, system event counting
+ * is disabled.
+ *
+ * Note that Pentium and Pentium Pro have different event specifications.
+ *
+ * The two events must be named. The names can be ascii or
+ * a decimal, octal or hexadecimal number as parsed by strtol(3C).
+ *
+ * The routine counts the number of errors encountered while parsing
+ * the string, if no errors are encountered, the event handle is
+ * returned.
+ */
+
+const char *
+cpc_getusage(int cpuver)
+{
+ switch (cpuver) {
+ case CPC_PENTIUM_PRO_MMX:
+ case CPC_PENTIUM_PRO:
+ return ("pic0=<event0>,pic1=<event1> "
+ "[,sys[0|1]] "
+ "[,nouser[0|1]] "
+ "[,noedge[0|1]] "
+ "[,pc[0|1]] "
+ "[,int[0|1]] "
+ "[,inv[0|1]] "
+ "[,cmask[0|1]=<maskspec>] "
+ "[,umask[0|1]=<maskspec>] ");
+ case CPC_PENTIUM_MMX:
+ case CPC_PENTIUM:
+ return ("pic0=<event0>,pic1=<event1> "
+ "[,sys[0|1]] "
+ "[,nouser[0|1]] "
+ "[,noedge[0|1]] "
+ "[,pc[0|1]]");
+ default:
+ return (NULL);
+ }
+}
+
+struct keyval {
+ char *kv_token;
+ int (*kv_action)(const char *,
+ const struct keyval *, int, char *, uint32_t *);
+ uint_t kv_regno;
+ uint32_t kv_mask;
+ int kv_shift;
+};
+
+/*ARGSUSED*/
+static int
+eightbits(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
+{
+ char *eptr = NULL;
+ long l;
+
+ if (value == NULL) {
+ __cpc_error(fn, gettext("missing '%s' value\n"),
+ kv->kv_token);
+ return (-1);
+ }
+ l = strtol(value, &eptr, 0);
+ if (value == eptr || l < 0 || l > UINT8_MAX) {
+ __cpc_error(fn, gettext("bad '%s' value\n"), kv->kv_token);
+ return (-1);
+ }
+ bits[kv->kv_regno] |= ((uint8_t)l & kv->kv_mask) << kv->kv_shift;
+ return (0);
+}
+
+static int
+picbits(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
+{
+ uint8_t val8;
+ uint_t regno;
+
+ regno = strcmp(kv->kv_token, "pic0") == 0 ? 0 : 1;
+
+ if (value == NULL) {
+ __cpc_error(fn, gettext("missing '%s' value\n"),
+ kv->kv_token);
+ return (-1);
+ }
+
+ if (__cpc_name_to_reg(cpuver, regno, value, &val8) != 0) {
+ switch (cpuver) {
+ case CPC_PENTIUM_PRO_MMX:
+ case CPC_PENTIUM_PRO:
+ assert(kv->kv_regno == regno);
+ __cpc_error(fn, gettext(
+ "PerfCtr%d cannot measure '%s' on this cpu\n"),
+ regno, value);
+ break;
+ case CPC_PENTIUM_MMX:
+ case CPC_PENTIUM:
+ assert(kv->kv_regno == 0);
+ __cpc_error(fn, gettext(
+ "CTR%d cannot measure '%s' on this cpu\n"),
+ regno, value);
+ break;
+ }
+ return (-1);
+ }
+ bits[kv->kv_regno] |= (val8 & kv->kv_mask) << kv->kv_shift;
+ return (0);
+}
+
+/*ARGSUSED2*/
+static int
+bitclr(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
+{
+ if (value != NULL) {
+ __cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
+ return (-1);
+ }
+ bits[kv->kv_regno] &= ~(kv->kv_mask << kv->kv_shift);
+ return (0);
+}
+
+/*ARGSUSED2*/
+static int
+bitset(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
+{
+ if (value != NULL) {
+ __cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
+ return (-1);
+ }
+ bits[kv->kv_regno] |= (kv->kv_mask << kv->kv_shift);
+ return (0);
+}
+
+static int
+nextpair(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint32_t *bits)
+{
+ int rv;
+
+ if (value != NULL) {
+ __cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
+ return (-1);
+ }
+ kv++;
+ if ((rv = kv->kv_action(fn, kv, cpuver, value, bits)) != 0)
+ return (rv);
+ kv++;
+ return (kv->kv_action(fn, kv, cpuver, value, bits));
+}
+
+/*
+ * This token table must match the keyval tables below.
+ */
+
+static char * const tokens[] = {
+#define D_pic0 0
+ "pic0", /* takes a valid event name */
+#define D_pic1 1
+ "pic1", /* takes a valid event name */
+#define D_nouser 2
+ "nouser", /* disables user counts */
+#define D_nouser0 3
+ "nouser0",
+#define D_nouser1 4
+ "nouser1",
+#define D_sys 5
+ "sys", /* enables system counts */
+#define D_sys0 6
+ "sys0",
+#define D_sys1 7
+ "sys1",
+#define D_noedge 8
+ "noedge", /* disable edge detect */
+#define D_noedge0 9
+ "noedge0",
+#define D_noedge1 10
+ "noedge1",
+#define D_pc 11
+ "pc", /* sets pin control high */
+#define D_pc0 12
+ "pc0",
+#define D_pc1 13
+ "pc1",
+
+/*
+ * These additional keywords are for Pentium Pro / Pentium II machines.
+ */
+#define D_int 14
+ "int", /* enable interrupt on counter overflow */
+#define D_int0 15
+ "int0",
+#define D_int1 16
+ "int1",
+#define D_inv 17
+ "inv", /* invert cmask comparison */
+#define D_inv0 18
+ "inv0",
+#define D_inv1 19
+ "inv1",
+#define D_umask0 20
+ "umask0", /* PerfCtr0 unit mask */
+#define D_umask1 21
+ "umask1", /* PerfCtr1 unit mask */
+#define D_cmask0 22
+ "cmask0", /* PerfCtr0 counter mask */
+#define D_cmask1 23
+ "cmask1", /* PerfCtr1 counter mask */
+ NULL
+};
+
+static const struct keyval p6_keyvals[] = {
+ { "pic0", picbits, 0,
+ CPC_P6_PES_PIC0_MASK, 0 },
+ { "pic1", picbits, 1,
+ CPC_P6_PES_PIC1_MASK, 0 },
+ { "nouser", nextpair },
+ { "nouser0", bitclr, 0,
+ UINT32_C(1), CPC_P6_PES_USR },
+ { "nouser1", bitclr, 1,
+ UINT32_C(1), CPC_P6_PES_USR },
+ { "sys", nextpair },
+ { "sys0", bitset, 0,
+ UINT32_C(1), CPC_P6_PES_OS },
+ { "sys1", bitset, 1,
+ UINT32_C(1), CPC_P6_PES_OS },
+ { "noedge", nextpair },
+ { "noedge0", bitclr, 0,
+ UINT32_C(1), CPC_P6_PES_E },
+ { "noedge1", bitclr, 1,
+ UINT32_C(1), CPC_P6_PES_E },
+ { "pc", nextpair },
+ { "pc0", bitset, 0,
+ UINT32_C(1), CPC_P6_PES_PC },
+ { "pc1", bitset, 1,
+ UINT32_C(1), CPC_P6_PES_PC },
+ { "int", nextpair },
+ { "int0", bitset, 0,
+ UINT32_C(1), CPC_P6_PES_INT },
+ { "int1", bitset, 1,
+ UINT32_C(1), CPC_P6_PES_INT },
+ { "inv", nextpair },
+ { "inv0", bitset, 0,
+ UINT32_C(1), CPC_P6_PES_INV },
+ { "inv1", bitset, 1,
+ UINT32_C(1), CPC_P6_PES_INV },
+ { "umask0", eightbits, 0,
+ CPC_P6_PES_UMASK_MASK, CPC_P6_PES_UMASK_SHIFT },
+ { "umask1", eightbits, 1,
+ CPC_P6_PES_UMASK_MASK, CPC_P6_PES_UMASK_SHIFT },
+ { "cmask0", eightbits, 0,
+ CPC_P6_PES_CMASK_MASK, CPC_P6_PES_CMASK_SHIFT },
+ { "cmask1", eightbits, 1,
+ CPC_P6_PES_CMASK_MASK, CPC_P6_PES_CMASK_SHIFT },
+};
+
+/*
+ * Note that this table -must- be an identically indexed
+ * subset of p6_keyvals.
+ */
+static const struct keyval p5_keyvals[] = {
+ { "pic0", picbits, 0,
+ CPC_P5_CESR_ES0_MASK, CPC_P5_CESR_ES0_SHIFT },
+ { "pic1", picbits, 0,
+ CPC_P5_CESR_ES1_MASK, CPC_P5_CESR_ES1_SHIFT },
+ { "nouser", nextpair },
+ { "nouser0", bitclr, 0,
+ UINT32_C(1), CPC_P5_CESR_USR0 },
+ { "nouser1", bitclr, 0,
+ UINT32_C(1), CPC_P5_CESR_USR1 },
+ { "sys", nextpair },
+ { "sys0", bitset, 0,
+ UINT32_C(1), CPC_P5_CESR_OS0 },
+ { "sys1", bitset, 0,
+ UINT32_C(1), CPC_P5_CESR_OS1 },
+ { "noedge", nextpair },
+ { "noedge0", bitset, 0,
+ UINT32_C(1), CPC_P5_CESR_CLK0 },
+ { "noedge1", bitset, 0,
+ UINT32_C(1), CPC_P5_CESR_CLK1 },
+ { "pc", nextpair },
+ { "pc0", bitset, 0,
+ UINT32_C(1), CPC_P5_CESR_PC0 },
+ { "pc1", bitset, 0,
+ UINT32_C(1), CPC_P5_CESR_PC1 },
+};
+
+#if !defined(NDEBUG)
+#pragma init(__tablecheck)
+
+static void
+__tablecheck(void)
+{
+ uint_t ntokens = sizeof (tokens) / sizeof (tokens[0]) - 1;
+ uint_t p6_nkeys = sizeof (p6_keyvals) / sizeof (p6_keyvals[0]);
+ uint_t p5_nkeys = sizeof (p5_keyvals) / sizeof (p5_keyvals[0]);
+ uint_t n;
+
+ assert(ntokens == p6_nkeys);
+ for (n = 0; n < ntokens; n++)
+ assert(strcmp(tokens[n], p6_keyvals[n].kv_token) == 0);
+ assert(p6_nkeys >= p5_nkeys);
+ for (n = 0; n < p5_nkeys; n++)
+ assert(strcmp(tokens[n], p5_keyvals[n].kv_token) == 0);
+}
+
+#endif /* !NDEBUG */
+
+int
+cpc_strtoevent(int cpuver, const char *spec, cpc_event_t *event)
+{
+ static const char fn[] = "strtoevent";
+ char *value;
+ char *pic[2];
+ char *opts;
+ int errcnt = 0;
+ uint_t ntokens;
+ const struct keyval *keyvals;
+ uint32_t *bits;
+
+ if (spec == NULL)
+ return (errcnt = 1);
+
+ bzero(event, sizeof (*event));
+ switch (event->ce_cpuver = cpuver) {
+ case CPC_PENTIUM_PRO_MMX:
+ case CPC_PENTIUM_PRO:
+ keyvals = p6_keyvals;
+ ntokens = sizeof (p6_keyvals) / sizeof (p6_keyvals[0]);
+ bits = &event->ce_pes[0];
+ bits[0] = bits[1] =
+ (1u << CPC_P6_PES_USR) | (1u << CPC_P6_PES_E);
+ break;
+ case CPC_PENTIUM_MMX:
+ case CPC_PENTIUM:
+ keyvals = p5_keyvals;
+ ntokens = sizeof (p5_keyvals) / sizeof (p5_keyvals[0]);
+ bits = &event->ce_cesr;
+ bits[0] =
+ (1u << CPC_P5_CESR_USR0) | (1u << CPC_P5_CESR_USR1);
+ break;
+ default:
+ return (errcnt = 1);
+ }
+
+ pic[0] = pic[1] = NULL;
+
+ opts = strcpy(alloca(strlen(spec) + 1), spec);
+ while (*opts != '\0') {
+ const struct keyval *kv;
+ int idx = getsubopt(&opts, tokens, &value);
+
+ if (idx >= 0 && idx < ntokens) {
+ kv = &keyvals[idx];
+ if (kv->kv_action(fn, kv, cpuver, value, bits) != 0) {
+ errcnt++;
+ break;
+ }
+
+ if (idx == D_pic0) {
+ if (pic[0] != NULL) {
+ __cpc_error(fn,
+ "repeated '%s' token\n",
+ tokens[idx]);
+ errcnt++;
+ break;
+ }
+ pic[0] = value;
+ } else if (idx == D_pic1) {
+ if (pic[1] != NULL) {
+ __cpc_error(fn,
+ "repeated '%s' token\n",
+ tokens[idx]);
+ errcnt++;
+ break;
+ }
+ pic[1] = value;
+ }
+ } else if (idx == -1) {
+ /*
+ * The token given wasn't recognized.
+ * See if it was an implicit pic specification..
+ */
+ if (pic[0] == NULL) {
+ kv = &keyvals[D_pic0];
+ if (kv->kv_action(fn,
+ kv, cpuver, value, bits) != 0) {
+ errcnt++;
+ break;
+ }
+ pic[0] = value;
+ } else if (pic[1] == NULL) {
+ kv = &keyvals[D_pic1];
+ if (kv->kv_action(fn,
+ kv, cpuver, value, bits) != 0) {
+ errcnt++;
+ break;
+ }
+ pic[1] = value;
+ } else {
+ __cpc_error(fn,
+ gettext("bad token '%s'\n"), value);
+ errcnt++;
+ break;
+ }
+ } else {
+ if (idx >= 0 &&
+ idx < sizeof (tokens) / sizeof (tokens[0]))
+ __cpc_error(fn,
+ gettext("bad token '%s'\n"), tokens[idx]);
+ else
+ __cpc_error(fn, gettext("bad token\n"));
+ errcnt++;
+ break;
+ }
+ }
+
+ if (pic[0] == NULL || pic[1] == NULL) {
+ __cpc_error(fn, gettext("two events must be specified\n"));
+ errcnt++;
+ }
+
+ return (errcnt);
+}
+
+/*
+ * Return a printable description of the control registers.
+ *
+ * This routine should always succeed (notwithstanding heap problems),
+ * but may not be able to correctly decode the registers, if, for
+ * example, a new processor is under test.
+ *
+ * The caller is responsible for free(3c)ing the string returned.
+ */
+
+static void
+flagstostr(char *buf, int flag0, int flag1, int defvalue, char *tok)
+{
+ buf += strlen(buf);
+ if (flag0 != defvalue) {
+ if (flag1 != defvalue)
+ (void) sprintf(buf, ",%s", tok);
+ else
+ (void) sprintf(buf, ",%s0", tok);
+ } else {
+ if (flag1 != defvalue)
+ (void) sprintf(buf, ",%s1", tok);
+ }
+}
+
+static void
+masktostr(char *buf, uint8_t bits, char *tok)
+{
+ if (bits != 0) {
+ buf += strlen(buf);
+ (void) sprintf(buf, ",%s=0x%x", tok, bits);
+ }
+}
+
+static char *
+val8tostr(uint8_t bits)
+{
+ char buf[2 + 2 + 1]; /* 0x %2x \0 */
+ (void) snprintf(buf, sizeof (buf), "0x%x", bits);
+ return (strdup(buf));
+}
+
+static char *
+regtostr(int cpuver, int regno, uint8_t bits)
+{
+ const char *sname;
+
+ if ((sname = __cpc_reg_to_name(cpuver, regno, bits)) != NULL)
+ return (strdup(sname));
+ return (val8tostr(bits));
+}
+
+struct xpes {
+ uint8_t cmask, umask, evsel;
+ int usr, sys, edge, inv, irupt, pc;
+};
+
+/*ARGSUSED1*/
+static void
+unmake_pes(uint32_t pes, int cpuver, struct xpes *xpes)
+{
+ xpes->cmask = (uint8_t)(pes >> CPC_P6_PES_CMASK_SHIFT);
+ xpes->pc = (pes >> CPC_P6_PES_PC) & 1u;
+ xpes->inv = (pes >> CPC_P6_PES_INV) & 1u;
+ xpes->irupt = (pes >> CPC_P6_PES_INT) & 1u;
+ xpes->edge = (pes >> CPC_P6_PES_E) & 1u;
+ xpes->sys = (pes >> CPC_P6_PES_OS) & 1u;
+ xpes->usr = (pes >> CPC_P6_PES_USR) & 1u;
+ xpes->umask = (uint8_t)(pes >> CPC_P6_PES_UMASK_SHIFT);
+ xpes->evsel = (uint8_t)pes;
+}
+
+struct xcesr {
+ uint8_t evsel[2];
+ int usr[2], sys[2], clk[2], pc[2];
+};
+
+/*ARGSUSED1*/
+static void
+unmake_cesr(uint32_t cesr, int cpuver, struct xcesr *xcesr)
+{
+ xcesr->evsel[0] = (cesr >> CPC_P5_CESR_ES0_SHIFT) &
+ CPC_P5_CESR_ES0_MASK;
+ xcesr->evsel[1] = (cesr >> CPC_P5_CESR_ES1_SHIFT) &
+ CPC_P5_CESR_ES1_MASK;
+ xcesr->usr[0] = (cesr >> CPC_P5_CESR_USR0) & 1u;
+ xcesr->usr[1] = (cesr >> CPC_P5_CESR_USR1) & 1u;
+ xcesr->sys[0] = (cesr >> CPC_P5_CESR_OS0) & 1u;
+ xcesr->sys[1] = (cesr >> CPC_P5_CESR_OS1) & 1u;
+ xcesr->clk[0] = (cesr >> CPC_P5_CESR_CLK0) & 1u;
+ xcesr->clk[1] = (cesr >> CPC_P5_CESR_CLK1) & 1u;
+ xcesr->pc[0] = (cesr >> CPC_P5_CESR_PC0) & 1u;
+ xcesr->pc[1] = (cesr >> CPC_P5_CESR_PC1) & 1u;
+ /*
+ * If usr and sys are both disabled, the counter is disabled.
+ */
+ if (xcesr->usr[0] == 0 && xcesr->sys[0] == 0)
+ xcesr->clk[0] = 0;
+ if (xcesr->usr[1] == 0 && xcesr->sys[1] == 0)
+ xcesr->clk[1] = 0;
+}
+
+char *
+cpc_eventtostr(cpc_event_t *event)
+{
+ char *pic[2];
+ char buffer[1024];
+ int cpuver = event->ce_cpuver;
+
+ switch (cpuver) {
+ case CPC_PENTIUM_PRO_MMX:
+ case CPC_PENTIUM_PRO:
+ {
+ struct xpes xpes[2];
+
+ unmake_pes(event->ce_pes[0], cpuver, &xpes[0]);
+ if ((pic[0] = regtostr(cpuver, 0, xpes[0].evsel)) == NULL)
+ return (NULL);
+
+ unmake_pes(event->ce_pes[1], cpuver, &xpes[1]);
+ if ((pic[1] = regtostr(cpuver, 1, xpes[1].evsel)) == NULL) {
+ free(pic[0]);
+ return (NULL);
+ }
+ (void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
+ tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
+ free(pic[1]);
+ free(pic[0]);
+ masktostr(buffer, xpes[0].cmask, tokens[D_cmask0]);
+ masktostr(buffer, xpes[1].cmask, tokens[D_cmask1]);
+ masktostr(buffer, xpes[0].umask, tokens[D_umask0]);
+ masktostr(buffer, xpes[1].umask, tokens[D_umask1]);
+ flagstostr(buffer,
+ xpes[0].usr, xpes[1].usr, 1, tokens[D_nouser]);
+ flagstostr(buffer,
+ xpes[0].sys, xpes[1].sys, 0, tokens[D_sys]);
+ flagstostr(buffer,
+ xpes[0].edge, xpes[1].edge, 1, tokens[D_noedge]);
+ flagstostr(buffer,
+ xpes[0].irupt, xpes[1].irupt, 0, tokens[D_int]);
+ flagstostr(buffer,
+ xpes[0].inv, xpes[1].inv, 0, tokens[D_inv]);
+ flagstostr(buffer,
+ xpes[0].pc, xpes[1].pc, 0, tokens[D_pc]);
+ } break;
+ case CPC_PENTIUM_MMX:
+ case CPC_PENTIUM:
+ {
+ struct xcesr xcesr;
+
+ unmake_cesr(event->ce_cesr, cpuver, &xcesr);
+ if ((pic[0] = regtostr(cpuver, 0, xcesr.evsel[0])) == NULL)
+ return (NULL);
+ if ((pic[1] = regtostr(cpuver, 1, xcesr.evsel[1])) == NULL) {
+ free(pic[0]);
+ return (NULL);
+ }
+ (void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
+ tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
+ free(pic[1]);
+ free(pic[0]);
+ flagstostr(buffer,
+ xcesr.usr[0], xcesr.usr[1], 1, tokens[D_nouser]);
+ flagstostr(buffer,
+ xcesr.sys[0], xcesr.sys[1], 0, tokens[D_sys]);
+ flagstostr(buffer,
+ xcesr.clk[0], xcesr.clk[1], 0, tokens[D_noedge]);
+ flagstostr(buffer,
+ xcesr.pc[0], xcesr.pc[1], 0, tokens[D_pc]);
+ } break;
+ default:
+ return (NULL);
+ }
+ return (strdup(buffer));
+}
+
+/*
+ * Utility operations on events
+ */
+void
+cpc_event_accum(cpc_event_t *accum, cpc_event_t *event)
+{
+ if (accum->ce_hrt < event->ce_hrt)
+ accum->ce_hrt = event->ce_hrt;
+ accum->ce_tsc += event->ce_tsc;
+ accum->ce_pic[0] += event->ce_pic[0];
+ accum->ce_pic[1] += event->ce_pic[1];
+}
+
+void
+cpc_event_diff(cpc_event_t *diff, cpc_event_t *left, cpc_event_t *right)
+{
+ diff->ce_hrt = left->ce_hrt;
+ diff->ce_tsc = left->ce_tsc - right->ce_tsc;
+ diff->ce_pic[0] = left->ce_pic[0] - right->ce_pic[0];
+ diff->ce_pic[1] = left->ce_pic[1] - right->ce_pic[1];
+}
+
+/*
+ * Given a cpc_event_t and cpc_bind_event() flags,
+ * translate the cpc_event_t into the cpc_set_t format.
+ *
+ * Returns NULL on failure.
+ */
+cpc_set_t *
+__cpc_eventtoset(cpc_t *cpc, cpc_event_t *event, int iflags)
+{
+ cpc_set_t *set;
+ int cpuver = event->ce_cpuver;
+ char *pic[2];
+ int flags[2] = { 0, 0 };
+ int i;
+ int j;
+ int nattrs;
+ cpc_attr_t *attr;
+ int intr;
+
+ if ((set = cpc_set_create(cpc)) == NULL) {
+ return (NULL);
+ }
+
+ if (iflags & CPC_BIND_EMT_OVF)
+ flags[0] = flags[1] = CPC_OVF_NOTIFY_EMT;
+
+ switch (cpuver) {
+ case CPC_PENTIUM_PRO_MMX:
+ case CPC_PENTIUM_PRO:
+ {
+ struct xpes xpes[2];
+
+ for (i = 0; i < 2; i++) {
+ intr = 0;
+ nattrs = j = 1;
+ unmake_pes(event->ce_pes[i], cpuver, &xpes[i]);
+ if ((pic[i] = regtostr(cpuver, i,
+ xpes[i].evsel)) == NULL) {
+ (void) cpc_set_destroy(cpc, set);
+ return (NULL);
+ }
+ if (xpes[i].usr == 1)
+ flags[i] |= CPC_COUNT_USER;
+ if (xpes[i].sys == 1)
+ flags[i] |= CPC_COUNT_SYSTEM;
+ if (xpes[i].irupt == 1) {
+ nattrs++;
+ intr = 1;
+ }
+
+ if (xpes[i].cmask)
+ nattrs++;
+ if (xpes[i].umask)
+ nattrs++;
+ if (xpes[i].inv)
+ nattrs++;
+ if (xpes[i].pc)
+ nattrs++;
+ if (xpes[i].edge == 0)
+ nattrs++;
+
+ if ((attr = (cpc_attr_t *)malloc(nattrs *
+ sizeof (cpc_attr_t))) == NULL) {
+ (void) cpc_set_destroy(cpc, set);
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ /*
+ * Ensure that pic[0] in the cpc_event_t is bound to
+ * physical pic0.
+ */
+ attr[0].ca_name = "picnum";
+ attr[0].ca_val = i;
+
+ if (intr) {
+ attr[j].ca_name = "int";
+ attr[j].ca_val = 1;
+ j++;
+ }
+ if (xpes[i].cmask) {
+ attr[j].ca_name = "cmask";
+ attr[j].ca_val = xpes[i].cmask;
+ j++;
+ }
+ if (xpes[i].umask) {
+ attr[j].ca_name = "umask";
+ attr[j].ca_val = xpes[i].umask;
+ j++;
+ }
+ if (xpes[i].inv) {
+ attr[j].ca_name = "inv";
+ attr[j].ca_val = 1;
+ j++;
+ }
+ if (xpes[i].pc) {
+ attr[j].ca_name = "pc";
+ attr[j].ca_val = 1;
+ j++;
+ }
+ if (xpes[i].edge == 0) {
+ attr[j].ca_name = "noedge";
+ attr[j].ca_val = 1;
+ j++;
+ }
+
+ if (cpc_set_add_request(cpc, set, pic[i],
+ event->ce_pic[i], flags[i], nattrs, attr) == -1) {
+ (void) cpc_set_destroy(cpc, set);
+ free(pic[i]);
+ free(attr);
+ return (NULL);
+ }
+ free(pic[i]);
+ free(attr);
+ }
+ }
+ break;
+ case CPC_PENTIUM_MMX:
+ case CPC_PENTIUM:
+ {
+ struct xcesr xcesr;
+ unmake_cesr(event->ce_cesr, cpuver, &xcesr);
+
+ for (i = 0; i < 2; i++) {
+ nattrs = j = 1;
+
+ if ((pic[i] = regtostr(cpuver, i, xcesr.evsel[i]))
+ == NULL) {
+ (void) cpc_set_destroy(cpc, set);
+ return (NULL);
+ }
+
+ if (xcesr.usr[i] == 1)
+ flags[i] |= CPC_COUNT_USER;
+ if (xcesr.sys[i] == 1)
+ flags[i] |= CPC_COUNT_SYSTEM;
+ if (xcesr.clk[i] == 1)
+ nattrs++;
+ if (xcesr.pc[i] == 1)
+ nattrs++;
+
+ if ((attr = (cpc_attr_t *)malloc(nattrs *
+ sizeof (cpc_attr_t))) == NULL) {
+ (void) cpc_set_destroy(cpc, set);
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ /*
+ * Ensure that pic[0] in the cpc_event_t is bound to
+ * physical pic0.
+ */
+ attr[0].ca_name = "picnum";
+ attr[0].ca_val = i;
+
+ if (xcesr.clk[i] == 1) {
+ attr[j].ca_name = "noedge";
+ attr[j].ca_val = 1;
+ j++;
+ }
+
+ if (xcesr.pc[i] == 1) {
+ attr[j].ca_name = "pc";
+ attr[j].ca_val = 1;
+ j++;
+ }
+
+ if (cpc_set_add_request(cpc, set, pic[i],
+ event->ce_pic[i], flags[i], nattrs, attr) == -1) {
+ (void) cpc_set_destroy(cpc, set);
+ free(pic[i]);
+ free(attr);
+ return (NULL);
+ }
+
+ free(pic[i]);
+ free(attr);
+ }
+ }
+ break;
+ default:
+ (void) cpc_set_destroy(cpc, set);
+ return (NULL);
+ }
+
+ return (set);
+}
diff --git a/usr/src/lib/libcpc/i386/getcpuid.h b/usr/src/lib/libcpc/i386/getcpuid.h
new file mode 100644
index 0000000000..161ec21ec3
--- /dev/null
+++ b/usr/src/lib/libcpc/i386/getcpuid.h
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _GETCPUID_H
+#define _GETCPUID_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint32_t cpc_getcpuid(uint32_t, uint32_t *, uint32_t *, uint32_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETCPUID_H */
diff --git a/usr/src/lib/libcpc/i386/getcpuid.s b/usr/src/lib/libcpc/i386/getcpuid.s
new file mode 100644
index 0000000000..ad2374e81a
--- /dev/null
+++ b/usr/src/lib/libcpc/i386/getcpuid.s
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/asm_linkage.h>
+
+#if defined(lint)
+
+#include <sys/types.h>
+#include <sys/inttypes.h>
+
+#include "getcpuid.h"
+
+/*ARGSUSED*/
+uint32_t
+cpc_getcpuid(uint32_t eax, uint32_t *ebxp, uint32_t *ecxp, uint32_t *edxp)
+{ return (0); }
+
+#else /* lint */
+
+ ENTRY(cpc_getcpuid)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ movl 8(%ebp), %eax
+ cpuid
+ pushl %eax
+ movl 0xc(%ebp), %eax
+ movl %ebx, (%eax)
+ movl 0x10(%ebp), %eax
+ movl %ecx, (%eax)
+ movl 0x14(%ebp), %eax
+ movl %edx, (%eax)
+ popl %eax
+ popl %ebx
+ popl %ebp
+ ret
+ SET_SIZE(cpc_getcpuid)
+
+#endif /* lint */
diff --git a/usr/src/lib/libcpc/sparc/Makefile b/usr/src/lib/libcpc/sparc/Makefile
new file mode 100644
index 0000000000..74eb98601b
--- /dev/null
+++ b/usr/src/lib/libcpc/sparc/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+MACHCOBJS = conf_ultra.o event_ultra.o
+
+include ../Makefile.com
+
+LDLIBS += -ldevinfo
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libcpc/sparc/conf_ultra.c b/usr/src/lib/libcpc/sparc/conf_ultra.c
new file mode 100644
index 0000000000..9b5d836ec9
--- /dev/null
+++ b/usr/src/lib/libcpc/sparc/conf_ultra.c
@@ -0,0 +1,548 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <string.h>
+#include <alloca.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libintl.h>
+#include <libdevinfo.h>
+
+#include "libcpc.h"
+#include "libcpc_impl.h"
+
+/*
+ * Configuration data for UltraSPARC performance counters.
+ *
+ * Definitions taken from [1], [2], [3] and [4]. See the references to
+ * understand what any of these settings actually means.
+ *
+ * Note that in the current draft of [2], there is some re-use
+ * of existing bit assignments in the various fields of the %pcr
+ * register - this may change before FCS.
+ *
+ * The following are the Internal Documents. Customers need to be
+ * told about the Public docs in cpc_getcpuref().
+ * [1] "UltraSPARC I & II User's Manual," January 1997.
+ * [2] "UltraSPARC-III Programmer's Reference Manual," April 1999.
+ * [3] "Cheetah+ Programmer's Reference Manual," November 2000.
+ * [4] "UltraSPARC-IIIi Programmer's Reference Manual," November 2000.
+ */
+
+#define V_US12 (1u << 0) /* specific to UltraSPARC 1 and 2 */
+#define V_US3 (1u << 1) /* specific to UltraSPARC 3 */
+#define V_US3_PLUS (1u << 2) /* specific to UltraSPARC 3 PLUS */
+#define V_US3_I (1u << 3) /* specific to UltraSPARC-IIIi */
+#define V_END (1u << 31)
+
+/*
+ * map from "cpu version" to flag bits
+ */
+static const uint_t cpuvermap[] = {
+ V_US12, /* CPC_ULTRA1 */
+ V_US12, /* CPC_ULTRA2 */
+ V_US3, /* CPC_ULTRA3 */
+ V_US3_PLUS, /* CPC_ULTRA3_PLUS */
+ V_US3_I /* CPC_ULTRA3I */
+};
+
+struct nametable {
+ const uint_t ver;
+ const uint8_t bits;
+ const char *name;
+};
+
+/*
+ * Definitions for counter 0
+ */
+
+#define USall_EVENTS_0(v) \
+ {v, 0x0, "Cycle_cnt"}, \
+ {v, 0x1, "Instr_cnt"}, \
+ {v, 0x2, "Dispatch0_IC_miss"}, \
+ {v, 0x8, "IC_ref"}, \
+ {v, 0x9, "DC_rd"}, \
+ {v, 0xa, "DC_wr"}, \
+ {v, 0xc, "EC_ref"}, \
+ {v, 0xe, "EC_snoop_inv"}
+
+static const struct nametable US12_names0[] = {
+ USall_EVENTS_0(V_US12),
+ {V_US12, 0x3, "Dispatch0_storeBuf"},
+ {V_US12, 0xb, "Load_use"},
+ {V_US12, 0xd, "EC_write_hit_RDO"},
+ {V_US12, 0xf, "EC_rd_hit"},
+ {V_END}
+};
+
+#define US3all_EVENTS_0(v) \
+ {v, 0x3, "Dispatch0_br_target"}, \
+ {v, 0x4, "Dispatch0_2nd_br"}, \
+ {v, 0x5, "Rstall_storeQ"}, \
+ {v, 0x6, "Rstall_IU_use"}, \
+ {v, 0xd, "EC_write_hit_RTO"}, \
+ {v, 0xf, "EC_rd_miss"}, \
+ {v, 0x10, "PC_port0_rd"}, \
+ {v, 0x11, "SI_snoop"}, \
+ {v, 0x12, "SI_ciq_flow"}, \
+ {v, 0x13, "SI_owned"}, \
+ {v, 0x14, "SW_count_0"}, \
+ {v, 0x15, "IU_Stat_Br_miss_taken"}, \
+ {v, 0x16, "IU_Stat_Br_count_taken"}, \
+ {v, 0x17, "Dispatch_rs_mispred"}, \
+ {v, 0x18, "FA_pipe_completion"}
+
+#define US3_MC_EVENTS_0(v) \
+ {v, 0x20, "MC_reads_0"}, \
+ {v, 0x21, "MC_reads_1"}, \
+ {v, 0x22, "MC_reads_2"}, \
+ {v, 0x23, "MC_reads_3"}, \
+ {v, 0x24, "MC_stalls_0"}, \
+ {v, 0x25, "MC_stalls_2"}
+
+#define US3_I_MC_EVENTS_0(v) \
+ {v, 0x20, "MC_read_dispatched"}, \
+ {v, 0x21, "MC_write_dispatched"}, \
+ {v, 0x22, "MC_read_returned_to_JBU"}, \
+ {v, 0x23, "MC_msl_busy_stall"}, \
+ {v, 0x24, "MC_mdb_overflow_stall"}, \
+ {v, 0x25, "MC_miu_spec_request"}
+
+static const struct nametable US3_names0[] = {
+ USall_EVENTS_0(V_US3),
+ US3all_EVENTS_0(V_US3),
+ US3_MC_EVENTS_0(V_US3),
+ {V_END}
+};
+
+static const struct nametable US3_PLUS_names0[] = {
+ USall_EVENTS_0(V_US3_PLUS),
+ US3all_EVENTS_0(V_US3_PLUS),
+ US3_MC_EVENTS_0(V_US3_PLUS),
+ {V_US3_PLUS, 0x19, "EC_wb_remote"},
+ {V_US3_PLUS, 0x1a, "EC_miss_local"},
+ {V_US3_PLUS, 0x1b, "EC_miss_mtag_remote"},
+ {V_END}
+};
+
+static const struct nametable US3_I_names0[] = {
+ USall_EVENTS_0(V_US3_I),
+ US3all_EVENTS_0(V_US3_I),
+ US3_I_MC_EVENTS_0(V_US3_I),
+ {V_US3_PLUS, 0x19, "EC_wb_remote"},
+ {V_US3_PLUS, 0x1a, "EC_miss_local"},
+ {V_US3_PLUS, 0x1b, "EC_miss_mtag_remote"},
+ {V_END}
+};
+
+#undef USall_EVENTS_0
+#undef US3all_EVENTS_0
+
+#define USall_EVENTS_1(v) \
+ {v, 0x0, "Cycle_cnt"}, \
+ {v, 0x1, "Instr_cnt"}, \
+ {v, 0x2, "Dispatch0_mispred"}, \
+ {v, 0xd, "EC_wb"}, \
+ {v, 0xe, "EC_snoop_cb"}
+
+static const struct nametable US12_names1[] = {
+ USall_EVENTS_1(V_US12),
+ {V_US12, 0x3, "Dispatch0_FP_use"},
+ {V_US12, 0x8, "IC_hit"},
+ {V_US12, 0x9, "DC_rd_hit"},
+ {V_US12, 0xa, "DC_wr_hit"},
+ {V_US12, 0xb, "Load_use_RAW"},
+ {V_US12, 0xc, "EC_hit"},
+ {V_US12, 0xf, "EC_ic_hit"},
+ {V_END}
+};
+
+#define US3all_EVENTS_1(v) \
+ {v, 0x3, "IC_miss_cancelled"}, \
+ {v, 0x5, "Re_FPU_bypass"}, \
+ {v, 0x6, "Re_DC_miss"}, \
+ {v, 0x7, "Re_EC_miss"}, \
+ {v, 0x8, "IC_miss"}, \
+ {v, 0x9, "DC_rd_miss"}, \
+ {v, 0xa, "DC_wr_miss"}, \
+ {v, 0xb, "Rstall_FP_use"}, \
+ {v, 0xc, "EC_misses"}, \
+ {v, 0xf, "EC_ic_miss"}, \
+ {v, 0x10, "Re_PC_miss"}, \
+ {v, 0x11, "ITLB_miss"}, \
+ {v, 0x12, "DTLB_miss"}, \
+ {v, 0x13, "WC_miss"}, \
+ {v, 0x14, "WC_snoop_cb"}, \
+ {v, 0x15, "WC_scrubbed"}, \
+ {v, 0x16, "WC_wb_wo_read"}, \
+ {v, 0x18, "PC_soft_hit"}, \
+ {v, 0x19, "PC_snoop_inv"}, \
+ {v, 0x1a, "PC_hard_hit"}, \
+ {v, 0x1b, "PC_port1_rd"}, \
+ {v, 0x1c, "SW_count_1"}, \
+ {v, 0x1d, "IU_Stat_Br_miss_untaken"}, \
+ {v, 0x1e, "IU_Stat_Br_count_untaken"}, \
+ {v, 0x1f, "PC_MS_misses"}, \
+ {v, 0x26, "Re_RAW_miss"}, \
+ {v, 0x27, "FM_pipe_completion"}
+
+#define US3_MC_EVENTS_1(v) \
+ {v, 0x20, "MC_writes_0"}, \
+ {v, 0x21, "MC_writes_1"}, \
+ {v, 0x22, "MC_writes_2"}, \
+ {v, 0x23, "MC_writes_3"}, \
+ {v, 0x24, "MC_stalls_1"}, \
+ {v, 0x25, "MC_stalls_3"}
+
+#define US3_I_MC_EVENTS_1(v) \
+ {v, 0x20, "MC_open_bank_cmds"}, \
+ {v, 0x21, "MC_reads"}, \
+ {v, 0x22, "MC_writes"}, \
+ {v, 0x23, "MC_page_close_stall"}
+
+static const struct nametable US3_names1[] = {
+ USall_EVENTS_1(V_US3),
+ US3all_EVENTS_1(V_US3),
+ US3_MC_EVENTS_1(V_US3),
+ {V_US3, 0x4, "Re_endian_miss"},
+ {V_END}
+};
+
+static const struct nametable US3_PLUS_names1[] = {
+ USall_EVENTS_1(V_US3_PLUS),
+ US3all_EVENTS_1(V_US3_PLUS),
+ US3_MC_EVENTS_1(V_US3_PLUS),
+ {V_US3_PLUS, 0x4, "Re_DC_missovhd"},
+ {V_US3_PLUS, 0x28, "EC_miss_mtag_remote"},
+ {V_US3_PLUS, 0x29, "EC_miss_remote"},
+ {V_END}
+};
+
+static const struct nametable US3_I_names1[] = {
+ USall_EVENTS_1(V_US3_I),
+ US3all_EVENTS_1(V_US3_I),
+ US3_I_MC_EVENTS_1(V_US3_I),
+ {V_US3_I, 0x4, "Re_DC_missovhd"},
+ {V_END}
+};
+#undef USall_EVENTS_1
+#undef US3all_EVENTS_1
+
+static const struct nametable *US12_names[2] = {
+ US12_names0,
+ US12_names1
+};
+
+static const struct nametable *US3_names[2] = {
+ US3_names0,
+ US3_names1
+};
+
+static const struct nametable *US3_PLUS_names[2] = {
+ US3_PLUS_names0,
+ US3_PLUS_names1
+};
+
+static const struct nametable *US3_I_names[2] = {
+ US3_I_names0,
+ US3_I_names1
+};
+
+#define MAPCPUVER(cpuver) (cpuvermap[(cpuver) - CPC_ULTRA1])
+
+static int
+validargs(int cpuver, int regno)
+{
+ if (regno < 0 || regno > 1)
+ return (0);
+ cpuver -= CPC_ULTRA1;
+ if (cpuver < 0 ||
+ cpuver >= sizeof (cpuvermap) / sizeof (cpuvermap[0]))
+ return (0);
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+versionmatch(int cpuver, int regno, const struct nametable *n)
+{
+ if (!validargs(cpuver, regno) || n->ver != MAPCPUVER(cpuver))
+ return (0);
+ return (1);
+}
+
+static const struct nametable *
+getnametable(int cpuver, int regno)
+{
+ const struct nametable *n;
+
+ if (!validargs(cpuver, regno))
+ return (NULL);
+
+ switch (MAPCPUVER(cpuver)) {
+ case V_US12:
+ n = US12_names[regno];
+ break;
+ case V_US3:
+ n = US3_names[regno];
+ break;
+ case V_US3_PLUS:
+ n = US3_PLUS_names[regno];
+ break;
+ case V_US3_I:
+ n = US3_I_names[regno];
+ break;
+ default:
+ n = NULL;
+ break;
+ }
+ return (n);
+}
+
+void
+cpc_walk_names(int cpuver, int regno, void *arg,
+ void (*action)(void *, int, const char *, uint8_t))
+{
+ const struct nametable *n;
+
+ if ((n = getnametable(cpuver, regno)) == NULL)
+ return;
+ for (; n->ver != V_END; n++)
+ if (versionmatch(cpuver, regno, n))
+ action(arg, regno, n->name, n->bits);
+}
+
+const char *
+__cpc_reg_to_name(int cpuver, int regno, uint8_t bits)
+{
+ const struct nametable *n;
+
+ if ((n = getnametable(cpuver, regno)) == NULL)
+ return (NULL);
+ for (; n->ver != V_END; n++)
+ if (bits == n->bits && versionmatch(cpuver, regno, n))
+ return (n->name);
+ return (NULL);
+}
+
+/*
+ * Register names can be specified as strings or even as numbers
+ */
+int
+__cpc_name_to_reg(int cpuver, int regno, const char *name, uint8_t *bits)
+{
+ const struct nametable *n;
+ char *eptr = NULL;
+ long value;
+
+ if ((n = getnametable(cpuver, regno)) == NULL || name == NULL)
+ return (-1);
+
+ for (; n->ver != V_END; n++)
+ if (strcmp(name, n->name) == 0 &&
+ versionmatch(cpuver, regno, n)) {
+ *bits = n->bits;
+ return (0);
+ }
+
+ value = strtol(name, &eptr, 0);
+ if (name != eptr && value >= 0 && value <= UINT8_MAX) {
+ *bits = (uint8_t)value;
+ return (0);
+ }
+
+ return (-1);
+}
+
+const char *
+cpc_getcciname(int cpuver)
+{
+ if (validargs(cpuver, 0))
+ switch (MAPCPUVER(cpuver)) {
+ case V_US12:
+ return ("UltraSPARC I&II");
+ case V_US3:
+ return ("UltraSPARC III");
+ case V_US3_PLUS:
+ return ("UltraSPARC III+ & IV");
+ case V_US3_I:
+ return ("UltraSPARC IIIi & IIIi+");
+ default:
+ break;
+ }
+ return (NULL);
+}
+
+#define CPU_REF_URL " Documentation for Sun processors can be found at: " \
+ "http://www.sun.com/processors/manuals"
+
+const char *
+cpc_getcpuref(int cpuver)
+{
+ if (validargs(cpuver, 0))
+ switch (MAPCPUVER(cpuver)) {
+ case V_US12:
+ return (gettext(
+ "See the \"UltraSPARC I/II User\'s Manual\" "
+ "(Part No. 802-7220-02) "
+ "for descriptions of these events." CPU_REF_URL));
+ case V_US3:
+ case V_US3_PLUS:
+ return (gettext(
+ "See the \"UltraSPARC III Cu User's Manual\" "
+ "for descriptions of these events." CPU_REF_URL));
+ case V_US3_I:
+ return (gettext(
+ "See the \"UltraSPARC IIIi User's Manual\" "
+ "for descriptions of these events." CPU_REF_URL));
+ default:
+ break;
+ }
+ return (NULL);
+}
+
+/*
+ * This is a functional interface to allow CPUs with fewer %pic registers
+ * to share the same data structure as those with more %pic registers
+ * within the same instruction family.
+ */
+uint_t
+cpc_getnpic(int cpuver)
+{
+ /*LINTED*/
+ cpc_event_t *event;
+
+ switch (cpuver) {
+ case CPC_ULTRA1:
+ case CPC_ULTRA2:
+ case CPC_ULTRA3:
+ case CPC_ULTRA3_PLUS:
+ case CPC_ULTRA3_I:
+ return (sizeof (event->ce_pic) / sizeof (event->ce_pic[0]));
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Compares the given string against the list of all known CPU node names, and
+ * returns the CPC CPU version code if there is a match. If there is no match,
+ * returns -1.
+ */
+static int
+node2ver(char *node)
+{
+ if (strcmp(node, "SUNW,UltraSPARC") == 0 ||
+ strcmp(node, "SUNW,UltraSPARC-II") == 0 ||
+ strcmp(node, "SUNW,UltraSPARC-IIi") == 0 ||
+ strcmp(node, "SUNW,UltraSPARC-IIe") == 0) {
+ return (CPC_ULTRA1);
+ } else if (strcmp(node, "SUNW,UltraSPARC-III") == 0)
+ return (CPC_ULTRA3);
+ else if (strcmp(node, "SUNW,UltraSPARC-III+") == 0 ||
+ strcmp(node, "SUNW,UltraSPARC-IV") == 0 ||
+ strcmp(node, "SUNW,UltraSPARC-IV+") == 0)
+ return (CPC_ULTRA3_PLUS);
+ else if (strcmp(node, "SUNW,UltraSPARC-IIIi") == 0 ||
+ strcmp(node, "SUNW,UltraSPARC-IIIi+") == 0)
+ return (CPC_ULTRA3_I);
+
+ return (-1);
+}
+
+static int
+cpc_get_cpu_ver(di_node_t di_node, void *arg)
+{
+ char *node_name, *compatible_array;
+ int n_names, i, found = 0;
+ int *ver = arg;
+
+ node_name = di_node_name(di_node);
+ if (node_name != NULL) {
+ if ((*ver = node2ver(node_name)) != -1)
+ found = 1;
+ else if (strncmp(node_name, "cpu", 4) == 0) {
+ /*
+ * CPU nodes associated with CMP use the generic name
+ * of "cpu". We must look at the compatible property
+ * in order to find the implementation specific name.
+ */
+ if ((n_names = di_compatible_names(di_node,
+ &compatible_array)) > 0) {
+ for (i = 0; i < n_names; i++) {
+ if ((*ver = node2ver(compatible_array))
+ != -1) {
+ found = 1;
+ break;
+ }
+ compatible_array +=
+ strlen(compatible_array) + 1;
+ }
+ }
+ }
+ }
+
+ if (found == 0)
+ return (DI_WALK_CONTINUE);
+
+ return (DI_WALK_TERMINATE);
+}
+
+/*
+ * Return the version of the current processor.
+ *
+ * Version -1 is defined as 'not performance counter capable'
+ *
+ * XXX A better solution would be to use the di_prom_props for the cpu
+ * devinfo nodes. That way we could look at the 'device-type', 'sparc-version'
+ * and 'implementation#' properties in order to determine which version of
+ * UltraSPARC we are running on.
+ *
+ * The problem with this is that di_prom_init() requires root access to
+ * open /dev/openprom and cputrack is not a root-only application so
+ * we have to settle for the di_props that we can see as non-root users.
+ */
+int
+cpc_getcpuver(void)
+{
+ static int ver = -1;
+
+ if (ver == -1) {
+ di_node_t di_root_node;
+
+ if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
+ return (-1);
+
+ (void) di_walk_node(di_root_node, DI_WALK_CLDFIRST,
+ (void *)&ver, cpc_get_cpu_ver);
+
+ di_fini(di_root_node);
+ }
+ return (ver);
+}
diff --git a/usr/src/lib/libcpc/sparc/event_ultra.c b/usr/src/lib/libcpc/sparc/event_ultra.c
new file mode 100644
index 0000000000..39aec138b6
--- /dev/null
+++ b/usr/src/lib/libcpc/sparc/event_ultra.c
@@ -0,0 +1,509 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to capture processor-dependencies in event specification.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <alloca.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <libintl.h>
+#include <assert.h>
+
+#include "libcpc.h"
+#include "libcpc_impl.h"
+
+/*
+ * Event specifications for UltraSPARC performance counters are based
+ * on the content of a getsubopt-like string.
+ * The string should contain something that looks like this:
+ *
+ * pic0=<eventspec>,pic1=<eventspec>
+ * [,nouser][,sys]
+ *
+ * For example:
+ * pic1=0x4,pic0=Instr_cnt
+ * or
+ * pic0=Instr_cnt,pic1=Cycle_cnt,nouser,sys
+ *
+ * The two events must be named. The names can be ascii or
+ * a decimal, octal or hexadecimal number as parsed by strtol(3C).
+ *
+ * By default, user event counting is enabled, system event counting
+ * is disabled.
+ *
+ * The routine counts the number of errors encountered while parsing
+ * the string, if no errors are encountered, the event handle is
+ * returned.
+ */
+
+const char *
+cpc_getusage(int cpuver)
+{
+ switch (cpuver) {
+ case CPC_ULTRA1:
+ case CPC_ULTRA2:
+ case CPC_ULTRA3:
+ case CPC_ULTRA3_PLUS:
+ case CPC_ULTRA3_I:
+ return ("pic0=<event0>,pic1=<event1> "
+ "[,sys] "
+ "[,nouser]");
+ default:
+ return (NULL);
+ }
+}
+
+/*
+ * This private structure is used to build tables that correspond
+ * to the bit patterns in the control registers of the processor.
+ */
+struct keyval {
+ char *kv_token;
+ int (*kv_action)(const char *,
+ const struct keyval *, int, char *, uint64_t *);
+ uint64_t kv_mask;
+ int kv_shift;
+};
+
+static int
+picbits(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint64_t *bits)
+{
+ uint8_t val8;
+ uint_t regno;
+
+ regno = strcmp(kv->kv_token, "pic0") == 0 ? 0 : 1;
+
+ if (value == NULL) {
+ __cpc_error(fn, gettext("missing '%s' value\n"),
+ kv->kv_token);
+ return (-1);
+ }
+ if (__cpc_name_to_reg(cpuver, regno, value, &val8) != 0) {
+ __cpc_error(fn, gettext("%%pic%d cannot measure "
+ "event '%s' on this cpu\n"), regno, value);
+ return (-1);
+ }
+ *bits |= (((uint64_t)val8 & kv->kv_mask) << kv->kv_shift);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+bitclr(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint64_t *bits)
+{
+ if (value != NULL) {
+ __cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
+ return (-1);
+ }
+ *bits &= ~(kv->kv_mask << kv->kv_shift);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+bitset(const char *fn,
+ const struct keyval *kv, int cpuver, char *value, uint64_t *bits)
+{
+ if (value != NULL) {
+ __cpc_error(fn, gettext("bad arg to '%s'\n"), kv->kv_token);
+ return (-1);
+ }
+ *bits |= (kv->kv_mask << kv->kv_shift);
+ return (0);
+}
+
+/*
+ * This token table must match the keyval tables below.
+ */
+
+static char * const tokens[] = {
+#define D_pic0 0
+ "pic0", /* takes a valid event name */
+#define D_pic1 1
+ "pic1", /* takes a valid event name */
+#define D_nouser 2
+ "nouser", /* disables user counts */
+#define D_sys 3
+ "sys", /* enables system counts */
+ NULL
+};
+
+static const struct keyval us2_keyvals[] = {
+ { "pic0", picbits,
+ CPC_ULTRA2_PCR_PIC0_MASK, CPC_ULTRA_PCR_PIC0_SHIFT },
+ { "pic1", picbits,
+ CPC_ULTRA2_PCR_PIC1_MASK, CPC_ULTRA_PCR_PIC1_SHIFT },
+ { "nouser", bitclr,
+ UINT64_C(1), CPC_ULTRA_PCR_USR },
+ { "sys", bitset,
+ UINT64_C(1), CPC_ULTRA_PCR_SYS },
+};
+
+static const struct keyval us3_keyvals[] = {
+ { "pic0", picbits,
+ CPC_ULTRA3_PCR_PIC0_MASK, CPC_ULTRA_PCR_PIC0_SHIFT },
+ { "pic1", picbits,
+ CPC_ULTRA3_PCR_PIC1_MASK, CPC_ULTRA_PCR_PIC1_SHIFT },
+ { "nouser", bitclr,
+ UINT64_C(1), CPC_ULTRA_PCR_USR },
+ { "sys", bitset,
+ UINT64_C(1), CPC_ULTRA_PCR_SYS },
+};
+
+#if !defined(NDEBUG)
+#pragma init(__tablecheck)
+
+static void
+__tablecheck(void)
+{
+ uint_t ntokens = sizeof (tokens) / sizeof (tokens[0]) - 1;
+ uint_t us3_nkeys = sizeof (us3_keyvals) / sizeof (us3_keyvals[0]);
+ uint_t us2_nkeys = sizeof (us2_keyvals) / sizeof (us2_keyvals[0]);
+ uint_t n;
+
+ assert(ntokens == us3_nkeys);
+ for (n = 0; n < ntokens; n++)
+ assert(strcmp(tokens[n], us3_keyvals[n].kv_token) == 0);
+ assert(us3_nkeys >= us2_nkeys);
+ for (n = 0; n < us2_nkeys; n++)
+ assert(strcmp(tokens[n], us2_keyvals[n].kv_token) == 0);
+}
+
+#endif /* !NDEBUG */
+
+int
+cpc_strtoevent(int cpuver, const char *spec, cpc_event_t *event)
+{
+ static const char fn[] = "strtoevent";
+ char *value;
+ char *pic[2];
+ char *opts;
+ int errcnt = 0;
+ uint_t ntokens;
+ const struct keyval *keyvals;
+ uint64_t *bits;
+
+ if (spec == NULL)
+ return (errcnt = 1);
+
+ bzero(event, sizeof (*event));
+ switch (event->ce_cpuver = cpuver) {
+ case CPC_ULTRA1:
+ case CPC_ULTRA2:
+ keyvals = us2_keyvals;
+ ntokens = sizeof (us2_keyvals) / sizeof (us2_keyvals[0]);
+ bits = &event->ce_pcr;
+ *bits = UINT64_C(1) << CPC_ULTRA_PCR_USR;
+ break;
+ case CPC_ULTRA3:
+ case CPC_ULTRA3_PLUS:
+ case CPC_ULTRA3_I:
+ keyvals = us3_keyvals;
+ ntokens = sizeof (us3_keyvals) / sizeof (us3_keyvals[0]);
+ bits = &event->ce_pcr;
+ *bits = UINT64_C(1) << CPC_ULTRA_PCR_USR;
+ break;
+ default:
+ return (errcnt = 1);
+ }
+
+ pic[0] = pic[1] = NULL;
+
+ opts = strcpy(alloca(strlen(spec) + 1), spec);
+ while (*opts != '\0') {
+ const struct keyval *kv;
+ int idx = getsubopt(&opts, tokens, &value);
+
+ if (idx >= 0 && idx < ntokens) {
+ kv = &keyvals[idx];
+ if (kv->kv_action(fn, kv, cpuver, value, bits) != 0) {
+ errcnt++;
+ break;
+ }
+
+ if (idx == D_pic0) {
+ if (pic[0] != NULL) {
+ __cpc_error(fn,
+ "repeated '%s' token\n",
+ tokens[idx]);
+ errcnt++;
+ break;
+ }
+ pic[0] = value;
+ } else if (idx == D_pic1) {
+ if (pic[1] != NULL) {
+ __cpc_error(fn,
+ "repeated '%s' token\n",
+ tokens[idx]);
+ errcnt++;
+ break;
+ }
+ pic[1] = value;
+ }
+ } else if (idx == -1) {
+ /*
+ * The token given wasn't recognized.
+ * See if it was an implicit pic specification..
+ */
+ if (pic[0] == NULL) {
+ kv = &keyvals[D_pic0];
+ if (kv->kv_action(fn,
+ kv, cpuver, value, bits) != 0) {
+ errcnt++;
+ break;
+ }
+ pic[0] = value;
+ } else if (pic[1] == NULL) {
+ kv = &keyvals[D_pic1];
+ if (kv->kv_action(fn,
+ kv, cpuver, value, bits) != 0) {
+ errcnt++;
+ break;
+ }
+ pic[1] = value;
+ } else {
+ __cpc_error(fn,
+ gettext("bad token '%s'\n"), value);
+ errcnt++;
+ break;
+ }
+ } else {
+ if (idx >= 0 &&
+ idx < sizeof (tokens) / sizeof (tokens[0]))
+ __cpc_error(fn,
+ gettext("bad token '%s'\n"), tokens[idx]);
+ else
+ __cpc_error(fn, gettext("bad token\n"));
+ errcnt++;
+ break;
+ }
+ }
+
+ if (pic[0] == NULL || pic[1] == NULL) {
+ __cpc_error(fn, gettext("two events must be specified\n"));
+ errcnt++;
+ }
+
+ return (errcnt);
+}
+
+/*
+ * Return a printable description of the control registers.
+ *
+ * This routine should always succeed (notwithstanding heap problems),
+ * but may not be able to correctly decode the registers, if, for
+ * example, a new processor is under test.
+ *
+ * The caller is responsible for free(3c)ing the string returned.
+ */
+
+static char *
+val8tostr(uint8_t bits)
+{
+ char buf[2 + 2 + 1]; /* 0x %2x \0 */
+ (void) snprintf(buf, sizeof (buf), "0x%x", bits);
+ return (strdup(buf));
+}
+
+static char *
+regtostr(int cpuver, int regno, uint8_t bits)
+{
+ const char *sname;
+
+ if ((sname = __cpc_reg_to_name(cpuver, regno, bits)) != NULL)
+ return (strdup(sname));
+ return (val8tostr(bits));
+}
+
+struct xpcr {
+ uint8_t pic[2];
+ int usr, sys;
+};
+
+static void
+unmake_pcr(uint64_t pcr, int cpuver, struct xpcr *xpcr)
+{
+ const struct keyval *kv;
+
+ switch (cpuver) {
+ case CPC_ULTRA1:
+ case CPC_ULTRA2:
+ default:
+ kv = us2_keyvals;
+ break;
+ case CPC_ULTRA3:
+ case CPC_ULTRA3_PLUS:
+ case CPC_ULTRA3_I:
+ kv = us3_keyvals;
+ break;
+ }
+ xpcr->pic[0] = (uint8_t)((pcr >> kv[D_pic0].kv_shift) &
+ kv[D_pic0].kv_mask);
+ xpcr->pic[1] = (uint8_t)((pcr >> kv[D_pic1].kv_shift) &
+ kv[D_pic1].kv_mask);
+ xpcr->usr = (pcr >> kv[D_nouser].kv_shift) &
+ kv[D_nouser].kv_mask;
+ xpcr->sys = (pcr >> kv[D_sys].kv_shift) &
+ kv[D_sys].kv_mask;
+}
+
+char *
+cpc_eventtostr(cpc_event_t *event)
+{
+ struct xpcr xpcr;
+ char *pic[2];
+ char buffer[1024];
+
+ switch (event->ce_cpuver) {
+ case CPC_ULTRA1:
+ case CPC_ULTRA2:
+ case CPC_ULTRA3:
+ case CPC_ULTRA3_PLUS:
+ case CPC_ULTRA3_I:
+ break;
+ default:
+ return (NULL);
+ }
+
+ unmake_pcr(event->ce_pcr, event->ce_cpuver, &xpcr);
+ if ((pic[0] = regtostr(event->ce_cpuver, 0, xpcr.pic[0])) == NULL)
+ return (NULL);
+ if ((pic[1] = regtostr(event->ce_cpuver, 1, xpcr.pic[1])) == NULL) {
+ free(pic[0]);
+ return (NULL);
+ }
+
+ (void) snprintf(buffer, sizeof (buffer), "%s=%s,%s=%s",
+ tokens[D_pic0], pic[0], tokens[D_pic1], pic[1]);
+
+ free(pic[1]);
+ free(pic[0]);
+
+ if (!xpcr.usr)
+ (void) strcat(strcat(buffer, ","), tokens[D_nouser]);
+ if (xpcr.sys)
+ (void) strcat(strcat(buffer, ","), tokens[D_sys]);
+
+ return (strdup(buffer));
+}
+
+/*
+ * Utility operations on events
+ */
+void
+cpc_event_accum(cpc_event_t *accum, cpc_event_t *event)
+{
+ if (accum->ce_hrt < event->ce_hrt)
+ accum->ce_hrt = event->ce_hrt;
+ accum->ce_tick += event->ce_tick;
+ accum->ce_pic[0] += event->ce_pic[0];
+ accum->ce_pic[1] += event->ce_pic[1];
+}
+
+void
+cpc_event_diff(cpc_event_t *diff, cpc_event_t *left, cpc_event_t *right)
+{
+ diff->ce_hrt = left->ce_hrt;
+ diff->ce_tick = left->ce_tick - right->ce_tick;
+ diff->ce_pic[0] = left->ce_pic[0] - right->ce_pic[0];
+ diff->ce_pic[1] = left->ce_pic[1] - right->ce_pic[1];
+}
+
+/*
+ * Given a cpc_event_t and cpc_bind_event() flags, translate the event into the
+ * cpc_set_t format.
+ *
+ * Returns NULL on failure.
+ */
+cpc_set_t *
+__cpc_eventtoset(cpc_t *cpc, cpc_event_t *event, int iflags)
+{
+ cpc_set_t *set = NULL;
+ struct xpcr xpcr;
+ char *pic[2];
+ uint32_t flag = 0;
+ cpc_attr_t attr = { "picnum", 0 };
+
+ switch (event->ce_cpuver) {
+ case CPC_ULTRA1:
+ case CPC_ULTRA2:
+ case CPC_ULTRA3:
+ case CPC_ULTRA3_PLUS:
+ case CPC_ULTRA3_I:
+ break;
+ default:
+ return (NULL);
+ }
+
+ unmake_pcr(event->ce_pcr, event->ce_cpuver, &xpcr);
+ if ((pic[0] = regtostr(event->ce_cpuver, 0, xpcr.pic[0])) == NULL)
+ return (NULL);
+ if ((pic[1] = regtostr(event->ce_cpuver, 1, xpcr.pic[1])) == NULL) {
+ free(pic[0]);
+ return (NULL);
+ }
+
+ if (xpcr.usr)
+ flag |= CPC_COUNT_USER;
+ if (xpcr.sys)
+ flag |= CPC_COUNT_SYSTEM;
+
+ if (iflags & CPC_BIND_EMT_OVF)
+ flag |= CPC_OVF_NOTIFY_EMT;
+
+ if ((set = cpc_set_create(cpc)) == NULL)
+ goto bad;
+
+ if (cpc_set_add_request(cpc, set, pic[0], event->ce_pic[0], flag,
+ 1, &attr) != 0)
+ goto bad;
+
+ attr.ca_val = 1;
+ if (cpc_set_add_request(cpc, set, pic[1], event->ce_pic[1], flag,
+ 1, &attr) != 1)
+ goto bad;
+
+ free(pic[0]);
+ free(pic[1]);
+
+ return (set);
+
+bad:
+ if (set != NULL)
+ (void) cpc_set_destroy(cpc, set);
+ free(pic[0]);
+ free(pic[1]);
+ return (NULL);
+}
diff --git a/usr/src/lib/libcpc/sparcv9/Makefile b/usr/src/lib/libcpc/sparcv9/Makefile
new file mode 100644
index 0000000000..6b0493b2d1
--- /dev/null
+++ b/usr/src/lib/libcpc/sparcv9/Makefile
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+MACHCOBJS = conf_ultra.o event_ultra.o
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+LDLIBS += -ldevinfo
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libcpc/spec/Makefile b/usr/src/lib/libcpc/spec/Makefile
new file mode 100644
index 0000000000..25277194e4
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/spec/Makefile
+
+include $(SRC)/lib/Makefile.spec.arch
diff --git a/usr/src/lib/libcpc/spec/Makefile.targ b/usr/src/lib/libcpc/spec/Makefile.targ
new file mode 100644
index 0000000000..13968ee539
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/Makefile.targ
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/spec/Makefile.targ
+
+LIBRARY = libcpc.a
+VERS = .1
+
+OBJECTS = cpc.o
diff --git a/usr/src/lib/libcpc/spec/amd64/Makefile b/usr/src/lib/libcpc/spec/amd64/Makefile
new file mode 100644
index 0000000000..d334868181
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/amd64/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+
+# Uncomment the following if the linker complains
+#amd64_C_PICFLAGS = $(amd64_C_BIGPICFLAGS)
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libcpc/spec/cpc.spec b/usr/src/lib/libcpc/spec/cpc.spec
new file mode 100644
index 0000000000..d4eae95935
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/cpc.spec
@@ -0,0 +1,448 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libpctx/spec/cpc.spec
+
+function cpc_version
+include <libcpc.h>
+declaration uint_t cpc_version(uint_t)
+version SUNW_1.1
+end
+
+function cpc_getcpuver
+include <libcpc.h>
+declaration int cpc_getcpuver(void)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_getcciname
+include <libcpc.h>
+declaration const char *cpc_getcciname(int cpuver)
+version SUNW_1.1
+exception ( $return == 0 )
+end
+
+function cpc_getcpuref
+include <libcpc.h>
+declaration const char *cpc_getcpuref(int cpuver)
+version SUNW_1.1
+exception ( $return == 0 )
+end
+
+function cpc_getusage
+include <libcpc.h>
+declaration const char *cpc_getusage(int cpuver)
+version SUNW_1.1
+exception ( $return == 0 )
+end
+
+function cpc_getnpic
+include <libcpc.h>
+declaration uint_t cpc_getnpic(int cpuver)
+version SUNW_1.1
+exception ( $return == 0 )
+end
+
+function cpc_walk_names
+include <libcpc.h>
+declaration void cpc_walk_names(int cpuver, int regno, void *arg, \
+ void (*action)(void *arg, \
+ int regno, const char *name, uint8_t bits))
+version SUNW_1.1
+end
+
+function cpc_seterrfn
+include <libcpc.h>
+declaration void cpc_seterrfn(cpc_errfn_t *errfn)
+version SUNW_1.1
+end
+
+function cpc_strtoevent
+include <libcpc.h>
+declaration int cpc_strtoevent(int cpuver, const char *spec, \
+ cpc_event_t *event)
+version SUNW_1.1
+exception ( $return != 0 )
+end
+
+function cpc_eventtostr
+include <libcpc.h>
+declaration char *cpc_eventtostr(cpc_event_t *event)
+version SUNW_1.1
+exception ( $return == 0 )
+end
+
+function cpc_event_accum
+include <libcpc.h>
+declaration void cpc_event_accum(cpc_event_t *accum, cpc_event_t *event)
+version SUNW_1.1
+end
+
+function cpc_event_diff
+include <libcpc.h>
+declaration void cpc_event_diff(cpc_event_t *diff, cpc_event_t *left, \
+ cpc_event_t *right)
+version SUNW_1.1
+end
+
+function cpc_access
+include <libcpc.h>
+declaration int cpc_access(void)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_bind_event
+include <libcpc.h>
+declaration int cpc_bind_event(cpc_event_t *event, int flags)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_take_sample
+include <libcpc.h>
+declaration int cpc_take_sample(cpc_event_t *event)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_count_usr_events
+include <libcpc.h>
+declaration int cpc_count_usr_events(int enable)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_count_sys_events
+include <libcpc.h>
+declaration int cpc_count_sys_events(int enable)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_rele
+include <libcpc.h>
+declaration int cpc_rele(void)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_pctx_bind_event
+include <libpctx.h>, <libcpc.h>
+declaration int cpc_pctx_bind_event(pctx_t *pctx, id_t lwpid, \
+ cpc_event_t *event, int flags)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_pctx_take_sample
+include <libpctx.h>, <libcpc.h>
+declaration int cpc_pctx_take_sample(pctx_t *pctx, id_t lwpid, \
+ cpc_event_t *event)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_pctx_rele
+include <libpctx.h>, <libcpc.h>
+declaration int cpc_pctx_rele(pctx_t *pctx, id_t lwpid)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_pctx_invalidate
+include <libpctx.h>, <libcpc.h>
+declaration int cpc_pctx_invalidate(pctx_t *pctx, id_t lwpid)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_shared_open
+include <libcpc.h>
+declaration int cpc_shared_open(void)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_shared_close
+include <libcpc.h>
+declaration void cpc_shared_close(int fd)
+version SUNW_1.1
+end
+
+function cpc_shared_bind_event
+include <libcpc.h>
+declaration int cpc_shared_bind_event(int fd, cpc_event_t *event, int flags)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_shared_take_sample
+include <libcpc.h>
+declaration int cpc_shared_take_sample(int fd, cpc_event_t *event)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_shared_rele
+include <libcpc.h>
+declaration int cpc_shared_rele(int fd)
+version SUNW_1.1
+exception ( $return == -1 )
+end
+
+function cpc_open
+include <libcpc.h>
+declaration cpc_t *cpc_open(int vers)
+version SUNW_1.2
+exception ( $return == NULL )
+end
+
+function cpc_close
+include <libcpc.h>
+declaration int cpc_close(cpc_t *cpc)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_set_create
+include <libcpc.h>
+declaration cpc_set_t *cpc_set_create(cpc_t *cpc)
+version SUNW_1.2
+exception ( $return == NULL )
+end
+
+function cpc_set_destroy
+include <libcpc.h>
+declaration int cpc_set_destroy(cpc_t *cpc, cpc_set_t *set)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_set_add_request
+include <libcpc.h>
+declaration int cpc_set_add_request(cpc_t *cpc, cpc_set_t *set, \
+ const char *event, uint64_t preset, uint_t flags, \
+ uint_t nattrs, const cpc_attr_t *attrs)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_request_preset
+include <libcpc.h>
+declaration int cpc_request_preset(cpc_t *cpc, int index, uint64_t preset)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_set_restart
+include <libcpc.h>
+declaration int cpc_set_restart(cpc_t *cpc, cpc_set_t *set)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_buf_create
+include <libcpc.h>
+declaration cpc_buf_t *cpc_buf_create(cpc_t *cpc, cpc_set_t *set)
+version SUNW_1.2
+exception ( $return == NULL )
+end
+
+function cpc_buf_destroy
+include <libcpc.h>
+declaration int cpc_buf_destroy(cpc_t *cpc, cpc_buf_t *buf)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_bind_curlwp
+include <libcpc.h>
+declaration int cpc_bind_curlwp(cpc_t *cpc, cpc_set_t *set, uint_t flags)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_bind_cpu
+include <libcpc.h>
+declaration int cpc_bind_cpu(cpc_t *cpc, processorid_t id, cpc_set_t *set, \
+ uint_t flags)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_bind_pctx
+include <libcpc.h>
+declaration int cpc_bind_pctx(cpc_t *cpc, pctx_t *pctx, id_t id, \
+ cpc_set_t *set, uint_t flags)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_unbind
+include <libcpc.h>
+declaration int cpc_unbind(cpc_t *cpc, cpc_set_t *set)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_set_sample
+include <libcpc.h>
+declaration int cpc_set_sample(cpc_t *cpc, cpc_set_t *set, cpc_buf_t *buf)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_buf_zero
+include <libcpc.h>
+declaration void cpc_buf_zero(cpc_t *cpc, cpc_buf_t *buf)
+version SUNW_1.2
+end
+
+function cpc_buf_sub
+include <libcpc.h>
+declaration void cpc_buf_sub(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, \
+ cpc_buf_t *b)
+version SUNW_1.2
+end
+
+function cpc_buf_add
+include <libcpc.h>
+declaration void cpc_buf_add(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, \
+ cpc_buf_t *b)
+version SUNW_1.2
+end
+
+function cpc_buf_copy
+include <libcpc.h>
+declaration void cpc_buf_copy(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *src)
+version SUNW_1.2
+end
+
+function cpc_buf_get
+include <libcpc.h>
+declaration int cpc_buf_get(cpc_t *cpc, cpc_buf_t *buf, int index, \
+ uint64_t *val)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_buf_set
+include <libcpc.h>
+declaration int cpc_buf_set(cpc_t *cpc, cpc_buf_t *buf, int index, \
+ uint64_t val)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_buf_hrtime
+include <libcpc.h>
+declaration hrtime_t cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf)
+version SUNW_1.2
+end
+
+function cpc_buf_tick
+include <libcpc.h>
+declaration uint64_t cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf)
+version SUNW_1.2
+end
+
+function cpc_walk_events_all
+include <libcpc.h>
+declaration void cpc_walk_events_all(cpc_t *cpc, void *arg, \
+ void (*action)(void *arg, const char *event))
+version SUNW_1.2
+end
+
+function cpc_walk_events_pic
+include <libcpc.h>
+declaration void cpc_walk_events_pic(cpc_t *cpc, uint_t picno, void *arg, \
+ void (*action)(void *arg, uint_t picno, const char *event));
+version SUNW_1.2
+end
+
+function cpc_walk_attrs
+include <libcpc.h>
+declaration void cpc_walk_attrs(cpc_t *cpc, void *arg, \
+ void (*action)(void *arg, const char *attr))
+version SUNW_1.2
+end
+
+function cpc_walk_requests
+include <libcpc.h>
+declaration void cpc_walk_requests(cpc_t *cpc, cpc_set_t *set, void *arg, \
+ void (*action)(void *arg, int index, const char *event, uint64_t preset, \
+ uint_t flags, int nattrs, const cpc_attr_t *attrs))
+version SUNW_1.2
+end
+
+function cpc_enable
+include <libcpc.h>
+declaration int cpc_enable(cpc_t *cpc)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_disable
+include <libcpc.h>
+declaration int cpc_disable(cpc_t *cpc)
+version SUNW_1.2
+exception ( $return == -1 )
+end
+
+function cpc_npic
+include <libcpc.h>
+declaration uint_t cpc_npic(cpc_t *cpc)
+version SUNW_1.2
+end
+
+function cpc_caps
+include <libcpc.h>
+declaration uint_t cpc_caps(cpc_t *cpc)
+version SUNW_1.2
+end
+
+function cpc_cciname
+include <libcpc.h>
+declaration const char *cpc_cciname(cpc_t *cpc)
+version SUNW_1.2
+exception ( $return == NULL )
+end
+
+function cpc_cpuref
+include <libcpc.h>
+declaration const char *cpc_cpuref(cpc_t *cpc)
+version SUNW_1.2
+exception ( $return == NULL )
+end
+
+function cpc_seterrhndlr
+include <libcpc.h>
+declaration int cpc_seterrhndlr(cpc_t *cpc, cpc_errhndlr_t *fn)
+version SUNW_1.2
+exception ( $return == -1 )
+end
diff --git a/usr/src/lib/libcpc/spec/i386/Makefile b/usr/src/lib/libcpc/spec/i386/Makefile
new file mode 100644
index 0000000000..c7d44e5cb9
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/i386/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/spec/i386/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+
+# Uncomment the following if the linker complains
+#i386_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libcpc/spec/sparc/Makefile b/usr/src/lib/libcpc/spec/sparc/Makefile
new file mode 100644
index 0000000000..3f5dabf87b
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/sparc/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/spec/sparc/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+
+# Uncomment the following if the linker complains
+#sparc_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libcpc/spec/sparcv9/Makefile b/usr/src/lib/libcpc/spec/sparcv9/Makefile
new file mode 100644
index 0000000000..709adc3baa
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/sparcv9/Makefile
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libcpc/spec/sparcv9/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+
+# Uncomment the following if the linker complains
+#sparcv9_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libcpc/spec/versions b/usr/src/lib/libcpc/spec/versions
new file mode 100644
index 0000000000..1d4572d689
--- /dev/null
+++ b/usr/src/lib/libcpc/spec/versions
@@ -0,0 +1,46 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+sparc {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}
+sparcv9 {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}
+i386 {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}
+amd64 {
+ SUNW_1.2;
+ SUNWprivate_1.1;
+}