1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
4 This file is part of GNU Binutils.
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)
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.
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. */
21 /* Hardware counter profiling */
25 /*---------------------------------------------------------------------------*/
28 #define IS_GLOBAL /* Mark global symbols */
29 #define HWCDRV_API static /* Mark functions used by hwcdrv API */
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
;
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 */
48 hwcdrv_init (hwcfuncs_abort_fn_t abort_ftn
, int* tsd_sz
)
55 int * cpuver
, const char ** cciname
,
56 uint_t
* npics
, const char ** docref
, uint64_t* support
) { }
59 hwcdrv_enable_mt (hwcfuncs_tsd_get_fn_t tsd_ftn
)
65 hwcdrv_get_descriptions (hwcf_hwc_cb_t
*hwc_find_action
,
66 hwcf_attr_cb_t
*attr_find_action
, Hwcentry
*hwcdef
)
72 hwcdrv_assign_regnos (Hwcentry
*entries
[], unsigned numctrs
)
78 hwcdrv_create_counters (unsigned hwcdef_cnt
, Hwcentry
*hwcdef
)
84 hwcdrv_read_events (hwc_event_t
*events
, hwc_event_samples_t
*samples
)
96 hwcdrv_overflow (siginfo_t
*si
, hwc_event_t
*s
, hwc_event_t
*t
)
102 hwcdrv_sighlr_restart (const hwc_event_t
*sample
)
108 hwcdrv_lwp_suspend (void)
114 hwcdrv_lwp_resume (void)
120 hwcdrv_free_counters (void)
126 hwcdrv_lwp_init (void)
132 hwcdrv_lwp_fini (void) { }
134 static hwcdrv_api_t hwcdrv_default
= {
138 hwcdrv_get_descriptions
,
139 hwcdrv_assign_regnos
,
140 hwcdrv_create_counters
,
144 hwcdrv_sighlr_restart
,
147 hwcdrv_free_counters
,
150 -1 // hwcdrv_init_status
153 static hwcdrv_api_t
*hwcdrv_driver
= &hwcdrv_default
;
156 /*---------------------------------------------------------------------------*/
159 /* print a counter definition (for debugging) */
161 ctrdefprint (int dbg_lvl
, const char * hdr
, Hwcentry
*phwcdef
)
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
);
171 /*---------------------------------------------------------------------------*/
172 /* errmsg buffering */
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
181 hwcfuncs_errmsg_get (char *buf
, size_t bufsize
, int enable
)
183 hwcfuncs_errmsg_enabled
= 0;
186 if (hwcfuncs_errmsg_valid
)
188 strncpy (buf
, hwcfuncs_errmsg_buf
, bufsize
);
189 buf
[bufsize
- 1] = 0;
194 hwcfuncs_errmsg_buf
[0] = 0;
195 hwcfuncs_errmsg_valid
= 0;
196 hwcfuncs_errmsg_enabled
= enable
;
200 /* used by cpc to log an error */
202 hwcfuncs_int_capture_errmsg (const char *fn
, int subcode
,
203 const char *fmt
, va_list ap
)
205 if (hwcfuncs_errmsg_enabled
&&
206 !hwcfuncs_errmsg_valid
)
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;
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.
222 hwcfuncs_int_logerr (const char *format
, ...)
225 va_start (va
, format
);
226 hwcfuncs_int_capture_errmsg ("logerr", 0, format
, va
);
230 /* utils to parse counter strings */
234 for (unsigned idx
= 0; idx
< MAX_PICS
; idx
++)
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;
244 /* initialize hwcdef[] based on user's counter definitions */
246 process_data_descriptor (const char *defstring
)
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().
261 if (!defstring
|| !strlen (defstring
))
262 return HWCFUNCS_ERROR_HWCARGS
;
263 ds
= strdup (defstring
);
265 return HWCFUNCS_ERROR_HWCINIT
;
267 for (idx
= 0; idx
< MAX_PICS
&& *dsp
; idx
++)
270 char *int_name
= NULL
;
271 regno_t reg
= REGNO_ANY
;
272 ABST_type memop
= ABST_NONE
;
275 unsigned sort_order
= (unsigned) - 1;
277 // Read use_perf_event_type, type, config
278 hwcdef
[idx
].use_perf_event_type
= (int) strtol (dsp
, &dsp
, 0);
281 err
= HWCFUNCS_ERROR_HWCARGS
;
284 hwcdef
[idx
].type
= (int) strtol (dsp
, &dsp
, 0);
287 err
= HWCFUNCS_ERROR_HWCARGS
;
290 hwcdef
[idx
].config
= strtol (dsp
, &dsp
, 0);
293 err
= HWCFUNCS_ERROR_HWCARGS
;
296 hwcdef
[idx
].config1
= strtol (dsp
, &dsp
, 0);
299 err
= HWCFUNCS_ERROR_HWCARGS
;
305 dsp
= strchr (dsp
, ':');
308 err
= HWCFUNCS_ERROR_HWCARGS
;
315 dsp
= strchr (dsp
, ':');
318 err
= HWCFUNCS_ERROR_HWCARGS
;
324 reg
= (int) strtol (dsp
, &dsp
, 0);
327 err
= HWCFUNCS_ERROR_HWCARGS
;
330 if (reg
< 0 && reg
!= -1)
332 err
= HWCFUNCS_ERROR_HWCARGS
;
336 hwcdef
[idx
].reg_num
= reg
;
339 interval
= (int) strtol (dsp
, &dsp
, 0);
342 err
= HWCFUNCS_ERROR_HWCARGS
;
347 err
= HWCFUNCS_ERROR_HWCARGS
;
350 hwcdef
[idx
].val
= interval
;
355 long long tmp_ll
= 0;
357 tmp_ll
= strtoll (dsp
, &dsp
, 0);
360 err
= HWCFUNCS_ERROR_HWCARGS
;
365 err
= HWCFUNCS_ERROR_HWCARGS
;
368 hwcdef
[idx
].min_time
= tmp_ll
;
371 hwcdef
[idx
].min_time
= 0;
374 sort_order
= (int) strtoul (dsp
, &dsp
, 0);
377 err
= HWCFUNCS_ERROR_HWCARGS
;
380 hwcdef
[idx
].sort_order
= sort_order
;
383 timecvt
= (int) strtol (dsp
, &dsp
, 0);
386 err
= HWCFUNCS_ERROR_HWCARGS
;
389 hwcdef
[idx
].timecvt
= timecvt
;
392 memop
= (ABST_type
) strtol (dsp
, &dsp
, 0);
393 if (*dsp
!= 0 && *dsp
++ != ',')
395 err
= HWCFUNCS_ERROR_HWCARGS
;
398 hwcdef
[idx
].memop
= memop
;
400 hwcdef
[idx
].name
= strdup (name
);
402 hwcdef
[idx
].name
= strdup (int_name
);
404 hwcdef
[idx
].int_name
= strdup (int_name
);
406 hwcdef
[idx
].int_name
= strdup (name
);
407 ctrdefprint (DBG_LT1
, "hwcfuncs: process_data_descriptor", &hwcdef
[idx
]);
411 err
= HWCFUNCS_ERROR_HWCARGS
;
413 logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp
);
420 /* initialize hwcdef[] based on user's counter definitions */
422 process_hwcentrylist (const Hwcentry
* entries
[], unsigned numctrs
)
426 if (numctrs
> cpcN_npics
)
428 logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics
); /*!*/
429 return HWCFUNCS_ERROR_HWCARGS
;
431 for (unsigned idx
= 0; idx
< numctrs
; idx
++)
433 Hwcentry
*phwcdef
= &hwcdef
[idx
];
434 *phwcdef
= *entries
[idx
];
436 phwcdef
->name
= strdup (phwcdef
->name
);
438 phwcdef
->name
= "NULL";
439 if (phwcdef
->int_name
)
440 phwcdef
->int_name
= strdup (phwcdef
->int_name
);
442 phwcdef
->int_name
= "NULL";
443 if (phwcdef
->val
< 0)
445 logerr (GTXT ("Negative interval specified for HW counter `%s'\n"), /*!*/
447 err
= HWCFUNCS_ERROR_HWCARGS
;
450 ctrdefprint (DBG_LT1
, "hwcfuncs: process_hwcentrylist", phwcdef
);
453 hwcdef_cnt
= numctrs
;
459 hwcfuncs_parse_attrs (const char *countername
, hwcfuncs_attr_t attrs
[],
460 unsigned max_attrs
, uint_t
*pnum_attrs
, char**errstring
)
469 counter_copy
= strdup (countername
);
471 /* advance pointer to first attribute */
472 tail
= strchr (counter_copy
, HWCFUNCS_PARSE_ATTR
);
476 /* remove regno and value, if supplied */
478 char *tmp
= strchr (counter_copy
, HWCFUNCS_PARSE_REGNUM
);
481 tmp
= strchr (counter_copy
, HWCFUNCS_PARSE_VALUE
);
489 if (nattrs
>= max_attrs
)
491 snprintf (errbuf
, sizeof (errbuf
),
492 GTXT ("Too many attributes defined in `%s'"),
494 goto mycpc2_parse_attrs_end
;
496 /* get attribute name */
498 tail
= strchr (head
, HWCFUNCS_PARSE_EQUAL
);
501 snprintf (errbuf
, sizeof (errbuf
),
502 GTXT ("Missing value for attribute `%s' in `%s'"),
504 goto mycpc2_parse_attrs_end
;
506 *tail
= 0; /* null terminate current component */
507 attrs
[nattrs
].ca_name
= head
;
509 /* get attribute value */
511 tail
= strchr (head
, HWCFUNCS_PARSE_ATTR
);
513 *tail
= 0; /* null terminate current component */
514 attrs
[nattrs
].ca_val
= strtoull (head
, &pch
, 0);
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
;
522 TprintfT (DBG_LT0
, "hwcfuncs: pic_: '%s', attribute[%u]"
524 counter_copy
, nattrs
, attrs
[nattrs
].ca_name
,
525 (long long unsigned int) attrs
[nattrs
].ca_val
);
531 mycpc2_parse_attrs_end
:
532 *pnum_attrs
= nattrs
;
541 *errstring
= strdup (errbuf
);
549 hwcfuncs_parse_ctr (const char *counter_def
, int *pplus
, char **pnameOnly
,
550 char **pattrs
, char **pregstr
, regno_t
*pregno
)
552 char *nameptr
, *copy
, *slash
, *attr_delim
;
555 nameptr
= copy
= strdup (counter_def
);
559 if (nameptr
[0] == HWCFUNCS_PARSE_BACKTRACK
)
564 else if (nameptr
[0] == HWCFUNCS_PARSE_BACKTRACK_OFF
)
576 slash
= strchr (nameptr
, HWCFUNCS_PARSE_REGNUM
);
579 /* the remaining string should be a number > 0 */
581 *pregstr
= strdup (slash
);
582 char *endchar
= NULL
;
583 regno
= (regno_t
) strtol (slash
+ 1, &endchar
, 0);
586 if (*(slash
+ 1) == '-')
588 /* terminate previous element up to slash */
597 attr_delim
= strchr (nameptr
, HWCFUNCS_PARSE_ATTR
);
598 if (attr_delim
!= NULL
)
601 *pattrs
= strdup (attr_delim
);
602 /* terminate previous element up to attr_delim */
606 *pnameOnly
= strdup (nameptr
);
610 /* create counters */
612 hwcfuncs_bind_descriptor (const char *defstring
)
614 int err
= process_data_descriptor (defstring
);
617 TprintfT (DBG_LT0
, "hwcfuncs: ERROR: hwcfuncs_bind_descriptor failed\n");
620 err
= hwcdrv_driver
->hwcdrv_create_counters (hwcdef_cnt
, hwcdef
);
626 hwcfuncs_bind_hwcentry (const Hwcentry
* entries
[], unsigned numctrs
)
629 err
= process_hwcentrylist (entries
, numctrs
);
632 TprintfT (DBG_LT0
, "hwcfuncs: ERROR: hwcfuncs_bind_hwcentry\n");
635 err
= hwcdrv_driver
->hwcdrv_create_counters (hwcdef_cnt
, hwcdef
);
640 IS_GLOBAL Hwcentry
**
641 hwcfuncs_get_ctrs (unsigned *defcnt
)
644 *defcnt
= hwcdef_cnt
;
648 extern hwcdrv_api_t hwcdrv_pcl_api
;
649 static int hwcdrv_driver_inited
= 0;
654 if (hwcdrv_driver_inited
)
655 return hwcdrv_driver
;
656 hwcdrv_driver_inited
= 1;
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)
664 hwcdrv_driver
->hwcdrv_get_info (NULL
, NULL
, &cpcN_npics
, NULL
, NULL
);
665 return hwcdrv_driver
;
667 hwcdrv_driver
= &hwcdrv_default
;
668 return hwcdrv_driver
;