From 9c40c9a0e7e43577e77a372c18aa733ee17d81c2 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sun, 4 Feb 2018 16:29:27 +0100 Subject: Add perf counter object --- linux/PerfCounter.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++ linux/PerfCounter.h | 54 +++++++++++++++++++++ 2 files changed, 189 insertions(+) create mode 100644 linux/PerfCounter.c create mode 100644 linux/PerfCounter.h diff --git a/linux/PerfCounter.c b/linux/PerfCounter.c new file mode 100644 index 00000000..0f7a0aad --- /dev/null +++ b/linux/PerfCounter.c @@ -0,0 +1,135 @@ +/* + * This file is based on tiptop. + * by Erven ROHOU + * Copyright (c) 2011, 2012, 2014 Inria + * License: GNU General Public License version 2. + */ + +#include "PerfCounter.h" + +#include +#include +#include + +#include "XAlloc.h" + +/*{ + +#include +#include +#include +#include +#include +#include +// The sys_perf_counter_open syscall and header files changed names +// between Linux 2.6.31 and 2.6.32. Do the mangling here. +#ifdef HAVE_LINUX_PERF_COUNTER_H +#include +#define STRUCT_NAME perf_counter_attr +#define SYSCALL_NUM __NR_perf_counter_open + +#elif HAVE_LINUX_PERF_EVENT_H +#include +#define STRUCT_NAME perf_event_attr +#define SYSCALL_NUM __NR_perf_event_open + +#else +#error Sorry, performance counters not supported on this system. +#endif + +typedef struct PerfCounter_ { + struct STRUCT_NAME events; + pid_t pid; + int fd; + uint64_t prevValue; + uint64_t value; +} PerfCounter; + +#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue) + +}*/ + +int PerfCounter_openFds = 0; +static int PerfCounter_fdLimit = -1; + +static void PerfCounter_initFdLimit() { + char name[100] = { 0 }; /* needs to fit the name /proc/xxxx/limits */ + snprintf(name, sizeof(name) - 1, "/proc/%d/limits", getpid()); + FILE* f = fopen(name, "r"); + if (f) { + char line[100]; + while (fgets(line, 100, f)) { + int n = sscanf(line, "Max open files %d", &PerfCounter_fdLimit); + if (n) { + break; + } + } + fclose(f); + } + + PerfCounter_fdLimit -= 20; /* keep some slack */ + if (PerfCounter_fdLimit == 0) { /* something went wrong */ + PerfCounter_fdLimit = 200; /* reasonable default? */ + } +} + +static long perf_event_open(struct STRUCT_NAME *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { + int ret = syscall(SYSCALL_NUM, hw_event, pid, cpu, group_fd, flags); + #if defined(__x86_64__) || defined(__i386__) + if (ret < 0 && ret > -4096) { + errno = -ret; + ret = -1; + } + #endif + return ret; +} + +PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config) { + if (PerfCounter_fdLimit == -1) { + PerfCounter_initFdLimit(); + } + PerfCounter* this = xCalloc(sizeof(PerfCounter), 1); + this->pid = pid; + this->events.disabled = 0; + this->events.pinned = 1; + this->events.exclude_hv = 1; + this->events.exclude_kernel = 1; + this->events.type = type; + this->events.config = config; + if (PerfCounter_openFds < PerfCounter_fdLimit) { + this->fd = perf_event_open(&this->events, pid, -1, -1, 0); + } else { + this->fd = -1; + } + if (this->fd != -1) { + PerfCounter_openFds++; + } + return this; +} + +void PerfCounter_delete(PerfCounter* this) { + if (!this) { + return; + } + if (this->fd != -1) { + PerfCounter_openFds--; + } + close(this->fd); + free(this); +} + +bool PerfCounter_read(PerfCounter* this) { + if (this->fd == -1) { + return false; + } + uint64_t value; + int r = read(this->fd, &value, sizeof(value)); + if (r != sizeof(value)) { + close(this->fd); + this->fd = perf_event_open(&this->events, this->pid, -1, -1, 0); + return false; + } + this->prevValue = this->value; + this->value = value; + return true; +} diff --git a/linux/PerfCounter.h b/linux/PerfCounter.h new file mode 100644 index 00000000..26352eca --- /dev/null +++ b/linux/PerfCounter.h @@ -0,0 +1,54 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_PerfCounter +#define HEADER_PerfCounter +/* + * This file is based on tiptop. + * by Erven ROHOU + * Copyright (c) 2011, 2012, 2014 Inria + * License: GNU General Public License version 2. + */ + + + +#include +#include +#include +#include +#include +#include +// The sys_perf_counter_open syscall and header files changed names +// between Linux 2.6.31 and 2.6.32. Do the mangling here. +#ifdef HAVE_LINUX_PERF_COUNTER_H +#include +#define STRUCT_NAME perf_counter_attr +#define SYSCALL_NUM __NR_perf_counter_open + +#elif HAVE_LINUX_PERF_EVENT_H +#include +#define STRUCT_NAME perf_event_attr +#define SYSCALL_NUM __NR_perf_event_open + +#else +#error Sorry, performance counters not supported on this system. +#endif + +typedef struct PerfCounter_ { + struct STRUCT_NAME events; + pid_t pid; + int fd; + uint64_t prevValue; + uint64_t value; +} PerfCounter; + +#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue) + + +extern int PerfCounter_openFds; +PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config); + +void PerfCounter_delete(PerfCounter* this); + +bool PerfCounter_read(PerfCounter* this); + +#endif -- cgit v1.2.3