| /** |
| ******************************************************************************** |
| * 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); |
| } |
| |
| |
| ''' |
| |
| } |