summaryrefslogtreecommitdiffstats
path: root/src/llvm/pmu/pmu-events.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/llvm/pmu/pmu-events.cpp')
-rw-r--r--src/llvm/pmu/pmu-events.cpp414
1 files changed, 414 insertions, 0 deletions
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
+ */
OpenPOWER on IntegriCloud