diff options
author | Timothy Pearson <tpearson@raptorengineering.com> | 2019-11-29 19:00:14 -0600 |
---|---|---|
committer | Timothy Pearson <tpearson@raptorengineering.com> | 2019-11-29 19:02:28 -0600 |
commit | 4b3250c5073149c59c5c11e06c2c0d93b6a9f5ff (patch) | |
tree | dce73321255f834f7b2d4c16fa49760edb534f27 /llvm/pmu | |
parent | a58047f7fbb055677e45c9a7d65ba40fbfad4b92 (diff) | |
download | hqemu-2.5.1_overlay.zip hqemu-2.5.1_overlay.tar.gz |
Initial overlay of HQEMU 2.5.2 changes onto underlying 2.5.1 QEMU GIT tree2.5.1_overlay
Diffstat (limited to 'llvm/pmu')
-rw-r--r-- | llvm/pmu/arm/arm-events.cpp | 42 | ||||
-rw-r--r-- | llvm/pmu/pmu-events.cpp | 414 | ||||
-rw-r--r-- | llvm/pmu/pmu.cpp | 491 | ||||
-rw-r--r-- | llvm/pmu/ppc/ppc-events.cpp | 37 | ||||
-rw-r--r-- | llvm/pmu/x86/x86-events.cpp | 41 |
5 files changed, 1025 insertions, 0 deletions
diff --git a/llvm/pmu/arm/arm-events.cpp b/llvm/pmu/arm/arm-events.cpp new file mode 100644 index 0000000..3da7339 --- /dev/null +++ b/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/llvm/pmu/pmu-events.cpp b/llvm/pmu/pmu-events.cpp new file mode 100644 index 0000000..d3f2d08 --- /dev/null +++ b/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/llvm/pmu/pmu.cpp b/llvm/pmu/pmu.cpp new file mode 100644 index 0000000..640997f --- /dev/null +++ b/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/llvm/pmu/ppc/ppc-events.cpp b/llvm/pmu/ppc/ppc-events.cpp new file mode 100644 index 0000000..249de52 --- /dev/null +++ b/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/llvm/pmu/x86/x86-events.cpp b/llvm/pmu/x86/x86-events.cpp new file mode 100644 index 0000000..fe25f70 --- /dev/null +++ b/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 + */ |