1 /* Copyright (C) 2021 Free Software Foundation, Inc. 2 Contributed by Oracle. 3 4 This file is part of GNU Binutils. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21 /* Hardware counter profiling */ 22 #include "hwcdrv.h" 23 #include "hwcfuncs.h" 24 25 /*---------------------------------------------------------------------------*/ 26 /* macros */ 27 28 #define IS_GLOBAL /* Mark global symbols */ 29 #define HWCDRV_API static /* Mark functions used by hwcdrv API */ 30 31 /*---------------------------------------------------------------------------*/ 32 /* static variables */ 33 static uint_t cpcN_npics; 34 static char hwcfuncs_errmsg_buf[1024]; 35 static int hwcfuncs_errmsg_enabled = 1; 36 static int hwcfuncs_errmsg_valid; 37 38 /* --- user counter selections and options */ 39 static unsigned hwcdef_cnt; /* number of *active* hardware counters */ 40 static Hwcentry hwcdef[MAX_PICS]; /* HWC definitions */ 41 static Hwcentry *hwctable[MAX_PICS]; /* HWC definitions */ 42 43 /* --- drivers --- */ 44 45 // default driver 46 47 HWCDRV_API int 48 hwcdrv_init (hwcfuncs_abort_fn_t abort_ftn, int* tsd_sz) 49 { 50 return -1; 51 } 52 53 HWCDRV_API void 54 hwcdrv_get_info ( 55 int * cpuver, const char ** cciname, 56 uint_t * npics, const char ** docref, uint64_t* support) { } 57 58 HWCDRV_API int 59 hwcdrv_enable_mt (hwcfuncs_tsd_get_fn_t tsd_ftn) 60 { 61 return -1; 62 } 63 64 HWCDRV_API int 65 hwcdrv_get_descriptions (hwcf_hwc_cb_t *hwc_find_action, 66 hwcf_attr_cb_t *attr_find_action) 67 { 68 return 0; 69 } 70 71 HWCDRV_API int 72 hwcdrv_assign_regnos (Hwcentry *entries[], unsigned numctrs) 73 { 74 return -1; 75 } 76 77 HWCDRV_API int 78 hwcdrv_create_counters (unsigned hwcdef_cnt, Hwcentry *hwcdef) 79 { 80 return -1; 81 } 82 83 HWCDRV_API int 84 hwcdrv_read_events (hwc_event_t *events, hwc_event_samples_t*samples) 85 { 86 return -1; 87 } 88 89 HWCDRV_API int 90 hwcdrv_start (void) 91 { 92 return -1; 93 } 94 95 HWCDRV_API int 96 hwcdrv_overflow (siginfo_t *si, hwc_event_t *s, hwc_event_t *t) 97 { 98 return 0; 99 } 100 101 HWCDRV_API int 102 hwcdrv_sighlr_restart (const hwc_event_t *sample) 103 { 104 return -1; 105 } 106 107 HWCDRV_API int 108 hwcdrv_lwp_suspend (void) 109 { 110 return -1; 111 } 112 113 HWCDRV_API int 114 hwcdrv_lwp_resume (void) 115 { 116 return -1; 117 } 118 119 HWCDRV_API int 120 hwcdrv_free_counters (void) 121 { 122 return 0; 123 } 124 125 HWCDRV_API int 126 hwcdrv_lwp_init (void) 127 { 128 return 0; 129 } 130 131 HWCDRV_API void 132 hwcdrv_lwp_fini (void) { } 133 134 static hwcdrv_api_t hwcdrv_default = { 135 hwcdrv_init, 136 hwcdrv_get_info, 137 hwcdrv_enable_mt, 138 hwcdrv_get_descriptions, 139 hwcdrv_assign_regnos, 140 hwcdrv_create_counters, 141 hwcdrv_start, 142 hwcdrv_overflow, 143 hwcdrv_read_events, 144 hwcdrv_sighlr_restart, 145 hwcdrv_lwp_suspend, 146 hwcdrv_lwp_resume, 147 hwcdrv_free_counters, 148 hwcdrv_lwp_init, 149 hwcdrv_lwp_fini, 150 -1 // hwcdrv_init_status 151 }; 152 153 static hwcdrv_api_t *hwcdrv_driver = &hwcdrv_default; 154 155 156 /*---------------------------------------------------------------------------*/ 157 /* misc */ 158 159 /* print a counter definition (for debugging) */ 160 static void 161 ctrdefprint (int dbg_lvl, const char * hdr, Hwcentry*phwcdef) 162 { 163 TprintfT (dbg_lvl, "%s: name='%s', int_name='%s'," 164 " reg_num=%d, timecvt=%d, memop=%d, " 165 "interval=%d, tag=%u, reg_list=%p\n", 166 hdr, phwcdef->name, phwcdef->int_name, phwcdef->reg_num, 167 phwcdef->timecvt, phwcdef->memop, phwcdef->val, 168 phwcdef->sort_order, phwcdef->reg_list); 169 } 170 171 /*---------------------------------------------------------------------------*/ 172 /* errmsg buffering */ 173 174 /* errmsg buffering is needed only because the most descriptive error 175 messages from CPC are delivered using a callback mechanism. 176 hwcfuncs_errmsg_get() should only be used during initialization, and 177 ideally, only to provide feedback to an end user when his counters can't 178 be bound to HW. 179 */ 180 IS_GLOBAL char * 181 hwcfuncs_errmsg_get (char *buf, size_t bufsize, int enable) 182 { 183 hwcfuncs_errmsg_enabled = 0; 184 if (buf && bufsize) 185 { 186 if (hwcfuncs_errmsg_valid) 187 { 188 strncpy (buf, hwcfuncs_errmsg_buf, bufsize); 189 buf[bufsize - 1] = 0; 190 } 191 else 192 *buf = 0; 193 } 194 hwcfuncs_errmsg_buf[0] = 0; 195 hwcfuncs_errmsg_valid = 0; 196 hwcfuncs_errmsg_enabled = enable; 197 return buf; 198 } 199 200 /* used by cpc to log an error */ 201 IS_GLOBAL void 202 hwcfuncs_int_capture_errmsg (const char *fn, int subcode, 203 const char *fmt, va_list ap) 204 { 205 if (hwcfuncs_errmsg_enabled && 206 !hwcfuncs_errmsg_valid) 207 { 208 vsnprintf (hwcfuncs_errmsg_buf, sizeof (hwcfuncs_errmsg_buf), fmt, ap); 209 TprintfT (DBG_LT0, "hwcfuncs: cpcN_capture_errmsg(): %s\n", 210 hwcfuncs_errmsg_buf); 211 hwcfuncs_errmsg_valid = 1; 212 } 213 return; 214 } 215 216 /* Log an internal error to the CPC error buffer. 217 * Note: only call this during init functions. 218 * Note: when most cpc calls fail, they will call cpcN_capture_errmsg() 219 * directly, so only call logerr() when a non-cpc function fails. 220 */ 221 IS_GLOBAL void 222 hwcfuncs_int_logerr (const char *format, ...) 223 { 224 va_list va; 225 va_start (va, format); 226 hwcfuncs_int_capture_errmsg ("logerr", 0, format, va); 227 va_end (va); 228 } 229 230 /* utils to parse counter strings */ 231 static void 232 clear_hwcdefs () 233 { 234 for (unsigned idx = 0; idx < MAX_PICS; idx++) 235 { 236 static Hwcentry empty; 237 hwcdef[idx] = empty; // leaks strings and reg_list array 238 hwcdef[idx].reg_num = REGNO_ANY; 239 hwcdef[idx].val = -1; 240 hwcdef[idx].sort_order = -1; 241 } 242 } 243 244 /* initialize hwcdef[] based on user's counter definitions */ 245 static int 246 process_data_descriptor (const char *defstring) 247 { 248 /* 249 * <defstring> format should be of format 250 * :%s:%s:0x%x:%d:%lld:%d:%d:0x%x[,%s...repeat for each ctr] 251 * where the counter fields are: 252 * :<userName>:<internalCtr>:<register>:<timeoutVal>[:m<min_time>]:<tag>:<timecvt>:<memop> 253 * See Coll_Ctrl::build_data_desc(). 254 */ 255 int err = 0; 256 char *ds = NULL; 257 char *dsp = NULL; 258 unsigned idx; 259 260 clear_hwcdefs (); 261 if (!defstring || !strlen (defstring)) 262 { 263 err = HWCFUNCS_ERROR_HWCARGS; 264 goto ext_hw_install_end; 265 } 266 ds = strdup (defstring); 267 if (!ds) 268 { 269 err = HWCFUNCS_ERROR_HWCINIT; 270 goto ext_hw_install_end; 271 } 272 dsp = ds; 273 274 for (idx = 0; idx < MAX_PICS && *dsp; idx++) 275 { 276 char *name = NULL; 277 char *int_name = NULL; 278 regno_t reg = REGNO_ANY; 279 ABST_type memop = ABST_NONE; 280 int interval = 0; 281 int timecvt = 0; 282 unsigned sort_order = (unsigned) - 1; 283 284 /* name */ 285 name = dsp; 286 dsp = strchr (dsp, ':'); 287 if (dsp == NULL) 288 { 289 err = HWCFUNCS_ERROR_HWCARGS; 290 goto ext_hw_install_end; 291 } 292 *dsp++ = (char) 0; 293 294 /* int_name */ 295 int_name = dsp; 296 dsp = strchr (dsp, ':'); 297 if (dsp == NULL) 298 { 299 err = HWCFUNCS_ERROR_HWCARGS; 300 goto ext_hw_install_end; 301 } 302 *dsp++ = (char) 0; 303 304 /* reg_num */ 305 reg = (int) strtol (dsp, &dsp, 0); 306 if (*dsp++ != ':') 307 { 308 err = HWCFUNCS_ERROR_HWCARGS; 309 goto ext_hw_install_end; 310 } 311 if (reg < 0 && reg != -1) 312 { 313 err = HWCFUNCS_ERROR_HWCARGS; 314 goto ext_hw_install_end; 315 } 316 if (reg >= 0) 317 hwcdef[idx].reg_num = reg; 318 319 /* val */ 320 interval = (int) strtol (dsp, &dsp, 0); 321 if (*dsp++ != ':') 322 { 323 err = HWCFUNCS_ERROR_HWCARGS; 324 goto ext_hw_install_end; 325 } 326 if (interval < 0) 327 { 328 err = HWCFUNCS_ERROR_HWCARGS; 329 goto ext_hw_install_end; 330 } 331 hwcdef[idx].val = interval; 332 333 /* min_time */ 334 /* 335 * This is a new field. 336 * An old launcher (dbx, etc.) would not include it. 337 * Detect the presence of the field by the char 'm'. 338 */ 339 if (*dsp == 'm') 340 { 341 long long tmp_ll = 0; 342 dsp++; 343 tmp_ll = strtoll (dsp, &dsp, 0); 344 if (*dsp++ != ':') 345 { 346 err = HWCFUNCS_ERROR_HWCARGS; 347 goto ext_hw_install_end; 348 } 349 if (tmp_ll < 0) 350 { 351 err = HWCFUNCS_ERROR_HWCARGS; 352 goto ext_hw_install_end; 353 } 354 hwcdef[idx].min_time = tmp_ll; 355 } 356 else 357 hwcdef[idx].min_time = 0; 358 359 /* sort_order */ 360 sort_order = (int) strtoul (dsp, &dsp, 0); 361 if (*dsp++ != ':') 362 { 363 err = HWCFUNCS_ERROR_HWCARGS; 364 goto ext_hw_install_end; 365 } 366 hwcdef[idx].sort_order = sort_order; 367 368 /* timecvt */ 369 timecvt = (int) strtol (dsp, &dsp, 0); 370 if (*dsp++ != ':') 371 { 372 err = HWCFUNCS_ERROR_HWCARGS; 373 goto ext_hw_install_end; 374 } 375 hwcdef[idx].timecvt = timecvt; 376 377 /* memop */ 378 memop = (ABST_type) strtol (dsp, &dsp, 0); 379 if (*dsp != 0 && *dsp++ != ',') 380 { 381 err = HWCFUNCS_ERROR_HWCARGS; 382 goto ext_hw_install_end; 383 } 384 hwcdef[idx].memop = memop; 385 if (*name) 386 hwcdef[idx].name = strdup (name); 387 else 388 hwcdef[idx].name = strdup (int_name); 389 if (*int_name) 390 hwcdef[idx].int_name = strdup (int_name); 391 else 392 hwcdef[idx].int_name = strdup (name); 393 ctrdefprint (DBG_LT1, "hwcfuncs: process_data_descriptor", &hwcdef[idx]); 394 } 395 396 if (*dsp) 397 { 398 TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): " 399 "ctr string had some trailing garbage:" 400 " '%s'\n", dsp); 401 err = HWCFUNCS_ERROR_HWCARGS; 402 goto ext_hw_install_end; 403 } 404 free (ds); 405 hwcdef_cnt = idx; 406 return 0; 407 408 ext_hw_install_end: 409 if (dsp && *dsp) 410 { 411 TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): " 412 " syntax error just before:" 413 " '%s;\n", dsp); 414 logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp); 415 } 416 else 417 logerr (GTXT ("Data descriptor syntax error\n")); 418 free (ds); 419 return err; 420 } 421 422 /* initialize hwcdef[] based on user's counter definitions */ 423 static int 424 process_hwcentrylist (const Hwcentry* entries[], unsigned numctrs) 425 { 426 int err = 0; 427 clear_hwcdefs (); 428 if (numctrs > cpcN_npics) 429 { 430 logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/ 431 return HWCFUNCS_ERROR_HWCARGS; 432 } 433 for (unsigned idx = 0; idx < numctrs; idx++) 434 { 435 Hwcentry *phwcdef = &hwcdef[idx]; 436 *phwcdef = *entries[idx]; 437 if (phwcdef->name) 438 phwcdef->name = strdup (phwcdef->name); 439 else 440 phwcdef->name = "NULL"; 441 if (phwcdef->int_name) 442 phwcdef->int_name = strdup (phwcdef->int_name); 443 else 444 phwcdef->int_name = "NULL"; 445 if (phwcdef->val < 0) 446 { 447 logerr (GTXT ("Negative interval specified for HW counter `%s'\n"), /*!*/ 448 phwcdef->name); 449 err = HWCFUNCS_ERROR_HWCARGS; 450 break; 451 } 452 ctrdefprint (DBG_LT1, "hwcfuncs: process_hwcentrylist", phwcdef); 453 } 454 if (!err) 455 hwcdef_cnt = numctrs; 456 return err; 457 } 458 459 /* see hwcfuncs.h */ 460 IS_GLOBAL void * 461 hwcfuncs_parse_attrs (const char *countername, hwcfuncs_attr_t attrs[], 462 unsigned max_attrs, uint_t *pnum_attrs, char**errstring) 463 { 464 char *head = NULL; 465 char *tail = NULL; 466 uint_t nattrs = 0; 467 char *counter_copy; 468 int success = 0; 469 char errbuf[512]; 470 errbuf[0] = 0; 471 counter_copy = strdup (countername); 472 473 /* advance pointer to first attribute */ 474 tail = strchr (counter_copy, HWCFUNCS_PARSE_ATTR); 475 if (tail) 476 *tail = 0; 477 478 /* remove regno and value, if supplied */ 479 { 480 char *tmp = strchr (counter_copy, HWCFUNCS_PARSE_REGNUM); 481 if (tmp) 482 *tmp = 0; 483 tmp = strchr (counter_copy, HWCFUNCS_PARSE_VALUE); 484 if (tmp) 485 *tmp = 0; 486 } 487 488 while (tail) 489 { 490 char *pch; 491 if (nattrs >= max_attrs) 492 { 493 snprintf (errbuf, sizeof (errbuf), 494 GTXT ("Too many attributes defined in `%s'"), 495 countername); 496 goto mycpc2_parse_attrs_end; 497 } 498 /* get attribute name */ 499 head = tail + 1; 500 tail = strchr (head, HWCFUNCS_PARSE_EQUAL); 501 if (!tail) 502 { 503 snprintf (errbuf, sizeof (errbuf), 504 GTXT ("Missing value for attribute `%s' in `%s'"), 505 head, countername); 506 goto mycpc2_parse_attrs_end; 507 } 508 *tail = 0; /* null terminate current component */ 509 attrs[nattrs].ca_name = head; 510 511 /* get attribute value */ 512 head = tail + 1; 513 tail = strchr (head, HWCFUNCS_PARSE_ATTR); 514 if (tail) 515 *tail = 0; /* null terminate current component */ 516 attrs[nattrs].ca_val = strtoull (head, &pch, 0); 517 if (pch == head) 518 { 519 snprintf (errbuf, sizeof (errbuf), 520 GTXT ("Illegal value for attribute `%s' in `%s'"), 521 attrs[nattrs].ca_name, countername); 522 goto mycpc2_parse_attrs_end; 523 } 524 TprintfT (DBG_LT0, "hwcfuncs: pic_: '%s', attribute[%u]" 525 " '%s' = 0x%llx\n", 526 counter_copy, nattrs, attrs[nattrs].ca_name, 527 (long long unsigned int) attrs[nattrs].ca_val); 528 529 nattrs++; 530 } 531 success = 1; 532 533 mycpc2_parse_attrs_end: 534 *pnum_attrs = nattrs; 535 if (success) 536 { 537 if (errstring) 538 *errstring = NULL; 539 } 540 else 541 { 542 if (errstring) 543 *errstring = strdup (errbuf); 544 free (counter_copy); 545 counter_copy = NULL; 546 } 547 return counter_copy; 548 } 549 550 IS_GLOBAL void 551 hwcfuncs_parse_ctr (const char *counter_def, int *pplus, char **pnameOnly, 552 char **pattrs, char **pregstr, regno_t *pregno) 553 { 554 char *nameptr, *copy, *slash, *attr_delim; 555 int plus; 556 regno_t regno; 557 nameptr = copy = strdup (counter_def); 558 559 /* plus */ 560 plus = 0; 561 if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK) 562 { 563 plus = 1; 564 nameptr++; 565 } 566 else if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK_OFF) 567 { 568 plus = -1; 569 nameptr++; 570 } 571 if (pplus) 572 *pplus = plus; 573 574 /* regno */ 575 regno = REGNO_ANY; 576 if (pregstr) 577 *pregstr = NULL; 578 slash = strchr (nameptr, HWCFUNCS_PARSE_REGNUM); 579 if (slash != NULL) 580 { 581 /* the remaining string should be a number > 0 */ 582 if (pregstr) 583 *pregstr = strdup (slash); 584 char *endchar = NULL; 585 regno = (regno_t) strtol (slash + 1, &endchar, 0); 586 if (*endchar != 0) 587 regno = -2; 588 if (*(slash + 1) == '-') 589 regno = -2; 590 /* terminate previous element up to slash */ 591 *slash = 0; 592 } 593 if (pregno) 594 *pregno = regno; 595 596 /* attrs */ 597 if (pattrs) 598 *pattrs = NULL; 599 attr_delim = strchr (nameptr, HWCFUNCS_PARSE_ATTR); 600 if (attr_delim != NULL) 601 { 602 if (pattrs) 603 *pattrs = strdup (attr_delim); 604 /* terminate previous element up to attr_delim */ 605 *attr_delim++ = 0; 606 } 607 if (pnameOnly) 608 *pnameOnly = strdup (nameptr); 609 free (copy); 610 } 611 612 /* create counters */ 613 IS_GLOBAL int 614 hwcfuncs_bind_descriptor (const char *defstring) 615 { 616 int err = process_data_descriptor (defstring); 617 if (err) 618 { 619 TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_descriptor failed\n"); 620 return err; 621 } 622 err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef); 623 return err; 624 } 625 626 /* see hwcfuncs.h */ 627 IS_GLOBAL int 628 hwcfuncs_bind_hwcentry (const Hwcentry* entries[], unsigned numctrs) 629 { 630 int err = -1; 631 err = process_hwcentrylist (entries, numctrs); 632 if (err) 633 { 634 TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_hwcentry\n"); 635 return err; 636 } 637 err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef); 638 return err; 639 } 640 641 /* see hwcfuncs.h */ 642 IS_GLOBAL Hwcentry ** 643 hwcfuncs_get_ctrs (unsigned *defcnt) 644 { 645 if (defcnt) 646 *defcnt = hwcdef_cnt; 647 return hwctable; 648 } 649 650 /* return 1 if <regno> is in Hwcentry's list */ 651 IS_GLOBAL int 652 regno_is_valid (const Hwcentry * pctr, regno_t regno) 653 { 654 regno_t *reg_list = pctr->reg_list; 655 if (REG_LIST_IS_EMPTY (reg_list)) 656 return 0; 657 if (regno == REGNO_ANY) /* wildcard */ 658 return 1; 659 for (int ii = 0; ii < MAX_PICS; ii++) 660 { 661 regno_t tmp = reg_list[ii]; 662 if (REG_LIST_EOL (tmp)) /* end of list */ 663 break; 664 if (tmp == regno) /* is in list */ 665 return 1; 666 } 667 return 0; 668 } 669 670 /* supplied by hwcdrv_api drivers */ 671 IS_GLOBAL int 672 hwcfuncs_assign_regnos (Hwcentry* entries[], 673 unsigned numctrs) 674 { 675 if (numctrs > cpcN_npics) 676 { 677 logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/ 678 return HWCFUNCS_ERROR_HWCARGS; 679 } 680 return hwcdrv_driver->hwcdrv_assign_regnos (entries, numctrs); 681 } 682 683 extern hwcdrv_api_t hwcdrv_pcl_api; 684 static int hwcdrv_driver_inited = 0; 685 686 hwcdrv_api_t * 687 get_hwcdrv () 688 { 689 if (hwcdrv_driver_inited) 690 return hwcdrv_driver; 691 hwcdrv_driver_inited = 1; 692 cpcN_npics = 0; 693 for (int i = 0; i < MAX_PICS; i++) 694 hwctable[i] = &hwcdef[i]; 695 hwcdrv_driver = &hwcdrv_pcl_api; 696 hwcdrv_driver->hwcdrv_init_status = hwcdrv_driver->hwcdrv_init (NULL, NULL); 697 if (hwcdrv_driver->hwcdrv_init_status == 0) 698 { 699 hwcdrv_driver->hwcdrv_get_info (NULL, NULL, &cpcN_npics, NULL, NULL); 700 return hwcdrv_driver; 701 } 702 hwcdrv_driver = &hwcdrv_default; 703 return hwcdrv_driver; 704 } 705