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