diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libcpc | |
download | illumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libcpc')
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; +} |