diff options
Diffstat (limited to 'tools/gator/daemon/PerfSource.cpp')
-rw-r--r-- | tools/gator/daemon/PerfSource.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp new file mode 100644 index 000000000000..ecfaa66832bd --- /dev/null +++ b/tools/gator/daemon/PerfSource.cpp @@ -0,0 +1,275 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "PerfSource.h" + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "Child.h" +#include "DynBuf.h" +#include "Logging.h" +#include "PerfDriver.h" +#include "Proc.h" +#include "SessionData.h" + +#define MS_PER_US 1000000 + +extern Child *child; + +static bool sendTracepointFormat(Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) { + if (!printb->printf(EVENTS_PATH "/%s/format", name)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + if (!b->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + buffer->format(b->getLength(), b->getBuf()); + + return true; +} + +PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 4*1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) { + long l = sysconf(_SC_PAGE_SIZE); + if (l < 0) { + logg->logError(__FILE__, __LINE__, "Unable to obtain the page size"); + handleException(); + } + gSessionData->mPageSize = static_cast<int>(l); + + l = sysconf(_SC_NPROCESSORS_CONF); + if (l < 0) { + logg->logError(__FILE__, __LINE__, "Unable to obtain the number of cores"); + handleException(); + } + gSessionData->mCores = static_cast<int>(l); +} + +PerfSource::~PerfSource() { +} + +struct PrepareParallelArgs { + PerfGroup *pg; + int cpu; +}; + +void *prepareParallel(void *arg) { + const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg; + args->pg->prepareCPU(args->cpu); + return NULL; +} + +bool PerfSource::prepare() { + DynBuf printb; + DynBuf b1; + DynBuf b2; + DynBuf b3; + long long schedSwitchId; + + // Reread cpuinfo since cores may have changed since startup + gSessionData->readCpuInfo(); + + if (0 + || !mMonitor.init() + || !mUEvent.init() + || !mMonitor.add(mUEvent.getFd()) + + || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0 + || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1) + + // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID + || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL | PERF_GROUP_PER_CPU) + + // Only want TID and IP but not RAW on timer + || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, PERF_GROUP_PER_CPU)) + + || !gSessionData->perf.enable(&mCountersGroup, &mBuffer) + || 0) { + logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.4 or later?", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!gSessionData->perf.summary(&mSummary)) { + logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + { + // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases + pthread_t threads[NR_CPUS]; + PrepareParallelArgs args[NR_CPUS]; + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + args[cpu].pg = &mCountersGroup; + args[cpu].cpu = cpu; + if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) { + logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (pthread_join(threads[cpu], NULL) != 0) { + logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + int numEvents = 0; + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor); + } + if (numEvents <= 0) { + logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Start events before reading proc to avoid race conditions + if (!mCountersGroup.start()) { + logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (!readProc(&mBuffer, true, &printb, &b1, &b2, &b3)) { + logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + mBuffer.commit(1); + + return true; +} + +static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu"; + +void PerfSource::run() { + int pipefd[2]; + + if (pipe(pipefd) != 0) { + logg->logError(__FILE__, __LINE__, "pipe failed"); + handleException(); + } + mInterruptFd = pipefd[1]; + + if (!mMonitor.add(pipefd[0])) { + logg->logError(__FILE__, __LINE__, "Monitor::add failed"); + handleException(); + } + + int timeout = -1; + if (gSessionData->mLiveRate > 0) { + timeout = gSessionData->mLiveRate/MS_PER_US; + } + + sem_post(mStartProfile); + + while (gSessionData->mSessionIsActive) { + // +1 for uevents, +1 for pipe + struct epoll_event events[NR_CPUS + 2]; + int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout); + if (ready < 0) { + logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); + handleException(); + } + + for (int i = 0; i < ready; ++i) { + if (events[i].data.fd == mUEvent.getFd()) { + if (!handleUEvent()) { + logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed"); + handleException(); + } + break; + } + } + + // send a notification that data is ready + sem_post(mSenderSem); + + // In one shot mode, stop collection once all the buffers are filled + // Assume timeout == 0 in this case + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__); + child->endSession(); + } + } + + mCountersGroup.stop(); + mBuffer.setDone(); + mIsDone = true; + + // send a notification that data is ready + sem_post(mSenderSem); + + mInterruptFd = -1; + close(pipefd[0]); + close(pipefd[1]); +} + +bool PerfSource::handleUEvent() { + UEventResult result; + if (!mUEvent.read(&result)) { + logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (strcmp(result.mSubsystem, "cpu") == 0) { + if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) { + logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__); + return false; + } + char *endptr; + errno = 0; + int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10); + if (errno != 0 || *endptr != '\0') { + logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + if (strcmp(result.mAction, "online") == 0) { + // Only call onlineCPU if prepareCPU succeeded + const bool result = mCountersGroup.prepareCPU(cpu) && + mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor); + mBuffer.commit(1); + return result; + } else if (strcmp(result.mAction, "offline") == 0) { + return mCountersGroup.offlineCPU(cpu); + } + } + + return true; +} + +void PerfSource::interrupt() { + if (mInterruptFd >= 0) { + int8_t c = 0; + // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread + if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) { + logg->logError(__FILE__, __LINE__, "write failed"); + handleException(); + } + } +} + +bool PerfSource::isDone () { + return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty(); +} + +void PerfSource::write (Sender *sender) { + if (!mSummary.isDone()) { + mSummary.write(sender); + gSessionData->mSentSummary = true; + } + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } + if (!mCountersBuf.send(sender)) { + logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed"); + handleException(); + } +} |