1 /* Copyright (C) 2021 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
)
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, reg_list=%p\n",
166 hdr
, phwcdef
->name
, phwcdef
->int_name
, phwcdef
->reg_num
,
167 phwcdef
->timecvt
, phwcdef
->memop
, phwcdef
->val
,
168 phwcdef
->sort_order
, phwcdef
->reg_list
);
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
))
263 err
= HWCFUNCS_ERROR_HWCARGS
;
264 goto ext_hw_install_end
;
266 ds
= strdup (defstring
);
269 err
= HWCFUNCS_ERROR_HWCINIT
;
270 goto ext_hw_install_end
;
274 for (idx
= 0; idx
< MAX_PICS
&& *dsp
; idx
++)
277 char *int_name
= NULL
;
278 regno_t reg
= REGNO_ANY
;
279 ABST_type memop
= ABST_NONE
;
282 unsigned sort_order
= (unsigned) - 1;
286 dsp
= strchr (dsp
, ':');
289 err
= HWCFUNCS_ERROR_HWCARGS
;
290 goto ext_hw_install_end
;
296 dsp
= strchr (dsp
, ':');
299 err
= HWCFUNCS_ERROR_HWCARGS
;
300 goto ext_hw_install_end
;
305 reg
= (int) strtol (dsp
, &dsp
, 0);
308 err
= HWCFUNCS_ERROR_HWCARGS
;
309 goto ext_hw_install_end
;
311 if (reg
< 0 && reg
!= -1)
313 err
= HWCFUNCS_ERROR_HWCARGS
;
314 goto ext_hw_install_end
;
317 hwcdef
[idx
].reg_num
= reg
;
320 interval
= (int) strtol (dsp
, &dsp
, 0);
323 err
= HWCFUNCS_ERROR_HWCARGS
;
324 goto ext_hw_install_end
;
328 err
= HWCFUNCS_ERROR_HWCARGS
;
329 goto ext_hw_install_end
;
331 hwcdef
[idx
].val
= interval
;
335 * This is a new field.
336 * An old launcher (dbx, etc.) would not include it.
337 * Detect the presence of the field by the char 'm'.
341 long long tmp_ll
= 0;
343 tmp_ll
= strtoll (dsp
, &dsp
, 0);
346 err
= HWCFUNCS_ERROR_HWCARGS
;
347 goto ext_hw_install_end
;
351 err
= HWCFUNCS_ERROR_HWCARGS
;
352 goto ext_hw_install_end
;
354 hwcdef
[idx
].min_time
= tmp_ll
;
357 hwcdef
[idx
].min_time
= 0;
360 sort_order
= (int) strtoul (dsp
, &dsp
, 0);
363 err
= HWCFUNCS_ERROR_HWCARGS
;
364 goto ext_hw_install_end
;
366 hwcdef
[idx
].sort_order
= sort_order
;
369 timecvt
= (int) strtol (dsp
, &dsp
, 0);
372 err
= HWCFUNCS_ERROR_HWCARGS
;
373 goto ext_hw_install_end
;
375 hwcdef
[idx
].timecvt
= timecvt
;
378 memop
= (ABST_type
) strtol (dsp
, &dsp
, 0);
379 if (*dsp
!= 0 && *dsp
++ != ',')
381 err
= HWCFUNCS_ERROR_HWCARGS
;
382 goto ext_hw_install_end
;
384 hwcdef
[idx
].memop
= memop
;
386 hwcdef
[idx
].name
= strdup (name
);
388 hwcdef
[idx
].name
= strdup (int_name
);
390 hwcdef
[idx
].int_name
= strdup (int_name
);
392 hwcdef
[idx
].int_name
= strdup (name
);
393 ctrdefprint (DBG_LT1
, "hwcfuncs: process_data_descriptor", &hwcdef
[idx
]);
398 TprintfT (DBG_LT0
, "hwcfuncs: ERROR: process_data_descriptor(): "
399 "ctr string had some trailing garbage:"
401 err
= HWCFUNCS_ERROR_HWCARGS
;
402 goto ext_hw_install_end
;
411 TprintfT (DBG_LT0
, "hwcfuncs: ERROR: process_data_descriptor(): "
412 " syntax error just before:"
414 logerr (GTXT ("Data descriptor syntax error near `%s'\n"), dsp
);
417 logerr (GTXT ("Data descriptor syntax error\n"));
422 /* initialize hwcdef[] based on user's counter definitions */
424 process_hwcentrylist (const Hwcentry
* entries
[], unsigned numctrs
)
428 if (numctrs
> cpcN_npics
)
430 logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics
); /*!*/
431 return HWCFUNCS_ERROR_HWCARGS
;
433 for (unsigned idx
= 0; idx
< numctrs
; idx
++)
435 Hwcentry
*phwcdef
= &hwcdef
[idx
];
436 *phwcdef
= *entries
[idx
];
438 phwcdef
->name
= strdup (phwcdef
->name
);
440 phwcdef
->name
= "NULL";
441 if (phwcdef
->int_name
)
442 phwcdef
->int_name
= strdup (phwcdef
->int_name
);
444 phwcdef
->int_name
= "NULL";
445 if (phwcdef
->val
< 0)
447 logerr (GTXT ("Negative interval specified for HW counter `%s'\n"), /*!*/
449 err
= HWCFUNCS_ERROR_HWCARGS
;
452 ctrdefprint (DBG_LT1
, "hwcfuncs: process_hwcentrylist", phwcdef
);
455 hwcdef_cnt
= numctrs
;
461 hwcfuncs_parse_attrs (const char *countername
, hwcfuncs_attr_t attrs
[],
462 unsigned max_attrs
, uint_t
*pnum_attrs
, char**errstring
)
471 counter_copy
= strdup (countername
);
473 /* advance pointer to first attribute */
474 tail
= strchr (counter_copy
, HWCFUNCS_PARSE_ATTR
);
478 /* remove regno and value, if supplied */
480 char *tmp
= strchr (counter_copy
, HWCFUNCS_PARSE_REGNUM
);
483 tmp
= strchr (counter_copy
, HWCFUNCS_PARSE_VALUE
);
491 if (nattrs
>= max_attrs
)
493 snprintf (errbuf
, sizeof (errbuf
),
494 GTXT ("Too many attributes defined in `%s'"),
496 goto mycpc2_parse_attrs_end
;
498 /* get attribute name */
500 tail
= strchr (head
, HWCFUNCS_PARSE_EQUAL
);
503 snprintf (errbuf
, sizeof (errbuf
),
504 GTXT ("Missing value for attribute `%s' in `%s'"),
506 goto mycpc2_parse_attrs_end
;
508 *tail
= 0; /* null terminate current component */
509 attrs
[nattrs
].ca_name
= head
;
511 /* get attribute value */
513 tail
= strchr (head
, HWCFUNCS_PARSE_ATTR
);
515 *tail
= 0; /* null terminate current component */
516 attrs
[nattrs
].ca_val
= strtoull (head
, &pch
, 0);
519 snprintf (errbuf
, sizeof (errbuf
),
520 GTXT ("Illegal value for attribute `%s' in `%s'"),
521 attrs
[nattrs
].ca_name
, countername
);
522 goto mycpc2_parse_attrs_end
;
524 TprintfT (DBG_LT0
, "hwcfuncs: pic_: '%s', attribute[%u]"
526 counter_copy
, nattrs
, attrs
[nattrs
].ca_name
,
527 (long long unsigned int) attrs
[nattrs
].ca_val
);
533 mycpc2_parse_attrs_end
:
534 *pnum_attrs
= nattrs
;
543 *errstring
= strdup (errbuf
);
551 hwcfuncs_parse_ctr (const char *counter_def
, int *pplus
, char **pnameOnly
,
552 char **pattrs
, char **pregstr
, regno_t
*pregno
)
554 char *nameptr
, *copy
, *slash
, *attr_delim
;
557 nameptr
= copy
= strdup (counter_def
);
561 if (nameptr
[0] == HWCFUNCS_PARSE_BACKTRACK
)
566 else if (nameptr
[0] == HWCFUNCS_PARSE_BACKTRACK_OFF
)
578 slash
= strchr (nameptr
, HWCFUNCS_PARSE_REGNUM
);
581 /* the remaining string should be a number > 0 */
583 *pregstr
= strdup (slash
);
584 char *endchar
= NULL
;
585 regno
= (regno_t
) strtol (slash
+ 1, &endchar
, 0);
588 if (*(slash
+ 1) == '-')
590 /* terminate previous element up to slash */
599 attr_delim
= strchr (nameptr
, HWCFUNCS_PARSE_ATTR
);
600 if (attr_delim
!= NULL
)
603 *pattrs
= strdup (attr_delim
);
604 /* terminate previous element up to attr_delim */
608 *pnameOnly
= strdup (nameptr
);
612 /* create counters */
614 hwcfuncs_bind_descriptor (const char *defstring
)
616 int err
= process_data_descriptor (defstring
);
619 TprintfT (DBG_LT0
, "hwcfuncs: ERROR: hwcfuncs_bind_descriptor failed\n");
622 err
= hwcdrv_driver
->hwcdrv_create_counters (hwcdef_cnt
, hwcdef
);
628 hwcfuncs_bind_hwcentry (const Hwcentry
* entries
[], unsigned numctrs
)
631 err
= process_hwcentrylist (entries
, numctrs
);
634 TprintfT (DBG_LT0
, "hwcfuncs: ERROR: hwcfuncs_bind_hwcentry\n");
637 err
= hwcdrv_driver
->hwcdrv_create_counters (hwcdef_cnt
, hwcdef
);
642 IS_GLOBAL Hwcentry
**
643 hwcfuncs_get_ctrs (unsigned *defcnt
)
646 *defcnt
= hwcdef_cnt
;
650 /* return 1 if <regno> is in Hwcentry's list */
652 regno_is_valid (const Hwcentry
* pctr
, regno_t regno
)
654 regno_t
*reg_list
= pctr
->reg_list
;
655 if (REG_LIST_IS_EMPTY (reg_list
))
657 if (regno
== REGNO_ANY
) /* wildcard */
659 for (int ii
= 0; ii
< MAX_PICS
; ii
++)
661 regno_t tmp
= reg_list
[ii
];
662 if (REG_LIST_EOL (tmp
)) /* end of list */
664 if (tmp
== regno
) /* is in list */
670 /* supplied by hwcdrv_api drivers */
672 hwcfuncs_assign_regnos (Hwcentry
* entries
[],
675 if (numctrs
> cpcN_npics
)
677 logerr (GTXT ("More than %d counters were specified\n"), cpcN_npics
); /*!*/
678 return HWCFUNCS_ERROR_HWCARGS
;
680 return hwcdrv_driver
->hwcdrv_assign_regnos (entries
, numctrs
);
683 extern hwcdrv_api_t hwcdrv_pcl_api
;
684 static int hwcdrv_driver_inited
= 0;
689 if (hwcdrv_driver_inited
)
690 return hwcdrv_driver
;
691 hwcdrv_driver_inited
= 1;
693 for (int i
= 0; i
< MAX_PICS
; i
++)
694 hwctable
[i
] = &hwcdef
[i
];
695 hwcdrv_driver
= &hwcdrv_pcl_api
;
696 hwcdrv_driver
->hwcdrv_init_status
= hwcdrv_driver
->hwcdrv_init (NULL
, NULL
);
697 if (hwcdrv_driver
->hwcdrv_init_status
== 0)
699 hwcdrv_driver
->hwcdrv_get_info (NULL
, NULL
, &cpcN_npics
, NULL
, NULL
);
700 return hwcdrv_driver
;
702 hwcdrv_driver
= &hwcdrv_default
;
703 return hwcdrv_driver
;