Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <libcpc.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <errno.h>
     30 #include <strings.h>
     31 #include <unistd.h>
     32 #include <stropts.h>
     33 #include <libintl.h>
     34 #include <signal.h>
     35 #include <sys/syscall.h>
     36 #include <sys/types.h>
     37 #include <sys/processor.h>
     38 #include <sys/procset.h>
     39 
     40 #include "libcpc_impl.h"
     41 
     42 #define	MASK32 0xFFFFFFFF
     43 
     44 /*
     45  * The library uses the cpc_lock field of the cpc_t struct to protect access to
     46  * the linked lists inside the cpc_t, and only the linked lists. It is NOT used
     47  * to protect against a user shooting his/herself in the foot (such as, for
     48  * instance, destroying the same set at the same time from different threads.).
     49  *
     50  * SIGEMT needs to be blocked while holding the lock, to prevent deadlock among
     51  * an app holding the lock and a signal handler attempting to sample or bind.
     52  */
     53 
     54 static char *cpc_get_list(int which, int arg);
     55 static void cpc_err(cpc_t *cpc, const char *fn, int subcode, ...);
     56 static int cpc_set_valid(cpc_t *cpc, cpc_set_t *set);
     57 static int cpc_lock(cpc_t *cpc);
     58 static void cpc_unlock(cpc_t *cpc, int blocked);
     59 static int cpc_valid_event(cpc_t *cpc, uint_t pic, const char *ev);
     60 static int cpc_valid_attr(cpc_t *cpc, char *attr);
     61 static void cpc_invalidate_pctx(cpc_t *cpc, pctx_t *pctx);
     62 
     63 cpc_t *
     64 cpc_open(int ver)
     65 {
     66 	cpc_t	*cpc;
     67 	void	(*sigsaved)();
     68 	int	error = 0;
     69 	int	i;
     70 	int	j;
     71 
     72 	if (ver != CPC_VER_CURRENT) {
     73 		/*
     74 		 * v1 clients must stick to the v1 interface: cpc_version()
     75 		 */
     76 		errno = EINVAL;
     77 		return (NULL);
     78 	}
     79 
     80 	/*
     81 	 * Call the syscall with invalid parameters.  If we get ENOSYS this CPU
     82 	 * has no CPC support.  We need to block SIGSYS because the syscall code
     83 	 * will send the signal if the system call fails to load.
     84 	 */
     85 	sigsaved = signal(SIGSYS, SIG_IGN);
     86 	if (syscall(SYS_cpc, -1, -1, -1, -1, -1) != -1) {
     87 		(void) signal(SIGSYS, sigsaved);
     88 		errno = EINVAL;
     89 		return (NULL);
     90 	}
     91 	error = errno;
     92 	(void) signal(SIGSYS, sigsaved);
     93 
     94 	if (error != EINVAL) {
     95 		errno = error;
     96 		return (NULL);
     97 	}
     98 
     99 	if ((cpc = malloc(sizeof (cpc_t))) == NULL) {
    100 		errno = ENOMEM;
    101 		return (NULL);
    102 	}
    103 
    104 	cpc->cpc_npic = syscall(SYS_cpc, CPC_NPIC, -1, 0, 0, 0);
    105 	cpc->cpc_caps = syscall(SYS_cpc, CPC_CAPS, -1, 0, 0, 0);
    106 
    107 	if (syscall(SYS_cpc, CPC_IMPL_NAME, -1, &cpc->cpc_cciname, 0, 0) != 0)
    108 		return (NULL);
    109 	if (syscall(SYS_cpc, CPC_CPUREF, -1, &cpc->cpc_cpuref, 0, 0) != 0)
    110 		return (NULL);
    111 
    112 
    113 	if ((cpc->cpc_attrlist = cpc_get_list(CPC_LIST_ATTRS, 0)) == NULL) {
    114 		free(cpc);
    115 		return (NULL);
    116 	}
    117 
    118 	if ((cpc->cpc_evlist = malloc(cpc->cpc_npic * sizeof (char *))) ==
    119 	    NULL) {
    120 		free(cpc->cpc_attrlist);
    121 		free(cpc);
    122 		return (NULL);
    123 	}
    124 
    125 	for (i = 0; i < cpc->cpc_npic; i++) {
    126 		if ((cpc->cpc_evlist[i] = cpc_get_list(CPC_LIST_EVENTS, i)) ==
    127 		    NULL)
    128 			break;
    129 	}
    130 	if (i != cpc->cpc_npic) {
    131 		for (j = 0; j < i; j++)
    132 			free(cpc->cpc_evlist[j]);
    133 		free(cpc->cpc_evlist);
    134 		free(cpc->cpc_attrlist);
    135 		free(cpc);
    136 		return (NULL);
    137 	}
    138 
    139 	cpc->cpc_sets = NULL;
    140 	cpc->cpc_bufs = NULL;
    141 	cpc->cpc_errfn = NULL;
    142 	(void) mutex_init(&cpc->cpc_lock, USYNC_THREAD, NULL);
    143 	__pctx_cpc_register_callback(cpc_invalidate_pctx);
    144 
    145 	return (cpc);
    146 }
    147 
    148 /*
    149  * Ensure state is cleaned up:
    150  *
    151  * - Hardware is unbound
    152  * - Sets are all destroyed
    153  * - Bufs are all freed
    154  */
    155 int
    156 cpc_close(cpc_t *cpc)
    157 {
    158 	while (cpc->cpc_sets != NULL) {
    159 		if (cpc->cpc_sets->cs_state != CS_UNBOUND)
    160 			(void) cpc_unbind(cpc, cpc->cpc_sets);
    161 		(void) cpc_set_destroy(cpc, cpc->cpc_sets);
    162 	}
    163 
    164 	while (cpc->cpc_bufs != NULL)
    165 		(void) cpc_buf_destroy(cpc, cpc->cpc_bufs);
    166 
    167 	free(cpc);
    168 	return (0);
    169 }
    170 
    171 cpc_set_t *
    172 cpc_set_create(cpc_t *cpc)
    173 {
    174 	cpc_set_t	*set;
    175 	int		sigblocked;
    176 
    177 	if ((set = malloc(sizeof (*set))) == NULL) {
    178 		errno = ENOMEM;
    179 		return (NULL);
    180 	}
    181 
    182 	set->cs_request = NULL;
    183 	set->cs_nreqs	= 0;
    184 	set->cs_state	= CS_UNBOUND;
    185 	set->cs_fd	= -1;
    186 	set->cs_pctx	= NULL;
    187 	set->cs_id	= -1;
    188 	set->cs_thr	= NULL;
    189 
    190 	sigblocked = cpc_lock(cpc);
    191 	set->cs_next = cpc->cpc_sets;
    192 	cpc->cpc_sets = set;
    193 	cpc_unlock(cpc, sigblocked);
    194 
    195 	return (set);
    196 }
    197 
    198 int
    199 cpc_set_destroy(cpc_t *cpc, cpc_set_t *set)
    200 {
    201 	cpc_set_t	*csp, *prev;
    202 	cpc_request_t	*req, *next;
    203 	int		sigblocked;
    204 
    205 	/*
    206 	 * Remove this set from the cpc handle's list of sets.
    207 	 */
    208 	sigblocked = cpc_lock(cpc);
    209 	for (csp = prev = cpc->cpc_sets; csp != NULL; csp = csp->cs_next) {
    210 		if (csp == set)
    211 			break;
    212 		prev = csp;
    213 	}
    214 	if (csp == NULL) {
    215 		cpc_unlock(cpc, sigblocked);
    216 		errno = EINVAL;
    217 		return (-1);
    218 	}
    219 	if (csp == cpc->cpc_sets)
    220 		cpc->cpc_sets = csp->cs_next;
    221 	prev->cs_next = csp->cs_next;
    222 	cpc_unlock(cpc, sigblocked);
    223 
    224 	if (csp->cs_state != CS_UNBOUND)
    225 		(void) cpc_unbind(cpc, csp);
    226 
    227 	for (req = csp->cs_request; req != NULL; req = next) {
    228 		next = req->cr_next;
    229 
    230 		if (req->cr_nattrs != 0)
    231 			free(req->cr_attr);
    232 
    233 		free(req);
    234 	}
    235 
    236 
    237 	free(set);
    238 
    239 	return (0);
    240 }
    241 
    242 /*ARGSUSED*/
    243 int
    244 cpc_set_add_request(cpc_t *cpc, cpc_set_t *set, const char *event,
    245     uint64_t preset, uint_t flags, uint_t nattrs, const cpc_attr_t *attrs)
    246 {
    247 	cpc_request_t	*req;
    248 	const char	*fn = "cpc_set_add_request";
    249 	int		i;
    250 	int		npics = cpc_npic(cpc);
    251 
    252 	if (cpc_set_valid(cpc, set) != 0 || set->cs_state != CS_UNBOUND) {
    253 		errno = EINVAL;
    254 		return (-1);
    255 	}
    256 
    257 	for (i = 0; i < npics; i++)
    258 		if (cpc_valid_event(cpc, i, event))
    259 			break;
    260 	if (i == npics) {
    261 		cpc_err(cpc, fn, CPC_INVALID_EVENT);
    262 		errno = EINVAL;
    263 		return (-1);
    264 	}
    265 
    266 	if ((req = malloc(sizeof (*req))) == NULL) {
    267 		errno = ENOMEM;
    268 		return (-1);
    269 	}
    270 
    271 	(void) strncpy(req->cr_event, event, CPC_MAX_EVENT_LEN);
    272 	req->cr_preset = preset;
    273 	req->cr_flags = flags;
    274 	req->cr_nattrs = nattrs;
    275 	req->cr_index = set->cs_nreqs;
    276 	req->cr_attr = NULL;
    277 
    278 	if (nattrs != 0) {
    279 		for (i = 0; i < nattrs; i++) {
    280 			/*
    281 			 * Verify that each attribute name is legal and valid.
    282 			 */
    283 			if (attrs[i].ca_name[0] == '\0' ||
    284 			    cpc_valid_attr(cpc, attrs[i].ca_name) == 0) {
    285 				cpc_err(cpc, fn, CPC_INVALID_ATTRIBUTE);
    286 				goto inval;
    287 			}
    288 
    289 			/*
    290 			 * If the user requested a specific picnum, ensure that
    291 			 * the pic can count the requested event.
    292 			 */
    293 			if (strncmp("picnum", attrs[i].ca_name, 8) == 0) {
    294 				if (attrs[i].ca_val >= npics) {
    295 					cpc_err(cpc, fn, CPC_INVALID_PICNUM);
    296 					goto inval;
    297 				}
    298 
    299 				if (cpc_valid_event(cpc, attrs[i].ca_val,
    300 				    req->cr_event) == 0) {
    301 					cpc_err(cpc, fn, CPC_PIC_NOT_CAPABLE);
    302 					goto inval;
    303 				}
    304 			}
    305 		}
    306 
    307 		if ((req->cr_attr = malloc(nattrs * sizeof (kcpc_attr_t)))
    308 		    == NULL) {
    309 			free(req);
    310 			return (-1);
    311 		}
    312 
    313 		for (i = 0; i < nattrs; i++) {
    314 			req->cr_attr[i].ka_val = attrs[i].ca_val;
    315 			(void) strncpy(req->cr_attr[i].ka_name,
    316 			    attrs[i].ca_name, CPC_MAX_ATTR_LEN);
    317 		}
    318 	} else
    319 		req->cr_attr = NULL;
    320 
    321 	req->cr_next = set->cs_request;
    322 	set->cs_request = req;
    323 	set->cs_nreqs++;
    324 
    325 	return (req->cr_index);
    326 
    327 inval:
    328 	free(req);
    329 	errno = EINVAL;
    330 	return (-1);
    331 }
    332 
    333 cpc_buf_t *
    334 cpc_buf_create(cpc_t *cpc, cpc_set_t *set)
    335 {
    336 	cpc_buf_t	*buf;
    337 	int		sigblocked;
    338 
    339 	if (cpc_set_valid(cpc, set) != 0) {
    340 		errno = EINVAL;
    341 		return (NULL);
    342 	}
    343 
    344 	if ((buf = malloc(sizeof (*buf))) == NULL)
    345 		return (NULL);
    346 
    347 	buf->cb_size = set->cs_nreqs * sizeof (uint64_t);
    348 	if ((buf->cb_data = malloc(buf->cb_size)) == NULL) {
    349 		free(buf);
    350 		return (NULL);
    351 	}
    352 
    353 	bzero(buf->cb_data, buf->cb_size);
    354 
    355 	buf->cb_hrtime = 0;
    356 	buf->cb_tick = 0;
    357 
    358 	sigblocked = cpc_lock(cpc);
    359 	buf->cb_next = cpc->cpc_bufs;
    360 	cpc->cpc_bufs = buf;
    361 	cpc_unlock(cpc, sigblocked);
    362 
    363 	return (buf);
    364 }
    365 
    366 int
    367 cpc_buf_destroy(cpc_t *cpc, cpc_buf_t *buf)
    368 {
    369 	cpc_buf_t	*cbp, *prev;
    370 	int		sigblocked;
    371 
    372 	/*
    373 	 * Remove this buf from the cpc handle's list of bufs.
    374 	 */
    375 	sigblocked = cpc_lock(cpc);
    376 	for (cbp = prev = cpc->cpc_bufs; cbp != NULL; cbp = cbp->cb_next) {
    377 		if (cbp == buf)
    378 			break;
    379 		prev = cbp;
    380 	}
    381 	if (cbp == NULL) {
    382 		cpc_unlock(cpc, sigblocked);
    383 		errno = EINVAL;
    384 		return (-1);
    385 	}
    386 	if (cbp == cpc->cpc_bufs)
    387 		cpc->cpc_bufs = cbp->cb_next;
    388 	prev->cb_next = cbp->cb_next;
    389 
    390 	cpc_unlock(cpc, sigblocked);
    391 	free(cbp->cb_data);
    392 	free(cbp);
    393 
    394 	return (0);
    395 }
    396 
    397 /*ARGSUSED*/
    398 int
    399 cpc_bind_curlwp(cpc_t *cpc, cpc_set_t *set, uint_t flags)
    400 {
    401 	char		*packed_set;
    402 	size_t		packsize;
    403 	int		ret;
    404 	int		subcode = -1;
    405 
    406 	/*
    407 	 * We don't bother checking cpc_set_valid() here, because this is in the
    408 	 * fast path of an app doing SIGEMT-based profiling as they restart the
    409 	 * counters from their signal handler.
    410 	 */
    411 	if (CPC_SET_VALID_FLAGS(flags) == 0 || set->cs_nreqs <= 0) {
    412 		errno = EINVAL;
    413 		return (-1);
    414 	}
    415 
    416 	if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
    417 		errno = ENOMEM;
    418 		return (-1);
    419 	}
    420 
    421 	ret = syscall(SYS_cpc, CPC_BIND, -1, packed_set, packsize, &subcode);
    422 	free(packed_set);
    423 
    424 	if (ret != 0) {
    425 		if (subcode != -1)
    426 			cpc_err(cpc, "cpc_bind_curlwp", subcode);
    427 		return (-1);
    428 	}
    429 
    430 	set->cs_thr = thr_self();
    431 	set->cs_state = CS_BOUND_CURLWP;
    432 	return (ret);
    433 }
    434 
    435 /*ARGSUSED*/
    436 int
    437 cpc_bind_pctx(cpc_t *cpc, pctx_t *pctx, id_t id, cpc_set_t *set, uint_t flags)
    438 {
    439 	char		*packed_set;
    440 	size_t		packsize;
    441 	int		ret;
    442 	int		subcode = -1;
    443 
    444 	/*
    445 	 * cpc_bind_pctx() currently has no valid flags.
    446 	 */
    447 	if (flags != 0 || cpc_set_valid(cpc, set) != 0 || set->cs_nreqs <= 0) {
    448 		errno = EINVAL;
    449 		return (-1);
    450 	}
    451 
    452 	if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
    453 		errno = ENOMEM;
    454 		return (-1);
    455 	}
    456 
    457 	ret = __pctx_cpc(pctx, cpc, CPC_BIND, id, packed_set, (void *)packsize,
    458 	    (void *)&subcode, -1);
    459 
    460 	free(packed_set);
    461 
    462 	if (ret == 0) {
    463 		set->cs_pctx = pctx;
    464 		set->cs_id = id;
    465 		set->cs_state = CS_BOUND_PCTX;
    466 	} else if (subcode != -1)
    467 		cpc_err(cpc, "cpc_bind_pctx", subcode);
    468 
    469 	return (ret);
    470 }
    471 
    472 /*ARGSUSED*/
    473 int
    474 cpc_bind_cpu(cpc_t *cpc, processorid_t id, cpc_set_t *set, uint_t flags)
    475 {
    476 	int		fd;
    477 	char		*packed_set;
    478 	size_t		packsize;
    479 	__cpc_args_t	cpc_args;
    480 	int		error;
    481 	const char	*fn = "cpc_bind_cpu";
    482 	int		subcode = -1;
    483 
    484 	/*
    485 	 * cpc_bind_cpu() currently has no valid flags.
    486 	 */
    487 	if (flags != 0 || cpc_set_valid(cpc, set) != 0 || set->cs_nreqs <= 0) {
    488 		errno = EINVAL;
    489 		return (-1);
    490 	}
    491 
    492 	if (processor_bind(P_LWPID, P_MYID, id, &set->cs_obind) == -1) {
    493 		cpc_err(cpc, fn, CPC_PBIND_FAILED);
    494 		return (-1);
    495 	}
    496 
    497 	if ((fd = open(CPUDRV_SHARED, O_RDWR)) < 0) {
    498 		error = errno;
    499 		(void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
    500 		errno = error;
    501 		return (-1);
    502 	}
    503 
    504 	/*
    505 	 * To avoid leaking file descriptors, if we find an existing fd here we
    506 	 * just close it. This is only a problem if a user attempts to bind the
    507 	 * same set to different CPUs without first unbinding it.
    508 	 */
    509 	if (set->cs_fd != -1)
    510 		(void) close(set->cs_fd);
    511 	set->cs_fd = fd;
    512 
    513 	if ((packed_set = __cpc_pack_set(set, flags, &packsize)) == NULL) {
    514 		(void) close(fd);
    515 		(void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
    516 		errno = ENOMEM;
    517 		return (-1);
    518 	}
    519 
    520 	cpc_args.udata1 = packed_set;
    521 	cpc_args.udata2 = (void *)packsize;
    522 	cpc_args.udata3 = (void *)&subcode;
    523 
    524 	if (ioctl(fd, CPCIO_BIND, &cpc_args) != 0) {
    525 		error = errno;
    526 		free(packed_set);
    527 		(void) close(fd);
    528 		(void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
    529 		if (subcode != -1)
    530 			cpc_err(cpc, fn, subcode);
    531 		errno = error;
    532 		return (-1);
    533 	}
    534 
    535 	free(packed_set);
    536 
    537 	set->cs_thr = thr_self();
    538 	set->cs_state = CS_BOUND_CPU;
    539 
    540 	return (0);
    541 }
    542 
    543 /*ARGSUSED*/
    544 int
    545 cpc_request_preset(cpc_t *cpc, int index, uint64_t preset)
    546 {
    547 	return (syscall(SYS_cpc, CPC_PRESET, -1, index,
    548 	    (uint32_t)(preset >> 32), (uint32_t)(preset & MASK32)));
    549 }
    550 
    551 /*ARGSUSED*/
    552 int
    553 cpc_set_restart(cpc_t *cpc, cpc_set_t *set)
    554 {
    555 	return (syscall(SYS_cpc, CPC_RESTART, -1, 0, 0, 0));
    556 }
    557 
    558 /*ARGSUSED*/
    559 int
    560 cpc_unbind(cpc_t *cpc, cpc_set_t *set)
    561 {
    562 	int		ret = 0;
    563 	int		error;
    564 
    565 	if (cpc_set_valid(cpc, set) != 0) {
    566 		errno = EINVAL;
    567 		return (-1);
    568 	}
    569 
    570 	switch (set->cs_state) {
    571 	case CS_UNBOUND:
    572 		errno = EINVAL;
    573 		return (-1);
    574 	case CS_BOUND_CURLWP:
    575 		ret = syscall(SYS_cpc, CPC_RELE, -1, 0, 0, 0);
    576 		error = errno;
    577 		break;
    578 	case CS_BOUND_CPU:
    579 		ret = ioctl(set->cs_fd, CPCIO_RELE, NULL);
    580 		error = errno;
    581 		(void) close(set->cs_fd);
    582 		set->cs_fd = -1;
    583 		(void) processor_bind(P_LWPID, P_MYID, set->cs_obind, NULL);
    584 		break;
    585 	case CS_BOUND_PCTX:
    586 		if (set->cs_pctx != NULL) {
    587 			ret = __pctx_cpc(set->cs_pctx, cpc, CPC_RELE,
    588 			    set->cs_id, 0, 0, 0, 0);
    589 			error = errno;
    590 		}
    591 		break;
    592 	}
    593 
    594 	set->cs_thr = NULL;
    595 	set->cs_id = -1;
    596 	set->cs_state = CS_UNBOUND;
    597 	if (ret != 0)
    598 		errno = error;
    599 	return (ret);
    600 }
    601 
    602 /*ARGSUSED*/
    603 int
    604 cpc_set_sample(cpc_t *cpc, cpc_set_t *set, cpc_buf_t *buf)
    605 {
    606 	__cpc_args_t args;
    607 
    608 	/*
    609 	 * The following check ensures that only the most recently bound set
    610 	 * can be sampled, as binding a set invalidates all other sets in the
    611 	 * cpc_t.
    612 	 */
    613 	if (set->cs_state == CS_UNBOUND ||
    614 	    buf->cb_size != set->cs_nreqs * sizeof (uint64_t)) {
    615 		errno = EINVAL;
    616 		return (-1);
    617 	}
    618 
    619 	switch (set->cs_state) {
    620 	case CS_BOUND_CURLWP:
    621 		return (syscall(SYS_cpc, CPC_SAMPLE, -1, buf->cb_data,
    622 		    &buf->cb_hrtime, &buf->cb_tick));
    623 	case CS_BOUND_CPU:
    624 		args.udata1 = buf->cb_data;
    625 		args.udata2 = &buf->cb_hrtime;
    626 		args.udata3 = &buf->cb_tick;
    627 		return (ioctl(set->cs_fd, CPCIO_SAMPLE, &args));
    628 	case CS_BOUND_PCTX:
    629 		return (__pctx_cpc(set->cs_pctx, cpc, CPC_SAMPLE, set->cs_id,
    630 		    buf->cb_data, &buf->cb_hrtime, &buf->cb_tick,
    631 		    buf->cb_size));
    632 	}
    633 
    634 	errno = EINVAL;
    635 	return (-1);
    636 }
    637 
    638 /*ARGSUSED*/
    639 void
    640 cpc_buf_sub(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, cpc_buf_t *b)
    641 {
    642 	int i;
    643 
    644 	if (a->cb_size != ds->cb_size || b->cb_size != ds->cb_size)
    645 		return;
    646 
    647 	ds->cb_hrtime = (a->cb_hrtime > b->cb_hrtime) ?
    648 	    a->cb_hrtime : b->cb_hrtime;
    649 	ds->cb_tick = a->cb_tick - b->cb_tick;
    650 
    651 	for (i = 0; i < ds->cb_size / sizeof (uint64_t); i++)
    652 		ds->cb_data[i] = a->cb_data[i] - b->cb_data[i];
    653 }
    654 
    655 /*ARGSUSED*/
    656 void
    657 cpc_buf_add(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *a, cpc_buf_t *b)
    658 {
    659 	int i;
    660 
    661 	if (a->cb_size != ds->cb_size || b->cb_size != ds->cb_size)
    662 		return;
    663 
    664 	ds->cb_hrtime = (a->cb_hrtime > b->cb_hrtime) ?
    665 	    a->cb_hrtime : b->cb_hrtime;
    666 	ds->cb_tick = a->cb_tick + b->cb_tick;
    667 
    668 	for (i = 0; i < ds->cb_size / sizeof (uint64_t); i++)
    669 		ds->cb_data[i] = a->cb_data[i] + b->cb_data[i];
    670 }
    671 
    672 /*ARGSUSED*/
    673 void
    674 cpc_buf_copy(cpc_t *cpc, cpc_buf_t *ds, cpc_buf_t *src)
    675 {
    676 	if (ds->cb_size != src->cb_size)
    677 		return;
    678 
    679 	bcopy(src->cb_data, ds->cb_data, ds->cb_size);
    680 	ds->cb_hrtime = src->cb_hrtime;
    681 	ds->cb_tick = src->cb_tick;
    682 }
    683 
    684 /*ARGSUSED*/
    685 void
    686 cpc_buf_zero(cpc_t *cpc, cpc_buf_t *buf)
    687 {
    688 	bzero(buf->cb_data, buf->cb_size);
    689 	buf->cb_hrtime = 0;
    690 	buf->cb_tick = 0;
    691 }
    692 
    693 /*
    694  * Gets or sets the value of the request specified by index.
    695  */
    696 /*ARGSUSED*/
    697 int
    698 cpc_buf_get(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t *val)
    699 {
    700 	*val = buf->cb_data[index];
    701 
    702 	return (0);
    703 }
    704 
    705 /*ARGSUSED*/
    706 int
    707 cpc_buf_set(cpc_t *cpc, cpc_buf_t *buf, int index, uint64_t val)
    708 {
    709 	buf->cb_data[index] = val;
    710 
    711 	return (0);
    712 }
    713 
    714 /*ARGSUSED*/
    715 hrtime_t
    716 cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf)
    717 {
    718 	return (buf->cb_hrtime);
    719 }
    720 
    721 /*ARGSUSED*/
    722 uint64_t
    723 cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf)
    724 {
    725 	return (buf->cb_tick);
    726 }
    727 
    728 static char *
    729 cpc_get_list(int which, int arg)
    730 {
    731 	int	szcmd;
    732 	int	size;
    733 	char	*list;
    734 
    735 	if (which == CPC_LIST_ATTRS)
    736 		szcmd = CPC_ATTRLIST_SIZE;
    737 	else
    738 		szcmd = CPC_EVLIST_SIZE;
    739 
    740 	if (syscall(SYS_cpc, szcmd, -1, &size, arg, 0) != 0)
    741 		return (NULL);
    742 
    743 	if ((list = malloc(size)) == NULL)
    744 		return (NULL);
    745 
    746 	if (syscall(SYS_cpc, which, -1, list, arg, 0) != 0) {
    747 		free(list);
    748 		return (NULL);
    749 	}
    750 
    751 	return (list);
    752 }
    753 
    754 /*ARGSUSED*/
    755 void
    756 cpc_walk_requests(cpc_t *cpc, cpc_set_t *set, void *arg,
    757     void (*action)(void *arg, int index, const char *event, uint64_t preset,
    758 	uint_t flags, int nattrs, const cpc_attr_t *attrs))
    759 {
    760 	cpc_request_t	*rp;
    761 	cpc_attr_t	*attrs = NULL;
    762 	int		i;
    763 
    764 	for (rp = set->cs_request; rp != NULL; rp = rp->cr_next) {
    765 		/*
    766 		 * Need to reconstruct a temporary cpc_attr_t array for req.
    767 		 */
    768 		if (rp->cr_nattrs != 0)
    769 			if ((attrs = malloc(rp->cr_nattrs *
    770 			    sizeof (cpc_attr_t))) == NULL)
    771 				return;
    772 		for (i = 0; i < rp->cr_nattrs; i++) {
    773 			attrs[i].ca_name = rp->cr_attr[i].ka_name;
    774 			attrs[i].ca_val = rp->cr_attr[i].ka_val;
    775 		}
    776 
    777 		action(arg, rp->cr_index, rp->cr_event, rp->cr_preset,
    778 		    rp->cr_flags, rp->cr_nattrs, attrs);
    779 
    780 		if (rp->cr_nattrs != 0)
    781 			free(attrs);
    782 	}
    783 }
    784 
    785 /*ARGSUSED*/
    786 static void
    787 cpc_walk_events_impl(cpc_t *cpc, void *arg,
    788     void (*action)(void *arg, const char *event), int is_generic)
    789 {
    790 	char		**list;
    791 	char		*p, *e;
    792 	int		i;
    793 	int		is_papi;
    794 	int		ncounters = cpc_npic(cpc);
    795 	cpc_strhash_t	*hash;
    796 
    797 	if ((list = malloc(ncounters * sizeof (char *))) == NULL)
    798 		return;
    799 
    800 	if ((hash = __cpc_strhash_alloc()) == NULL) {
    801 		free(list);
    802 		return;
    803 	}
    804 
    805 	for (i = 0; i < ncounters; i++) {
    806 		if ((list[i] = strdup(cpc->cpc_evlist[i])) == NULL)
    807 			goto err;
    808 		p = list[i];
    809 		while ((e = strchr(p, ',')) != NULL) {
    810 			*e = '\0';
    811 
    812 			/*
    813 			 * Based on is_generic flag, skip appropriate
    814 			 * event names.
    815 			 */
    816 			is_papi = (strncmp(p, "PAPI", 4) == 0);
    817 			if (is_generic != is_papi) {
    818 				p = e + 1;
    819 				continue;
    820 			}
    821 
    822 			if (__cpc_strhash_add(hash, p) == -1)
    823 				goto err;
    824 
    825 			p = e + 1;
    826 		}
    827 
    828 		is_papi = (strncmp(p, "PAPI", 4) == 0);
    829 		if (is_generic == is_papi) {
    830 			if (__cpc_strhash_add(hash, p) == -1)
    831 				goto err;
    832 		}
    833 	}
    834 
    835 	while ((p = __cpc_strhash_next(hash)) != NULL)
    836 		action(arg, p);
    837 
    838 err:
    839 	__cpc_strhash_free(hash);
    840 	for (i = 0; i < ncounters; i++)
    841 		free(list[i]);
    842 	free(list);
    843 }
    844 
    845 /*ARGSUSED*/
    846 void
    847 cpc_walk_events_all(cpc_t *cpc, void *arg,
    848 		    void (*action)(void *arg, const char *event))
    849 {
    850 	cpc_walk_events_impl(cpc, arg, action, 0);
    851 }
    852 
    853 
    854 /*ARGSUSED*/
    855 void
    856 cpc_walk_generic_events_all(cpc_t *cpc, void *arg,
    857 			    void (*action)(void *arg, const char *event))
    858 {
    859 	cpc_walk_events_impl(cpc, arg, action, 1);
    860 }
    861 
    862 /*ARGSUSED*/
    863 static void
    864 cpc_walk_events_pic_impl(cpc_t *cpc, uint_t picno, void *arg,
    865     void (*action)(void *arg, uint_t picno, const char *event), int is_generic)
    866 {
    867 	char	*p;
    868 	char	*e;
    869 	char	*list;
    870 	int	is_papi;
    871 
    872 	if (picno >= cpc->cpc_npic) {
    873 		errno = EINVAL;
    874 		return;
    875 	}
    876 
    877 	if ((list = strdup(cpc->cpc_evlist[picno])) == NULL)
    878 		return;
    879 
    880 	/*
    881 	 * List now points to a comma-separated list of events supported by
    882 	 * the designated pic.
    883 	 */
    884 	p = list;
    885 	while ((e = strchr(p, ',')) != NULL) {
    886 		*e = '\0';
    887 
    888 		/*
    889 		 * Based on is_generic flag, skip appropriate
    890 		 * event names.
    891 		 */
    892 		is_papi = (strncmp(p, "PAPI", 4) == 0);
    893 		if (is_generic != is_papi) {
    894 			p = e + 1;
    895 			continue;
    896 		}
    897 
    898 		action(arg, picno, p);
    899 		p = e + 1;
    900 	}
    901 
    902 	is_papi = (strncmp(p, "PAPI", 4) == 0);
    903 	if (is_generic == is_papi)
    904 		action(arg, picno, p);
    905 
    906 	free(list);
    907 }
    908 
    909 /*ARGSUSED*/
    910 void
    911 cpc_walk_events_pic(cpc_t *cpc, uint_t picno, void *arg,
    912     void (*action)(void *arg, uint_t picno, const char *event))
    913 {
    914 	cpc_walk_events_pic_impl(cpc, picno, arg, action, 0);
    915 }
    916 
    917 /*ARGSUSED*/
    918 void
    919 cpc_walk_generic_events_pic(cpc_t *cpc, uint_t picno, void *arg,
    920     void (*action)(void *arg, uint_t picno, const char *event))
    921 {
    922 	cpc_walk_events_pic_impl(cpc, picno, arg, action, 1);
    923 }
    924 
    925 /*ARGSUSED*/
    926 void
    927 cpc_walk_attrs(cpc_t *cpc, void *arg,
    928     void (*action)(void *arg, const char *attr))
    929 {
    930 	char	*p;
    931 	char	*e;
    932 	char	*list;
    933 
    934 	if ((list = strdup(cpc->cpc_attrlist)) == NULL)
    935 		return;
    936 
    937 	/*
    938 	 * Platforms with no attributes will return an empty string.
    939 	 */
    940 	if (*list == '\0')
    941 		return;
    942 
    943 	/*
    944 	 * List now points to a comma-separated list of attributes supported by
    945 	 * the underlying platform.
    946 	 */
    947 	p = list;
    948 	while ((e = strchr(p, ',')) != NULL) {
    949 		*e = '\0';
    950 		action(arg, p);
    951 		p = e + 1;
    952 	}
    953 	action(arg, p);
    954 
    955 	free(list);
    956 }
    957 
    958 /*ARGSUSED*/
    959 int
    960 cpc_enable(cpc_t *cpc)
    961 {
    962 	return (syscall(SYS_cpc, CPC_ENABLE, -1, 0, 0, 0));
    963 }
    964 
    965 /*ARGSUSED*/
    966 int
    967 cpc_disable(cpc_t *cpc)
    968 {
    969 	return (syscall(SYS_cpc, CPC_DISABLE, -1, 0, 0, 0));
    970 }
    971 
    972 /*ARGSUSED*/
    973 uint_t
    974 cpc_npic(cpc_t *cpc)
    975 {
    976 	return (cpc->cpc_npic);
    977 }
    978 
    979 /*ARGSUSED*/
    980 uint_t
    981 cpc_caps(cpc_t *cpc)
    982 {
    983 	return (cpc->cpc_caps);
    984 }
    985 
    986 const char *
    987 cpc_cciname(cpc_t *cpc)
    988 {
    989 	return (cpc->cpc_cciname);
    990 }
    991 
    992 const char *
    993 cpc_cpuref(cpc_t *cpc)
    994 {
    995 	return (cpc->cpc_cpuref);
    996 }
    997 
    998 int
    999 cpc_seterrhndlr(cpc_t *cpc, cpc_errhndlr_t *fn)
   1000 {
   1001 	cpc->cpc_errfn = fn;
   1002 	return (0);
   1003 }
   1004 
   1005 /*
   1006  * These strings may contain printf() conversion specifiers.
   1007  */
   1008 static const char *errstr[] = {
   1009 "",						/* zero slot filler */
   1010 "Unknown event\n",				/* CPC_INVALID_EVENT */
   1011 "Invalid counter number\n",			/* CPC_INVALID_PICNUM */
   1012 "Unknown attribute\n",				/* CPC_INVALID_ATTRIBUTE */
   1013 "Attribute out of range\n",			/* CPC_ATTRIBUTE_OUT_OF_RANGE */
   1014 "Hardware resource unavailable\n",		/* CPC_RESOURCE_UNAVAIL */
   1015 "Counter cannot count requested event\n",	/* CPC_PIC_NOT_CAPABLE */
   1016 "Invalid flags in a request\n",			/* CPC_REQ_INVALID_FLAGS */
   1017 "Requests conflict with each other\n",		/* CPC_CONFLICTING_REQS */
   1018 "Attribute requires the cpc_cpu privilege\n",  /* CPC_ATTR_REQUIRES_PRIVILEGE */
   1019 "Couldn't bind LWP to requested processor\n",	/* CPC_PBIND_FAILED */
   1020 "Hypervisor event access denied\n"		/* CPC_HV_NO_ACCESS */
   1021 };
   1022 
   1023 /*VARARGS3*/
   1024 static void
   1025 cpc_err(cpc_t *cpc, const char *fn, int subcode, ...)
   1026 {
   1027 	va_list		ap;
   1028 	const char	*str;
   1029 	int		error;
   1030 
   1031 	/*
   1032 	 * If subcode is -1, there is no specific description for this error.
   1033 	 */
   1034 	if (subcode == -1)
   1035 		return;
   1036 
   1037 	/*
   1038 	 * We need to preserve errno across calls to this function to prevent it
   1039 	 * from being clobbered while here, or in the user's error handler.
   1040 	 */
   1041 	error = errno;
   1042 
   1043 	str = dgettext(TEXT_DOMAIN, errstr[subcode]);
   1044 
   1045 	va_start(ap, subcode);
   1046 	if (cpc->cpc_errfn != NULL)
   1047 		cpc->cpc_errfn(fn, subcode, str, ap);
   1048 	else {
   1049 		/*
   1050 		 * If printf() conversion specifiers are added to the errstr[]
   1051 		 * table, this call needs to be changed to vfprintf().
   1052 		 */
   1053 		(void) fprintf(stderr, "libcpc: %s: %s", fn, str);
   1054 	}
   1055 	va_end(ap);
   1056 
   1057 	errno = error;
   1058 }
   1059 
   1060 /*
   1061  * Hook used by libpctx to alert libcpc when a pctx handle is going away.
   1062  * This is necessary to prevent libcpc from attempting a libpctx operation on a
   1063  * stale and invalid pctx_t handle. Since pctx_t's are cached by libcpc, we need
   1064  * to be notified when they go away.
   1065  */
   1066 static void
   1067 cpc_invalidate_pctx(cpc_t *cpc, pctx_t *pctx)
   1068 {
   1069 	cpc_set_t	*set;
   1070 	int		sigblocked;
   1071 
   1072 	sigblocked = cpc_lock(cpc);
   1073 	for (set = cpc->cpc_sets; set != NULL; set = set->cs_next)
   1074 		if (set->cs_pctx == pctx)
   1075 			set->cs_pctx = NULL;
   1076 	cpc_unlock(cpc, sigblocked);
   1077 }
   1078 
   1079 /*
   1080  * Check that the set is valid; if so it will be in the cpc handle's
   1081  * list of sets. The lock protects the list of sets, but not the set
   1082  * itself.
   1083  */
   1084 static int
   1085 cpc_set_valid(cpc_t *cpc, cpc_set_t *set)
   1086 {
   1087 	cpc_set_t	*csp;
   1088 	int		sigblocked;
   1089 
   1090 	sigblocked = cpc_lock(cpc);
   1091 	for (csp = cpc->cpc_sets; csp != NULL; csp = csp->cs_next)
   1092 		if (csp == set)
   1093 			break;
   1094 	cpc_unlock(cpc, sigblocked);
   1095 	if (csp == NULL)
   1096 		return (-1);
   1097 	return (0);
   1098 }
   1099 
   1100 static int
   1101 cpc_lock(cpc_t *cpc)
   1102 {
   1103 	int ret = (sigset(SIGEMT, SIG_HOLD) == SIG_HOLD);
   1104 	(void) mutex_lock(&cpc->cpc_lock);
   1105 	return (ret);
   1106 }
   1107 
   1108 static void
   1109 cpc_unlock(cpc_t *cpc, int sigblocked)
   1110 {
   1111 	(void) mutex_unlock(&cpc->cpc_lock);
   1112 	if (sigblocked == 0)
   1113 		(void) sigrelse(SIGEMT);
   1114 }
   1115 
   1116 struct priv {
   1117 	const char *name;
   1118 	int found;
   1119 };
   1120 
   1121 /*ARGSUSED*/
   1122 static void
   1123 ev_walker(void *arg, uint_t picno, const char *ev)
   1124 {
   1125 	if (strcmp(((struct priv *)arg)->name, ev) == 0)
   1126 		((struct priv *)arg)->found = 1;
   1127 }
   1128 
   1129 static void
   1130 at_walker(void *arg, const char *at)
   1131 {
   1132 	if (strcmp(((struct priv *)arg)->name, at) == 0)
   1133 		((struct priv *)arg)->found = 1;
   1134 }
   1135 
   1136 static int
   1137 cpc_valid_event(cpc_t *cpc, uint_t pic, const char *ev)
   1138 {
   1139 	struct priv pr = { NULL, 0 };
   1140 	char *end_ev;
   1141 	int err;
   1142 
   1143 	pr.name = ev;
   1144 	cpc_walk_events_pic(cpc, pic, &pr, ev_walker);
   1145 	if (pr.found)
   1146 		return (1);
   1147 
   1148 	cpc_walk_generic_events_pic(cpc, pic, &pr, ev_walker);
   1149 	if (pr.found)
   1150 		return (1);
   1151 
   1152 	/*
   1153 	 * Before assuming this is an invalid event, see if we have been given
   1154 	 * a raw event code.
   1155 	 * Check the second argument of strtol() to ensure invalid events
   1156 	 * beginning with number do not go through.
   1157 	 */
   1158 	err = errno;
   1159 	errno = 0;
   1160 	(void) strtol(ev, &end_ev, 0);
   1161 	if ((errno == 0) && (*end_ev == '\0')) {
   1162 		/*
   1163 		 * Success - this is a valid raw code in hex, decimal, or octal.
   1164 		 */
   1165 		errno = err;
   1166 		return (1);
   1167 	}
   1168 
   1169 	errno = err;
   1170 	return (0);
   1171 }
   1172 
   1173 static int
   1174 cpc_valid_attr(cpc_t *cpc, char *attr)
   1175 {
   1176 	struct priv pr = { NULL, 0 };
   1177 
   1178 	pr.name = attr;
   1179 	cpc_walk_attrs(cpc, &pr, at_walker);
   1180 	return (pr.found);
   1181 }
   1182