Skip to content

Commit e5b406f

Browse files
Merge pull request #509 from Treece-Burgess/11-10-2025-topdown-read-reset-stop
topdown: Restructure code for error checking with sys call, make _topdown_reset actually reset, and avoid closing file descriptors when calling PAPI_read
2 parents c60634e + 5929b6b commit e5b406f

File tree

1 file changed

+138
-89
lines changed

1 file changed

+138
-89
lines changed

src/components/topdown/topdown.c

Lines changed: 138 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -568,53 +568,53 @@ _topdown_init_control_state(hwd_control_state_t *ctl)
568568
int metrics_fd = -1;
569569
void *slots_p, *metrics_p;
570570

571-
/* set up slots */
571+
// Set up slots
572572
memset(&slots, 0, sizeof(slots));
573573
slots.type = PERF_TYPE_RAW;
574574
slots.size = sizeof(struct perf_event_attr);
575575
slots.config = 0x0400ull;
576576
slots.exclude_kernel = 1;
577577

578-
/* open slots */
578+
// Open slots
579579
slots_fd = perf_event_open(&slots, 0, -1, -1, 0);
580580
if (slots_fd < 0)
581581
{
582582
retval = PAPI_ENOMEM;
583583
goto fn_fail;
584584
}
585585

586-
/* memory mapping the fd to permit _rdpmc calls from userspace */
586+
// Memory mapping the fd to permit _rdpmc calls from userspace
587587
slots_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, slots_fd, 0);
588588
if (slots_p == (void *) -1L)
589589
{
590590
retval = PAPI_ENOMEM;
591591
goto fn_fail;
592592
}
593593

594-
/* set up metrics */
594+
// Set up metrics
595595
memset(&metrics, 0, sizeof(metrics));
596596
metrics.type = PERF_TYPE_RAW;
597597
metrics.size = sizeof(struct perf_event_attr);
598598
metrics.config = 0x8000;
599599
metrics.exclude_kernel = 1;
600600

601-
/* open metrics with slots as the group leader */
601+
// Open metrics with slots as the group leader
602602
metrics_fd = perf_event_open(&metrics, 0, -1, slots_fd, 0);
603603
if (metrics_fd < 0)
604604
{
605605
retval = PAPI_ENOMEM;
606606
goto fn_fail;
607607
}
608608

609-
/* memory mapping the fd to permit _rdpmc calls from userspace */
609+
// Memory mapping the fd to permit _rdpmc calls from userspace
610610
metrics_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, metrics_fd, 0);
611611
if (metrics_p == (void *) -1L)
612612
{
613613
retval = PAPI_ENOMEM;
614614
goto fn_fail;
615615
}
616616

617-
/* we set up with no errors, so fill out the control state */
617+
// We set up with no errors, so fill out the control state
618618
control->slots_fd = slots_fd;
619619
control->slots_p;
620620
control->metrics_fd = metrics_fd;
@@ -624,7 +624,7 @@ _topdown_init_control_state(hwd_control_state_t *ctl)
624624
return retval;
625625

626626
fn_fail:
627-
/* we need to close & free whatever we opened and allocated */
627+
// We need to close & free whatever we opened and allocated
628628
if (slots_p != NULL)
629629
munmap(slots_p, getpagesize());
630630
if (metrics_p != NULL)
@@ -642,127 +642,176 @@ _topdown_update_control_state(hwd_control_state_t *ctl,
642642
int count,
643643
hwd_context_t *ctx)
644644
{
645-
int i, index;
646645
(void)ctx;
647-
648646
_topdown_control_state_t *control = (_topdown_control_state_t *)ctl;
649647

648+
int i;
650649
for (i = 0; i < TOPDOWN_MAX_COUNTERS; i++)
651650
{
652651
control->being_measured[i] = 0;
653652
}
654653

655654
for (i = 0; i < count; i++)
656655
{
657-
index = native[i].ni_event & PAPI_NATIVE_AND_MASK;
656+
int index = native[i].ni_event & PAPI_NATIVE_AND_MASK;
658657
native[i].ni_position = topdown_native_events[index].selector - 1;
659658
control->being_measured[index] = 1;
660659
}
661660

662661
return PAPI_OK;
663662
}
664663

