diff options
Diffstat (limited to 'tools/gator/daemon/Buffer.cpp')
-rw-r--r-- | tools/gator/daemon/Buffer.cpp | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp new file mode 100644 index 000000000000..dd19f7f8be76 --- /dev/null +++ b/tools/gator/daemon/Buffer.cpp @@ -0,0 +1,391 @@ +/** + * Copyright (C) ARM Limited 2013-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 "Buffer.h" + +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +#define mask (mSize - 1) + +enum { + CODE_PEA = 1, + CODE_KEYS = 2, + CODE_FORMAT = 3, + CODE_MAPS = 4, + CODE_COMM = 5, + CODE_KEYS_OLD = 6, +}; + +// Summary Frame Messages +enum { + MESSAGE_SUMMARY = 1, + MESSAGE_CORE_NAME = 3, +}; + +// From gator_marshaling.c +#define NEWLINE_CANARY \ + /* Unix */ \ + "1\n" \ + /* Windows */ \ + "2\r\n" \ + /* Mac OS */ \ + "3\r" \ + /* RISC OS */ \ + "4\n\r" \ + /* Add another character so the length isn't 0x0a bytes */ \ + "5" + +Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) { + if ((mSize & mask) != 0) { + logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2"); + handleException(); + } + frame(); +} + +Buffer::~Buffer() { + delete [] mBuf; +} + +void Buffer::write(Sender *const sender) { + if (!commitReady()) { + return; + } + + // determine the size of two halves + int length1 = mCommitPos - mReadPos; + char *buffer1 = mBuf + mReadPos; + int length2 = 0; + char *buffer2 = mBuf; + if (length1 < 0) { + length1 = mSize - mReadPos; + length2 = mCommitPos; + } + + logg->logMessage("Sending data length1: %i length2: %i", length1, length2); + + // start, middle or end + if (length1 > 0) { + sender->writeData(buffer1, length1, RESPONSE_APC_DATA); + } + + // possible wrap around + if (length2 > 0) { + sender->writeData(buffer2, length2, RESPONSE_APC_DATA); + } + + mReadPos = mCommitPos; +} + +bool Buffer::commitReady() const { + return mCommitPos != mReadPos; +} + +int Buffer::bytesAvailable() const { + int filled = mWritePos - mReadPos; + if (filled < 0) { + filled += mSize; + } + + int remaining = mSize - filled; + + if (mAvailable) { + // Give some extra room; also allows space to insert the overflow error packet + remaining -= 200; + } else { + // Hysteresis, prevents multiple overflow messages + remaining -= 2000; + } + + return remaining; +} + +bool Buffer::checkSpace(const int bytes) { + const int remaining = bytesAvailable(); + + if (remaining < bytes) { + mAvailable = false; + } else { + mAvailable = true; + } + + return mAvailable; +} + +int Buffer::contiguousSpaceAvailable() const { + int remaining = bytesAvailable(); + int contiguous = mSize - mWritePos; + if (remaining < contiguous) { + return remaining; + } else { + return contiguous; + } +} + +void Buffer::commit(const uint64_t time) { + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + const int typeLength = gSessionData->mLocalCapture ? 0 : 1; + int length = mWritePos - mCommitPos; + if (length < 0) { + length += mSize; + } + length = length - typeLength - sizeof(int32_t); + for (size_t byte = 0; byte < sizeof(int32_t); byte++) { + mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; + } + + logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); + mCommitPos = mWritePos; + + if (gSessionData->mLiveRate > 0) { + while (time > mCommitTime) { + mCommitTime += gSessionData->mLiveRate; + } + } + + if (!mIsDone) { + frame(); + } + + // send a notification that data is ready + sem_post(mReaderSem); +} + +void Buffer::check(const uint64_t time) { + int filled = mWritePos - mCommitPos; + if (filled < 0) { + filled += mSize; + } + if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { + commit(time); + } +} + +void Buffer::packInt(char *const buf, const int size, int &writePos, int32_t x) { + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + buf[(writePos + packedBytes) & /*mask*/(size - 1)] = b; + packedBytes++; + } + + writePos = (writePos + packedBytes) & /*mask*/(size - 1); +} + +void Buffer::packInt(int32_t x) { + packInt(mBuf, mSize, mWritePos, x); +} + +void Buffer::packInt64(int64_t x) { + int packedBytes = 0; + int more = true; + while (more) { + // low order 7 bits of x + char b = x & 0x7f; + x >>= 7; + + if ((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0)) { + more = false; + } else { + b |= 0x80; + } + + mBuf[(mWritePos + packedBytes) & mask] = b; + packedBytes++; + } + + mWritePos = (mWritePos + packedBytes) & mask; +} + +void Buffer::writeBytes(const void *const data, size_t count) { + size_t i; + for (i = 0; i < count; ++i) { + mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i]; + } + + mWritePos = (mWritePos + i) & mask; +} + +void Buffer::writeString(const char *const str) { + const int len = strlen(str); + packInt(len); + writeBytes(str, len); +} + +void Buffer::frame() { + if (!gSessionData->mLocalCapture) { + packInt(RESPONSE_APC_DATA); + } + // Reserve space for the length + mWritePos += sizeof(int32_t); + packInt(mBufType); + packInt(mCore); +} + +void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) { + packInt(MESSAGE_SUMMARY); + writeString(NEWLINE_CANARY); + packInt64(timestamp); + packInt64(uptime); + packInt64(monotonicDelta); + writeString("uname"); + writeString(uname); + writeString(""); + check(1); +} + +void Buffer::coreName(const int core, const int cpuid, const char *const name) { + if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) { + packInt(MESSAGE_CORE_NAME); + packInt(core); + packInt(cpuid); + writeString(name); + } + check(1); +} + +bool Buffer::eventHeader(const uint64_t curr_time) { + bool retval = false; + if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + packInt(0); // key of zero indicates a timestamp + packInt64(curr_time); + retval = true; + } + + return retval; +} + +bool Buffer::eventTid(const int tid) { + bool retval = false; + if (checkSpace(2 * MAXSIZE_PACK32)) { + packInt(1); // key of 1 indicates a tid + packInt(tid); + retval = true; + } + + return retval; +} + +void Buffer::event(const int32_t key, const int32_t value) { + if (checkSpace(2 * MAXSIZE_PACK32)) { + packInt(key); + packInt(value); + } +} + +void Buffer::event64(const int64_t key, const int64_t value) { + if (checkSpace(2 * MAXSIZE_PACK64)) { + packInt64(key); + packInt64(value); + } +} + +void Buffer::pea(const struct perf_event_attr *const pea, int key) { + if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) { + packInt(CODE_PEA); + writeBytes(pea, pea->size); + packInt(key); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + // Don't know the real perf time so use 1 as it will work for now + check(1); +} + +void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) { + if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) { + packInt(CODE_KEYS); + packInt(count); + for (int i = 0; i < count; ++i) { + packInt64(ids[i]); + packInt(keys[i]); + } + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::keysOld(const int keyCount, const int *const keys, const int bytes, const char *const buf) { + if (checkSpace((2 + keyCount) * MAXSIZE_PACK32 + bytes)) { + packInt(CODE_KEYS_OLD); + packInt(keyCount); + for (int i = 0; i < keyCount; ++i) { + packInt(keys[i]); + } + writeBytes(buf, bytes); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::format(const int length, const char *const format) { + if (checkSpace(MAXSIZE_PACK32 + length + 1)) { + packInt(CODE_FORMAT); + writeBytes(format, length + 1); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::maps(const int pid, const int tid, const char *const maps) { + const int mapsLen = strlen(maps) + 1; + if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) { + packInt(CODE_MAPS); + packInt(pid); + packInt(tid); + writeBytes(maps, mapsLen); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) { + const int imageLen = strlen(image) + 1; + const int commLen = strlen(comm) + 1; + if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) { + packInt(CODE_COMM); + packInt(pid); + packInt(tid); + writeBytes(image, imageLen); + writeBytes(comm, commLen); + } else { + logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs"); + handleException(); + } + check(1); +} + +void Buffer::setDone() { + mIsDone = true; + commit(0); +} + +bool Buffer::isDone() const { + return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos; +} |