I cannot use "perf_event_open()" with PERF_COUNT_HW_CACHE_LL type

Viewed 171

As suggested by perf_event_open() man page, I am using libpfm4 (man page) to create its perf_event_attr attribute.

pfm_perf_encode_arg_t arg;
struct perf_event_attr pea;
(...)
arg.attr = &pea;
ret = pfm_get_os_event_encoding(
                  "PERF_COUNT_HW_CACHE_LL:READ:ACCESS:u",
                  PFM_PLM3, PFM_OS_PERF_EVENT_EXT, &arg);

Printing the data in arg and pea, I get this:

  arg.fstr : perf::PERF_COUNT_HW_CACHE_LL:READ:ACCESS:u=1:k=0:h=0:precise=0:excl=0:mg=0:mh=1
  pea.type : PERF_TYPE_HW_CACHE
pea.config : 0x2

Where pea.config coincides with the formula found in perf_event_open() man page.

config = (perf_hw_cache_id) |
         (perf_hw_cache_op_id << 8) |
         (perf_hw_cache_op_result_id << 16);

Inspecting the bit flags of pea, all seems right too. But when I use pea in perf_event_open() I get a file descriptor -1.

int fd;
fd = perf_event_open(&pea, 0, -1, -1, 0);
if (fd == -1){
    fprintf(stderr, "Error: Couldn't open leader %lXh\n", pea.config);
    exit(EXIT_FAILURE);
}

I am using an AMD Zen2 processor.

Other approaches I have tried

A) If I change pea.type to PERF_TYPE_RAW and use the raw values provided by AMD in pea.config,

L3 Accesses              : L3Event[0xFF0F00000040FF04]
L3 Miss (includes Chg2X) : L3Event[0xFF0F000000400104]

I can open and read the values. However, they are unbelievable small (less than 20 for accesses or misses). I guess I am reading something completely wrong, here.

B) In contrast, if I keep using libpfm4 to measure the gereric CACHE-MISSES or CACHE-REFERENCES, which according to perf_event_open() man page usually map to LLC, I get counts of the order of 10^10. I don't like this solution, because I don't know what am I actually measuring.

The code I am messing up with:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <err.h>
#include <perfmon/pfmlib.h>
#include <perfmon/pfmlib_perf_event.h>
#define USE_LIBPFM4 1

//Print a number @in in its binary form
void printb(char *open, uint64_t in, char *close){
    int i, j, str_size = 64 + 3*3 + 4*3 + 1; //bits + spaces + \0
    char str[str_size];
    memset(str, 0, str_size);
    for(i=j=0; i<64; i++){
        if(1ULL<<i & in) str[j] = '1';
        else str[j] = '_';
        if((i+1)%4 == 0 && i+1 < 64){
            j++;
            if((i+1)%16 == 0){
                str[j] = ' '; j++;
                str[j] = ' '; j++;
                str[j] = ' ';
            }else str[j] = ' ';
        }
        j++;
    }
    printf("%s", open);
    for(i=str_size-1; i>=0; i--) printf("%c",str[i]);
    printf("%s", close);
}

int main(int argc, char **argv) {
    /**
     ** INITIALIZE
     **/
    int ret;
    ret = pfm_initialize();
    if (ret != PFM_SUCCESS)
        errx(1, "cannot initialize library %s", pfm_strerror(ret));

    /**
     ** SETUP perf_event_attr THROUGH libpfm4 OR MANUALLY
     **/
    pfm_perf_encode_arg_t arg;
    struct perf_event_attr pea;
    char full_name[512], *fn = full_name;
    memset(&arg, 0, sizeof(arg));
    memset(&pea, 0, sizeof(pea));
    memset(fn, 0, 512);
    arg.attr = &pea;
    arg.fstr = &fn;
    arg.size = sizeof(pfm_perf_encode_arg_t);
    pea.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
    /**
     ** Some available counter names:
     ** "RETIRED_INSTRUCTIONS"
     ** "CACHE-MISSES" : generic of perf. It "usually"
     **                  refers to LL cache. It counts a lot
     ** "PERF_COUNT_HW_CACHE_LL:READ:MISS:u" : The thing I want to read.
     **                                        It does not work :(
     **/
    if(USE_LIBPFM4){
        ret = pfm_get_os_event_encoding("PERF_COUNT_HW_CACHE_LL:READ:MISS:u",
                                        PFM_PLM3, PFM_OS_PERF_EVENT_EXT, &arg);
        if (ret != PFM_SUCCESS)
            errx(1, "cannot get encoding %s", pfm_strerror(ret));
    }else{
        pea.type = PERF_TYPE_RAW;
        strncpy(*arg.fstr, "RAW_MISS", 8); //made up name
        pea.config = 0xFF0F000000400104; //from AMD's page
        pea.exclude_kernel = 1;
        pea.exclude_hv = 1;
        pea.exclude_guest = 1;
    }

    /**
     ** PEEKING INTO @arg AND @pea
     **/
    char *type[] = {"PERF_TYPE_HARDWARE", "PERF_TYPE_SOFTWARE",
                 "PERF_TYPE_TRACEPOINT", "PERF_TYPE_HW_CACHE",
                 "PERF_TYPE_RAW", "PERF_TYPE_BREAKPOINT"};
    uint64_t *flags = (uint64_t*) ((char*)&pea + 2*sizeof(uint32_t)
                                   + 4*sizeof(uint64_t));
    printf("  arg.fstr : %s\n", *arg.fstr);
    printf("  pea.type : %s\n", type[pea.type]);
    printb("pea.config : ", pea.config, "\n");
    printb(" pea.flags : ", *flags, "\n");
    printf("             " //flag names' mnemonics
           "[           ---reserved---            ]tc   "
           "abkn wcuc mukg hsmp   pwte ifcm ihku epid\n");

    /**
     ** OPEN PERF EVENT
     **/
    int fd; //event file descriptor
    fd = perf_event_open(&pea, 0, -1, -1, 0);
    if (fd == -1){
        fprintf(stderr, "Error: Couldn't open leader %lXh\n", pea.config);
        exit(EXIT_FAILURE);
    }
    
    /**
     ** MEASURE A JUMPY FOR LOOP
     **/
    int *p = (int*) malloc(800000*sizeof(int));
    if(!p){
        fprintf(stderr, "Error: Malloc is angry\n");
        exit(-1);
    }
    ioctl(fd, PERF_EVENT_IOC_RESET, 0); // ready... set...
    ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); // go!
    size_t n, o;
    for(size_t i=0; i < 10000000000; i++){
        n = (i*347697+997)%800000;
        p[n] = n;
    }
    ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); // stop

    /**
     ** READ COUNT OUT
     **/
    struct read_format {
        uint64_t nr;
        struct {
            uint64_t val;
            uint64_t id;
        } rec[64];
    } rf;
    read(fd, &rf, sizeof(rf));
    for(int i=0; i < rf.nr; i++)
        fprintf(stderr, "%2d) ID(%ld): %ld\n", i, rf.rec[i].id, rf.rec[i].val);
    
    return 0;
}

How could I measure the actual PERF_COUNT_HW_CACHE_LL:READ:MISS:u event?

Thanks for your time and help.

0 Answers
Related