blob: c3f37fa022ceb43a3b8d43601eb479f25950ba22 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2020-2021 Robert Bosch GmbH.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
* *******************************************************************************
*/
package org.eclipse.app4mc.slg.commons.m2t.generators
class InstrumentationGenerator {
// Suppress default constructor
private new() {
throw new IllegalStateException("Utility class");
}
static def String getHeaderFileContent()
'''
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>
#include <inttypes.h>
void instrument_start_measurement(int counter);
void instrument_stop_measurement(int counter);
int instrument_start(pid_t pid, uint64_t event_list[], int total_events, int counter);
void instrument_stop(int fd, int counter);
void instrument_read(int fd);
'''
static def String getSourceFileContent()
'''
#include "instrument.h"
#define LOGFILE "codesnippet.log"
//average
#define ROWLENGTH 80
#define FUNCTIONREPEATS 200
#define MAXCOUNTERS 7
#define MAXDEVIATION 0.01
/* -------------------------------------------------------------------------------------------------------------------------------------- */
/* CONFIGURE EVENTS HERE */
/* -------------------------------------------------------------------------------------------------------------------------------------- */
#define NUM_EVENTS 6
// currently L1 Instruction Cache Access missing
// Events: {memory access, L1 Data Cache Access, LD_RETIRED, ST_RETIRED, L2 Data Cache Access, Linefill because of prefetch}
uint64_t eventlist[NUM_EVENTS] = {0x0006, 0x0007, 0x0016, 0x00E7, 0x00E8, 0x0003};
int fd = 0;
void instrument_start_measurement(int counter){
fd = instrument_start(0, eventlist, NUM_EVENTS, counter);
}
void instrument_stop_measurement(int counter){
instrument_stop(fd, counter);
}
static long perf_event_define(pid_t proccess_id, int group_fd, uint64_t pinned, uint32_t event_type, uint64_t event)
{
struct perf_event_attr hw_event;
pid_t pid = proccess_id; // measure the current process/thread
int cpu = -1; // measure on any cpu
unsigned long flags = 0;
int fd_current;
memset(&hw_event, 0, sizeof(struct perf_event_attr));
hw_event.type = event_type;
hw_event.size = sizeof(struct perf_event_attr);
hw_event.config = event;
hw_event.disabled = 1; // off by default. specifies whether the counter starts out disabled or enabled.
hw_event.exclude_kernel = 1; // excluding events that happen in the kernel-space
hw_event.exclude_hv = 1; // excluding events that happen in the hypervisor
hw_event.pinned = pinned; // specifies the counter to be on the CPU if at all possible. applies only to hardware counters and only to group leaders.
hw_event.exclude_user = 0; // excludes events that happen in user space
hw_event.exclude_callchain_kernel = 1; // Do not include kernel callchains.
hw_event.exclude_callchain_user = 0; // Do not include user callchains.
// hw_event.inherit = 1; // Inherit does not work for some combinations of read_format values, such as PERF_FORMAT_GROUP.
//hw_event.exclusive = 1; // not working for counters other than cycle counter
//hw_event.exclude_idle = 1; // doesn't work
hw_event.read_format = PERF_FORMAT_GROUP; // Allows all counter values in an event group to be read with one read
fd_current = syscall(__NR_perf_event_open, &hw_event, pid, cpu, group_fd, flags);
if (fd_current == -1) {
printf("Error opening leader %llx\n", hw_event.config);
exit(EXIT_FAILURE);
}
return fd_current;
}
int instrument_start(pid_t pid, uint64_t event_list[], int total_events, int counter){
int i;
if(counter==0){
FILE * file_pointer;
file_pointer = fopen(LOGFILE, "w+");
fprintf(file_pointer, "CPU cycles\t\t");
for (i = 0; i < total_events; i++){
switch(event_list[i]){
case 0x0001: fprintf(file_pointer, "L1 i refill\t\t"); break;
case 0x0002: fprintf(file_pointer, "L1 i tlb refill\t\t"); break;
case 0x0003: fprintf(file_pointer, "L1 d refill\t\t"); break;
case 0x0004: fprintf(file_pointer, "L1 d access\t\t"); break;
case 0x0005: fprintf(file_pointer, "L1 d tlb refill\t\t"); break;
case 0x0006: fprintf(file_pointer, "LD_RETIRED\t\t"); break;
case 0x0007: fprintf(file_pointer, "ST_RETIRED\t\t"); break;
case 0x0013: fprintf(file_pointer, "mem access\t\t"); break;
case 0x0014: fprintf(file_pointer, "L1 i access\t\t"); break;
case 0x0016: fprintf(file_pointer, "L2 d access\t\t"); break;
case 0x0017: fprintf(file_pointer, "L2 d refill\t\t"); break;
case 0x00C2: fprintf(file_pointer, "prefetch\t\t"); break;
case 0x00E7: fprintf(file_pointer, "_______E8\t\t"); break;
case 0x00E8: fprintf(file_pointer, "_______E7\t\t"); break;
default: fprintf(file_pointer, "%" PRId64 "\t\t", event_list[i]); break;
}
}
fprintf(file_pointer, "\n");
fclose(file_pointer);
}
fd = perf_event_define(pid, -1, 1, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
for (i = 0; i < total_events; i++)
perf_event_define(pid, fd, 0, PERF_TYPE_RAW, event_list[i]);
/* Example usage:
perf_event_define(fd, 0, PERF_TYPE_RAW, 0x0008);
perf_event_define(fd, 0, PERF_TYPE_RAW, 0x0012);
perf_event_define(fd, 0, PERF_TYPE_RAW, 0x0004);
perf_event_define(fd, 0, PERF_TYPE_RAW, 0x0003);
perf_event_define(fd, 0, PERF_TYPE_RAW, 0x0016);
perf_event_define(fd, 0, PERF_TYPE_RAW, 0x0017);
int fd = perf_event_define(-1, 1, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
perf_event_define(fd, 0, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
perf_event_define(fd, 0, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
perf_event_define(fd , 0, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES);
perf_event_define(fd , 0, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
perf_event_define(fd , 0, PERF_TYPE_HW_CACHE, (PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16));
perf_event_define(fd , 0, PERF_TYPE_HW_CACHE, (PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16));
*/
ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
return fd;
}
struct read_format {
uint64_t nr; // The number of events
struct {
uint64_t value; // The value of the event
//uint64_t id; // PERF_FORMAT_ID
} values[];
};
void instrument_stop(int fd, int counter){
ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
char buf[4096];
read(fd, buf, sizeof(buf));
int i;
struct read_format* rf = (struct read_format*) buf;
FILE * file_pointer;
file_pointer = fopen(LOGFILE, "a");
for (i = 0; i < rf->nr; i++){
if(i == (rf->nr - 1)){
fprintf(file_pointer, "%" PRId64, rf->values[i].value);
break;
}
fprintf(file_pointer, "%" PRId64 "\t\t\t", rf->values[i].value);
//fprintf(file_pointer, "id: %" PRId64 "\t", rf->values[i].id);
}
fprintf(file_pointer, "\n");
fclose(file_pointer);
close(fd);
if(counter == (FUNCTIONREPEATS-1)){
file_pointer = fopen(LOGFILE, "r");
char line[ROWLENGTH];
char * del = "\t\t\t";
char *ptr;
uint64_t help[MAXCOUNTERS] = {0};
uint64_t average[MAXCOUNTERS] = {0};
i = 0;
if(file_pointer != NULL){
fgets(line, sizeof(line), file_pointer); // first row should be ignored
fgets(line, sizeof(line), file_pointer); // second row does not make any sense
while(fgets(line, sizeof(line), file_pointer) != NULL){
ptr = strtok(line, del);
while(ptr != NULL){
help[i] = strtoul(ptr, NULL, 0);
i++;
//ptr = (char)ptr;
ptr = strtok(NULL, del);
}
for(i = 0; i < rf->nr; i++){
average[i] = average[i] + help[i];
}
i = 0;
}
for(i = 0; i < rf->nr; i++){
if(average[i] != 0){
average[i] /= FUNCTIONREPEATS;
}
}
}
fclose(file_pointer);
file_pointer = fopen(LOGFILE, "r");
i = 0;
int deviation[MAXCOUNTERS] = {0};
if(file_pointer != NULL){
fgets(line, sizeof(line), file_pointer); // first row should be ignored
fgets(line, sizeof(line), file_pointer); // second row does not make any sense
while(fgets(line, sizeof(line), file_pointer) != NULL){
ptr = strtok(line, del);
while(ptr != NULL){
help[i] = strtoul(ptr, NULL, 0);
i++;
//ptr = (char)ptr;
ptr = strtok(NULL, del);
}
for(i = 0; i < rf->nr; i++){
if(((average[i]*(1+MAXDEVIATION)) < help[i]) || ((average[i]*(1-MAXDEVIATION)) > help[i])){
deviation[i]++;
}
}
i = 0;
}
}
fclose(file_pointer);
file_pointer = fopen(LOGFILE, "a");
fprintf(file_pointer, "\nAVERAGES:\n");
for (i = 0; i < rf->nr; i++){
if(i == (rf->nr - 1)){
fprintf(file_pointer, "%" PRId64, average[i]);
break;
}
fprintf(file_pointer, "%" PRId64 "\t\t\t", average[i]);
}
fprintf(file_pointer, "\nDEVIATIONS:\n");
for (i = 0; i < rf->nr; i++){
if(i == (rf->nr - 1)){
fprintf(file_pointer, "%d", deviation[i]);
break;
}
fprintf(file_pointer, "%d\t\t\t\t", deviation[i]);
}
fclose(file_pointer);
}
}
void instrument_read(int fd){
char buf[4096];
read(fd, buf, sizeof(buf));
int i;
struct read_format* rf = (struct read_format*) buf;
FILE * file_pointer;
file_pointer = fopen(LOGFILE, "a");
for (i = 0; i < rf->nr; i++)
fprintf(file_pointer, "%" PRId64 "\t", rf->values[i].value);
//fprintf(file_pointer, "ID: %" PRId64 "\t", rf->values[i].id);
fprintf(file_pointer, "\n");
fclose(file_pointer);
ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP);
}
'''
}