Home | History | Annotate | Line # | Download | only in common
hwcfuncs.c revision 1.1.1.3
      1 /* Copyright (C) 2021-2025 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, Hwcentry *hwcdef)
     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\n",
    166 	    hdr, phwcdef->name, phwcdef->int_name, phwcdef->reg_num,
    167 	    phwcdef->timecvt, phwcdef->memop, phwcdef->val,
    168 	    phwcdef->sort_order);
    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 static 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     return HWCFUNCS_ERROR_HWCARGS;
    263   ds = strdup (defstring);
    264   if (!ds)
    265     return HWCFUNCS_ERROR_HWCINIT;
    266   dsp = ds;
    267   for (idx = 0; idx < MAX_PICS && *dsp; idx++)
    268     {
    269       char *name = NULL;
    270       char *int_name = NULL;
    271       regno_t reg = REGNO_ANY;
    272       ABST_type memop = ABST_NONE;
    273       int interval = 0;
    274       int timecvt = 0;
    275       unsigned sort_order = (unsigned) - 1;
    276 
    277       // Read use_perf_event_type, type, config
    278       hwcdef[idx].use_perf_event_type = (int) strtol (dsp, &dsp, 0);
    279       if (*dsp++ != ':')
    280 	{
    281 	  err = HWCFUNCS_ERROR_HWCARGS;
    282 	  break;
    283 	}
    284       hwcdef[idx].type = (int) strtol (dsp, &dsp, 0);
    285       if (*dsp++ != ':')
    286 	{
    287 	  err = HWCFUNCS_ERROR_HWCARGS;
    288 	  break;
    289 	}
    290       hwcdef[idx].config = strtol (dsp, &dsp, 0);
    291       if (*dsp++ != ':')
    292 	{
    293 	  err = HWCFUNCS_ERROR_HWCARGS;
    294 	  break;
    295 	}
    296       hwcdef[idx].config1 = strtol (dsp, &dsp, 0);
    297       if (*dsp++ != ':')
    298 	{
    299 	  err = HWCFUNCS_ERROR_HWCARGS;
    300 	  break;
    301 	}
    302 
    303       /* name */
    304       name = dsp;
    305       dsp = strchr (dsp, ':');
    306       if (dsp == NULL)
    307 	{
    308 	  err = HWCFUNCS_ERROR_HWCARGS;
    309 	  break;
    310 	}
    311       *dsp++ = (char) 0;
    312 
    313       /* int_name */
    314       int_name = dsp;
    315       dsp = strchr (dsp, ':');
    316       if (dsp == NULL)
    317 	{
    318 	  err = HWCFUNCS_ERROR_HWCARGS;
    319 	  break;
    320 	}
    321       *dsp++ = (char) 0;
    322 
    323       /* reg_num */
    324       reg = (int) strtol (dsp, &dsp, 0);
    325       if (*dsp++ != ':')
    326 	{
    327 	  err = HWCFUNCS_ERROR_HWCARGS;
    328 	  break;
    329 	}
    330       if (reg < 0 && reg != -1)
    331 	{
    332 	  err = HWCFUNCS_ERROR_HWCARGS;
    333 	  break;
    334 	}
    335       if (reg >= 0)
    336 	hwcdef[idx].reg_num = reg;
    337 
    338       /* val */
    339       interval = (int) strtol (dsp, &dsp, 0);
    340       if (*dsp++ != ':')
    341 	{
    342 	  err = HWCFUNCS_ERROR_HWCARGS;
    343 	  break;
    344 	}
    345       if (interval < 0)
    346 	{
    347 	  err = HWCFUNCS_ERROR_HWCARGS;
    348 	  break;
    349 	}
    350       hwcdef[idx].val = interval;
    351 
    352       /* min_time */
    353       if (*dsp == 'm')
    354 	{
    355 	  long long tmp_ll = 0;
    356 	  dsp++;
    357 	  tmp_ll = strtoll (dsp, &dsp, 0);
    358 	  if (*dsp++ != ':')
    359 	    {
    360 	      err = HWCFUNCS_ERROR_HWCARGS;
    361 	      break;
    362 	    }
    363 	  if (tmp_ll < 0)
    364 	    {
    365 	      err = HWCFUNCS_ERROR_HWCARGS;
    366 	      break;
    367 	    }
    368 	  hwcdef[idx].min_time = tmp_ll;
    369 	}
    370       else
    371 	hwcdef[idx].min_time = 0;
    372 
    373       /* sort_order */
    374       sort_order = (int) strtoul (dsp, &dsp, 0);
    375       if (*dsp++ != ':')
    376 	{
    377 	  err = HWCFUNCS_ERROR_HWCARGS;
    378 	  break;
    379 	}
    380       hwcdef[idx].sort_order = sort_order;
    381 
    382       /* timecvt */
    383       timecvt = (int) strtol (dsp, &dsp, 0);
    384       if (*dsp++ != ':')
    385 	{
    386 	  err = HWCFUNCS_ERROR_HWCARGS;
    387 	  break;
    388 	}
    389       hwcdef[idx].timecvt = timecvt;
    390 
    391       /* memop */
    392       memop = (ABST_type) strtol (dsp, &dsp, 0);
    393       if (*dsp != 0 && *dsp++ != ',')
    394 	{
    395 	  err = HWCFUNCS_ERROR_HWCARGS;
    396 	  break;
    397 	}
    398       hwcdef[idx].memop = memop;
    399       if (*name)
    400 	hwcdef[idx].name = strdup (name);
    401       else
    402 	hwcdef[idx].name = strdup (int_name);
    403       if (*int_name)
    404 	hwcdef[idx].int_name = strdup (int_name);
    405       else
    406 	hwcdef[idx].int_name = strdup (name);
    407       ctrdefprint (DBG_LT1, "hwcfuncs: process_data_descriptor", &hwcdef[idx]);
    408     }
    409 
    410   if (*dsp)
    411     err = HWCFUNCS_ERROR_HWCARGS;
    412   if (err != 0)
    413     logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp);
    414   else
    415     hwcdef_cnt = idx;
    416   free (ds);
    417   return err;
    418 }
    419 
    420 /* initialize hwcdef[] based on user's counter definitions */
    421 static int
    422 process_hwcentrylist (const Hwcentry* entries[], unsigned numctrs)
    423 {
    424   int err = 0;
    425   clear_hwcdefs ();
    426   if (numctrs > cpcN_npics)
    427     {
    428       logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/
    429       return HWCFUNCS_ERROR_HWCARGS;
    430     }
    431   for (unsigned idx = 0; idx < numctrs; idx++)
    432     {
    433       Hwcentry *phwcdef = &hwcdef[idx];
    434       *phwcdef = *entries[idx];
    435       if (phwcdef->name)
    436 	phwcdef->name = strdup (phwcdef->name);
    437       else
    438 	phwcdef->name = "NULL";
    439       if (phwcdef->int_name)
    440 	phwcdef->int_name = strdup (phwcdef->int_name);
    441       else
    442 	phwcdef->int_name = "NULL";
    443       if (phwcdef->val < 0)
    444 	{
    445 	  logerr (GTXT ("Negative interval specified for HW counter `%s'\n"), /*!*/
    446 		  phwcdef->name);
    447 	  err = HWCFUNCS_ERROR_HWCARGS;
    448 	  break;
    449 	}
    450       ctrdefprint (DBG_LT1, "hwcfuncs: process_hwcentrylist", phwcdef);
    451     }
    452   if (!err)
    453     hwcdef_cnt = numctrs;
    454   return err;
    455 }
    456 
    457 /* see hwcfuncs.h */
    458 IS_GLOBAL void *
    459 hwcfuncs_parse_attrs (const char *countername, hwcfuncs_attr_t attrs[],
    460 		      unsigned max_attrs, uint_t *pnum_attrs, char**errstring)
    461 {
    462   char *head = NULL;
    463   char *tail = NULL;
    464   uint_t nattrs = 0;
    465   char *counter_copy;
    466   int success = 0;
    467   char errbuf[512];
    468   errbuf[0] = 0;
    469   counter_copy = strdup (countername);
    470 
    471   /* advance pointer to first attribute */
    472   tail = strchr (counter_copy, HWCFUNCS_PARSE_ATTR);
    473   if (tail)
    474     *tail = 0;
    475 
    476   /* remove regno and value, if supplied */
    477   {
    478     char *tmp = strchr (counter_copy, HWCFUNCS_PARSE_REGNUM);
    479     if (tmp)
    480       *tmp = 0;
    481     tmp = strchr (counter_copy, HWCFUNCS_PARSE_VALUE);
    482     if (tmp)
    483       *tmp = 0;
    484   }
    485 
    486   while (tail)
    487     {
    488       char *pch;
    489       if (nattrs >= max_attrs)
    490 	{
    491 	  snprintf (errbuf, sizeof (errbuf),
    492 		    GTXT ("Too many attributes defined in `%s'"),
    493 		    countername);
    494 	  goto mycpc2_parse_attrs_end;
    495 	}
    496       /* get attribute name */
    497       head = tail + 1;
    498       tail = strchr (head, HWCFUNCS_PARSE_EQUAL);
    499       if (!tail)
    500 	{
    501 	  snprintf (errbuf, sizeof (errbuf),
    502 		    GTXT ("Missing value for attribute `%s' in `%s'"),
    503 		    head, countername);
    504 	  goto mycpc2_parse_attrs_end;
    505 	}
    506       *tail = 0; /* null terminate current component */
    507       attrs[nattrs].ca_name = head;
    508 
    509       /* get attribute value */
    510       head = tail + 1;
    511       tail = strchr (head, HWCFUNCS_PARSE_ATTR);
    512       if (tail)
    513 	*tail = 0; /* null terminate current component */
    514       attrs[nattrs].ca_val = strtoull (head, &pch, 0);
    515       if (pch == head)
    516 	{
    517 	  snprintf (errbuf, sizeof (errbuf),
    518 		    GTXT ("Illegal value for attribute `%s' in `%s'"),
    519 		    attrs[nattrs].ca_name, countername);
    520 	  goto mycpc2_parse_attrs_end;
    521 	}
    522       TprintfT (DBG_LT0, "hwcfuncs: pic_: '%s', attribute[%u]"
    523 		" '%s' = 0x%llx\n",
    524 		counter_copy, nattrs, attrs[nattrs].ca_name,
    525 		(long long unsigned int) attrs[nattrs].ca_val);
    526 
    527       nattrs++;
    528     }
    529   success = 1;
    530 
    531 mycpc2_parse_attrs_end:
    532   *pnum_attrs = nattrs;
    533   if (success)
    534     {
    535       if (errstring)
    536 	*errstring = NULL;
    537     }
    538   else
    539     {
    540       if (errstring)
    541 	*errstring = strdup (errbuf);
    542       free (counter_copy);
    543       counter_copy = NULL;
    544     }
    545   return counter_copy;
    546 }
    547 
    548 IS_GLOBAL void
    549 hwcfuncs_parse_ctr (const char *counter_def, int *pplus, char **pnameOnly,
    550 		    char **pattrs, char **pregstr, regno_t *pregno)
    551 {
    552   char *nameptr, *copy, *slash, *attr_delim;
    553   int plus;
    554   regno_t regno;
    555   nameptr = copy = strdup (counter_def);
    556 
    557   /* plus */
    558   plus = 0;
    559   if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK)
    560     {
    561       plus = 1;
    562       nameptr++;
    563     }
    564   else if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK_OFF)
    565     {
    566       plus = -1;
    567       nameptr++;
    568     }
    569   if (pplus)
    570     *pplus = plus;
    571 
    572   /* regno */
    573   regno = REGNO_ANY;
    574   if (pregstr)
    575     *pregstr = NULL;
    576   slash = strchr (nameptr, HWCFUNCS_PARSE_REGNUM);
    577   if (slash != NULL)
    578     {
    579       /* the remaining string should be a number > 0 */
    580       if (pregstr)
    581 	*pregstr = strdup (slash);
    582       char *endchar = NULL;
    583       regno = (regno_t) strtol (slash + 1, &endchar, 0);
    584       if (*endchar != 0)
    585 	regno = -2;
    586       if (*(slash + 1) == '-')
    587 	regno = -2;
    588       /* terminate previous element up to slash */
    589       *slash = 0;
    590     }
    591   if (pregno)
    592     *pregno = regno;
    593 
    594   /* attrs */
    595   if (pattrs)
    596     *pattrs = NULL;
    597   attr_delim = strchr (nameptr, HWCFUNCS_PARSE_ATTR);
    598   if (attr_delim != NULL)
    599     {
    600       if (pattrs)
    601 	*pattrs = strdup (attr_delim);
    602       /* terminate previous element up to attr_delim */
    603       *attr_delim++ = 0;
    604     }
    605   if (pnameOnly)
    606     *pnameOnly = strdup (nameptr);
    607   free (copy);
    608 }
    609 
    610 /* create counters */
    611 IS_GLOBAL int
    612 hwcfuncs_bind_descriptor (const char *defstring)
    613 {
    614   int err = process_data_descriptor (defstring);
    615   if (err)
    616     {
    617       TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_descriptor failed\n");
    618       return err;
    619     }
    620   err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef);
    621   return err;
    622 }
    623 
    624 /* see hwcfuncs.h */
    625 IS_GLOBAL int
    626 hwcfuncs_bind_hwcentry (const Hwcentry* entries[], unsigned numctrs)
    627 {
    628   int err = -1;
    629   err = process_hwcentrylist (entries, numctrs);
    630   if (err)
    631     {
    632       TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_hwcentry\n");
    633       return err;
    634     }
    635   err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef);
    636   return err;
    637 }
    638 
    639 /* see hwcfuncs.h */
    640 IS_GLOBAL Hwcentry **
    641 hwcfuncs_get_ctrs (unsigned *defcnt)
    642 {
    643   if (defcnt)
    644     *defcnt = hwcdef_cnt;
    645   return hwctable;
    646 }
    647 
    648 extern hwcdrv_api_t hwcdrv_pcl_api;
    649 static int hwcdrv_driver_inited = 0;
    650 
    651 hwcdrv_api_t *
    652 get_hwcdrv ()
    653 {
    654   if (hwcdrv_driver_inited)
    655     return hwcdrv_driver;
    656   hwcdrv_driver_inited = 1;
    657   cpcN_npics = 0;
    658   for (int i = 0; i < MAX_PICS; i++)
    659     hwctable[i] = &hwcdef[i];
    660   hwcdrv_driver = &hwcdrv_pcl_api;
    661   hwcdrv_driver->hwcdrv_init_status = hwcdrv_driver->hwcdrv_init (NULL, NULL);
    662   if (hwcdrv_driver->hwcdrv_init_status == 0)
    663     {
    664       hwcdrv_driver->hwcdrv_get_info (NULL, NULL, &cpcN_npics, NULL, NULL);
    665       return hwcdrv_driver;
    666     }
    667   hwcdrv_driver = &hwcdrv_default;
    668   return hwcdrv_driver;
    669 }
    670