summaryrefslogtreecommitdiffstats
path: root/src/llvm/pmu
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm/pmu')
-rw-r--r--src/llvm/pmu/arm/arm-events.cpp42
-rw-r--r--src/llvm/pmu/pmu-events.cpp414
-rw-r--r--src/llvm/pmu/pmu.cpp491
-rw-r--r--src/llvm/pmu/ppc/ppc-events.cpp37
-rw-r--r--src/llvm/pmu/x86/x86-events.cpp41
5 files changed, 1025 insertions, 0 deletions
diff --git a/src/llvm/pmu/arm/arm-events.cpp b/src/llvm/pmu/arm/arm-events.cpp
new file mode 100644
index 0000000..3da7339
--- /dev/null
+++ b/src/llvm/pmu/arm/arm-events.cpp
@@ -0,0 +1,42 @@
+/*
+ * (C) 2018 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
+ * See COPYRIGHT in top-level directory.
+ */
+
+#include "pmu/pmu-global.h"
+
+namespace pmu {
+
+/* ARMv8 recommended implementation defined event types.
+ * (copied from linux-4.x/arch/arm64/kernel/perf_event.c) */
+#define ICACHE_MISS_CONFIG (0x01)
+#define MEM_LOADS_CONFIG (0x06)
+#define MEM_STORES_CONFIG (0x07)
+
+
+extern EventID PreEvents[PMU_EVENT_MAX]; /* Pre-defined events. */
+
+static void ARMSetupEventCode()
+{
+#define SetupEvent(_Event,_Config) \
+ PreEvents[_Event].Type = PERF_TYPE_RAW; \
+ PreEvents[_Event].Config = _Config;
+
+ SetupEvent(PMU_ICACHE_MISSES, ICACHE_MISS_CONFIG);
+ SetupEvent(PMU_MEM_LOADS, MEM_LOADS_CONFIG);
+ SetupEvent(PMU_MEM_STORES, MEM_STORES_CONFIG);
+
+#undef SetEventCode
+}
+
+int ARMInit()
+{
+ ARMSetupEventCode();
+ return PMU_OK;
+}
+
+} /* namespace pmu */
+
+/*
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
diff --git a/src/llvm/pmu/pmu-events.cpp b/src/llvm/pmu/pmu-events.cpp
new file mode 100644
index 0000000..d3f2d08
--- /dev/null
+++ b/src/llvm/pmu/pmu-events.cpp
@@ -0,0 +1,414 @@
+/*
+ * (C) 2018 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
+ * See COPYRIGHT in top-level directory.
+ */
+
+#include <algorithm>
+#include <signal.h>
+#include <sys/time.h>
+#include "llvm-soft-perfmon.h"
+#include "pmu/pmu-global.h"
+#include "pmu/pmu-events.h"
+
+
+namespace {
+
+/* Mutex */
+class Mutex {
+ pthread_mutex_t M;
+public:
+ Mutex() { pthread_mutex_init(&M, nullptr); }
+ inline void acquire() { pthread_mutex_lock(&M); }
+ inline void release() { pthread_mutex_unlock(&M); }
+ inline bool trylock() { return pthread_mutex_trylock(&M) == 0; }
+};
+
+class MutexGuard {
+ Mutex &M;
+public:
+ MutexGuard(Mutex &M) : M(M) { M.acquire(); }
+ ~MutexGuard() { M.release(); }
+};
+
+}
+
+/*
+ * Performance Monitoring Unit (PMU).
+ */
+namespace pmu {
+
+static Mutex Lock;
+
+SampleList *ReadSampleData(PMUEvent *Event);
+
+/* The timer interrupt handler. */
+void DefaultHandler(int signum, siginfo_t *info, void *data)
+{
+ /* If the thread is signaled while it is currently holding the lock, we
+ * might enter deadlock if we attempt to acquire the lock. Use trylock to
+ * detect such a condition and return from this handler if we cannot
+ * successfully acquire the lock. */
+ if (Lock.trylock() == false)
+ return;
+
+ /* We have hold the lock. Iterate over all sampling events and process
+ * the sample buffer. */
+
+ auto &SampleEvents = EventMgr->SampleEvents;
+ if (SampleEvents.empty()) {
+ Lock.release();
+ return;
+ }
+
+ struct timeval Start, End, Elapse;
+ if (SP->Mode & SPM_HPM)
+ gettimeofday(&Start, nullptr);
+
+ for (auto I = SampleEvents.begin(), E = SampleEvents.end(); I != E; ++I) {
+ PMUEvent *Event = *I;
+ if (Event->Mode & MODE_SAMPLE) {
+ SampleList *Data = ReadSampleData(Event);
+ if (Data)
+ Event->SampleHandler(Event->Hndl, SampleDataPtr(Data),
+ Event->Opaque);
+ }
+ }
+
+ auto &ChangedEvents = EventMgr->ChangedEvents;
+ if (!ChangedEvents.empty()) {
+ for (auto *Event : ChangedEvents) {
+ if (Event->State == STATE_GOTO_STOP) {
+ Event->State = STATE_STOP;
+ SampleEvents.remove(Event);
+ } else if (Event->State == STATE_GOTO_START) {
+ Event->State = STATE_START;
+ SampleEvents.push_back(Event);
+ }
+ }
+ ChangedEvents.clear();
+ }
+
+ if (SP->Mode & SPM_HPM) {
+ gettimeofday(&End, nullptr);
+ timersub(&End, &Start, &Elapse);
+ SP->SampleTime += Elapse.tv_sec * 1e6 + Elapse.tv_usec;
+ }
+
+ if (!SampleEvents.empty())
+ EventMgr->EventTimer->Start();
+ Lock.release();
+}
+
+/*
+ * Event Manager
+ */
+EventManager::EventManager()
+{
+ for (unsigned i = 0; i < PMU_MAX_EVENTS; ++i) {
+ Events[i].Hndl = i;
+ FreeEvents.push_back(&Events[i]);
+ }
+
+ /* Install the signal handler for the timer. */
+ struct sigaction act;
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_sigaction = DefaultHandler;
+ act.sa_flags = SA_SIGINFO;
+ sigaction(PMU_SIGNAL_NUM, &act, 0);
+
+ EventTimer = new Timer(PMU_SIGNAL_NUM, SysConfig.SignalReceiver);
+}
+
+EventManager::~EventManager()
+{
+ EventTimer->Stop();
+ delete EventTimer;
+}
+
+/* Return the event of the input handle. */
+PMUEvent *EventManager::GetEvent(Handle Hndl)
+{
+ if (Hndl >= PMU_MAX_EVENTS)
+ return nullptr;
+ return &Events[Hndl];
+}
+
+/* Add a counting event and return its handle. */
+Handle EventManager::AddEvent(int fd)
+{
+ MutexGuard Locked(Lock);
+
+ if (FreeEvents.empty())
+ return PMU_INVALID_HNDL;
+
+ auto Event = FreeEvents.front();
+ FreeEvents.pop_front();
+
+ Event->FD.push_back(fd);
+ Event->Data.Base = nullptr;
+ Event->Aux.Base = nullptr;
+ Event->OverflowHandler = nullptr;
+
+ Event->Mode = MODE_COUNTER;
+ Event->State = STATE_STOP;
+
+ return Event->Hndl;
+}
+
+/* Add a sampling event and return its handle. */
+Handle EventManager::AddSampleEvent(unsigned NumFDs, int *FD, uint64_t DataSize,
+ void *Data, uint32_t Mode,
+ SampleConfig &Config)
+{
+ MutexGuard Locked(Lock);
+
+ if (FreeEvents.empty())
+ return PMU_INVALID_HNDL;
+
+ auto Event = FreeEvents.front();
+ FreeEvents.pop_front();
+
+ for (unsigned i = 0; i < NumFDs; ++i)
+ Event->FD.push_back(FD[i]);
+
+ Event->Data.Base = Data;
+ Event->Data.Size = DataSize;
+ Event->Data.Prev = 0;
+ Event->Aux.Base = nullptr;
+ Event->Aux.Size = 0;
+ Event->Aux.Prev = 0;
+ Event->Watermark = std::min(Config.Watermark, DataSize);
+ Event->SampleHandler = Config.SampleHandler;
+ Event->Opaque = Config.Opaque;
+
+ Event->Mode = MODE_SAMPLE | Mode;
+ Event->State = STATE_STOP;
+
+ return Event->Hndl;
+}
+
+/* Notify that an event is started. */
+void EventManager::StartEvent(PMUEvent *Event, bool ShouldLock)
+{
+ if (ShouldLock) {
+ MutexGuard Locked(Lock);
+
+ /* We don't add this event to the sampling event list if user doesn't
+ * provide a valid overflow handler for a sampling event. */
+ if (Event->State == STATE_STOP && Event->OverflowHandler) {
+ SampleEvents.push_back(Event);
+ EventTimer->Start();
+ }
+ Event->State = STATE_START;
+ } else {
+ /* We are within the overflow handling and it's not safe to change the
+ * structure of the sampling event list. Here we only change the state
+ * of the event and the event list will be fixed at the end of the
+ * overflow handling. */
+ if (Event->State == STATE_STOP && Event->OverflowHandler) {
+ Event->State = STATE_GOTO_START;
+ ChangedEvents.push_back(Event);
+ }
+ }
+}
+
+/* Notify that an event is stopped. */
+void EventManager::StopEvent(PMUEvent *Event, bool ShouldLock)
+{
+ if (ShouldLock) {
+ /* If this is a sampling event and is currently under sampling, remove
+ * it from the sampling event list. */
+ Lock.acquire();
+ if (Event->State == STATE_START && Event->OverflowHandler) {
+ SampleEvents.remove(Event);
+ if (SampleEvents.empty())
+ EventTimer->Stop();
+ }
+ Event->State = STATE_STOP;
+ Lock.release();
+ } else {
+ /* We are within the overflow handling and it's not safe to change the
+ * structure of the sampling event list. Here we only change the state
+ * of the event and the event list will be fixed at the end of the
+ * overflow handling. */
+ if (Event->State == STATE_START && Event->OverflowHandler) {
+ Event->State = STATE_GOTO_STOP;
+ ChangedEvents.push_back(Event);
+ }
+ }
+}
+
+/* Notify that an event is deleted. */
+void EventManager::DeleteEvent(PMUEvent *Event)
+{
+ MutexGuard Locked(Lock);
+
+ Event->FD.clear();
+ FreeEvents.push_back(Event);
+}
+
+/* Stop the event manager. */
+void EventManager::Pause()
+{
+ MutexGuard Locked(Lock);
+ if (!SampleEvents.empty())
+ EventTimer->Stop();
+}
+
+/* Restart the event manager. */
+void EventManager::Resume()
+{
+ MutexGuard Locked(Lock);
+ if (!SampleEvents.empty())
+ EventTimer->Start();
+}
+
+/*
+ * Buffer processing
+ */
+static uint8_t *CopyData(uint8_t *Data, uint64_t DataSize, uint64_t Head, uint64_t Tail) {
+ uint64_t Mask = DataSize - 1;
+ uint64_t Size = Head - Tail;
+ uint64_t HeadOff = Head & Mask;
+ uint64_t TailOff = Tail & Mask;
+ uint8_t *Buf = new uint8_t[Size];
+
+ if (HeadOff > TailOff) {
+ memcpy(Buf, Data + TailOff, Size);
+ } else {
+ uint64_t UpperSize = DataSize - TailOff;
+ memcpy(Buf, Data + TailOff, UpperSize);
+ memcpy(&Buf[UpperSize], Data, HeadOff);
+ }
+ return Buf;
+}
+
+/* Process and decode the sample buffer. */
+SampleList *ReadSampleData(PMUEvent *Event)
+{
+ uint64_t Head = perf_read_data_head(Event->Data.Base);
+ uint64_t Old = Event->Data.Prev;
+ uint64_t Size = Head - Old;
+ uint8_t *Data = (uint8_t *)Event->Data.Base + SysConfig.PageSize;
+ uint64_t DataSize = Event->Data.Size - SysConfig.PageSize;
+ SampleList *OutData = nullptr;
+
+ if (Size < Event->Watermark)
+ return OutData;
+
+ OutData = new SampleList;
+ if (Size == 0)
+ return OutData;
+
+ /* Overwrite head if we failed to keep up with the mmap data. */
+ if (Size > DataSize) {
+ Event->Data.Prev = Head;
+ perf_write_data_tail(Event->Data.Base, Head);
+ return OutData;
+ }
+
+ /* Process the buffer. */
+ uint8_t *Buf = CopyData(Data, DataSize, Head, Old);
+ uint8_t *Orig = Buf, *BufEnd = Buf + Size;
+ bool SampleIP = Event->Mode & MODE_SAMPLE_IP;
+ bool ReadFormat = Event->Mode & MODE_SAMPLE_READ;
+ bool ReadGroup = Event->FD.size() > 1;
+
+ while (1) {
+ /* Check if we have enough size for the event header. */
+ if (Buf + sizeof(struct perf_event_header) > BufEnd)
+ break;
+
+ auto *Header = (struct perf_event_header *)Buf;
+ Buf += sizeof(struct perf_event_header);
+
+ /* Check if we have enough size for the sample payload. */
+ if (Buf + Header->size > BufEnd)
+ break;
+
+ if (Header->size == 0)
+ continue;
+
+ /* Skip this sample if it's not a PERF_RECORD_SAMPLE type. */
+ if (Header->type != PERF_RECORD_SAMPLE) {
+ Buf += Header->size;
+ continue;
+ }
+
+ if (SampleIP) { /* if PERF_SAMPLE_IP */
+ uint64_t ip = *(uint64_t *)Buf;
+ Buf += 8;
+ OutData->push_back(ip);
+ }
+ if (ReadFormat) { /* if PERF_SAMPLE_READ */
+ if (ReadGroup) {
+ uint64_t nr = *(uint64_t *)Buf;
+ Buf += 8;
+ while (nr--) {
+ uint64_t value = *(uint64_t *)Buf;
+ Buf += 8;
+ OutData->push_back(value);
+ }
+ } else {
+ uint64_t value = *(uint64_t *)Buf;
+ Buf += 8;
+ OutData->push_back(value);
+ }
+ }
+ }
+
+ delete [] Orig;
+
+ /* We have finished the buffer. Update data tail. */
+ Event->Data.Prev = Head;
+ perf_write_data_tail(Event->Data.Base, Head);
+
+ return OutData;
+}
+
+/*
+ * Timer
+ */
+Timer::Timer(int Signum, int TID)
+{
+ struct sigevent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.sigev_value.sival_int = 0;
+ ev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
+ ev.sigev_signo = Signum;
+ ev._sigev_un._tid = TID;
+ timer_create(CLOCK_REALTIME, &ev, &T);
+}
+
+Timer::~Timer()
+{
+ Stop();
+ timer_delete(T);
+}
+
+/* Fire a timer which expires just once. */
+void Timer::Start()
+{
+ struct itimerspec Timeout;
+ Timeout.it_interval.tv_sec = 0;
+ Timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
+ Timeout.it_value.tv_sec = 0;
+ Timeout.it_value.tv_nsec = SysConfig.Timeout;
+ timer_settime(T, 0 /* RELATIVE */, &Timeout, NULL);
+}
+
+void Timer::Stop()
+{
+ struct itimerspec Timeout;
+ Timeout.it_interval.tv_sec = 0;
+ Timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
+ Timeout.it_value.tv_sec = 0;
+ Timeout.it_value.tv_nsec = 0;
+ timer_settime(T, 0 /* RELATIVE */, &Timeout, NULL);
+}
+
+} /* namespace pmu */
+
+/*
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
diff --git a/src/llvm/pmu/pmu.cpp b/src/llvm/pmu/pmu.cpp
new file mode 100644
index 0000000..640997f
--- /dev/null
+++ b/src/llvm/pmu/pmu.cpp
@@ -0,0 +1,491 @@
+/*
+ * (C) 2018 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
+ * See COPYRIGHT in top-level directory.
+ */
+
+#include <errno.h>
+#include <sys/mman.h>
+#include "pmu/pmu-global.h"
+#include "pmu/pmu-events.h"
+
+/*
+ * Performance Monitoring Unit (PMU) tools.
+ */
+namespace pmu {
+
+static bool InitOnce;
+
+EventManager *EventMgr; /* Event manager. */
+GlobalConfig SysConfig; /* System-wide configuration. */
+EventID PreEvents[PMU_EVENT_MAX]; /* Pre-defined events. */
+
+
+/* Initialize system-wide configuration. */
+static void SetupGlobalConfig(PMUConfig &Config)
+{
+ /* Get page size. */
+ SysConfig.PageSize = getpagesize();
+
+ /* Configure timeout and signal receiver for the timer. */
+ SysConfig.SignalReceiver = Config.SignalReceiver;
+ if (SysConfig.SignalReceiver <= 0)
+ SysConfig.SignalReceiver = getpid();
+
+ SysConfig.Timeout = Config.Timeout;
+ if (SysConfig.Timeout == 0)
+ SysConfig.Timeout = PMU_TIMER_PERIOD;
+
+ SysConfig.Timeout *= 1000; /* nanosecond */
+
+ /* Determine the Linux Perf version used by this tool and the kernel.
+ * We set the last few bytes of the perf_event_attr structure and see the
+ * size field returned from the kernel. */
+
+ SysConfig.PerfVersion = 0;
+ SysConfig.OSPerfVersion = 0;
+
+ struct perf_event_attr attr;
+ perf_attr_init(&attr, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
+ attr.aux_watermark = 1;
+ int fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
+ close(fd);
+
+#define CheckPerfVersion(_Ver) \
+ do { \
+ SysConfig.PerfVersion = _Ver; \
+ if (attr.size == PERF_ATTR_SIZE_VER##_Ver) \
+ SysConfig.OSPerfVersion = _Ver; \
+ } while(0)
+
+ CheckPerfVersion(1);
+ CheckPerfVersion(2);
+ CheckPerfVersion(3);
+ CheckPerfVersion(4);
+ CheckPerfVersion(5);
+
+#undef CheckPerfVersion
+}
+
+/* Initialize pre-defined events. */
+static void SetupDefaultEvent()
+{
+ for (unsigned i = 0; i < PMU_EVENT_MAX; ++i) {
+ PreEvents[i].Type = -1;
+ PreEvents[i].Config = -1;
+ }
+
+#define SetupEvent(_Event,_Config) \
+ PreEvents[_Event].Type = PERF_TYPE_HARDWARE; \
+ PreEvents[_Event].Config = _Config;
+
+ /* Basic events. */
+ SetupEvent(PMU_CPU_CYCLES, PERF_COUNT_HW_CPU_CYCLES);
+ SetupEvent(PMU_REF_CPU_CYCLES, PERF_COUNT_HW_REF_CPU_CYCLES);
+ SetupEvent(PMU_INSTRUCTIONS, PERF_COUNT_HW_INSTRUCTIONS);
+ SetupEvent(PMU_LLC_REFERENCES, PERF_COUNT_HW_CACHE_REFERENCES);
+ SetupEvent(PMU_LLC_MISSES, PERF_COUNT_HW_CACHE_MISSES);
+ SetupEvent(PMU_BRANCH_INSTRUCTIONS, PERF_COUNT_HW_BRANCH_INSTRUCTIONS);
+ SetupEvent(PMU_BRANCH_MISSES, PERF_COUNT_HW_BRANCH_MISSES);
+
+#undef SetEventCode
+}
+
+/* Initialize the PMU module. */
+int PMU::Init(PMUConfig &Config)
+{
+ if (InitOnce == true)
+ return PMU_OK;
+
+ /* Set the global configuration. */
+ SetupGlobalConfig(Config);
+
+ /* Initialize pre-defined event codes. */
+ SetupDefaultEvent();
+
+ /* Allocate event manager. */
+ EventMgr = new EventManager;
+
+ /* Initialize target-specific events. */
+#if defined(__i386__) || defined(__x86_64__)
+ X86Init();
+#elif defined(__arm__) || defined (__aarch64__)
+ ARMInit();
+#elif defined(_ARCH_PPC) || defined(_ARCH_PPC64)
+ PPCInit();
+#endif
+
+ Config.PerfVersion = SysConfig.PerfVersion;
+ Config.OSPerfVersion = SysConfig.OSPerfVersion;
+
+ InitOnce = true;
+ return PMU_OK;
+}
+
+/* Finalize the PMU module. */
+int PMU::Finalize(void)
+{
+ if (InitOnce == false)
+ return PMU_OK;
+
+ delete EventMgr;
+
+ InitOnce = false;
+ return PMU_OK;
+}
+
+/* Stop the PMU module. */
+int PMU::Pause(void)
+{
+ EventMgr->Pause();
+ return PMU_OK;
+}
+
+/* Restart the PMU module. */
+int PMU::Resume(void)
+{
+ EventMgr->Resume();
+ return PMU_OK;
+}
+
+/* Start a counting/sampling/tracing event. */
+int PMU::Start(Handle Hndl)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+
+ if (perf_event_start(Event->getFD()) != 0)
+ return PMU_EEVENT;
+
+ EventMgr->StartEvent(Event);
+
+ return PMU_OK;
+}
+
+/* Stop a counting/sampling/tracing event. */
+int PMU::Stop(Handle Hndl)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+
+ if (perf_event_stop(Event->getFD()) != 0)
+ return PMU_EEVENT;
+
+ EventMgr->StopEvent(Event);
+
+ return PMU_OK;
+}
+
+/* Reset the hardware counter. */
+int PMU::Reset(Handle Hndl)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+
+ if (perf_event_reset(Event->getFD()) != 0)
+ return PMU_EEVENT;
+ return PMU_OK;
+}
+
+/* Remove an event. */
+int PMU::Cleanup(Handle Hndl)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+
+ /* Do stop the event if the user hasn't called it. */
+ if (Event->State != STATE_STOP) {
+ int EC = Stop(Hndl);
+ if (EC != PMU_OK)
+ return EC;
+ }
+
+ /* At this point, this event has been removed from the sampling list and we
+ * no longer get overflow handling (if this is a sampling event). We are
+ * now able to release all resources. */
+
+ /* Stop all events in a group. */
+ for (auto fd : Event->FD)
+ perf_event_stop(fd);
+
+ /* Release allocated buffers. */
+ if (Event->Data.Base)
+ munmap(Event->Data.Base, Event->Data.Size);
+ if (Event->Aux.Base)
+ munmap(Event->Aux.Base, Event->Aux.Size);
+
+ for (auto fd : Event->FD)
+ close(fd);
+
+ EventMgr->DeleteEvent(Event);
+ return PMU_OK;
+}
+
+/* Start/stop a sampling/tracing event without acquiring a lock.
+ * Note that these two function should only be used within the overflow
+ * handler. Since the overflow handling is already in a locked section,
+ * acquiring a lock is not required. */
+int PMU::StartUnlocked(Handle Hndl)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+ if (Event->Mode & MODE_COUNTER)
+ return PMU_EINVAL;
+
+ if (perf_event_start(Event->getFD()) != 0)
+ return PMU_EEVENT;
+
+ EventMgr->StartEvent(Event, false);
+
+ return PMU_OK;
+}
+
+int PMU::StopUnlocked(Handle Hndl)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+ if (Event->Mode & MODE_COUNTER)
+ return PMU_EINVAL;
+
+ if (perf_event_stop(Event->getFD()) != 0)
+ return PMU_EEVENT;
+
+ EventMgr->StopEvent(Event, false);
+
+ return PMU_OK;
+}
+
+/* Open an event using the pre-defined event code. */
+int PMU::CreateEvent(unsigned EventCode, Handle &Hndl)
+{
+ int fd;
+ struct perf_event_attr Attr;
+
+ Hndl = PMU_INVALID_HNDL;
+
+ if (EventCode >= PMU_EVENT_MAX)
+ return PMU_EINVAL;
+ if (PreEvents[EventCode].Type == -1)
+ return PMU_ENOEVENT;
+
+ perf_attr_init(&Attr, PreEvents[EventCode].Type, PreEvents[EventCode].Config);
+ fd = sys_perf_event_open(&Attr, 0, -1, -1, 0);
+ if (fd < 0)
+ return ErrorCode(errno);
+
+ Hndl = EventMgr->AddEvent(fd);
+ if (Hndl == PMU_INVALID_HNDL) {
+ close(fd);
+ return PMU_ENOMEM;
+ }
+ return PMU_OK;
+}
+
+/* Open an event using the raw event number and umask value.
+ * The raw event code is computed as (RawEvent | (Umask << 8)). */
+int PMU::CreateRawEvent(unsigned RawEvent, unsigned Umask, Handle &Hndl)
+{
+ int fd;
+ struct perf_event_attr Attr;
+
+ Hndl = PMU_INVALID_HNDL;
+
+ perf_attr_init(&Attr, PERF_TYPE_RAW, RawEvent | (Umask << 8));
+ fd = sys_perf_event_open(&Attr, 0, -1, -1, 0);
+ if (fd < 0)
+ return ErrorCode(errno);
+
+ Hndl = EventMgr->AddEvent(fd);
+ if (Hndl == PMU_INVALID_HNDL) {
+ close(fd);
+ return PMU_ENOMEM;
+ }
+ return PMU_OK;
+}
+
+/* Open a sampling event, with the 1st EventCode as the interrupt event.
+ * The sample data will be recorded in a vector of type 'uint64_t'.
+ * The following vector shows the data format of sampling with N events:
+ * { pc, val1, val2, ..., valN, # 1st sample
+ * ...
+ * pc, val1, val2, ..., valN }; # nth sample
+ *
+ * Note that ownwership of the output vector is transferred to the user.
+ * It is the user's responsibility to free the resource of the vector. */
+int PMU::CreateSampleEvent(SampleConfig &Config, Handle &Hndl)
+{
+ unsigned i, NumEvents = Config.NumEvents;
+ unsigned NumPages = Config.NumPages;
+ uint64_t Period = Config.Period;
+ int fds[PMU_GROUP_EVENTS], EC = PMU_ENOMEM;
+ uint64_t DataSize;
+ void *Data;
+
+ Hndl = PMU_INVALID_HNDL;
+
+ if (NumPages == 0)
+ NumPages = PMU_SAMPLE_PAGES;
+ if (Period < 1e3)
+ Period = PMU_SAMPLE_PERIOD;
+
+ if (NumEvents == 0 || NumEvents > PMU_GROUP_EVENTS || !isPowerOf2(NumPages))
+ return PMU_EINVAL;
+
+ /* Check event codes. */
+ for (i = 0; i < NumEvents; ++i) {
+ unsigned EventCode = Config.EventCode[i];
+ if (EventCode >= PMU_EVENT_MAX)
+ return PMU_EINVAL;
+ if (PreEvents[EventCode].Type == -1)
+ return PMU_ENOEVENT;
+ }
+
+ /* Open the events. If more than one event is requested, set read_format
+ * to PERF_FORMAT_GROUP. */
+ fds[0] = -1;
+ for (i = 0; i < NumEvents; ++i) {
+ struct perf_event_attr Attr;
+ unsigned EventCode = Config.EventCode[i];
+ perf_attr_init(&Attr, PreEvents[EventCode].Type, PreEvents[EventCode].Config);
+
+ Attr.disabled = !i;
+ if (i == 0) {
+ Attr.sample_period = Period;
+ Attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_READ;
+ Attr.read_format = (NumEvents > 1) ? PERF_FORMAT_GROUP : 0;
+ }
+
+ fds[i] = sys_perf_event_open(&Attr, 0, -1, fds[0], 0);
+ if (fds[i] < 0) {
+ EC = ErrorCode(errno);
+ goto failed;
+ }
+ }
+
+ /* Allocate buffer for the sampling data. */
+ DataSize = (1 + NumPages) * SysConfig.PageSize;
+ Data = mmap(nullptr, DataSize, PROT_READ|PROT_WRITE, MAP_SHARED, fds[0], 0);
+ if (Data == MAP_FAILED)
+ goto failed;
+
+ Hndl = EventMgr->AddSampleEvent(NumEvents, fds, DataSize, Data,
+ MODE_SAMPLE_IP | MODE_SAMPLE_READ, Config);
+ if (Hndl == PMU_INVALID_HNDL) {
+ munmap(Data, DataSize);
+ goto failed;
+ }
+ return PMU_OK;
+
+failed:
+ while (--i)
+ close(fds[i]);
+ return EC;
+}
+
+/* Generate an IP histogram using EventCode as the interrupt event.
+ * The IP histogram will be recorded in a vector of type 'uint64_t' with
+ * the format: { pc1, pc2, pc3, ..., pcN }.
+ * Note that ownwership of the output vector is transferred to the user.
+ * It is the user's responsibility to free the resource of the vector. */
+int PMU::CreateSampleIP(Sample1Config &Config, Handle &Hndl)
+{
+ int fd;
+ unsigned EventCode = Config.EventCode;
+ unsigned NumPages = Config.NumPages;
+ uint64_t Period = Config.Period;
+ uint64_t DataSize;
+ void *Data;
+
+ Hndl = PMU_INVALID_HNDL;
+
+ if (NumPages == 0)
+ NumPages = PMU_SAMPLE_PAGES;
+ if (Period < 1e3)
+ Period = PMU_SAMPLE_PERIOD;
+
+ if (!isPowerOf2(NumPages))
+ return PMU_EINVAL;
+
+ /* Check the events. */
+ if (EventCode >= PMU_EVENT_MAX)
+ return PMU_EINVAL;
+ if (PreEvents[EventCode].Type == -1)
+ return PMU_ENOEVENT;
+
+ struct perf_event_attr Attr;
+ perf_attr_init(&Attr, PreEvents[EventCode].Type, PreEvents[EventCode].Config);
+
+ Attr.disabled = 1;
+ Attr.sample_period = Period;
+ Attr.sample_type = PERF_SAMPLE_IP;
+
+ fd = sys_perf_event_open(&Attr, 0, -1, -1, 0);
+ if (fd < 0)
+ return ErrorCode(errno);
+
+ /* Allocate buffer for the sampling data. */
+ DataSize = (1 + NumPages) * SysConfig.PageSize;
+ Data = mmap(nullptr, DataSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (Data == MAP_FAILED)
+ goto failed;
+
+ /* Set the sampling config. */
+ SampleConfig SConfig;
+ SConfig.NumEvents = 1;
+ SConfig.EventCode[0] = Config.EventCode;
+ SConfig.NumPages = NumPages;
+ SConfig.Period = Period;
+ SConfig.Watermark = Config.Watermark;
+ SConfig.SampleHandler = Config.SampleHandler;
+ SConfig.Opaque = Config.Opaque;
+
+ Hndl = EventMgr->AddSampleEvent(1, &fd, DataSize, Data, MODE_SAMPLE_IP, SConfig);
+ if (Hndl == PMU_INVALID_HNDL) {
+ munmap(Data, DataSize);
+ goto failed;
+ }
+ return PMU_OK;
+
+failed:
+ close(fd);
+ return PMU_ENOMEM;
+}
+
+/* Read value from the hardware counter. */
+int PMU::ReadEvent(Handle Hndl, uint64_t &Value)
+{
+ auto Event = EventMgr->GetEvent(Hndl);
+ if (!Event)
+ return PMU_EINVAL;
+
+ if (read(Event->getFD(), &Value, sizeof(uint64_t)) != sizeof(uint64_t))
+ return PMU_EEVENT;
+ return PMU_OK;
+}
+
+/* Convert error code to string. */
+const char *PMU::strerror(int ErrCode)
+{
+ switch (ErrCode) {
+ case PMU_OK: return "Success";
+ case PMU_EINVAL: return "Invalid argument";
+ case PMU_ENOMEM: return "Insufficient memory";
+ case PMU_ENOEVENT: return "Pre-defined event not available";
+ case PMU_EEVENT: return "Hardware event error";
+ case PMU_EPERM: return "Permission denied";
+ case PMU_EINTER: return "Internal error";
+ case PMU_EDECODER: return "Decoder error";
+ default: return "Unknown error";
+ }
+}
+
+} /* namespace pmu */
+
+/*
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
diff --git a/src/llvm/pmu/ppc/ppc-events.cpp b/src/llvm/pmu/ppc/ppc-events.cpp
new file mode 100644
index 0000000..249de52
--- /dev/null
+++ b/src/llvm/pmu/ppc/ppc-events.cpp
@@ -0,0 +1,37 @@
+/*
+ * (C) 2018 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
+ * See COPYRIGHT in top-level directory.
+ */
+
+#include "pmu/pmu-global.h"
+
+namespace pmu {
+
+#define ICACHE_MISS_CONFIG (0x200fd)
+#define MEM_LOADS_CONFIG (0x100fc)
+
+extern EventID PreEvents[PMU_EVENT_MAX]; /* Pre-defined events. */
+
+static void PPCSetupEventCode()
+{
+#define SetupEvent(_Event,_Config) \
+ PreEvents[_Event].Type = PERF_TYPE_RAW; \
+ PreEvents[_Event].Config = _Config;
+
+ SetupEvent(PMU_ICACHE_MISSES, ICACHE_MISS_CONFIG);
+ SetupEvent(PMU_MEM_LOADS, MEM_LOADS_CONFIG);
+
+#undef SetEventCode
+}
+
+int PPCInit()
+{
+ PPCSetupEventCode();
+ return PMU_OK;
+}
+
+} /* namespace pmu */
+
+/*
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
diff --git a/src/llvm/pmu/x86/x86-events.cpp b/src/llvm/pmu/x86/x86-events.cpp
new file mode 100644
index 0000000..fe25f70
--- /dev/null
+++ b/src/llvm/pmu/x86/x86-events.cpp
@@ -0,0 +1,41 @@
+/*
+ * (C) 2018 by Computer System Laboratory, IIS, Academia Sinica, Taiwan.
+ * See COPYRIGHT in top-level directory.
+ */
+
+#include "pmu/pmu-global.h"
+
+namespace pmu {
+
+#define ICACHE_HIT_CONFIG (0x83 | (0x1 << 8)) /* skylake/event=0x83,umask=0x1/ */
+#define ICACHE_MISS_CONFIG (0x83 | (0x2 << 8)) /* skylake/event=0x83,umask=0x2/ */
+#define MEM_LOADS_CONFIG (0xd0 | (0x81 << 8 )) /* skylake/event=0xd0,umask=0x81/ */
+#define MEM_STORES_CONFIG (0xd0 | (0x82 << 8 )) /* skylake/event=0xd0,umask=0x82/ */
+
+extern EventID PreEvents[PMU_EVENT_MAX]; /* Pre-defined events. */
+
+static void X86SetupEventCode()
+{
+#define SetupEvent(_Event,_Config) \
+ PreEvents[_Event].Type = PERF_TYPE_RAW; \
+ PreEvents[_Event].Config = _Config;
+
+ SetupEvent(PMU_ICACHE_HITS, ICACHE_HIT_CONFIG);
+ SetupEvent(PMU_ICACHE_MISSES, ICACHE_MISS_CONFIG);
+ SetupEvent(PMU_MEM_LOADS, MEM_LOADS_CONFIG);
+ SetupEvent(PMU_MEM_STORES, MEM_STORES_CONFIG);
+
+#undef SetEventCode
+}
+
+int X86Init()
+{
+ X86SetupEventCode();
+ return PMU_OK;
+}
+
+} /* namespace pmu */
+
+/*
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
OpenPOWER on IntegriCloud