665-
static int
666-
_topdown_start(hwd_context_t *ctx, hwd_control_state_t *ctl)
664+
/**@class _wrapper_perf_event_ioc_reset
665+
* @brief Reset the PERF_METRICS counter and slots to maintain precision
666+
* as perf the recommendation of section 21.3.9.3 of the IA-32
667+
* Architectures Software Developer's Manual. Resetting means we
668+
* do not need to record 'before' metrics/slot values, as they are
669+
* always effectively 0. Despite the reset meaning we don't need to
670+
* record the slots value at all, the SDM states that SLOTS and the
671+
* PERF_METRICS MSR should be reset together.
672+
*/
673+
static
674+
int _wrapper_perf_event_ioc_reset(_topdown_control_state_t *control)
667675
{
668-
(void) ctx;
669-
_topdown_control_state_t *control = (_topdown_control_state_t *)ctl;
670-
671-
/* reset the PERF_METRICS counter and slots to maintain precision */
672-
/* as per the recommendation of section 21.3.9.3 of the IA-32
673-
/* Architectures Software Developer’s Manual. Resetting means we do not */
674-
/* need to record 'before' metrics/slots values, as they are always */
675-
/* effectively 0. Despite the reset meaning we don't need to record */
676-
/* the slots value at all, the SDM states that SLOTS and the PERF_METRICS */
677-
/* MSR should be reset together, so we do that here. */
678-
679-
/* these ioctl calls do not need to be protected by assert_affinity() */
680-
ioctl(control->slots_fd, PERF_EVENT_IOC_RESET, 0);
681-
ioctl(control->metrics_fd, PERF_EVENT_IOC_RESET, 0);
676+
// This ioctl call does not need to be protected by assert_affinity()
677+
int retval = ioctl(control->slots_fd, PERF_EVENT_IOC_RESET, 0);
678+
if (retval != 0) {
679+
SUBDBG("Resetting control->slots_fd failed.\n");
680+
return PAPI_ESYS;
681+
}
682+
683+
// This ioctl call does not need to be protected by assert_affinity()
684+
retval = ioctl(control->metrics_fd, PERF_EVENT_IOC_RESET, 0);
685+
if (retval != 0) {
686+
SUBDBG("Resetting control->metrics_fd failed.\n");
687+
return PAPI_ESYS;
688+
}
689+
690+
return PAPI_OK;
691+
}
682692

683-
return PAPI_OK;
693+
/**@class _wrapper_perf_event_ioc_enable
694+
* @brief Enable the event's.
695+
*
696+
* NOTE: As control->slots_fd is the group leader for control->metrics_fd
697+
* you could use PERF_IOC_FLAG_GROUP in the ioctl argument to enable all events
698+
* in the group i.e. control->metrics_fd. However, from Linux 2.6.31 to Linux 3.4
699+
* PERF_IOC_FLAG_GROUP ioctl argument was broken.
700+
*/
701+
static
702+
int _wrapper_perf_event_ioc_enable(_topdown_control_state_t *control)
703+
{
704+
int retval = ioctl(control->slots_fd, PERF_EVENT_IOC_ENABLE, 0);
705+
if (retval != PAPI_OK) {
706+
SUBDBG("Enabling control->slots_fd failed.\n");
707+
return retval;
708+
}
709+
710+
retval = ioctl(control->metrics_fd, PERF_EVENT_IOC_ENABLE, 0);
711+
if (retval != PAPI_OK) {
712+
SUBDBG("Enabling control->metrics_fd failed.\n");
713+
return retval;
714+
}
715+
716+
return PAPI_OK;
684717
}
685718

