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 /* 172 * Terminate everything that runs in pctx_run 173 */ 174 void 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 * 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 741 cpc_buf_hrtime(cpc_t *cpc, cpc_buf_t *buf) 742 { 743 return (buf->cb_hrtime); 744 } 745 746 /*ARGSUSED*/ 747 uint64_t 748 cpc_buf_tick(cpc_t *cpc, cpc_buf_t *buf) 749 { 750 return (buf->cb_tick); 751 } 752 753 static char * 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 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 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 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 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 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 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 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 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 985 cpc_enable(cpc_t *cpc) 986 { 987 return (syscall(SYS_cpc, CPC_ENABLE, -1, 0, 0, 0)); 988 } 989 990 /*ARGSUSED*/ 991 int 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 999 cpc_npic(cpc_t *cpc) 1000 { 1001 return (cpc->cpc_npic); 1002 } 1003 1004 /*ARGSUSED*/ 1005 uint_t 1006 cpc_caps(cpc_t *cpc) 1007 { 1008 return (cpc->cpc_caps); 1009 } 1010 1011 const char * 1012 cpc_cciname(cpc_t *cpc) 1013 { 1014 return (cpc->cpc_cciname); 1015 } 1016 1017 const char * 1018 cpc_cpuref(cpc_t *cpc) 1019 { 1020 return (cpc->cpc_cpuref); 1021 } 1022 1023 int 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 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 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 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 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 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 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 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 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 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