https://hal.archives-ouvertes.fr/hal-03453537
Raw File
counters_individual.c
/*******************************************************
 Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>

    This file is part of Mojitos.

    Mojitos is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Mojitos is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.

 *******************************************************/

#include <linux/perf_event.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdint.h>
#include <asm/unistd.h>
#include <stdint.h>

struct _counter_t {
  int nbcores;
  int nbperf;
  int **counters;
  uint64_t *counters_values;
  uint64_t *tmp_counters_values;

  int* perf_indexes;
  
};
typedef struct _counter_t* counter_t;

#include "counters_option.h"

void show_all_counters() {
  for(int i=0; i<nb_counter_option;i++)
    printf("%s\n", perf_static_info[i].name);
}

void perf_type_key(__u32 **perf_type, __u64 **perf_key, int *indexes, int nb){
  *perf_type = malloc(nb*sizeof(__u32));
  *perf_key  = malloc(nb*sizeof(__u64));
  for(int i=0; i<nb; i++) {
    (*perf_key)[i]  = perf_static_info[indexes[i]].perf_key;
    (*perf_type)[i] = perf_static_info[indexes[i]].perf_type;
  }
}
void perf_event_list(char *perf_string, int *nb_perf, int **perf_indexes) {
  char *token;
  *nb_perf=0;
  *perf_indexes=NULL;
  while((token=strtok(perf_string, ",")) != NULL) {
    perf_string = NULL;
    int i;
    for(i=0; i<nb_counter_option; i++) {
      if(strcmp(perf_static_info[i].name, token) == 0) {
	(*nb_perf)++;
	(*perf_indexes) = realloc(*perf_indexes, sizeof(int)*(*nb_perf));
	(*perf_indexes)[*nb_perf-1]=i;
	break;
      }
    }
    if(i == nb_counter_option) {
      fprintf(stderr, "Unknown performance counter: %s\n", token);
      exit(EXIT_FAILURE);
    }
  }
}

static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
			    int cpu, int group_fd, unsigned long flags) {
  long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
  if (res == -1) {
    perror("perf_event_open");
    fprintf(stderr, "Error opening leader %llx\n", hw_event->config);
    exit(EXIT_FAILURE);
  }
  return res;
}

counter_t _init_counters(const int nb_perf, const __u32 *types, const __u64 *names) {
  struct perf_event_attr pe;
  unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN);
  memset(&pe, 0, sizeof(struct perf_event_attr));
  pe.size = sizeof(struct perf_event_attr);
  pe.disabled = 1;

  counter_t counters = malloc(sizeof(struct _counter_t));
  counters->nbperf = nb_perf;
  counters->nbcores=nbcores;
  counters->counters=malloc(nb_perf*sizeof(int*));
  for (int i=0; i<nb_perf; i++) {
    pe.type = types[i];
    pe.config = names[i];
    counters->counters[i] = malloc(nbcores*sizeof(int));

    for (int core=0; core<nbcores; core++) {
      counters->counters[i][core] = perf_event_open(&pe, -1, core, -1, PERF_FLAG_FD_CLOEXEC);
    }
  }
  return counters;
}

void clean_counters(void *ptr) {
  counter_t counters = (counter_t) ptr;
  for(int counter=0; counter<counters->nbperf; counter++) {
    for(int core=0; core<counters->nbcores; core++)
      close(counters->counters[counter][core]);
    free(counters->counters[counter]);
  }
  free(counters->counters);
  free(counters->counters_values);
  free(counters->tmp_counters_values);
  free(counters->perf_indexes);

  free(counters);
}

void start_counters(counter_t counters) {
  for(int counter=0; counter<counters->nbperf; counter++)
    for(int core=0; core<counters->nbcores; core++)
      ioctl(counters->counters[counter][core], PERF_EVENT_IOC_ENABLE, 0);
}

void reset_counters(counter_t counters) {
  for(int counter=0; counter<counters->nbperf; counter++)
    for(int core=0; core<counters->nbcores; core++)
      ioctl(counters->counters[counter][core], PERF_EVENT_IOC_RESET, 0);
}

void _get_counters(counter_t counters, uint64_t *values) {
  for(int i=0; i<counters->nbperf; i++) {
    uint64_t accu=0;
    uint64_t count=0;
    for (int core=0; core<counters->nbcores; core++) {
      if (-1 == read(counters->counters[i][core], &count, sizeof(uint64_t))) {
	fprintf(stderr, "Cannot read result");
	exit(EXIT_FAILURE);
      }
      accu += count;
    }
    values[i] = accu;
  }
}






unsigned int init_counters(char* args, void **state) {
  int nb_perf;
  int* perf_indexes=NULL;

  perf_event_list(args, &nb_perf, &perf_indexes);

  __u32* perf_type;
  __u64* perf_key;
  perf_type_key(&perf_type, &perf_key, perf_indexes, nb_perf);
  counter_t fd = _init_counters(nb_perf, perf_type, perf_key);
  free(perf_type);
  free(perf_key);

  fd->perf_indexes = perf_indexes;
  fd->counters_values = malloc(nb_perf*sizeof(uint64_t));
  fd->tmp_counters_values = malloc(nb_perf*sizeof(uint64_t));
  start_counters(fd);
  _get_counters(fd, fd->counters_values);
  *state = (void*) fd;

  return nb_perf;
}

unsigned int get_counters(uint64_t* results, void*ptr) {
  counter_t state = (counter_t) ptr;

_get_counters(state, state->tmp_counters_values);
  for(int i=0; i<state->nbperf; i++)
    results[i] = state->tmp_counters_values[i] - state->counters_values[i];

  memcpy(state->counters_values, state->tmp_counters_values, state->nbperf * sizeof(uint64_t));
  return state->nbperf;
}

void label_counters(char **labels, void*ptr) {
  counter_t state = (counter_t) ptr;
  for(int i=0; i<state->nbperf;i++)
    labels[i] = perf_static_info[state->perf_indexes[i]].name;

}
back to top