686719
static int
687-
_topdown_stop(hwd_context_t *ctx, hwd_control_state_t *ctl)
720+
_topdown_start(hwd_context_t *ctx, hwd_control_state_t *ctl)
688721
{
689-
_topdown_context_t *context = (_topdown_context_t *)ctx;
722+
(void)ctx;
690723
_topdown_control_state_t *control = (_topdown_control_state_t *)ctl;
691-
unsigned long long metrics_after;
692-
693-
int i, retval;
694-
double ma, mb, perc;
695-
696-
retval = PAPI_OK;
697-
698-
metrics_after = read_metrics();
699724

700-
/* extract the values */
701-
for (i = 0; i < TOPDOWN_MAX_COUNTERS; i++)
702-
{
703-
if (control->being_measured[i])
704-
{
705-
/* handle case where the metric is not derived */
706-
if (topdown_native_events[i].metric_idx >= 0)
707-
{
708-
perc = extract_metric(topdown_native_events[i].metric_idx,
709-
metrics_after) * 100.0;
710-
}
711-
else /* handle case where the metric is derived */
712-
{
713-
/* metric perc = parent perc - sibling perc */
714-
perc = extract_metric(
715-
topdown_native_events[i].derived_parent_idx,
716-
metrics_after) * 100.0
717-
- extract_metric(
718-
topdown_native_events[i].derived_sibling_idx,
719-
metrics_after) * 100.0;
720-
}
725+
// Reset counters to maintain precision.
726+
int retval = _wrapper_perf_event_ioc_reset(control);
727+
if (retval != PAPI_OK) {
728+
return retval;
729+
}
721730

722-
/* sometimes the percentage will be a very small negative value */
723-
/* instead of 0 due to floating point error. tidy that up: */
724-
if (perc < 0.0) {
725-
perc = 0.0;
726-
}
727-
728-
/* store the raw bits of the double into the counter value */
729-
control->count[i] = *(long long*)&perc;
730-
}
731+
retval = _wrapper_perf_event_ioc_enable(control);
732+
if (retval != PAPI_OK) {
733+
return retval;
731734
}
732735

733-
fn_exit:
734-
/* free & close everything in the control state */
735-
munmap(control->slots_p, getpagesize());
736-
control->slots_p = NULL;
737-
munmap(control->metrics_p, getpagesize());
738-
control->metrics_p = NULL;
739-
close(control->slots_fd);
740-
control->slots_fd = -1;
741-
close(control->metrics_fd);
742-
control->metrics_fd = -1;
743-
744-
return retval;
736+
return PAPI_OK;
745737
}
746738

747739
static int
748740
_topdown_read(hwd_context_t *ctx, hwd_control_state_t *ctl,
749-
long long **events, int flags)
741+
long long **events, int flags)
750742
{
751-
(void)flags;
743+
(void)flags;
744+
(void)ctx;
745+
_topdown_control_state_t *control = (_topdown_control_state_t *)ctl;
746+
747+
// Extract the values
748+
unsigned long long metrics_after = read_metrics();
749+
int i;
750+
for (i = 0; i < TOPDOWN_MAX_COUNTERS; i++)
751+
{
752+
if (control->being_measured[i])
753+
{
754+
double perc;
755+
// Handle case where the metric is not derived
756+
if (topdown_native_events[i].metric_idx >= 0)
757+
{
758+
perc = extract_metric(topdown_native_events[i].metric_idx,
759+
metrics_after) * 100.0;
760+
}
761+
else // Handle case where the metric is derived
762+
{
763+
// Metric perc = parent perc - sibling perc
764+
perc = extract_metric(
765+
topdown_native_events[i].derived_parent_idx,
766+
metrics_after) * 100.0
767+
- extract_metric(
768+
topdown_native_events[i].derived_sibling_idx,
769+
metrics_after) * 100.0;
770+
}
771+
772+
// Sometimes the percentage will be a very small negative value
773+
// instead of 0 due to floating point error. tidy that up:
774+
if (perc < 0.0) {
775+
perc = 0.0;
776+
}
777+
778+
// Store the raw bits of the double into the counter value
779+
control->count[i] = *(long long*)&perc;
780+
}
781+
}
782+
// Pass back a pointer to our results
783+
*events = control->count;
784+
785+
return PAPI_OK;
786+
}
752787

753-
_topdown_stop(ctx, ctl);
788+
static int
789+
_topdown_reset(hwd_context_t *ctx, hwd_control_state_t *ctl)
790+
{
791+
(void)ctx;
792+
_topdown_control_state_t *control = (_topdown_control_state_t *)ctl;
754793

755-
/* Pass back a pointer to our results */
756-
*events = ((_topdown_control_state_t *)ctl)->count;
794+
int retval = _wrapper_perf_event_ioc_reset(control);
795+
if (retval != PAPI_OK) {
796+
return retval;
797+
}
757798

758-
return PAPI_OK;
799+
return PAPI_OK;
759800
}
760801

761802
static int
762-
_topdown_reset(hwd_context_t *ctx, hwd_control_state_t *ctl)
803+
_topdown_stop(hwd_context_t *ctx, hwd_control_state_t *ctl)
763804
{
764-
( void ) ctx;
765-
( void ) ctl;
805+
(void)ctx;
806+
_topdown_control_state_t *control = (_topdown_control_state_t *)ctl;
807+
808+
// slots_fd is defined as the group leader in _topdown_init_control_state;
809+
// therefore, disabling slots_fd will disable the entire group.
810+
int retval = ioctl(control->slots_fd, PERF_EVENT_IOC_DISABLE, 0);
811+
if (retval != 0) {
812+
SUBDBG("Disabling the entire event group failed.\n");
813+
return PAPI_ESYS;
814+
}
766815

767816
return PAPI_OK;
768817
}

0 commit comments

Comments
 (0)