123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- /* Copyright (C) 2021 Free Software Foundation, Inc.
- Contributed by Oracle.
- This file is part of GNU Binutils.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- /* Hardware counter profiling */
- #include "hwcdrv.h"
- #include "hwcfuncs.h"
- /*---------------------------------------------------------------------------*/
- /* macros */
- #define IS_GLOBAL /* Mark global symbols */
- #define HWCDRV_API static /* Mark functions used by hwcdrv API */
- /*---------------------------------------------------------------------------*/
- /* static variables */
- static uint_t cpcN_npics;
- static char hwcfuncs_errmsg_buf[1024];
- static int hwcfuncs_errmsg_enabled = 1;
- static int hwcfuncs_errmsg_valid;
- /* --- user counter selections and options */
- static unsigned hwcdef_cnt; /* number of *active* hardware counters */
- static Hwcentry hwcdef[MAX_PICS]; /* HWC definitions */
- static Hwcentry *hwctable[MAX_PICS]; /* HWC definitions */
- /* --- drivers --- */
- // default driver
- HWCDRV_API int
- hwcdrv_init (hwcfuncs_abort_fn_t abort_ftn, int* tsd_sz)
- {
- return -1;
- }
- HWCDRV_API void
- hwcdrv_get_info (
- int * cpuver, const char ** cciname,
- uint_t * npics, const char ** docref, uint64_t* support) { }
- HWCDRV_API int
- hwcdrv_enable_mt (hwcfuncs_tsd_get_fn_t tsd_ftn)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_get_descriptions (hwcf_hwc_cb_t *hwc_find_action,
- hwcf_attr_cb_t *attr_find_action)
- {
- return 0;
- }
- HWCDRV_API int
- hwcdrv_assign_regnos (Hwcentry *entries[], unsigned numctrs)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_create_counters (unsigned hwcdef_cnt, Hwcentry *hwcdef)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_read_events (hwc_event_t *events, hwc_event_samples_t*samples)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_start (void)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_overflow (siginfo_t *si, hwc_event_t *s, hwc_event_t *t)
- {
- return 0;
- }
- HWCDRV_API int
- hwcdrv_sighlr_restart (const hwc_event_t *sample)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_lwp_suspend (void)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_lwp_resume (void)
- {
- return -1;
- }
- HWCDRV_API int
- hwcdrv_free_counters (void)
- {
- return 0;
- }
- HWCDRV_API int
- hwcdrv_lwp_init (void)
- {
- return 0;
- }
- HWCDRV_API void
- hwcdrv_lwp_fini (void) { }
- static hwcdrv_api_t hwcdrv_default = {
- hwcdrv_init,
- hwcdrv_get_info,
- hwcdrv_enable_mt,
- hwcdrv_get_descriptions,
- hwcdrv_assign_regnos,
- hwcdrv_create_counters,
- hwcdrv_start,
- hwcdrv_overflow,
- hwcdrv_read_events,
- hwcdrv_sighlr_restart,
- hwcdrv_lwp_suspend,
- hwcdrv_lwp_resume,
- hwcdrv_free_counters,
- hwcdrv_lwp_init,
- hwcdrv_lwp_fini,
- -1 // hwcdrv_init_status
- };
- static hwcdrv_api_t *hwcdrv_driver = &hwcdrv_default;
- /*---------------------------------------------------------------------------*/
- /* misc */
- /* print a counter definition (for debugging) */
- static void
- ctrdefprint (int dbg_lvl, const char * hdr, Hwcentry*phwcdef)
- {
- TprintfT (dbg_lvl, "%s: name='%s', int_name='%s',"
- " reg_num=%d, timecvt=%d, memop=%d, "
- "interval=%d, tag=%u, reg_list=%p\n",
- hdr, phwcdef->name, phwcdef->int_name, phwcdef->reg_num,
- phwcdef->timecvt, phwcdef->memop, phwcdef->val,
- phwcdef->sort_order, phwcdef->reg_list);
- }
- /*---------------------------------------------------------------------------*/
- /* errmsg buffering */
- /* errmsg buffering is needed only because the most descriptive error
- messages from CPC are delivered using a callback mechanism.
- hwcfuncs_errmsg_get() should only be used during initialization, and
- ideally, only to provide feedback to an end user when his counters can't
- be bound to HW.
- */
- IS_GLOBAL char *
- hwcfuncs_errmsg_get (char *buf, size_t bufsize, int enable)
- {
- hwcfuncs_errmsg_enabled = 0;
- if (buf && bufsize)
- {
- if (hwcfuncs_errmsg_valid)
- {
- strncpy (buf, hwcfuncs_errmsg_buf, bufsize);
- buf[bufsize - 1] = 0;
- }
- else
- *buf = 0;
- }
- hwcfuncs_errmsg_buf[0] = 0;
- hwcfuncs_errmsg_valid = 0;
- hwcfuncs_errmsg_enabled = enable;
- return buf;
- }
- /* used by cpc to log an error */
- IS_GLOBAL void
- hwcfuncs_int_capture_errmsg (const char *fn, int subcode,
- const char *fmt, va_list ap)
- {
- if (hwcfuncs_errmsg_enabled &&
- !hwcfuncs_errmsg_valid)
- {
- vsnprintf (hwcfuncs_errmsg_buf, sizeof (hwcfuncs_errmsg_buf), fmt, ap);
- TprintfT (DBG_LT0, "hwcfuncs: cpcN_capture_errmsg(): %s\n",
- hwcfuncs_errmsg_buf);
- hwcfuncs_errmsg_valid = 1;
- }
- return;
- }
- /* Log an internal error to the CPC error buffer.
- * Note: only call this during init functions.
- * Note: when most cpc calls fail, they will call cpcN_capture_errmsg()
- * directly, so only call logerr() when a non-cpc function fails.
- */
- IS_GLOBAL void
- hwcfuncs_int_logerr (const char *format, ...)
- {
- va_list va;
- va_start (va, format);
- hwcfuncs_int_capture_errmsg ("logerr", 0, format, va);
- va_end (va);
- }
- /* utils to parse counter strings */
- static void
- clear_hwcdefs ()
- {
- for (unsigned idx = 0; idx < MAX_PICS; idx++)
- {
- static Hwcentry empty;
- hwcdef[idx] = empty; // leaks strings and reg_list array
- hwcdef[idx].reg_num = REGNO_ANY;
- hwcdef[idx].val = -1;
- hwcdef[idx].sort_order = -1;
- }
- }
- /* initialize hwcdef[] based on user's counter definitions */
- static int
- process_data_descriptor (const char *defstring)
- {
- /*
- * <defstring> format should be of format
- * :%s:%s:0x%x:%d:%lld:%d:%d:0x%x[,%s...repeat for each ctr]
- * where the counter fields are:
- * :<userName>:<internalCtr>:<register>:<timeoutVal>[:m<min_time>]:<tag>:<timecvt>:<memop>
- * See Coll_Ctrl::build_data_desc().
- */
- int err = 0;
- char *ds = NULL;
- char *dsp = NULL;
- unsigned idx;
- clear_hwcdefs ();
- if (!defstring || !strlen (defstring))
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- ds = strdup (defstring);
- if (!ds)
- {
- err = HWCFUNCS_ERROR_HWCINIT;
- goto ext_hw_install_end;
- }
- dsp = ds;
- for (idx = 0; idx < MAX_PICS && *dsp; idx++)
- {
- char *name = NULL;
- char *int_name = NULL;
- regno_t reg = REGNO_ANY;
- ABST_type memop = ABST_NONE;
- int interval = 0;
- int timecvt = 0;
- unsigned sort_order = (unsigned) - 1;
- /* name */
- name = dsp;
- dsp = strchr (dsp, ':');
- if (dsp == NULL)
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- *dsp++ = (char) 0;
- /* int_name */
- int_name = dsp;
- dsp = strchr (dsp, ':');
- if (dsp == NULL)
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- *dsp++ = (char) 0;
- /* reg_num */
- reg = (int) strtol (dsp, &dsp, 0);
- if (*dsp++ != ':')
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- if (reg < 0 && reg != -1)
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- if (reg >= 0)
- hwcdef[idx].reg_num = reg;
- /* val */
- interval = (int) strtol (dsp, &dsp, 0);
- if (*dsp++ != ':')
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- if (interval < 0)
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- hwcdef[idx].val = interval;
- /* min_time */
- /*
- * This is a new field.
- * An old launcher (dbx, etc.) would not include it.
- * Detect the presence of the field by the char 'm'.
- */
- if (*dsp == 'm')
- {
- long long tmp_ll = 0;
- dsp++;
- tmp_ll = strtoll (dsp, &dsp, 0);
- if (*dsp++ != ':')
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- if (tmp_ll < 0)
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- hwcdef[idx].min_time = tmp_ll;
- }
- else
- hwcdef[idx].min_time = 0;
- /* sort_order */
- sort_order = (int) strtoul (dsp, &dsp, 0);
- if (*dsp++ != ':')
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- hwcdef[idx].sort_order = sort_order;
- /* timecvt */
- timecvt = (int) strtol (dsp, &dsp, 0);
- if (*dsp++ != ':')
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- hwcdef[idx].timecvt = timecvt;
- /* memop */
- memop = (ABST_type) strtol (dsp, &dsp, 0);
- if (*dsp != 0 && *dsp++ != ',')
- {
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- hwcdef[idx].memop = memop;
- if (*name)
- hwcdef[idx].name = strdup (name);
- else
- hwcdef[idx].name = strdup (int_name);
- if (*int_name)
- hwcdef[idx].int_name = strdup (int_name);
- else
- hwcdef[idx].int_name = strdup (name);
- ctrdefprint (DBG_LT1, "hwcfuncs: process_data_descriptor", &hwcdef[idx]);
- }
- if (*dsp)
- {
- TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): "
- "ctr string had some trailing garbage:"
- " '%s'\n", dsp);
- err = HWCFUNCS_ERROR_HWCARGS;
- goto ext_hw_install_end;
- }
- free (ds);
- hwcdef_cnt = idx;
- return 0;
- ext_hw_install_end:
- if (dsp && *dsp)
- {
- TprintfT (DBG_LT0, "hwcfuncs: ERROR: process_data_descriptor(): "
- " syntax error just before:"
- " '%s;\n", dsp);
- logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp);
- }
- else
- logerr (GTXT ("Data descriptor syntax error\n"));
- free (ds);
- return err;
- }
- /* initialize hwcdef[] based on user's counter definitions */
- static int
- process_hwcentrylist (const Hwcentry* entries[], unsigned numctrs)
- {
- int err = 0;
- clear_hwcdefs ();
- if (numctrs > cpcN_npics)
- {
- logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/
- return HWCFUNCS_ERROR_HWCARGS;
- }
- for (unsigned idx = 0; idx < numctrs; idx++)
- {
- Hwcentry *phwcdef = &hwcdef[idx];
- *phwcdef = *entries[idx];
- if (phwcdef->name)
- phwcdef->name = strdup (phwcdef->name);
- else
- phwcdef->name = "NULL";
- if (phwcdef->int_name)
- phwcdef->int_name = strdup (phwcdef->int_name);
- else
- phwcdef->int_name = "NULL";
- if (phwcdef->val < 0)
- {
- logerr (GTXT ("Negative interval specified for HW counter `%s'\n"), /*!*/
- phwcdef->name);
- err = HWCFUNCS_ERROR_HWCARGS;
- break;
- }
- ctrdefprint (DBG_LT1, "hwcfuncs: process_hwcentrylist", phwcdef);
- }
- if (!err)
- hwcdef_cnt = numctrs;
- return err;
- }
- /* see hwcfuncs.h */
- IS_GLOBAL void *
- hwcfuncs_parse_attrs (const char *countername, hwcfuncs_attr_t attrs[],
- unsigned max_attrs, uint_t *pnum_attrs, char**errstring)
- {
- char *head = NULL;
- char *tail = NULL;
- uint_t nattrs = 0;
- char *counter_copy;
- int success = 0;
- char errbuf[512];
- errbuf[0] = 0;
- counter_copy = strdup (countername);
- /* advance pointer to first attribute */
- tail = strchr (counter_copy, HWCFUNCS_PARSE_ATTR);
- if (tail)
- *tail = 0;
- /* remove regno and value, if supplied */
- {
- char *tmp = strchr (counter_copy, HWCFUNCS_PARSE_REGNUM);
- if (tmp)
- *tmp = 0;
- tmp = strchr (counter_copy, HWCFUNCS_PARSE_VALUE);
- if (tmp)
- *tmp = 0;
- }
- while (tail)
- {
- char *pch;
- if (nattrs >= max_attrs)
- {
- snprintf (errbuf, sizeof (errbuf),
- GTXT ("Too many attributes defined in `%s'"),
- countername);
- goto mycpc2_parse_attrs_end;
- }
- /* get attribute name */
- head = tail + 1;
- tail = strchr (head, HWCFUNCS_PARSE_EQUAL);
- if (!tail)
- {
- snprintf (errbuf, sizeof (errbuf),
- GTXT ("Missing value for attribute `%s' in `%s'"),
- head, countername);
- goto mycpc2_parse_attrs_end;
- }
- *tail = 0; /* null terminate current component */
- attrs[nattrs].ca_name = head;
- /* get attribute value */
- head = tail + 1;
- tail = strchr (head, HWCFUNCS_PARSE_ATTR);
- if (tail)
- *tail = 0; /* null terminate current component */
- attrs[nattrs].ca_val = strtoull (head, &pch, 0);
- if (pch == head)
- {
- snprintf (errbuf, sizeof (errbuf),
- GTXT ("Illegal value for attribute `%s' in `%s'"),
- attrs[nattrs].ca_name, countername);
- goto mycpc2_parse_attrs_end;
- }
- TprintfT (DBG_LT0, "hwcfuncs: pic_: '%s', attribute[%u]"
- " '%s' = 0x%llx\n",
- counter_copy, nattrs, attrs[nattrs].ca_name,
- (long long unsigned int) attrs[nattrs].ca_val);
- nattrs++;
- }
- success = 1;
- mycpc2_parse_attrs_end:
- *pnum_attrs = nattrs;
- if (success)
- {
- if (errstring)
- *errstring = NULL;
- }
- else
- {
- if (errstring)
- *errstring = strdup (errbuf);
- free (counter_copy);
- counter_copy = NULL;
- }
- return counter_copy;
- }
- IS_GLOBAL void
- hwcfuncs_parse_ctr (const char *counter_def, int *pplus, char **pnameOnly,
- char **pattrs, char **pregstr, regno_t *pregno)
- {
- char *nameptr, *copy, *slash, *attr_delim;
- int plus;
- regno_t regno;
- nameptr = copy = strdup (counter_def);
- /* plus */
- plus = 0;
- if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK)
- {
- plus = 1;
- nameptr++;
- }
- else if (nameptr[0] == HWCFUNCS_PARSE_BACKTRACK_OFF)
- {
- plus = -1;
- nameptr++;
- }
- if (pplus)
- *pplus = plus;
- /* regno */
- regno = REGNO_ANY;
- if (pregstr)
- *pregstr = NULL;
- slash = strchr (nameptr, HWCFUNCS_PARSE_REGNUM);
- if (slash != NULL)
- {
- /* the remaining string should be a number > 0 */
- if (pregstr)
- *pregstr = strdup (slash);
- char *endchar = NULL;
- regno = (regno_t) strtol (slash + 1, &endchar, 0);
- if (*endchar != 0)
- regno = -2;
- if (*(slash + 1) == '-')
- regno = -2;
- /* terminate previous element up to slash */
- *slash = 0;
- }
- if (pregno)
- *pregno = regno;
- /* attrs */
- if (pattrs)
- *pattrs = NULL;
- attr_delim = strchr (nameptr, HWCFUNCS_PARSE_ATTR);
- if (attr_delim != NULL)
- {
- if (pattrs)
- *pattrs = strdup (attr_delim);
- /* terminate previous element up to attr_delim */
- *attr_delim++ = 0;
- }
- if (pnameOnly)
- *pnameOnly = strdup (nameptr);
- free (copy);
- }
- /* create counters */
- IS_GLOBAL int
- hwcfuncs_bind_descriptor (const char *defstring)
- {
- int err = process_data_descriptor (defstring);
- if (err)
- {
- TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_descriptor failed\n");
- return err;
- }
- err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef);
- return err;
- }
- /* see hwcfuncs.h */
- IS_GLOBAL int
- hwcfuncs_bind_hwcentry (const Hwcentry* entries[], unsigned numctrs)
- {
- int err = -1;
- err = process_hwcentrylist (entries, numctrs);
- if (err)
- {
- TprintfT (DBG_LT0, "hwcfuncs: ERROR: hwcfuncs_bind_hwcentry\n");
- return err;
- }
- err = hwcdrv_driver->hwcdrv_create_counters (hwcdef_cnt, hwcdef);
- return err;
- }
- /* see hwcfuncs.h */
- IS_GLOBAL Hwcentry **
- hwcfuncs_get_ctrs (unsigned *defcnt)
- {
- if (defcnt)
- *defcnt = hwcdef_cnt;
- return hwctable;
- }
- /* return 1 if <regno> is in Hwcentry's list */
- IS_GLOBAL int
- regno_is_valid (const Hwcentry * pctr, regno_t regno)
- {
- regno_t *reg_list = pctr->reg_list;
- if (REG_LIST_IS_EMPTY (reg_list))
- return 0;
- if (regno == REGNO_ANY) /* wildcard */
- return 1;
- for (int ii = 0; ii < MAX_PICS; ii++)
- {
- regno_t tmp = reg_list[ii];
- if (REG_LIST_EOL (tmp)) /* end of list */
- break;
- if (tmp == regno) /* is in list */
- return 1;
- }
- return 0;
- }
- /* supplied by hwcdrv_api drivers */
- IS_GLOBAL int
- hwcfuncs_assign_regnos (Hwcentry* entries[],
- unsigned numctrs)
- {
- if (numctrs > cpcN_npics)
- {
- logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics); /*!*/
- return HWCFUNCS_ERROR_HWCARGS;
- }
- return hwcdrv_driver->hwcdrv_assign_regnos (entries, numctrs);
- }
- extern hwcdrv_api_t hwcdrv_pcl_api;
- static int hwcdrv_driver_inited = 0;
- hwcdrv_api_t *
- get_hwcdrv ()
- {
- if (hwcdrv_driver_inited)
- return hwcdrv_driver;
- hwcdrv_driver_inited = 1;
- cpcN_npics = 0;
- for (int i = 0; i < MAX_PICS; i++)
- hwctable[i] = &hwcdef[i];
- hwcdrv_driver = &hwcdrv_pcl_api;
- hwcdrv_driver->hwcdrv_init_status = hwcdrv_driver->hwcdrv_init (NULL, NULL);
- if (hwcdrv_driver->hwcdrv_init_status == 0)
- {
- hwcdrv_driver->hwcdrv_get_info (NULL, NULL, &cpcN_npics, NULL, NULL);
- return hwcdrv_driver;
- }
- hwcdrv_driver = &hwcdrv_default;
- return hwcdrv_driver;
- }
|