diff --git a/src/components/topdown/README.md b/src/components/topdown/README.md new file mode 100644 index 000000000..e5be3e983 --- /dev/null +++ b/src/components/topdown/README.md @@ -0,0 +1,35 @@ +# TOPDOWN Component + +The `topdown` component enables accessing the `PERF_METRICS` Model Specific +Register (MSR) of modern Intel PMUs, and makes it simple to properly +interpret the results. + +* [Enabling the TOPDOWN Component](#enabling-the-topdown-component) +* [Adding More Architectures](#adding_more_architectures) + +## Enabling the TOPDOWN Component + +To enable reading of topdown metrics the user needs to link against a +PAPI library that was configured with the topdown component enabled. As an +example the following command: `./configure --with-components="topdown"` is +sufficient to enable the component. + +## Interpreting Results + +The events added by this component ending in "_PERC" should be cast to double +values in order to be properly interpreted as percentages. An example of how +to do so follows: + + PAPI_start(EventSet); + + /* some block of code... */ + + PAPI_stop(EventSet, values); + + printf("First metric was %.1f\n", *((double *)(&values[0]))); + +## Adding More Architectures + +To contribute more supported architectures to the component, add the cpuid model +of the architecture to the case statement in `_topdown_init_component` of +[topdown.c](./topdown.c) and set the relevant options (`supports_l2`, etc.) \ No newline at end of file diff --git a/src/components/topdown/Rules.topdown b/src/components/topdown/Rules.topdown new file mode 100644 index 000000000..cc1b54be9 --- /dev/null +++ b/src/components/topdown/Rules.topdown @@ -0,0 +1,5 @@ +COMPSRCS += components/topdown/topdown.c +COMPOBJS += topdown.o + +topdown.o: components/topdown/topdown.c components/topdown/topdown.h $(HEADERS) + $(CC) $(LIBCFLAGS) $(OPTFLAGS) -c components/topdown/topdown.c -o topdown.o diff --git a/src/components/topdown/tests/Makefile b/src/components/topdown/tests/Makefile new file mode 100644 index 000000000..b6c7cd089 --- /dev/null +++ b/src/components/topdown/tests/Makefile @@ -0,0 +1,22 @@ +NAME=topdown +include ../../Makefile_comp_tests.target + +%.o:%.c + $(CC) $(CFLAGS) $(OPTFLAGS) $(INCLUDE) -c -o $@ $< + +TESTS = topdown_basic topdown_L1 topdown_L2 + +topdown_tests: $(TESTS) + +topdown_basic: topdown_basic.o $(UTILOBJS) $(PAPILIB) + $(CC) $(CFLAGS) $(INCLUDE) -o topdown_basic topdown_basic.o $(UTILOBJS) $(PAPILIB) $(LDFLAGS) + +topdown_L1: topdown_L1.o $(UTILOBJS) $(PAPILIB) + $(CC) $(CFLAGS) $(INCLUDE) -o topdown_L1 topdown_L1.o $(UTILOBJS) $(PAPILIB) $(LDFLAGS) + +topdown_L2: topdown_L2.o $(UTILOBJS) $(PAPILIB) + $(CC) $(CFLAGS) $(INCLUDE) -o topdown_L2 topdown_L2.o $(UTILOBJS) $(PAPILIB) $(LDFLAGS) + + +clean: + rm -f $(TESTS) *.o diff --git a/src/components/topdown/tests/topdown_L1.c b/src/components/topdown/tests/topdown_L1.c new file mode 100644 index 000000000..da2c6a5f8 --- /dev/null +++ b/src/components/topdown/tests/topdown_L1.c @@ -0,0 +1,177 @@ +/* + * Specifically tests that the Level 1 topdown events make sense. + */ + +#include +#include +#include +#include + +#include "papi.h" +#include "papi_test.h" + +#define NUM_EVENTS 4 +#define PERC_TOLERANCE 1.5 + +// fibonacci function to serve as a benchable code section +void __attribute__((optimize("O0"))) fib(int n) +{ + long i, a = 0; + int b = 1; + for (i = 0; i < n; i++) + { + b = b + a; + a = b - a; + } +} + +int main(int argc, char **argv) +{ + int i, quiet, retval; + int EventSet = PAPI_NULL; + const PAPI_component_info_t *cmpinfo = NULL; + int numcmp, cid, topdown_cid = -1; + long long values[NUM_EVENTS]; + double tmp; + + /* Set TESTS_QUIET variable */ + quiet = tests_quiet(argc, argv); + + /* PAPI Initialization */ + retval = PAPI_library_init(PAPI_VER_CURRENT); + if (retval != PAPI_VER_CURRENT) + { + test_fail(__FILE__, __LINE__, "PAPI_library_init failed\n", retval); + } + + if (!quiet) + { + printf("Testing topdown component with PAPI %d.%d.%d\n", + PAPI_VERSION_MAJOR(PAPI_VERSION), + PAPI_VERSION_MINOR(PAPI_VERSION), + PAPI_VERSION_REVISION(PAPI_VERSION)); + } + + /*******************************/ + /* Find the topdown component */ + /*******************************/ + numcmp = PAPI_num_components(); + for (cid = 0; cid < numcmp; cid++) + { + if ((cmpinfo = PAPI_get_component_info(cid)) == NULL) + { + test_fail(__FILE__, __LINE__, "PAPI_get_component_info failed\n", 0); + } + if (!quiet) + { + printf("\tComponent %d - %d events - %s\n", cid, + cmpinfo->num_native_events, + cmpinfo->name); + } + if (strstr(cmpinfo->name, "topdown")) + { + topdown_cid = cid; + + /* check that the component is enabled */ + if (cmpinfo->disabled) + { + printf("Topdown component is disabled: %s\n", cmpinfo->disabled_reason); + test_fail(__FILE__, __LINE__, "Component is not enabled\n", 0); + } + } + } + + if (topdown_cid < 0) + { + test_skip(__FILE__, __LINE__, "Topdown component not found\n", 0); + } + + if (!quiet) + { + printf("\nFound Topdown Component at id %d\n", topdown_cid); + printf("\nAdding the level 1 topdown metrics..\n"); + } + + /* Create EventSet */ + retval = PAPI_create_eventset(&EventSet); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "PAPI_create_eventset()", retval); + } + + /* Add the level 1 topdown metrics */ + retval = PAPI_add_named_event(EventSet, "TOPDOWN_RETIRING_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_RETIRING_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_BAD_SPEC_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_BAD_SPEC_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_FE_BOUND_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_FE_BOUND_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_BE_BOUND_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_BE_BOUND_PERC", retval); + } + + /* stat a loop-based calculation of the sum of the fibonacci sequence */ + /* the workload needs to be fairly large in order to acquire an accurate */ + /* set of measurements */ + PAPI_start(EventSet); + fib(6000000); + PAPI_stop(EventSet, values); + + /* run some sanity checks: */ + + /* first, the sum of all level 1 metric percentages should be 100% */ + tmp = 0; + for (i=0; i 100 + PERC_TOLERANCE) { + test_fail(__FILE__, __LINE__, + "Level 1 topdown metric percentages did not sum to 100%%\n", 1); + } + + if (!quiet) + printf("\tRetiring:\t%.1f%%\n", *((double *)(&values[0]))); + + /* next, verify that the percentage of bad spec slots is reasonable. */ + /* for this benchmark, we can expect very low rate of bad speculation */ + /* due to the fact that it consists of a simple for loop */ + if (!quiet) + printf("\tBad spec:\t%.1f%%\n", *((double *)(&values[1]))); + if (*((double *)(&values[1])) > 5.0) { + test_warn(__FILE__, __LINE__, + "The percentage of slots affected by bad speculation was unexpectedly high", 1); + } + + /* finally, make sure the frontend/backend bound percentages make sense */ + /* we should expect this benchmark to be significantly more limited */ + /* by the back end, so check that be bound is larger than the fe bound */ + if (!quiet) { + printf("\tFrontend bound:\t%.1f%%\n", *((double *)(&values[2]))); + printf("\tBackend bound:\t%.1f%%\n", *((double *)(&values[3]))); + + } + if (*((double *)(&values[2])) > *((double *)(&values[3]))) { + test_warn(__FILE__, __LINE__, + "Frontend bound should be significantly smaller than backend bound", 1); + } + + return 0; +} \ No newline at end of file diff --git a/src/components/topdown/tests/topdown_L2.c b/src/components/topdown/tests/topdown_L2.c new file mode 100644 index 000000000..52338e476 --- /dev/null +++ b/src/components/topdown/tests/topdown_L2.c @@ -0,0 +1,223 @@ +/* + * Specifically tests that the Level 2 topdown events make sense. + */ + +#include +#include +#include +#include + +#include "papi.h" +#include "papi_test.h" + +#define NUM_EVENTS 8 +#define PERC_TOLERANCE 1.5 + +// fibonacci function to serve as a benchable code section +void __attribute__((optimize("O0"))) fib(int n) +{ + long i, a = 0; + int b = 1; + for (i = 0; i < n; i++) + { + b = b + a; + a = b - a; + } +} + +int main(int argc, char **argv) +{ + int i, quiet, retval; + int EventSet = PAPI_NULL; + const PAPI_component_info_t *cmpinfo = NULL; + int numcmp, cid, topdown_cid = -1; + long long values[NUM_EVENTS]; + double tmp; + + /* Set TESTS_QUIET variable */ + quiet = tests_quiet(argc, argv); + + /* PAPI Initialization */ + retval = PAPI_library_init(PAPI_VER_CURRENT); + if (retval != PAPI_VER_CURRENT) + { + test_fail(__FILE__, __LINE__, "PAPI_library_init failed\n", retval); + } + + if (!quiet) + { + printf("Testing topdown component with PAPI %d.%d.%d\n", + PAPI_VERSION_MAJOR(PAPI_VERSION), + PAPI_VERSION_MINOR(PAPI_VERSION), + PAPI_VERSION_REVISION(PAPI_VERSION)); + } + + /*******************************/ + /* Find the topdown component */ + /*******************************/ + numcmp = PAPI_num_components(); + for (cid = 0; cid < numcmp; cid++) + { + if ((cmpinfo = PAPI_get_component_info(cid)) == NULL) + { + test_fail(__FILE__, __LINE__, "PAPI_get_component_info failed\n", 0); + } + if (!quiet) + { + printf("\tComponent %d - %d events - %s\n", cid, + cmpinfo->num_native_events, + cmpinfo->name); + } + if (strstr(cmpinfo->name, "topdown")) + { + topdown_cid = cid; + + /* check that the component is enabled */ + if (cmpinfo->disabled) + { + printf("Topdown component is disabled: %s\n", cmpinfo->disabled_reason); + test_fail(__FILE__, __LINE__, "The TOPDOWN component is not enabled\n", 0); + } + } + } + + if (topdown_cid < 0) + { + test_skip(__FILE__, __LINE__, "Topdown component not found\n", 0); + } + + if (!quiet) + { + printf("\nFound Topdown Component at id %d\n", topdown_cid); + printf("\nAdding the level 2 topdown metrics..\n"); + } + + /* Create EventSet */ + retval = PAPI_create_eventset(&EventSet); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "PAPI_create_eventset()", retval); + } + + /* Add the level 2 topdown metrics */ + /* if we can't, just skip because not all processors support level 2 */ + retval = PAPI_add_named_event(EventSet, "TOPDOWN_HEAVY_OPS_PERC"); + if (retval != PAPI_OK) + { + test_skip(__FILE__, __LINE__, + "Error adding TOPDOWN_HEAVY_OPS_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_LIGHT_OPS_PERC"); + if (retval != PAPI_OK) + { + /* if the first L2 event was successfully added though, */ + /* subsequent failures indicate a deeper problem */ + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_LIGHT_OPS_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_BR_MISPREDICT_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_BR_MISPREDICT_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_MACHINE_CLEARS_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_MACHINE_CLEARS_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_FETCH_LAT_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_FETCH_LAT_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_FETCH_BAND_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_FETCH_BAND_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_MEM_BOUND_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_MEM_BOUND_PERC", retval); + } + retval = PAPI_add_named_event(EventSet, "TOPDOWN_CORE_BOUND_PERC"); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "Error adding TOPDOWN_CORE_BOUND_PERC", retval); + } + + /* stat a loop-based calculation of the sum of the fibonacci sequence */ + /* the workload needs to be fairly large in order to acquire an accurate */ + /* set of measurements */ + PAPI_start(EventSet); + fib(6000000); + PAPI_stop(EventSet, values); + + /* run some sanity checks: */ + + /* first, the sum of all level 2 metric percentages should be 100% */ + tmp = 0; + for (i=0; i 100 + PERC_TOLERANCE) { + test_fail(__FILE__, __LINE__, + "Level 2 topdown metric percentages did not sum to 100%%\n", 1); + } + + /* next, check that we are retiring more light ops than heavy ops */ + /* this is a very reasonable expectation for a simple loop performing + /* scalar add and multiply operations */ + if (!quiet) { + printf("\tHeavy ops:\t%.1f%%\n", *((double *)(&values[0]))); + printf("\tLight ops:\t%.1f%%\n", *((double *)(&values[1]))); + + } + if (*((double *)(&values[0])) > *((double *)(&values[1]))) { + test_warn(__FILE__, __LINE__, + "Heavy ops should be much smaller than light ops", 1); + } + + /* next, check that the branch mispredictions and machine clears */ + /* are insignificant as this benchmark should have good speculation */ + if (!quiet) { + printf("\tBranch mispredictions:\t%.1f%%\n", *((double *)(&values[2]))); + printf("\tMachine clears:\t%.1f%%\n", *((double *)(&values[3]))); + } + if ((*((double *)(&values[2])) + *((double *)(&values[3]))) > 5.0) { + test_warn(__FILE__, __LINE__, + "Bad speculation should be insignificant for this workload", 1); + } + + /* next, check that the fetch latency and bandwidth are insignificant */ + if (!quiet) { + printf("\tFetch latency:\t%.1f%%\n", *((double *)(&values[4]))); + printf("\tFetch bandwidth:\t%.1f%%\n", *((double *)(&values[5]))); + } + if ((*((double *)(&values[4])) + *((double *)(&values[5]))) > 10.0) { + test_warn(__FILE__, __LINE__, + "Frontend bound should be insignificant for this workload", 1); + } + + /* finally, check that core bound is greater than memory bound. */ + /* we can expect this because there are no memory loads/stores here */ + if (!quiet) { + printf("\tMemory bound:\t%.1f%%\n", *((double *)(&values[6]))); + printf("\tCore bound:\t%.1f%%\n", *((double *)(&values[7]))); + } + if (*((double *)(&values[6])) > *((double *)(&values[7]))) { + test_warn(__FILE__, __LINE__, + "The workload should be significantly more core bound than memory bound", 1); + } + + return 0; +} \ No newline at end of file diff --git a/src/components/topdown/tests/topdown_basic.c b/src/components/topdown/tests/topdown_basic.c new file mode 100644 index 000000000..723a0fc96 --- /dev/null +++ b/src/components/topdown/tests/topdown_basic.c @@ -0,0 +1,175 @@ +/* + * Basic test that just adds all of the topdown events and make sure they dont + * produce any errors. + */ + +#include +#include +#include +#include + +#include "papi.h" +#include "papi_test.h" + +// fibonacci function to serve as a benchable code section +void __attribute__((optimize("O0"))) fib(int n) +{ + long i, a = 0; + int b = 1; + for (i = 0; i < n; i++) + { + b = b + a; + a = b - a; + } +} + +int main(int argc, char **argv) +{ + int i, quiet, retval; + int EventSet = PAPI_NULL; + const PAPI_component_info_t *cmpinfo = NULL; + int numcmp, cid, topdown_cid = -1; + int code, maximum_code = 0; + char event_name[PAPI_MAX_STR_LEN]; + PAPI_event_info_t event_info; + int num_events = 0; + long long *values; + + /* Set TESTS_QUIET variable */ + quiet = tests_quiet(argc, argv); + + /* PAPI Initialization */ + retval = PAPI_library_init(PAPI_VER_CURRENT); + if (retval != PAPI_VER_CURRENT) + { + test_fail(__FILE__, __LINE__, "PAPI_library_init failed\n", retval); + } + + if (!quiet) + { + printf("Testing topdown component with PAPI %d.%d.%d\n", + PAPI_VERSION_MAJOR(PAPI_VERSION), + PAPI_VERSION_MINOR(PAPI_VERSION), + PAPI_VERSION_REVISION(PAPI_VERSION)); + } + + /*******************************/ + /* Find the topdown component */ + /*******************************/ + numcmp = PAPI_num_components(); + for (cid = 0; cid < numcmp; cid++) + { + if ((cmpinfo = PAPI_get_component_info(cid)) == NULL) + { + test_fail(__FILE__, __LINE__, "PAPI_get_component_info failed\n", 0); + } + if (!quiet) + { + printf("\tComponent %d - %d events - %s\n", cid, + cmpinfo->num_native_events, + cmpinfo->name); + } + if (strstr(cmpinfo->name, "topdown")) + { + topdown_cid = cid; + + /* check that the component is enabled */ + if (cmpinfo->disabled) + { + printf("Topdown component is disabled: %s\n", cmpinfo->disabled_reason); + test_fail(__FILE__, __LINE__, "Component is not enabled\n", 0); + } + } + } + + if (topdown_cid < 0) + { + test_skip(__FILE__, __LINE__, "Topdown component not found\n", 0); + } + + if (!quiet) + { + printf("\nFound Topdown Component at id %d\n", topdown_cid); + printf("\nListing all events in this component:\n"); + } + + /* Create EventSet */ + retval = PAPI_create_eventset(&EventSet); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "PAPI_create_eventset()", retval); + } + + /*****************************************************/ + /* Add all the events to an eventset as a basic test */ + /*****************************************************/ + code = PAPI_NATIVE_MASK; + retval = PAPI_enum_cmp_event(&code, PAPI_ENUM_FIRST, topdown_cid); + + while (retval == PAPI_OK) + { + if (PAPI_event_code_to_name(code, event_name) != PAPI_OK) + { + printf("Error translating %#x\n", code); + test_fail(__FILE__, __LINE__, + "PAPI_event_code_to_name", retval); + } + + if (PAPI_get_event_info(code, &event_info) != PAPI_OK) + { + printf("Error getting info for event %#x\n", code); + test_fail(__FILE__, __LINE__, + "PAPI_get_event_info()", retval); + } + + retval = PAPI_add_event(EventSet, code); + if (retval != PAPI_OK) + { + test_fail(__FILE__, __LINE__, + "PAPI_add_event()", retval); + } + + if (!quiet) + { + printf("\tEvent %#x: %s -- %s\n", + code, event_name, event_info.long_descr); + } + + num_events += 1; + maximum_code = code; + retval = PAPI_enum_cmp_event(&code, PAPI_ENUM_EVENTS, topdown_cid); + } + if (!quiet) + printf("\n"); + + /* ensure there is space for the output values */ + values = calloc(num_events, sizeof(long long)); + if (values == NULL) + { + test_fail(__FILE__, __LINE__, + "Insufficient memory", retval); + } + + /* now stat some code to make sure the events work */ + PAPI_start(EventSet); + fib(6000000); + PAPI_stop(EventSet, values); + + if (!quiet) + printf("Values:\n"); + for (i = 0; i < num_events; i++) + { + /* ensure the metric percentages are between 0 and 100 */ + if (*((double *)(&values[i])) < 0 || *((double *)(&values[i])) > 100.0) + { + test_fail(__FILE__, __LINE__, + "Topdown metric was not a valid percentage", retval); + } + + if (!quiet) + printf("\t%d:\t%.1lf%%\n", i, *((double *)(&values[i]))); + } + + return 0; +} \ No newline at end of file diff --git a/src/components/topdown/topdown.c b/src/components/topdown/topdown.c new file mode 100644 index 000000000..425321ed9 --- /dev/null +++ b/src/components/topdown/topdown.c @@ -0,0 +1,726 @@ + +#include +#include +#include +#include /* msr? */ +#include +#include +#include +#include + +/* Headers required by PAPI */ +#include "papi.h" +#include "papi_internal.h" +#include "papi_vector.h" +#include "papi_memory.h" /* defines papi_malloc(), etc. */ + +#include "topdown.h" + +// The following macro follows if a string function has an error. It should +// never happen; but it is necessary to prevent compiler warnings. We print +// something just in case there is programmer error in invoking the function. +#define HANDLE_STRING_ERROR \ + { \ + fprintf(stderr, "%s:%i unexpected string function error.\n", __FILE__, __LINE__); \ + exit(-1); \ + } + +papi_vector_t _topdown_vector; + +static _topdown_native_event_entry_t *topdown_native_events = NULL; +static int num_events = 0; + +/********************************/ +/* Internal component functions */ +/********************************/ + +/* In case headers aren't new enough to have __NR_perf_event_open */ +#ifndef __NR_perf_event_open +#define __NR_perf_event_open 298 /* __x86_64__ is the only arch we support */ +#endif + +__attribute__((weak)) int perf_event_open(struct perf_event_attr *attr, pid_t pid, + int cpu, int group_fd, unsigned long flags) +{ + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); +} + +/* read SLOTS */ +static inline unsigned long long read_slots(void) +{ + return _rdpmc(TOPDOWN_PERF_FIXED | TOPDOWN_FIXED_COUNTER_SLOTS); +} + +/* read PERF_METRICS */ +static inline unsigned long long read_metrics(void) +{ + return _rdpmc(TOPDOWN_PERF_METRICS | TOPDOWN_METRIC_COUNTER_TOPDOWN_L1_L2); // TODO: Make this platform aware +} + +/* extract the metric defined by event i from the value */ +float extract_metric(int i, unsigned long long val) +{ + return (double)(((val) >> (i * 8)) & 0xff) / 0xff; +} + +/**************************************/ +/* Hybrid processor support functions */ +/**************************************/ + +typedef struct { + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; +} cpuid_reg_t; + +void cpuid2( cpuid_reg_t *reg, unsigned int func, unsigned int subfunc ) +{ + __asm__ ("cpuid;" + : "=a" (reg->eax), "=b" (reg->ebx), "=c" (reg->ecx), "=d" (reg->edx) + : "a" (func), "c" (subfunc)); +} + +#define INTEL_CORE_TYPE_EFFICIENT 0x20 /* also known as 'ATOM' */ +#define INTEL_CORE_TYPE_PERFORMANCE 0x40 /* also known as 'CORE' */ + +/* ensure the core this process is running on is of the correct type */ +int active_core_type_is(int core_type) +{ + cpuid_reg_t reg; + + /* check that CPUID leaf 0x1A is supported */ + cpuid2(®, 0, 0); + if (reg.eax < 0x1a) return PAPI_ENOSUPP; + cpuid2(®, 0x1a, 0); + if (reg.eax == 0) return PAPI_ENOSUPP; + + return ((reg.eax >> 24) & 0xff) == core_type; +} + +/***********************************************/ +/* Required PAPI component interface functions */ +/***********************************************/ + +static int +_topdown_init_component(int cidx) +{ + unsigned long long val; + int err, i; + int retval = PAPI_OK; + int supports_l2; + + char *strCpy; + const PAPI_hw_info_t *hw_info; + + /* Check for processor support */ + hw_info = &(_papi_hwi_system_info.hw_info); + switch (hw_info->vendor) + { + case PAPI_VENDOR_INTEL: + case PAPI_VENDOR_AMD: + break; + default: + err = snprintf(_topdown_vector.cmp_info.disabled_reason, + PAPI_MAX_STR_LEN, "Not a supported processor"); + _topdown_vector.cmp_info.disabled_reason[PAPI_MAX_STR_LEN - 1] = 0; + if (err > PAPI_MAX_STR_LEN) + HANDLE_STRING_ERROR; + retval = PAPI_ENOSUPP; + goto fn_fail; + } + + /* Ideally, we should check the IA32_PERF_CAPABILITIES MSR for */ + /* PERF_METRICS support. However, since doing this requires a */ + /* sysadmin to go through a lot of hassle, it may be better to + /* just hardcode supported platforms instead */ + + if (hw_info->vendor == PAPI_VENDOR_INTEL) + { + if (hw_info->cpuid_family != 6) + { + /* Not a family 6 machine */ + strCpy = strncpy(_topdown_vector.cmp_info.disabled_reason, + "CPU family not supported", PAPI_MAX_STR_LEN); + _topdown_vector.cmp_info.disabled_reason[PAPI_MAX_STR_LEN - 1] = 0; + if (strCpy == NULL) + HANDLE_STRING_ERROR; + retval = PAPI_ENOIMPL; + goto fn_fail; + } + + /* Detect topdown support */ + switch (hw_info->cpuid_model) + { + /* The model id can be found in Table 2-1 of the */ + /* IA-32 Architectures Software Developer’s Manual */ + + /* homogeneous machines */ + // TODO + + /* hybrid machines */ + case 0xb7: /* RaptorLake-S/HX */ + case 0xba: /* RaptorLake */ + case 0xbf: /* RaptorLake */ + supports_l2 = 1; + + /* make sure that for RaptorLake we are running on a P core */ + if (!active_core_type_is(INTEL_CORE_TYPE_PERFORMANCE)) { + strCpy = strncpy(_topdown_vector.cmp_info.disabled_reason, + "Topdown metrics are not supported on RaptorLake efficiency cores. Ensure this program is run on a performance core.", PAPI_MAX_STR_LEN); + _topdown_vector.cmp_info.disabled_reason[PAPI_MAX_STR_LEN - 1] = 0; + if (strCpy == NULL) + HANDLE_STRING_ERROR; + retval = PAPI_ECMP; + goto fn_fail; + } + break; + + default: /* not a supported model */ + strCpy = strncpy(_topdown_vector.cmp_info.disabled_reason, + "CPU model not supported", PAPI_MAX_STR_LEN); + _topdown_vector.cmp_info.disabled_reason[PAPI_MAX_STR_LEN - 1] = 0; + if (strCpy == NULL) + HANDLE_STRING_ERROR; + retval = PAPI_ENOIMPL; + goto fn_fail; + } + } + + /* allocate the events table */ + topdown_native_events = (_topdown_native_event_entry_t *) + papi_calloc(TOPDOWN_MAX_COUNTERS, sizeof(_topdown_native_event_entry_t)); + if (topdown_native_events == NULL) + { + err = snprintf(_topdown_vector.cmp_info.disabled_reason, PAPI_MAX_STR_LEN, + "%s:%i topdown_native_events papi_calloc for %lu bytes failed.", + __FILE__, __LINE__, TOPDOWN_MAX_COUNTERS * sizeof(_topdown_native_event_entry_t)); + _topdown_vector.cmp_info.disabled_reason[PAPI_MAX_STR_LEN - 1] = 0; + if (err > PAPI_MAX_STR_LEN) + HANDLE_STRING_ERROR; + retval = PAPI_ENOMEM; + goto fn_fail; + } + + /* fill out the events table */ + i = 0; + + /* level 1 events */ + strcpy(topdown_native_events[i].name, "TOPDOWN_RETIRING_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were retiring instructions"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_RETIRING; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_BAD_SPEC_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were stalled due to bad speculation"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_BAD_SPEC; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_FE_BOUND_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were waiting on the frontend"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_FE_BOUND; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_BE_BOUND_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were waiting on the backend"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_BE_BOUND; + topdown_native_events[i].selector = i + 1; + + if (supports_l2) { + /* level 2 events */ + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_HEAVY_OPS_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were retiring heavy operations"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_HEAVY_OPS; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_BR_MISPREDICT_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were wasted due to branch misses"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_BR_MISPREDICT; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_FETCH_LAT_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were stalled due to no uops being issued"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_FETCH_LAT; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_MEM_BOUND_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were stalled due to demand load/store instructions"); + topdown_native_events[i].metric_idx = TOPDOWN_METRIC_IDX_MEM_BOUND; + topdown_native_events[i].selector = i + 1; + + /* derived level 2 events */ + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_LIGHT_OPS_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were retiring light operations"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = -1; + topdown_native_events[i].derived_parent_idx = TOPDOWN_METRIC_IDX_RETIRING; + topdown_native_events[i].derived_sibling_idx = TOPDOWN_METRIC_IDX_HEAVY_OPS; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_MACHINE_CLEARS_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were wasted due to pipeline resets"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = -1; + topdown_native_events[i].derived_parent_idx = TOPDOWN_METRIC_IDX_BAD_SPEC; + topdown_native_events[i].derived_sibling_idx = TOPDOWN_METRIC_IDX_BR_MISPREDICT; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_FETCH_BAND_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were wasted due to less uops being issued than there are slots"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = -1; + topdown_native_events[i].derived_parent_idx = TOPDOWN_METRIC_IDX_FE_BOUND; + topdown_native_events[i].derived_sibling_idx = TOPDOWN_METRIC_IDX_FETCH_LAT; + topdown_native_events[i].selector = i + 1; + + i++; + strcpy(topdown_native_events[i].name, "TOPDOWN_CORE_BOUND_PERC"); + strcpy(topdown_native_events[i].description, "The percentage of pipeline slots that were stalled due to insufficient non-memory core resources"); + strcpy(topdown_native_events[i].units, "%"); + topdown_native_events[i].return_type = PAPI_DATATYPE_FP64; + topdown_native_events[i].metric_idx = -1; + topdown_native_events[i].derived_parent_idx = TOPDOWN_METRIC_IDX_BE_BOUND; + topdown_native_events[i].derived_sibling_idx = TOPDOWN_METRIC_IDX_MEM_BOUND; + topdown_native_events[i].selector = i + 1; + } + + num_events = i + 1; + + /* Export the total number of events available */ + _topdown_vector.cmp_info.num_native_events = num_events; + _topdown_vector.cmp_info.num_cntrs = num_events; + _topdown_vector.cmp_info.num_mpx_cntrs = num_events; + + /* Export the component id */ + _topdown_vector.cmp_info.CmpIdx = cidx; + +fn_exit: + _papi_hwd[cidx]->cmp_info.disabled = retval; + return retval; +fn_fail: + goto fn_exit; +} + +static int +_topdown_init_thread(hwd_context_t *ctx) +{ + (void)ctx; + return PAPI_OK; +} + +static int +_topdown_init_control_state(hwd_control_state_t *ctl) +{ + _topdown_control_state_t *control = (_topdown_control_state_t *)ctl; + + int retval = PAPI_OK; + struct perf_event_attr slots, metrics; + int slots_fd = -1; + int metrics_fd = -1; + void *slots_p, *metrics_p; + + /* set up slots */ + memset(&slots, 0, sizeof(slots)); + slots.type = PERF_TYPE_RAW; + slots.size = sizeof(struct perf_event_attr); + slots.config = 0x0400ull; + slots.exclude_kernel = 1; + + /* open slots */ + slots_fd = perf_event_open(&slots, 0, -1, -1, 0); + if (slots_fd < 0) + { + retval = PAPI_ENOMEM; + goto fn_fail; + } + + /* memory mapping the fd to permit _rdpmc calls from userspace */ + slots_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, slots_fd, 0); + if (!slots_p) + { + retval = PAPI_ENOMEM; + goto fn_fail; + } + + /* set up metrics */ + memset(&metrics, 0, sizeof(metrics)); + metrics.type = PERF_TYPE_RAW; + metrics.size = sizeof(struct perf_event_attr); + metrics.config = 0x8000; + metrics.exclude_kernel = 1; + + /* open metrics with slots as the group leader */ + metrics_fd = perf_event_open(&metrics, 0, -1, slots_fd, 0); + if (metrics_fd < 0) + { + retval = PAPI_ENOMEM; + goto fn_fail; + } + + /* memory mapping the fd to permit _rdpmc calls from userspace */ + metrics_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, metrics_fd, 0); + if (!metrics_p) + { + retval = PAPI_ENOMEM; + goto fn_fail; + } + + /* we set up with no errors, so fill out the control state */ + control->slots_fd = slots_fd; + control->slots_p; + control->metrics_fd = metrics_fd; + control->metrics_p; + +fn_exit: + return retval; + +fn_fail: + /* we need to close & free whatever we opened and allocated */ + if (slots_p != NULL) + munmap(slots_p, getpagesize()); + if (metrics_p != NULL) + munmap(metrics_p, getpagesize()); + if (slots_fd >= 0) + close(slots_fd); + if (metrics_fd >= 0) + close(metrics_fd); + goto fn_exit; +} + +static int +_topdown_update_control_state(hwd_control_state_t *ctl, + NativeInfo_t *native, + int count, + hwd_context_t *ctx) +{ + int i, index; + (void)ctx; + + _topdown_control_state_t *control = (_topdown_control_state_t *)ctl; + + for (i = 0; i < TOPDOWN_MAX_COUNTERS; i++) + { + control->being_measured[i] = 0; + } + + for (i = 0; i < count; i++) + { + index = native[i].ni_event & PAPI_NATIVE_AND_MASK; + native[i].ni_position = topdown_native_events[index].selector - 1; + control->being_measured[index] = 1; + } + + return PAPI_OK; +} + +static int +_topdown_start(hwd_context_t *ctx, hwd_control_state_t *ctl) +{ + (void) ctx; + _topdown_control_state_t *control = (_topdown_control_state_t *)ctl; + + /* reset the PERF_METRICS counter and slots to maintain precision */ + /* as per the recommendation section 21.3.9.3 of the IA-32 Architectures */ + /* Software Developer’s Manual */ + ioctl(control->slots_fd, PERF_EVENT_IOC_RESET, 0); + ioctl(control->metrics_fd, PERF_EVENT_IOC_RESET, 0); + + /* record the before values */ + control->slots_before = read_slots(); + control->metrics_before = read_metrics(); + + return PAPI_OK; +} + +static int +_topdown_stop(hwd_context_t *ctx, hwd_control_state_t *ctl) +{ + _topdown_context_t *context = (_topdown_context_t *)ctx; + _topdown_control_state_t *control = (_topdown_control_state_t *)ctl; + unsigned long long slots_after, slots_delta, metrics_after; + + int i; + double ma, mb, perc, tmp; + + slots_after = read_slots(); + metrics_after = read_metrics(); + + slots_delta = slots_after - control->slots_before; + + /* extract the values */ + for (i = 0; i < TOPDOWN_MAX_COUNTERS; i++) + { + if (control->being_measured[i]) + { + /* handle case where the metric is not derived */ + if (topdown_native_events[i].metric_idx >= 0) + { + /* get the before and after metric as a fraction between */ + /* 0.0 and 1.0, and scale by slots */ + ma = extract_metric(topdown_native_events[i].metric_idx, + control->metrics_before) * control->slots_before; + mb = extract_metric(topdown_native_events[i].metric_idx, + metrics_after) * slots_after; + + /* calculate the percentage of slots it was measured in */ + perc = (mb - ma) / slots_delta * 100.0; + } + else + { /* handle case where the metric is derived */ + /* get the percentage measured for the parent metric */ + ma = extract_metric(topdown_native_events[i].derived_parent_idx, + control->metrics_before) * control->slots_before; + mb = extract_metric(topdown_native_events[i].derived_parent_idx, + metrics_after) * slots_after; + tmp = (mb - ma) / slots_delta * 100.0; + + /* get the percentage measured for the sibling metric */ + ma = extract_metric(topdown_native_events[i].derived_sibling_idx, + control->metrics_before) * control->slots_before; + mb = extract_metric(topdown_native_events[i].derived_sibling_idx, + metrics_after) * slots_after; + + /* metric perc = parent perc - sibling perc */ + perc = tmp - ((mb - ma) / slots_delta * 100.0); + } + + /* sometimes the percentage will be a very small negative value */ + /* instead of 0 due to floating point error. tidy that up: */ + if (perc < 0.0) { + perc = 0.0; + } + + /* store the raw bits of the double into the counter value */ + control->count[i] = *(long long*)&perc; + } + } + + /* free & close everything in the control state */ + munmap(control->slots_p, getpagesize()); + control->slots_p = NULL; + munmap(control->metrics_p, getpagesize()); + control->metrics_p = NULL; + close(control->slots_fd); + control->slots_fd = -1; + close(control->metrics_fd); + control->metrics_fd = -1; + + return PAPI_OK; +} + +static int +_topdown_read(hwd_context_t *ctx, hwd_control_state_t *ctl, + long long **events, int flags) +{ + (void)flags; + + _topdown_stop(ctx, ctl); + + /* Pass back a pointer to our results */ + *events = ((_topdown_control_state_t *)ctl)->count; + + return PAPI_OK; +} + +static int +_topdown_reset(hwd_context_t *ctx, hwd_control_state_t *ctl) +{ + ( void ) ctx; + ( void ) ctl; + + return PAPI_OK; +} + +static int +_topdown_shutdown_component(void) +{ + /* Free anything we allocated */ + papi_free(topdown_native_events); + + return PAPI_OK; +} + +static int +_topdown_shutdown_thread(hwd_context_t *ctx) +{ + ( void ) ctx; + + return PAPI_OK; +} + +static int +_topdown_ctl(hwd_context_t *ctx, int code, _papi_int_option_t *option) +{ + ( void ) ctx; + ( void ) code; + ( void ) option; + + return PAPI_OK; +} + +static int +_topdown_set_domain(hwd_control_state_t *cntrl, int domain) +{ + (void) cntrl; + (void) domain; + + return PAPI_OK; +} + +static int +_topdown_ntv_enum_events(unsigned int *EventCode, int modifier) +{ + + int index; + + switch (modifier) + { + case PAPI_ENUM_FIRST: + /* return the first event that we support */ + *EventCode = 0; + return PAPI_OK; + + case PAPI_ENUM_EVENTS: + index = *EventCode; + /* Make sure we have at least 1 more event after us */ + if (index < num_events - 1) + { + /* This assumes a non-sparse mapping of the events */ + *EventCode = *EventCode + 1; + return PAPI_OK; + } + else + { + return PAPI_ENOEVNT; + } + break; + + default: + return PAPI_EINVAL; + } + + return PAPI_EINVAL; +} + +static int +_topdown_ntv_code_to_name(unsigned int EventCode, char *name, int len) +{ + int index = EventCode & PAPI_NATIVE_AND_MASK; + + if (index >= 0 && index < num_events) + { + strncpy(name, topdown_native_events[index].name, len); + return PAPI_OK; + } + + return PAPI_ENOEVNT; +} + +static int +_topdown_ntv_code_to_descr(unsigned int EventCode, char *descr, int len) +{ + int index = EventCode; + + if (index >= 0 && index < num_events) + { + strncpy(descr, topdown_native_events[index].description, len); + return PAPI_OK; + } + return PAPI_ENOEVNT; +} + +static int +_topdown_ntv_code_to_info(unsigned int EventCode, PAPI_event_info_t *info) +{ + + int index = EventCode; + + if ((index < 0) || (index >= num_events)) + return PAPI_ENOEVNT; + + strncpy(info->symbol, topdown_native_events[index].name, + sizeof(info->symbol) - 1); + info->symbol[sizeof(info->symbol) - 1] = '\0'; + + strncpy(info->long_descr, topdown_native_events[index].description, + sizeof(info->long_descr) - 1); + info->long_descr[sizeof(info->long_descr) - 1] = '\0'; + + strncpy(info->units, topdown_native_events[index].units, + sizeof(info->units) - 1); + info->units[sizeof(info->units) - 1] = '\0'; + + info->data_type = topdown_native_events[index].return_type; + + return PAPI_OK; +} + +/** Vector that points to entry points for our component */ +papi_vector_t _topdown_vector = { + .cmp_info = { + .name = "topdown", + .short_name = "topdown", + .description = "A component for using Intel's topdown metrics", + .version = "0.1", + .support_version = "n/a", + .kernel_version = "n/a", + .default_domain = PAPI_DOM_USER, + .available_domains = PAPI_DOM_USER, + .default_granularity = PAPI_GRN_THR, + .available_granularities = PAPI_GRN_THR, + .hardware_intr_sig = PAPI_INT_SIGNAL, + }, + + /* Sizes of framework-opaque component-private structures */ + .size = { + .context = sizeof(_topdown_context_t), + .control_state = sizeof(_topdown_control_state_t), + }, + + /* Used for general PAPI interactions */ + .start = _topdown_start, + .stop = _topdown_stop, + .read = _topdown_read, + .reset = _topdown_reset, + .init_component = _topdown_init_component, + .init_thread = _topdown_init_thread, + .init_control_state = _topdown_init_control_state, + .update_control_state = _topdown_update_control_state, + .ctl = _topdown_ctl, + .shutdown_thread = _topdown_shutdown_thread, + .shutdown_component = _topdown_shutdown_component, + .set_domain = _topdown_set_domain, + + /* Name Mapping Functions */ + .ntv_enum_events = _topdown_ntv_enum_events, + .ntv_code_to_name = _topdown_ntv_code_to_name, + .ntv_code_to_descr = _topdown_ntv_code_to_descr, + .ntv_code_to_info = _topdown_ntv_code_to_info, +}; \ No newline at end of file diff --git a/src/components/topdown/topdown.h b/src/components/topdown/topdown.h new file mode 100644 index 000000000..02268af38 --- /dev/null +++ b/src/components/topdown/topdown.h @@ -0,0 +1,57 @@ + +/* these MSR access defines are constant based on the assumptoin that */ +/* new architectures will not change them */ +#define TOPDOWN_PERF_FIXED (1 << 30) /* return fixed counters */ +#define TOPDOWN_PERF_METRICS (1 << 29) /* return metric counters */ + +#define TOPDOWN_FIXED_COUNTER_SLOTS 3 +#define TOPDOWN_METRIC_COUNTER_TOPDOWN_L1_L2 0 + +/* L1 Topdown indices in the PERF_METRICS counter */ +#define TOPDOWN_METRIC_IDX_RETIRING 0 +#define TOPDOWN_METRIC_IDX_BAD_SPEC 1 +#define TOPDOWN_METRIC_IDX_FE_BOUND 2 +#define TOPDOWN_METRIC_IDX_BE_BOUND 3 + +/* L2 Topdown indices in the PERF_METRICS counter */ +/* The L2 events not here are derived from the others */ +#define TOPDOWN_METRIC_IDX_HEAVY_OPS 4 +#define TOPDOWN_METRIC_IDX_BR_MISPREDICT 5 +#define TOPDOWN_METRIC_IDX_FETCH_LAT 6 +#define TOPDOWN_METRIC_IDX_MEM_BOUND 7 + +/** Holds per event information */ +typedef struct topdown_native_event_entry +{ + int selector; /* signifies which counter slot is being used. indexed from 1 */ + + char name[PAPI_MAX_STR_LEN]; + char description[PAPI_MAX_STR_LEN]; + char units[PAPI_MIN_STR_LEN]; /* the unit to use for this event */ + int return_type; /* the PAPI return type to use for this event */ + + int metric_idx; /* index in PERF_METRICS. if -1, it's derived */ + int derived_parent_idx; /* if derived, which parent do we subtract from */ + int derived_sibling_idx; /* if derived, which metric do we subtract */ + +} _topdown_native_event_entry_t; + +/** Holds per event-set information */ +typedef struct topdown_control_state +{ +#define TOPDOWN_MAX_COUNTERS 16 + int being_measured[TOPDOWN_MAX_COUNTERS]; + long long count[TOPDOWN_MAX_COUNTERS]; + + int slots_fd; /* file descriptor for the slots fixed counter */ + void *slots_p; /* we need this in ctl so it can be freed */ + unsigned long long slots_before; + int metrics_fd; /* file descriptor for the PERF_METRICS counter */ + void *metrics_p; /* we need this in ctl so it can be freed */ + unsigned long long metrics_before; +} _topdown_control_state_t; + +/** Holds per thread information */ +typedef struct topdown_context +{ +} _topdown_context_t; \ No newline at end of file