diff options
Diffstat (limited to 'daemon/ExternalDriver.cpp')
-rw-r--r-- | daemon/ExternalDriver.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/daemon/ExternalDriver.cpp b/daemon/ExternalDriver.cpp new file mode 100644 index 0000000..c1b7983 --- /dev/null +++ b/daemon/ExternalDriver.cpp @@ -0,0 +1,269 @@ +/** + * Copyright (C) ARM Limited 2010-2015. 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 "ExternalDriver.h" + +#include <stdio.h> +#include <unistd.h> + +#include "Buffer.h" +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +static const char MALI_UTGARD_SETUP[] = "\0mali-utgard-setup"; +static const char SETUP_VERSION[] = "ANNOTATE_SETUP 1\n"; +static const size_t HEADER_SIZE = 1 + sizeof(uint32_t); + +#define HEADER_ERROR 0x80 +#define HEADER_ACK 0x81 +#define HEADER_REQUEST_COUNTERS 0x82 +#define HEADER_COUNTERS 0x83 +#define HEADER_ENABLE_COUNTERS 0x84 +#define HEADER_START 0x85 + +static uint32_t readLEInt(char *const buf) +{ + size_t i; + uint32_t v; + + v = 0; + for (i = 0; i < sizeof(v); ++i) + v |= (uint32_t)buf[i] << 8*i; + + return v; +} + +static int readPackedInt(char *const buf, const size_t bufSize, size_t *const pos, uint64_t *const l) +{ + uint8_t shift = 0; + uint8_t b = ~0; + + *l = 0; + while ((b & 0x80) != 0) { + if (*pos >= bufSize) { + return -1; + } + b = buf[*pos]; + *pos += 1; + *l |= (uint64_t)(b & 0x7f) << shift; + shift += 7; + } + + if (shift < 8*sizeof(*l) && (b & 0x40) != 0) { + *l |= -(1 << shift); + } + + return 0; +} + +class ExternalCounter : public DriverCounter { +public: + ExternalCounter(DriverCounter *next, const char *name, int cores) : DriverCounter(next, name), mCores(cores), mEvent(-1) {} + + ~ExternalCounter() { + } + + int getCores() const { return mCores; } + void setEvent(const int event) { mEvent = event; } + int getEvent() const { return mEvent; } + +private: + const int mCores; + int mEvent; + + // Intentionally undefined + ExternalCounter(const ExternalCounter &); + ExternalCounter &operator=(const ExternalCounter &); +}; + +ExternalDriver::ExternalDriver() : mUds(-1), mQueried(false), mStarted(false) { +} + +bool ExternalDriver::connect() const { + if (mUds < 0) { + mUds = OlySocket::connect(MALI_UTGARD_SETUP, sizeof(MALI_UTGARD_SETUP)); + if (mUds >= 0 && !writeAll(mUds, SETUP_VERSION, sizeof(SETUP_VERSION) - 1)) { + logg->logError("Unable to send setup version"); + handleException(); + } + } + return mUds >= 0; +} + +void ExternalDriver::disconnect() { + if (mUds >= 0) { + close(mUds); + mUds = -1; + mStarted = false; + } +} + +void ExternalDriver::query() const { + if (mQueried) { + return; + } + // Only try once even if it fails otherwise not all the possible counters may be shown + mQueried = true; + + char *const buf = gSessionData->mSharedData->mMaliUtgardCounters; + const size_t bufSize = sizeof(gSessionData->mSharedData->mMaliUtgardCounters); + size_t size = 0; + + if (!connect()) { + size = gSessionData->mSharedData->mMaliUtgardCountersSize; + logg->logMessage("Unable to connect, using cached version; size: %zi", size); + } else { + gSessionData->mSharedData->mMaliUtgardCountersSize = 0; + + buf[0] = HEADER_REQUEST_COUNTERS; + size_t pos = HEADER_SIZE; + Buffer::writeLEInt((unsigned char *)(buf + 1), pos); + if (!writeAll(mUds, buf, pos)) { + logg->logError("Unable to send request counters message"); + handleException(); + } + + if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_COUNTERS) { + logg->logError("Unable to read request counters response header"); + handleException(); + } + size = readLEInt(buf + 1); + if (size > bufSize || !readAll(mUds, buf, size - HEADER_SIZE)) { + logg->logError("Unable to read request counters response"); + handleException(); + } + + size -= HEADER_SIZE; + gSessionData->mSharedData->mMaliUtgardCountersSize = size; + logg->logMessage("Requested counters; size: %zi", size); + } + + size_t pos = 0; + while (pos < size) { + size_t begin = pos; + char *name = NULL; + uint64_t cores = -1; + while (pos < size && buf[pos] != '\0') { + ++pos; + } + if (pos > begin) { + name = strndup(buf + begin, pos - begin); + } + if (pos < size && buf[pos] == '\0') { + ++pos; + } + ; + if (name != NULL && readPackedInt(buf, bufSize, &pos, &cores) == 0) { + // Cheat so that this can be 'const' + ((ExternalDriver *)(this))->setCounters(new ExternalCounter(getCounters(), name, cores)); + } + } + + if (pos != size) { + logg->logError("Unable to parse request counters response"); + handleException(); + } +} + +void ExternalDriver::start() { + if (!connect()) { + return; + } + + if (mStarted) { + return; + } + // Only start once + mStarted = true; + + char buf[1<<12]; + int pos; + + buf[0] = HEADER_ENABLE_COUNTERS; + pos = HEADER_SIZE; + for (ExternalCounter *counter = static_cast<ExternalCounter *>(getCounters()); counter != NULL; counter = static_cast<ExternalCounter *>(counter->getNext())) { + if (!counter->isEnabled()) { + continue; + } + size_t nameLen = strlen(counter->getName()); + if (pos + nameLen + 1 + 2*Buffer::MAXSIZE_PACK32 > sizeof(buf)) { + logg->logError("Unable to enable counters, message is too large"); + handleException(); + } + memcpy(buf + pos, counter->getName(), nameLen + 1); + pos += nameLen + 1; + Buffer::packInt(buf, sizeof(buf), pos, counter->getEvent()); + Buffer::packInt(buf, sizeof(buf), pos, counter->getKey()); + } + Buffer::writeLEInt((unsigned char *)(buf + 1), pos); + if (!writeAll(mUds, buf, pos)) { + logg->logError("Unable to send enable counters message"); + handleException(); + } + + size_t size = 0; + if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_ACK) { + logg->logError("Unable to read enable counters response header"); + handleException(); + } + size = readLEInt(buf + 1); + if (size != HEADER_SIZE) { + logg->logError("Unable to parse enable counters response"); + handleException(); + } + + buf[0] = HEADER_START; + pos = HEADER_SIZE; + // ns/sec / samples/sec = ns/sample + // For sample rate of none, sample every 100ms + Buffer::packInt(buf, sizeof(buf), pos, NS_PER_S / (gSessionData->mSampleRate == 0 ? 10 : gSessionData->mSampleRate)); + Buffer::packInt(buf, sizeof(buf), pos, gSessionData->mLiveRate); + Buffer::writeLEInt((unsigned char *)(buf + 1), pos); + if (!writeAll(mUds, buf, pos)) { + logg->logError("Unable to send start message"); + handleException(); + } + + size = 0; + if (!readAll(mUds, buf, HEADER_SIZE) || buf[0] != (char)HEADER_ACK) { + logg->logError("Unable to read start response header"); + handleException(); + } + size = readLEInt(buf + 1); + if (size != HEADER_SIZE) { + logg->logError("Unable to parse start response"); + handleException(); + } +} + +bool ExternalDriver::claimCounter(const Counter &counter) const { + query(); + return super::claimCounter(counter); +} + +void ExternalDriver::setupCounter(Counter &counter) { + ExternalCounter *const externalCounter = static_cast<ExternalCounter *>(findCounter(counter)); + if (externalCounter == NULL) { + counter.setEnabled(false); + return; + } + externalCounter->setEnabled(true); + counter.setKey(externalCounter->getKey()); + if (counter.getEvent() != -1) { + externalCounter->setEvent(counter.getEvent()); + } + if (externalCounter->getCores() > 0) { + counter.setCores(externalCounter->getCores()); + } +} + +void ExternalDriver::resetCounters() { + query(); + super::resetCounters(); +} |