|  | /* Copyright (C) 2021-2023 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; | 
|  | } |