mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
971ae1844e
gprofng/ChangeLog 2024-07-07 Vladimir Mezentsev <vladimir.mezentsev@oracle.com>. * common/hwc_cpus.h: New constant for Intel Ice Lake processor. * common/hwcdrv.c: Add a new argument to hwcfuncs_get_x86_eventsel. Set config1 in perf_event_attr. Remove the use of memset. * common/core_pcbe.c (core_pcbe_get_eventnum): Return 0. * common/hwcentry.h: Add config1. * src/collctrl.cc (Coll_Ctrl::build_data_desc):Set config1. * common/hwcfuncs.c (process_data_descriptor): Set config1. * common/hwctable.c: Add the hwc table for Intel Ice Lake processor. * src/hwc_intel_icelake.h: New file.
670 lines
15 KiB
C
670 lines
15 KiB
C
/* Copyright (C) 2021-2024 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, Hwcentry *hwcdef)
|
|
{
|
|
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\n",
|
|
hdr, phwcdef->name, phwcdef->int_name, phwcdef->reg_num,
|
|
phwcdef->timecvt, phwcdef->memop, phwcdef->val,
|
|
phwcdef->sort_order);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* 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 */
|
|
static 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))
|
|
return HWCFUNCS_ERROR_HWCARGS;
|
|
ds = strdup (defstring);
|
|
if (!ds)
|
|
return HWCFUNCS_ERROR_HWCINIT;
|
|
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;
|
|
|
|
// Read use_perf_event_type, type, config
|
|
hwcdef[idx].use_perf_event_type = (int) strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
hwcdef[idx].type = (int) strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
hwcdef[idx].config = strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
hwcdef[idx].config1 = strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
|
|
/* name */
|
|
name = dsp;
|
|
dsp = strchr (dsp, ':');
|
|
if (dsp == NULL)
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
*dsp++ = (char) 0;
|
|
|
|
/* int_name */
|
|
int_name = dsp;
|
|
dsp = strchr (dsp, ':');
|
|
if (dsp == NULL)
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
*dsp++ = (char) 0;
|
|
|
|
/* reg_num */
|
|
reg = (int) strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
if (reg < 0 && reg != -1)
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
if (reg >= 0)
|
|
hwcdef[idx].reg_num = reg;
|
|
|
|
/* val */
|
|
interval = (int) strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
if (interval < 0)
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
hwcdef[idx].val = interval;
|
|
|
|
/* min_time */
|
|
if (*dsp == 'm')
|
|
{
|
|
long long tmp_ll = 0;
|
|
dsp++;
|
|
tmp_ll = strtoll (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
if (tmp_ll < 0)
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
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;
|
|
break;
|
|
}
|
|
hwcdef[idx].sort_order = sort_order;
|
|
|
|
/* timecvt */
|
|
timecvt = (int) strtol (dsp, &dsp, 0);
|
|
if (*dsp++ != ':')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
hwcdef[idx].timecvt = timecvt;
|
|
|
|
/* memop */
|
|
memop = (ABST_type) strtol (dsp, &dsp, 0);
|
|
if (*dsp != 0 && *dsp++ != ',')
|
|
{
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
break;
|
|
}
|
|
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)
|
|
err = HWCFUNCS_ERROR_HWCARGS;
|
|
if (err != 0)
|
|
logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp);
|
|
else
|
|
hwcdef_cnt = idx;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|