diff options
Diffstat (limited to 'daemon')
78 files changed, 3804 insertions, 599 deletions
diff --git a/daemon/Android.mk b/daemon/Android.mk index a042971..045d028 100644 --- a/daemon/Android.mk +++ b/daemon/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h) +XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h) LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors @@ -9,22 +9,33 @@ LOCAL_SRC_FILES := \ Buffer.cpp \ CapturedXML.cpp \ Child.cpp \ - Collector.cpp \ ConfigurationXML.cpp \ Driver.cpp \ + DriverSource.cpp \ + DynBuf.cpp \ EventsXML.cpp \ + ExternalSource.cpp \ Fifo.cpp \ Hwmon.cpp \ KMod.cpp \ LocalCapture.cpp \ Logging.cpp \ main.cpp \ + Monitor.cpp \ OlySocket.cpp \ OlyUtility.cpp \ + PerfBuffer.cpp \ + PerfDriver.cpp \ + PerfGroup.cpp \ + PerfSource.cpp \ + Proc.cpp \ Sender.cpp \ SessionData.cpp \ SessionXML.cpp \ + Source.cpp \ StreamlineSetup.cpp \ + UEvent.cpp \ + UserSpaceSource.cpp \ libsensors/access.c \ libsensors/conf-lex.c \ libsensors/conf-parse.c \ diff --git a/daemon/Buffer.cpp b/daemon/Buffer.cpp index 090a715..93557da 100644 --- a/daemon/Buffer.cpp +++ b/daemon/Buffer.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -12,33 +12,60 @@ #include "Sender.h" #include "SessionData.h" -#define mask (size - 1) - -Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) { - if ((size & mask) != 0) { +#define mask (mSize - 1) + +enum { + CODE_PEA = 1, + CODE_KEYS = 2, + CODE_FORMAT = 3, + CODE_MAPS = 4, + CODE_COMM = 5, +}; + +// 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 [] buf; +Buffer::~Buffer() { + delete [] mBuf; } -void Buffer::write (Sender * const sender) { +void Buffer::write(Sender *const sender) { if (!commitReady()) { return; } // determine the size of two halves - int length1 = commitPos - readPos; - char * buffer1 = buf + readPos; + int length1 = mCommitPos - mReadPos; + char *buffer1 = mBuf + mReadPos; int length2 = 0; - char * buffer2 = buf; + char *buffer2 = mBuf; if (length1 < 0) { - length1 = size - readPos; - length2 = commitPos; + length1 = mSize - mReadPos; + length2 = mCommitPos; } logg->logMessage("Sending data length1: %i length2: %i", length1, length2); @@ -53,22 +80,22 @@ void Buffer::write (Sender * const sender) { sender->writeData(buffer2, length2, RESPONSE_APC_DATA); } - readPos = commitPos; + mReadPos = mCommitPos; } -bool Buffer::commitReady () const { - return commitPos != readPos; +bool Buffer::commitReady() const { + return mCommitPos != mReadPos; } -int Buffer::bytesAvailable () const { - int filled = writePos - readPos; +int Buffer::bytesAvailable() const { + int filled = mWritePos - mReadPos; if (filled < 0) { - filled += size; + filled += mSize; } - int remaining = size - filled; + int remaining = mSize - filled; - if (available) { + if (mAvailable) { // Give some extra room; also allows space to insert the overflow error packet remaining -= 200; } else { @@ -79,58 +106,68 @@ int Buffer::bytesAvailable () const { return remaining; } -bool Buffer::checkSpace (const int bytes) { +bool Buffer::checkSpace(const int bytes) { const int remaining = bytesAvailable(); if (remaining < bytes) { - available = false; + mAvailable = false; } else { - available = true; + mAvailable = true; } - return available; + 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) { +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 = writePos - commitPos; + int length = mWritePos - mCommitPos; if (length < 0) { - length += size; + length += mSize; } length = length - typeLength - sizeof(int32_t); for (size_t byte = 0; byte < sizeof(int32_t); byte++) { - buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; + mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF; } - logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos); - commitPos = writePos; + logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos); + mCommitPos = mWritePos; if (gSessionData->mLiveRate > 0) { - while (time > commitTime) { - commitTime += gSessionData->mLiveRate; + while (time > mCommitTime) { + mCommitTime += gSessionData->mLiveRate; } } - if (!done) { + if (!mIsDone) { frame(); } // send a notification that data is ready - sem_post(readerSem); + sem_post(mReaderSem); } -void Buffer::check (const uint64_t time) { - int filled = writePos - commitPos; +void Buffer::check(const uint64_t time) { + int filled = mWritePos - mCommitPos; if (filled < 0) { - filled += size; + filled += mSize; } - if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) { + if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) { commit(time); } } -void Buffer::packInt (int32_t x) { +void Buffer::packInt(int32_t x) { int packedBytes = 0; int more = true; while (more) { @@ -144,14 +181,14 @@ void Buffer::packInt (int32_t x) { b |= 0x80; } - buf[(writePos + packedBytes) & mask] = b; + mBuf[(mWritePos + packedBytes) & mask] = b; packedBytes++; } - writePos = (writePos + packedBytes) & mask; + mWritePos = (mWritePos + packedBytes) & mask; } -void Buffer::packInt64 (int64_t x) { +void Buffer::packInt64(int64_t x) { int packedBytes = 0; int more = true; while (more) { @@ -165,24 +202,61 @@ void Buffer::packInt64 (int64_t x) { b |= 0x80; } - buf[(writePos + packedBytes) & mask] = b; + mBuf[(mWritePos + packedBytes) & mask] = b; packedBytes++; } - writePos = (writePos + packedBytes) & mask; + 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::frame () { +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 - writePos += sizeof(int32_t); - packInt(buftype); - packInt(core); + mWritePos += sizeof(int32_t); + packInt(mBufType); + packInt(mCore); } -bool Buffer::eventHeader (const uint64_t curr_time) { +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 @@ -193,9 +267,9 @@ bool Buffer::eventHeader (const uint64_t curr_time) { return retval; } -bool Buffer::eventTid (const int tid) { +bool Buffer::eventTid(const int tid) { bool retval = false; - if (checkSpace(2*MAXSIZE_PACK32)) { + if (checkSpace(2 * MAXSIZE_PACK32)) { packInt(1); // key of 1 indicates a tid packInt(tid); retval = true; @@ -204,25 +278,94 @@ bool Buffer::eventTid (const int tid) { return retval; } -void Buffer::event (const int32_t key, const int32_t value) { +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) { +void Buffer::event64(const int64_t key, const int64_t value) { if (checkSpace(2 * MAXSIZE_PACK64)) { packInt64(key); packInt64(value); } } -void Buffer::setDone () { - done = true; +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::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 done && readPos == commitPos && commitPos == writePos; +bool Buffer::isDone() const { + return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos; } diff --git a/daemon/Buffer.h b/daemon/Buffer.h index b3c8d78..5023777 100644 --- a/daemon/Buffer.h +++ b/daemon/Buffer.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -9,54 +9,89 @@ #ifndef BUFFER_H #define BUFFER_H -#include <stddef.h> #include <stdint.h> #include <semaphore.h> +#include "k/perf_event.h" + class Sender; +enum { + FRAME_SUMMARY = 1, + FRAME_BLOCK_COUNTER = 5, + FRAME_EXTERNAL = 10, + FRAME_PERF_ATTRS = 11, + FRAME_PERF = 12, +}; + class Buffer { public: static const size_t MAXSIZE_PACK32 = 5; static const size_t MAXSIZE_PACK64 = 10; - Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem); - ~Buffer (); + Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem); + ~Buffer(); + + void write(Sender *sender); + + int bytesAvailable() const; + int contiguousSpaceAvailable() const; + void commit(const uint64_t time); + void check(const uint64_t time); - void write (Sender * sender); + void frame(); - int bytesAvailable () const; - void commit (const uint64_t time); - void check (const uint64_t time); + // Summary messages + void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname); + void coreName(const int core, const int cpuid, const char *const name); - void frame (); + // Block Counter messages + bool eventHeader(uint64_t curr_time); + bool eventTid(int tid); + void event(int32_t key, int32_t value); + void event64(int64_t key, int64_t value); - bool eventHeader (uint64_t curr_time); - bool eventTid (int tid); - void event (int32_t key, int32_t value); - void event64 (int64_t key, int64_t value); + // Perf Attrs messages + void pea(const struct perf_event_attr *const pea, int key); + void keys(const int count, const __u64 *const ids, const int *const keys); + void format(const int length, const char *const format); + void maps(const int pid, const int tid, const char *const maps); + void comm(const int pid, const int tid, const char *const image, const char *const comm); - void setDone (); - bool isDone () const; + void setDone(); + bool isDone() const; + + // Prefer a new member to using these functions if possible + char *getWritePos() { return mBuf + mWritePos; } + void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); } + + static void writeLEInt(unsigned char *buf, int v) { + buf[0] = (v >> 0) & 0xFF; + buf[1] = (v >> 8) & 0xFF; + buf[2] = (v >> 16) & 0xFF; + buf[3] = (v >> 24) & 0xFF; + } private: - bool commitReady () const; - bool checkSpace (int bytes); - - void packInt (int32_t x); - void packInt64 (int64_t x); - - const int32_t core; - const int32_t buftype; - const int size; - int readPos; - int writePos; - int commitPos; - bool available; - bool done; - char *const buf; - uint64_t commitTime; - sem_t *const readerSem; + bool commitReady() const; + bool checkSpace(int bytes); + + void packInt(int32_t x); + void packInt64(int64_t x); + void writeBytes(const void *const data, size_t count); + void writeString(const char *const str); + + const int32_t mCore; + const int32_t mBufType; + const int mSize; + int mReadPos; + int mWritePos; + int mCommitPos; + bool mAvailable; + bool mIsDone; + char *const mBuf; + uint64_t mCommitTime; + sem_t *const mReaderSem; // Intentionally unimplemented Buffer(const Buffer &); diff --git a/daemon/CapturedXML.cpp b/daemon/CapturedXML.cpp index 30c4c44..cf79b72 100644 --- a/daemon/CapturedXML.cpp +++ b/daemon/CapturedXML.cpp @@ -1,16 +1,18 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 "CapturedXML.h" + #include <stdlib.h> #include <string.h> #include <dirent.h> + #include "SessionData.h" -#include "CapturedXML.h" #include "Logging.h" #include "OlyUtility.h" @@ -30,6 +32,9 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { captured = mxmlNewElement(xml, "captured"); mxmlElementSetAttr(captured, "version", "1"); + if (gSessionData->perf.isSetup()) { + mxmlElementSetAttr(captured, "type", "Perf"); + } mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); if (includeTime) { // Send the following only after the capture is complete if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) @@ -41,7 +46,7 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) { mxmlElementSetAttr(target, "name", gSessionData->mCoreName); mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); - mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId); + mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId); if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) { mxmlElementSetAttr(target, "supports_live", "yes"); diff --git a/daemon/CapturedXML.h b/daemon/CapturedXML.h index b0482f5..efc1e52 100644 --- a/daemon/CapturedXML.h +++ b/daemon/CapturedXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/Child.cpp b/daemon/Child.cpp index 9ee2ef8..ca33561 100644 --- a/daemon/Child.cpp +++ b/daemon/Child.cpp @@ -1,38 +1,39 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 "Child.h" + #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <sys/prctl.h> + #include "Logging.h" #include "CapturedXML.h" #include "SessionData.h" -#include "Child.h" #include "LocalCapture.h" -#include "Collector.h" #include "Sender.h" #include "OlyUtility.h" +#include "OlySocket.h" #include "StreamlineSetup.h" #include "ConfigurationXML.h" #include "Driver.h" -#include "Fifo.h" -#include "Buffer.h" - -#define NS_PER_S ((uint64_t)1000000000) -#define NS_PER_US 1000 +#include "PerfSource.h" +#include "DriverSource.h" +#include "UserSpaceSource.h" +#include "ExternalSource.h" static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads -static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads -static Buffer* buffer = NULL; +static Source *primarySource = NULL; +static Source *userSpaceSource = NULL; +static Source *externalSource = NULL; static Sender* sender = NULL; // Shared by Child.cpp and spawned threads -static Collector* collector = NULL; Child* child = NULL; // shared by Child.cpp and main.cpp extern void cleanUp(); @@ -78,7 +79,7 @@ static void child_handler(int signum) { } beenHere = true; logg->logMessage("Gator is shutting down."); - if (signum == SIGALRM || !collector) { + if (signum == SIGALRM || !primarySource) { exit(1); } else { child->endSession(); @@ -139,77 +140,22 @@ static void *stopThread(void *) { return 0; } -static void *countersThread(void *) { - prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); - - gSessionData->hwmon.start(); - - int64_t monotonic_started = 0; - while (monotonic_started <= 0) { - usleep(10); - - if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { - logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); - handleException(); - } - } - - uint64_t next_time = 0; - while (gSessionData->mSessionIsActive) { - struct timespec ts; -#ifndef CLOCK_MONOTONIC_RAW - // Android doesn't have this defined but it was added in Linux 2.6.28 -#define CLOCK_MONOTONIC_RAW 4 -#endif - if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { - logg->logError(__FILE__, __LINE__, "Failed to get uptime"); - handleException(); - } - const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; - // Sample ten times a second ignoring gSessionData->mSampleRate - next_time += NS_PER_S/10;//gSessionData->mSampleRate; - if (next_time < curr_time) { - logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); - next_time = curr_time; - } - - if (buffer->eventHeader(curr_time)) { - gSessionData->hwmon.read(buffer); - // Only check after writing all counters so that time and corresponding counters appear in the same frame - buffer->check(curr_time); - } - - if (buffer->bytesAvailable() <= 0) { - logg->logMessage("One shot (counters)"); - child->endSession(); - } - - usleep((next_time - curr_time)/NS_PER_US); - } - - buffer->setDone(); - - return NULL; -} - static void *senderThread(void *) { - int length = 1; - char* data; char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; sem_post(&senderThreadStarted); prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); sem_wait(&haltPipeline); - while (length > 0 || !buffer->isDone()) { + while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) { sem_wait(&senderSem); - data = collectorFifo->read(&length); - if (data != NULL) { - sender->writeData(data, length, RESPONSE_APC_DATA); - collectorFifo->release(); + + primarySource->write(sender); + if (userSpaceSource != NULL) { + userSpaceSource->write(sender); } - if (!buffer->isDone()) { - buffer->write(sender); + if (externalSource != NULL) { + externalSource->write(sender); } } @@ -255,15 +201,13 @@ void Child::initialization() { void Child::endSession() { gSessionData->mSessionIsActive = false; - collector->stop(); + primarySource->interrupt(); sem_post(&haltPipeline); } void Child::run() { - char* collectBuffer; - int bytesCollected = 0; LocalCapture* localCapture = NULL; - pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID; + pthread_t durationThreadID, stopThreadID, senderThreadID; prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); @@ -282,7 +226,11 @@ void Child::run() { { ConfigurationXML configuration; } // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated - collector = new Collector(); + if (!gSessionData->perf.isSetup()) { + primarySource = new DriverSource(&senderSem, &startProfile); + } else { + primarySource = new PerfSource(&senderSem, &startProfile); + } // Initialize all drivers for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { @@ -317,15 +265,11 @@ void Child::run() { free(xmlString); } - // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length - logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); - collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); - - // Get the initial pointer to the collect buffer - collectBuffer = collectorFifo->start(); - - // Create a new Block Counter Buffer - buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem); + // Must be after session XML is parsed + if (!primarySource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } // Sender thread shall be halted until it is signaled for one shot mode sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); @@ -340,14 +284,21 @@ void Child::run() { thread_creation_success = false; } - bool startcountersThread = gSessionData->hwmon.countersEnabled(); - if (startcountersThread) { - if (pthread_create(&countersThreadID, NULL, countersThread, this)) { - thread_creation_success = false; + if (gSessionData->hwmon.countersEnabled()) { + userSpaceSource = new UserSpaceSource(&senderSem); + if (!userSpaceSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); } - } else { - // Let senderThread know there is no buffer data to send - buffer->setDone(); + userSpaceSource->start(); + } + if (access("/tmp/gator", F_OK) == 0) { + externalSource = new ExternalSource(&senderSem); + if (!externalSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare for capture"); + handleException(); + } + externalSource->start(); } if (!thread_creation_success) { @@ -359,28 +310,13 @@ void Child::run() { sem_wait(&senderThreadStarted); // Start profiling - logg->logMessage("********** Profiling started **********"); - collector->start(); - sem_post(&startProfile); - - // Collect Data - do { - // This command will stall until data is received from the driver - bytesCollected = collector->collect(collectBuffer); - - // In one shot mode, stop collection once all the buffers are filled - if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { - if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { - logg->logMessage("One shot"); - endSession(); - } - } - collectBuffer = collectorFifo->write(bytesCollected); - } while (bytesCollected > 0); - logg->logMessage("Exit collect data loop"); + primarySource->run(); - if (startcountersThread) { - pthread_join(countersThreadID, NULL); + if (externalSource != NULL) { + externalSource->join(); + } + if (userSpaceSource != NULL) { + userSpaceSource->join(); } // Wait for the other threads to exit @@ -401,9 +337,9 @@ void Child::run() { logg->logMessage("Profiling ended."); - delete buffer; - delete collectorFifo; + delete externalSource; + delete userSpaceSource; + delete primarySource; delete sender; - delete collector; delete localCapture; } diff --git a/daemon/Child.h b/daemon/Child.h index 0330e9d..9e206d7 100644 --- a/daemon/Child.h +++ b/daemon/Child.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -9,8 +9,6 @@ #ifndef __CHILD_H__ #define __CHILD_H__ -#include <pthread.h> - class OlySocket; class Child { diff --git a/daemon/Collector.h b/daemon/Collector.h deleted file mode 100644 index c5e9eac..0000000 --- a/daemon/Collector.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2013. 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. - */ - -#ifndef __COLLECTOR_H__ -#define __COLLECTOR_H__ - -#include <stdio.h> - -class Collector { -public: - Collector(); - ~Collector(); - void start(); - void stop(); - int collect(char* buffer); - int getBufferSize() {return mBufferSize;} - - static int readIntDriver(const char* path, int* value); - static int readInt64Driver(const char* path, int64_t* value); - static int writeDriver(const char* path, int value); - static int writeDriver(const char* path, int64_t value); - static int writeDriver(const char* path, const char* data); - static int writeReadDriver(const char* path, int* value); - static int writeReadDriver(const char* path, int64_t* value); - -private: - int mBufferSize; - int mBufferFD; - - void checkVersion(); -}; - -#endif //__COLLECTOR_H__ diff --git a/daemon/Config.h b/daemon/Config.h new file mode 100644 index 0000000..6f5e2aa --- /dev/null +++ b/daemon/Config.h @@ -0,0 +1,17 @@ +/** + * 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. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define ARRAY_LENGTH(A) static_cast<int>(sizeof(A)/sizeof((A)[0])) + +#define MAX_PERFORMANCE_COUNTERS 50 +#define NR_CPUS 16 + +#endif // CONFIG_H diff --git a/daemon/ConfigurationXML.cpp b/daemon/ConfigurationXML.cpp index 2a5252a..fd479f2 100644 --- a/daemon/ConfigurationXML.cpp +++ b/daemon/ConfigurationXML.cpp @@ -1,15 +1,17 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 "ConfigurationXML.h" + #include <string.h> #include <stdlib.h> #include <dirent.h> -#include "ConfigurationXML.h" + #include "Driver.h" #include "Logging.h" #include "OlyUtility.h" @@ -67,6 +69,7 @@ int ConfigurationXML::parse(const char* configurationXML) { // clear counter overflow gSessionData->mCounterOverflow = 0; + gSessionData->mIsEBS = false; mIndex = 0; // disable all counters prior to parsing the configuration xml @@ -155,6 +158,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER)); if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16)); if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10)); + if (counter.getCount() > 0) { + gSessionData->mIsEBS = true; + } counter.setEnabled(true); // Associate a driver with each counter @@ -181,9 +187,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) { } void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) { -#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len - xml = (const char *)configuration_xml; - len = configuration_xml_len; +#include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len + xml = (const char *)defaults_xml; + len = defaults_xml_len; } void ConfigurationXML::getPath(char* path) { diff --git a/daemon/ConfigurationXML.h b/daemon/ConfigurationXML.h index 5650f48..efa415e 100644 --- a/daemon/ConfigurationXML.h +++ b/daemon/ConfigurationXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/Counter.h b/daemon/Counter.h index 231a85d..6891745 100644 --- a/daemon/Counter.h +++ b/daemon/Counter.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -25,7 +25,7 @@ public: void clear () { mType[0] = '\0'; mEnabled = false; - mEvent = 0; + mEvent = -1; mCount = 0; mKey = 0; mDriver = NULL; diff --git a/daemon/Driver.cpp b/daemon/Driver.cpp index c262467..09e0401 100644 --- a/daemon/Driver.cpp +++ b/daemon/Driver.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 diff --git a/daemon/Driver.h b/daemon/Driver.h index f3a932f..e5ed7b6 100644 --- a/daemon/Driver.h +++ b/daemon/Driver.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -27,7 +27,7 @@ public: virtual void setupCounter(Counter &counter) = 0; // Emits available counters - virtual void writeCounters(mxml_node_t *root) const = 0; + virtual int writeCounters(mxml_node_t *root) const = 0; // Emits possible dynamically generated events/counters virtual void writeEvents(mxml_node_t *) const {} diff --git a/daemon/Collector.cpp b/daemon/DriverSource.cpp index bf73534..f78ec6b 100644 --- a/daemon/Collector.cpp +++ b/daemon/DriverSource.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -8,23 +8,47 @@ #define __STDC_FORMAT_MACROS +#include "DriverSource.h" + #include <fcntl.h> -#include <unistd.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <sys/time.h> #include <inttypes.h> -#include "Collector.h" -#include "SessionData.h" +#include <unistd.h> + +#include "Child.h" +#include "Fifo.h" #include "Logging.h" #include "Sender.h" +#include "SessionData.h" + +extern Child *child; -// Driver initialization independent of session settings -Collector::Collector() { - mBufferFD = 0; +DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) { + int driver_version = 0; - checkVersion(); + if (readIntDriver("/dev/gator/version", &driver_version) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); + handleException(); + } + + // Verify the driver version matches the daemon version + if (driver_version != PROTOCOL_VERSION) { + if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { + // One of the mismatched versions is development version + logg->logError(__FILE__, __LINE__, + "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" + ">> The following must be synchronized from engineering repository:\n" + ">> * gator driver\n" + ">> * gator daemon\n" + ">> * Streamline", driver_version, PROTOCOL_VERSION); + handleException(); + } else { + // Release version mismatch + logg->logError(__FILE__, __LINE__, + "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" + ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); + handleException(); + } + } int enable = -1; if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { @@ -37,14 +61,15 @@ Collector::Collector() { gSessionData->mCores = 1; } - mBufferSize = 0; if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); handleException(); } } -Collector::~Collector() { +DriverSource::~DriverSource() { + delete mFifo; + // Write zero for safety, as a zero should have already been written writeDriver("/dev/gator/enable", "0"); @@ -54,36 +79,21 @@ Collector::~Collector() { } } -void Collector::checkVersion() { - int driver_version = 0; - - if (readIntDriver("/dev/gator/version", &driver_version) == -1) { - logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); - handleException(); - } +bool DriverSource::prepare() { + // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length + logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize); + mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem); - // Verify the driver version matches the daemon version - if (driver_version != PROTOCOL_VERSION) { - if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { - // One of the mismatched versions is development version - logg->logError(__FILE__, __LINE__, - "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" - ">> The following must be synchronized from engineering repository:\n" - ">> * gator driver\n" - ">> * gator daemon\n" - ">> * Streamline", driver_version, PROTOCOL_VERSION); - handleException(); - } else { - // Release version mismatch - logg->logError(__FILE__, __LINE__, - "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" - ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); - handleException(); - } - } + return true; } -void Collector::start() { +void DriverSource::run() { + // Get the initial pointer to the collect buffer + char *collectBuffer = mFifo->start(); + int bytesCollected = 0; + + logg->logMessage("********** Profiling started **********"); + // Set the maximum backtrace depth if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); @@ -125,79 +135,112 @@ void Collector::start() { } lseek(mBufferFD, 0, SEEK_SET); + + sem_post(mStartProfile); + + // Collect Data + do { + // This command will stall until data is received from the driver + // Calls event_buffer_read in the driver + errno = 0; + bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); + + // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data + if (bytesCollected == -1 && errno == EINTR) { + bytesCollected = read(mBufferFD, collectBuffer, mBufferSize); + } + + // return the total bytes written + logg->logMessage("Driver read of %d bytes", bytesCollected); + + // In one shot mode, stop collection once all the buffers are filled + if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { + if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) { + logg->logMessage("One shot"); + child->endSession(); + } + } + collectBuffer = mFifo->write(bytesCollected); + } while (bytesCollected > 0); + + logg->logMessage("Exit collect data loop"); } -// These commands should cause the read() function in collect() to return -void Collector::stop() { - // This will stop the driver from profiling +void DriverSource::interrupt() { + // This command should cause the read() function in collect() to return and stop the driver from profiling if (writeDriver("/dev/gator/enable", "0") != 0) { logg->logMessage("Stopping kernel failed"); } } -int Collector::collect(char* buffer) { - // Calls event_buffer_read in the driver - int bytesRead; - - errno = 0; - bytesRead = read(mBufferFD, buffer, mBufferSize); +bool DriverSource::isDone() { + return mLength <= 0; +} - // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data - if (bytesRead == -1 && errno == EINTR) { - bytesRead = read(mBufferFD, buffer, mBufferSize); +void DriverSource::write(Sender *sender) { + char *data = mFifo->read(&mLength); + if (data != NULL) { + sender->writeData(data, mLength, RESPONSE_APC_DATA); + mFifo->release(); } - - // return the total bytes written - logg->logMessage("Driver read of %d bytes", bytesRead); - return bytesRead; } -int Collector::readIntDriver(const char* fullpath, int* value) { - FILE* file = fopen(fullpath, "r"); - if (file == NULL) { +int DriverSource::readIntDriver(const char *fullpath, int *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { + return -1; + } + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { return -1; } - if (fscanf(file, "%u", value) != 1) { - fclose(file); + data[bytes] = '\0'; + + char *endptr; + errno = 0; + *value = strtol(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { logg->logMessage("Invalid value in file %s", fullpath); return -1; } - fclose(file); + return 0; } -int Collector::readInt64Driver(const char* fullpath, int64_t* value) { - FILE* file = fopen(fullpath, "r"); - if (file == NULL) { +int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) { + char data[40]; // Sufficiently large to hold any integer + const int fd = open(fullpath, O_RDONLY); + if (fd < 0) { return -1; } - if (fscanf(file, "%" SCNi64, value) != 1) { - fclose(file); - logg->logMessage("Invalid value in file %s", fullpath); + + const ssize_t bytes = read(fd, data, sizeof(data) - 1); + close(fd); + if (bytes < 0) { return -1; } - fclose(file); - return 0; -} + data[bytes] = '\0'; -int Collector::writeDriver(const char* path, int value) { - char data[40]; // Sufficiently large to hold any integer - snprintf(data, sizeof(data), "%d", value); - return writeDriver(path, data); -} + char *endptr; + errno = 0; + *value = strtoll(data, &endptr, 10); + if (errno != 0 || *endptr != '\n') { + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } -int Collector::writeDriver(const char* path, int64_t value) { - char data[40]; // Sufficiently large to hold any integer - snprintf(data, sizeof(data), "%" PRIi64, value); - return writeDriver(path, data); + return 0; } -int Collector::writeDriver(const char* fullpath, const char* data) { +int DriverSource::writeDriver(const char *fullpath, const char *data) { int fd = open(fullpath, O_WRONLY); if (fd < 0) { return -1; } - if (write(fd, data, strlen(data)) < 0) { + if (::write(fd, data, strlen(data)) < 0) { close(fd); logg->logMessage("Opened but could not write to %s", fullpath); return -1; @@ -206,14 +249,26 @@ int Collector::writeDriver(const char* fullpath, const char* data) { return 0; } -int Collector::writeReadDriver(const char* path, int* value) { +int DriverSource::writeDriver(const char *path, int value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%d", value); + return writeDriver(path, data); +} + +int DriverSource::writeDriver(const char *path, int64_t value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%" PRIi64, value); + return writeDriver(path, data); +} + +int DriverSource::writeReadDriver(const char *path, int *value) { if (writeDriver(path, *value) || readIntDriver(path, value)) { return -1; } return 0; } -int Collector::writeReadDriver(const char* path, int64_t* value) { +int DriverSource::writeReadDriver(const char *path, int64_t *value) { if (writeDriver(path, *value) || readInt64Driver(path, value)) { return -1; } diff --git a/daemon/DriverSource.h b/daemon/DriverSource.h new file mode 100644 index 0000000..dcf1078 --- /dev/null +++ b/daemon/DriverSource.h @@ -0,0 +1,52 @@ +/** + * 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. + */ + +#ifndef DRIVERSOURCE_H +#define DRIVERSOURCE_H + +#include <semaphore.h> +#include <stdint.h> + +#include "Source.h" + +class Fifo; + +class DriverSource : public Source { +public: + DriverSource(sem_t *senderSem, sem_t *startProfile); + ~DriverSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + + static int readIntDriver(const char *fullpath, int *value); + static int readInt64Driver(const char *fullpath, int64_t *value); + static int writeDriver(const char *fullpath, const char *data); + static int writeDriver(const char *path, int value); + static int writeDriver(const char *path, int64_t value); + static int writeReadDriver(const char *path, int *value); + static int writeReadDriver(const char *path, int64_t *value); + +private: + Fifo *mFifo; + sem_t *const mSenderSem; + sem_t *const mStartProfile; + int mBufferSize; + int mBufferFD; + int mLength; + + // Intentionally unimplemented + DriverSource(const DriverSource &); + DriverSource &operator=(const DriverSource &); +}; + +#endif // DRIVERSOURCE_H diff --git a/daemon/DynBuf.cpp b/daemon/DynBuf.cpp new file mode 100644 index 0000000..6f92b33 --- /dev/null +++ b/daemon/DynBuf.cpp @@ -0,0 +1,139 @@ +/** + * 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 "DynBuf.h" + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> + +#include "Logging.h" + +// Pick an aggressive size as buffer is primarily used for disk IO +#define MIN_BUFFER_FREE (1 << 12) + +int DynBuf::resize(const size_t minCapacity) { + size_t scaledCapacity = 2 * capacity; + if (scaledCapacity < minCapacity) { + scaledCapacity = minCapacity; + } + if (scaledCapacity < 2 * MIN_BUFFER_FREE) { + scaledCapacity = 2 * MIN_BUFFER_FREE; + } + capacity = scaledCapacity; + + buf = static_cast<char *>(realloc(buf, capacity)); + if (buf == NULL) { + return -errno; + } + + return 0; +} + +bool DynBuf::read(const char *const path) { + int result = false; + + const int fd = open(path, O_RDONLY); + if (fd < 0) { + logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + length = 0; + + for (;;) { + const size_t minCapacity = length + MIN_BUFFER_FREE + 1; + if (capacity < minCapacity) { + if (resize(minCapacity) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + + const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } else if (bytes == 0) { + break; + } + length += bytes; + } + + buf[length] = '\0'; + result = true; + + fail: + close(fd); + + return result; +} + +int DynBuf::readlink(const char *const path) { + ssize_t bytes = MIN_BUFFER_FREE; + + for (;;) { + if (static_cast<size_t>(bytes) >= capacity) { + const int err = resize(2 * bytes); + if (err != 0) { + return err; + } + } + bytes = ::readlink(path, buf, capacity); + if (bytes < 0) { + return -errno; + } else if (static_cast<size_t>(bytes) < capacity) { + break; + } + } + + length = bytes; + buf[bytes] = '\0'; + + return 0; +} + +bool DynBuf::printf(const char *format, ...) { + va_list ap; + + if (capacity <= 0) { + if (resize(2 * MIN_BUFFER_FREE) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + va_start(ap, format); + int bytes = vsnprintf(buf, capacity, format, ap); + va_end(ap); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (static_cast<size_t>(bytes) > capacity) { + if (resize(bytes + 1) != 0) { + logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + va_start(ap, format); + bytes = vsnprintf(buf, capacity, format, ap); + va_end(ap); + if (bytes < 0) { + logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + length = bytes; + + return true; +} diff --git a/daemon/DynBuf.h b/daemon/DynBuf.h new file mode 100644 index 0000000..2f4554a --- /dev/null +++ b/daemon/DynBuf.h @@ -0,0 +1,52 @@ +/** + * 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. + */ + +#ifndef DYNBUF_H +#define DYNBUF_H + +#include <stdlib.h> + +class DynBuf { +public: + DynBuf() : capacity(0), length(0), buf(NULL) {} + ~DynBuf() { + reset(); + } + + inline void reset() { + capacity = 0; + length = 0; + if (buf != NULL) { + free(buf); + buf = NULL; + } + } + + bool read(const char *const path); + // On error instead of printing the error and returning false, this returns -errno + int readlink(const char *const path); + __attribute__ ((format(printf, 2, 3))) + bool printf(const char *format, ...); + + size_t getLength() const { return length; } + const char *getBuf() const { return buf; } + char *getBuf() { return buf; } + +private: + int resize(const size_t minCapacity); + + size_t capacity; + size_t length; + char *buf; + + // Intentionally undefined + DynBuf(const DynBuf &); + DynBuf &operator=(const DynBuf &); +}; + +#endif // DYNBUF_H diff --git a/daemon/EventsXML.cpp b/daemon/EventsXML.cpp index 2a80482..a07a046 100644 --- a/daemon/EventsXML.cpp +++ b/daemon/EventsXML.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -35,7 +35,7 @@ char* EventsXML::getXML() { fclose(fl); } else { logg->logMessage("Unable to locate events.xml, using default"); - xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK); + xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); } // Add dynamic events from the drivers diff --git a/daemon/EventsXML.h b/daemon/EventsXML.h index 8e693ef..6cd1560 100644 --- a/daemon/EventsXML.h +++ b/daemon/EventsXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 diff --git a/daemon/ExternalSource.cpp b/daemon/ExternalSource.cpp new file mode 100644 index 0000000..fe5824b --- /dev/null +++ b/daemon/ExternalSource.cpp @@ -0,0 +1,56 @@ +/** + * 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 "ExternalSource.h" + +#include <sys/prctl.h> + +#include "Logging.h" +#include "OlySocket.h" +#include "SessionData.h" + +ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") { +} + +ExternalSource::~ExternalSource() { +} + +bool ExternalSource::prepare() { + return true; +} + +void ExternalSource::run() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-uds", 0, 0, 0); + + while (gSessionData->mSessionIsActive) { + // Will be aborted when the socket is closed at the end of the capture + int length = mSock.receive(mBuffer.getWritePos(), mBuffer.contiguousSpaceAvailable()); + if (length <= 0) { + break; + } + + mBuffer.advanceWrite(length); + mBuffer.check(0); + } + + mBuffer.setDone(); +} + +void ExternalSource::interrupt() { + // Do nothing +} + +bool ExternalSource::isDone() { + return mBuffer.isDone(); +} + +void ExternalSource::write(Sender *sender) { + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } +} diff --git a/daemon/ExternalSource.h b/daemon/ExternalSource.h new file mode 100644 index 0000000..2052bdf --- /dev/null +++ b/daemon/ExternalSource.h @@ -0,0 +1,40 @@ +/** + * 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. + */ + +#ifndef EXTERNALSOURCE_H +#define EXTERNALSOURCE_H + +#include <semaphore.h> + +#include "Buffer.h" +#include "OlySocket.h" +#include "Source.h" + +// Unix domain socket counters from external sources like graphics drivers +class ExternalSource : public Source { +public: + ExternalSource(sem_t *senderSem); + ~ExternalSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + Buffer mBuffer; + OlySocket mSock; + + // Intentionally unimplemented + ExternalSource(const ExternalSource &); + ExternalSource &operator=(const ExternalSource &); +}; + +#endif // EXTERNALSOURCE_H diff --git a/daemon/Fifo.cpp b/daemon/Fifo.cpp index 250a4d0..f672e92 100644 --- a/daemon/Fifo.cpp +++ b/daemon/Fifo.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/Fifo.h b/daemon/Fifo.h index d25cd68..7dd7426 100644 --- a/daemon/Fifo.h +++ b/daemon/Fifo.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -12,7 +12,7 @@ #ifdef WIN32 #include <windows.h> #define sem_t HANDLE -#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL) +#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL) #define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE) #define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL) #define sem_destroy(sem) CloseHandle(*(sem)) diff --git a/daemon/Hwmon.cpp b/daemon/Hwmon.cpp index 1d7c0da..778f307 100644 --- a/daemon/Hwmon.cpp +++ b/daemon/Hwmon.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -17,7 +17,7 @@ class HwmonCounter { public: - HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature); + HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature); ~HwmonCounter(); HwmonCounter *getNext() const { return next; } @@ -69,7 +69,7 @@ private: HwmonCounter &operator=(const HwmonCounter &); }; -HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { +HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) { int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1; char *chip_name = new char[len]; @@ -205,6 +205,23 @@ bool HwmonCounter::canRead() { } Hwmon::Hwmon() : counters(NULL) { +} + +Hwmon::~Hwmon() { + while (counters != NULL) { + HwmonCounter * counter = counters; + counters = counter->getNext(); + delete counter; + } + sensors_cleanup(); +} + +void Hwmon::setup() { + // hwmon does not currently work with perf + if (gSessionData->perf.isSetup()) { + return; + } + int err = sensors_init(NULL); if (err) { logg->logMessage("Failed to initialize libsensors! (%d)", err); @@ -218,20 +235,11 @@ Hwmon::Hwmon() : counters(NULL) { int feature_nr = 0; const sensors_feature *feature; while ((feature = sensors_get_features(chip, &feature_nr))) { - counters = new HwmonCounter(counters, getEventKey(), chip, feature); + counters = new HwmonCounter(counters, chip, feature); } } } -Hwmon::~Hwmon() { - while (counters != NULL) { - HwmonCounter * counter = counters; - counters = counter->getNext(); - delete counter; - } - sensors_cleanup(); -} - HwmonCounter *Hwmon::findCounter(const Counter &counter) const { for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) { if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) { @@ -271,14 +279,18 @@ void Hwmon::setupCounter(Counter &counter) { counter.setKey(hwmonCounter->getKey()); } -void Hwmon::writeCounters(mxml_node_t *root) const { +int Hwmon::writeCounters(mxml_node_t *root) const { + int count = 0; for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) { if (!counter->canRead()) { continue; } mxml_node_t *node = mxmlNewElement(root, "counter"); mxmlElementSetAttr(node, "name", counter->getName()); + ++count; } + + return count; } void Hwmon::writeEvents(mxml_node_t *root) const { diff --git a/daemon/Hwmon.h b/daemon/Hwmon.h index 46bb42e..a22a360 100644 --- a/daemon/Hwmon.h +++ b/daemon/Hwmon.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -19,12 +19,14 @@ public: Hwmon(); ~Hwmon(); + void setup(); + bool claimCounter(const Counter &counter) const; bool countersEnabled() const; void resetCounters(); void setupCounter(Counter &counter); - void writeCounters(mxml_node_t *root) const; + int writeCounters(mxml_node_t *root) const; void writeEvents(mxml_node_t *root) const; void start(); diff --git a/daemon/KMod.cpp b/daemon/KMod.cpp index 559297f..9300002 100644 --- a/daemon/KMod.cpp +++ b/daemon/KMod.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -12,9 +12,9 @@ #include <dirent.h> #include <unistd.h> -#include "Collector.h" #include "ConfigurationXML.h" #include "Counter.h" +#include "DriverSource.h" #include "Logging.h" // Claim all the counters in /dev/gator/events @@ -38,9 +38,9 @@ void KMod::resetCounters() { continue; snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name); snprintf(text, sizeof(text), "%s/enabled", base); - Collector::writeDriver(text, 0); + DriverSource::writeDriver(text, 0); snprintf(text, sizeof(text), "%s/count", base); - Collector::writeDriver(text, 0); + DriverSource::writeDriver(text, 0); } closedir(dir); } @@ -53,22 +53,22 @@ void KMod::setupCounter(Counter &counter) { snprintf(text, sizeof(text), "%s/enabled", base); int enabled = true; - if (Collector::writeReadDriver(text, &enabled) || !enabled) { + if (DriverSource::writeReadDriver(text, &enabled) || !enabled) { counter.setEnabled(false); return; } snprintf(text, sizeof(text), "%s/key", base); int key = 0; - Collector::readIntDriver(text, &key); + DriverSource::readIntDriver(text, &key); counter.setKey(key); snprintf(text, sizeof(text), "%s/event", base); - Collector::writeDriver(text, counter.getEvent()); + DriverSource::writeDriver(text, counter.getEvent()); snprintf(text, sizeof(text), "%s/count", base); if (access(text, F_OK) == 0) { int count = counter.getCount(); - if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) { + if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) { logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount()); handleException(); } @@ -80,23 +80,26 @@ void KMod::setupCounter(Counter &counter) { } } -void KMod::writeCounters(mxml_node_t *root) const { +int KMod::writeCounters(mxml_node_t *root) const { struct dirent *ent; mxml_node_t *counter; // counters.xml is simply a file listing of /dev/gator/events DIR* dir = opendir("/dev/gator/events"); if (dir == NULL) { - logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events"); - handleException(); + return 0; } + int count = 0; while ((ent = readdir(dir)) != NULL) { // skip hidden files, current dir, and parent dir if (ent->d_name[0] == '.') continue; counter = mxmlNewElement(root, "counter"); mxmlElementSetAttr(counter, "name", ent->d_name); + ++count; } closedir(dir); + + return count; } diff --git a/daemon/KMod.h b/daemon/KMod.h index 7974262..fb7fc8a 100644 --- a/daemon/KMod.h +++ b/daemon/KMod.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2013. All rights reserved. + * 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 @@ -21,7 +21,7 @@ public: void resetCounters(); void setupCounter(Counter &counter); - void writeCounters(mxml_node_t *root) const; + int writeCounters(mxml_node_t *root) const; }; #endif // KMOD_H diff --git a/daemon/LocalCapture.cpp b/daemon/LocalCapture.cpp index 3235a34..d2a4b79 100644 --- a/daemon/LocalCapture.cpp +++ b/daemon/LocalCapture.cpp @@ -1,18 +1,20 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 "LocalCapture.h" + #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <string.h> #include <stdlib.h> #include <unistd.h> -#include "LocalCapture.h" + #include "SessionData.h" #include "Logging.h" #include "OlyUtility.h" diff --git a/daemon/LocalCapture.h b/daemon/LocalCapture.h index 8042d6a..aadecce 100644 --- a/daemon/LocalCapture.h +++ b/daemon/LocalCapture.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/Logging.cpp b/daemon/Logging.cpp index 5fd45b5..b8d3178 100644 --- a/daemon/Logging.cpp +++ b/daemon/Logging.cpp @@ -1,11 +1,13 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 "Logging.h" + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -23,8 +25,6 @@ #define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) #endif -#include "Logging.h" - // Global thread-safe logging Logging* logg = NULL; diff --git a/daemon/Logging.h b/daemon/Logging.h index 8f960de..6ae3280 100644 --- a/daemon/Logging.h +++ b/daemon/Logging.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -9,14 +9,7 @@ #ifndef __LOGGING_H__ #define __LOGGING_H__ -#include <stdio.h> -#include <string.h> -#include <limits.h> -#ifdef WIN32 -#include <windows.h> -#else #include <pthread.h> -#endif #define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord" @@ -33,11 +26,7 @@ private: char mErrBuf[4096]; // Arbitrarily large buffer to hold a string char mLogBuf[4096]; // Arbitrarily large buffer to hold a string bool mDebug; -#ifdef WIN32 - HANDLE mLoggingMutex; -#else pthread_mutex_t mLoggingMutex; -#endif }; extern Logging* logg; diff --git a/daemon/Monitor.cpp b/daemon/Monitor.cpp new file mode 100644 index 0000000..90d5c47 --- /dev/null +++ b/daemon/Monitor.cpp @@ -0,0 +1,61 @@ +/** + * 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 "Monitor.h" + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "Logging.h" + +Monitor::Monitor() : mFd(-1) { +} + +Monitor::~Monitor() { + if (mFd >= -1) { + close(mFd); + } +} + +bool Monitor::init() { + mFd = epoll_create(16); + if (mFd < 0) { + logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +bool Monitor::add(const int fd) { + struct epoll_event event; + memset(&event, 0, sizeof(event)); + event.data.fd = fd; + event.events = EPOLLIN; + if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) { + logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) { + int result = epoll_wait(mFd, events, maxevents, timeout); + if (result < 0) { + // Ignore if the call was interrupted as this will happen when SIGINT is received + if (errno == EINTR) { + result = 0; + } else { + logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__); + } + } + + return result; +} diff --git a/daemon/Monitor.h b/daemon/Monitor.h new file mode 100644 index 0000000..6e268b6 --- /dev/null +++ b/daemon/Monitor.h @@ -0,0 +1,32 @@ +/** + * 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. + */ + +#ifndef MONITOR_H +#define MONITOR_H + +#include <sys/epoll.h> + +class Monitor { +public: + Monitor(); + ~Monitor(); + + bool init(); + bool add(const int fd); + int wait(struct epoll_event *const events, int maxevents, int timeout); + +private: + + int mFd; + + // Intentionally unimplemented + Monitor(const Monitor &); + Monitor &operator=(const Monitor &); +}; + +#endif // MONITOR_H diff --git a/daemon/OlySocket.cpp b/daemon/OlySocket.cpp index ab5c3c2..26e4768 100644 --- a/daemon/OlySocket.cpp +++ b/daemon/OlySocket.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -15,6 +15,7 @@ #else #include <netinet/in.h> #include <sys/socket.h> +#include <sys/un.h> #include <unistd.h> #include <netdb.h> #endif @@ -30,7 +31,7 @@ #define SHUTDOWN_RX_TX SHUT_RDWR #endif -OlySocket::OlySocket(int port, bool multiple) { +OlyServerSocket::OlyServerSocket(int port) { #ifdef WIN32 WSADATA wsaData; if (WSAStartup(0x0202, &wsaData) != 0) { @@ -39,24 +40,82 @@ OlySocket::OlySocket(int port, bool multiple) { } #endif - if (multiple) { - createServerSocket(port); - } else { - createSingleServerConnection(port); - } + createServerSocket(port); } -OlySocket::OlySocket(int port, char* host) { - mFDServer = 0; +OlySocket::OlySocket(int port, const char* host) { createClientSocket(host, port); } +OlySocket::OlySocket(int socketID) : mSocketID(socketID) { +} + +#ifndef WIN32 + +OlyServerSocket::OlyServerSocket(const char* path) { + // Create socket + mFDServer = socket(PF_UNIX, SOCK_STREAM, 0); + if (mFDServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } + + unlink(path); + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_un sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; + + // Bind the socket to an address + if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "Binding of server socket failed."); + handleException(); + } + + // Listen for connections on this socket + if (listen(mFDServer, 1) < 0) { + logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + handleException(); + } +} + +OlySocket::OlySocket(const char* path) { + mSocketID = socket(PF_UNIX, SOCK_STREAM, 0); + if (mSocketID < 0) { + return; + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_un sockaddr; + memset((void*)&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sun_family = AF_UNIX; + strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1); + sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; + + if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + close(mSocketID); + mSocketID = -1; + return; + } +} + +#endif + OlySocket::~OlySocket() { if (mSocketID > 0) { CLOSE_SOCKET(mSocketID); } } +OlyServerSocket::~OlyServerSocket() { + if (mFDServer > 0) { + CLOSE_SOCKET(mFDServer); + } +} + void OlySocket::shutdownConnection() { // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions shutdown(mSocketID, SHUTDOWN_RX_TX); @@ -70,7 +129,7 @@ void OlySocket::closeSocket() { } } -void OlySocket::closeServerSocket() { +void OlyServerSocket::closeServerSocket() { if (CLOSE_SOCKET(mFDServer) != 0) { logg->logError(__FILE__, __LINE__, "Failed to close server socket."); handleException(); @@ -78,7 +137,7 @@ void OlySocket::closeServerSocket() { mFDServer = 0; } -void OlySocket::createClientSocket(char* hostname, int portno) { +void OlySocket::createClientSocket(const char* hostname, int portno) { #ifdef WIN32 // TODO: Implement for Windows #else @@ -119,14 +178,7 @@ void OlySocket::createClientSocket(char* hostname, int portno) { #endif } -void OlySocket::createSingleServerConnection(int port) { - createServerSocket(port); - - mSocketID = acceptConnection(); - closeServerSocket(); -} - -void OlySocket::createServerSocket(int port) { +void OlyServerSocket::createServerSocket(int port) { int family = AF_INET6; // Create socket @@ -169,22 +221,23 @@ void OlySocket::createServerSocket(int port) { // mSocketID is always set to the most recently accepted connection // The user of this class should maintain the different socket connections, e.g. by forking the process -int OlySocket::acceptConnection() { +int OlyServerSocket::acceptConnection() { + int socketID; if (mFDServer <= 0) { logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); handleException(); } // Accept a connection, note that this call blocks until a client connects - mSocketID = accept(mFDServer, NULL, NULL); - if (mSocketID < 0) { + socketID = accept(mFDServer, NULL, NULL); + if (socketID < 0) { logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); handleException(); } - return mSocketID; + return socketID; } -void OlySocket::send(char* buffer, int size) { +void OlySocket::send(const char* buffer, int size) { if (size <= 0 || buffer == NULL) { return; } diff --git a/daemon/OlySocket.h b/daemon/OlySocket.h index 5bab7d1..eab786b 100644 --- a/daemon/OlySocket.h +++ b/daemon/OlySocket.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -9,27 +9,44 @@ #ifndef __OLY_SOCKET_H__ #define __OLY_SOCKET_H__ -#include <string.h> - class OlySocket { public: - OlySocket(int port, bool multipleConnections = false); - OlySocket(int port, char* hostname); + OlySocket(int port, const char* hostname); + OlySocket(int socketID); +#ifndef WIN32 + OlySocket(const char* path); +#endif ~OlySocket(); - int acceptConnection(); + void closeSocket(); - void closeServerSocket(); void shutdownConnection(); - void send(char* buffer, int size); - void sendString(const char* string) {send((char*)string, strlen(string));} + void send(const char* buffer, int size); int receive(char* buffer, int size); int receiveNBytes(char* buffer, int size); int receiveString(char* buffer, int size); - int getSocketID() {return mSocketID;} + + bool isValid() const { return mSocketID >= 0; } + +private: + int mSocketID; + + void createClientSocket(const char* hostname, int port); +}; + +class OlyServerSocket { +public: + OlyServerSocket(int port); +#ifndef WIN32 + OlyServerSocket(const char* path); +#endif + ~OlyServerSocket(); + + int acceptConnection(); + void closeServerSocket(); + private: - int mSocketID, mFDServer; - void createClientSocket(char* hostname, int port); - void createSingleServerConnection(int port); + int mFDServer; + void createServerSocket(int port); }; diff --git a/daemon/OlyUtility.cpp b/daemon/OlyUtility.cpp index 0b22d6e..45340a2 100644 --- a/daemon/OlyUtility.cpp +++ b/daemon/OlyUtility.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/OlyUtility.h b/daemon/OlyUtility.h index abab0a5..1d26beb 100644 --- a/daemon/OlyUtility.h +++ b/daemon/OlyUtility.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/PerfBuffer.cpp b/daemon/PerfBuffer.cpp new file mode 100644 index 0000000..5fad583 --- /dev/null +++ b/daemon/PerfBuffer.cpp @@ -0,0 +1,139 @@ +/** + * 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 "PerfBuffer.h" + +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include "Buffer.h" +#include "Logging.h" +#include "Sender.h" +#include "SessionData.h" + +PerfBuffer::PerfBuffer() { + for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) { + mBuf[cpu] = MAP_FAILED; + mDiscard[cpu] = false; + } +} + +PerfBuffer::~PerfBuffer() { + for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) { + if (mBuf[cpu] != MAP_FAILED) { + munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + } + } +} + +bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) { + if (fd == groupFd) { + if (mBuf[cpu] != MAP_FAILED) { + logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu); + return false; + } + + // The buffer isn't mapped yet + mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mBuf[cpu] == MAP_FAILED) { + logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Check the version + struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); + if (pemp->compat_version != 0) { + logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } else { + if (mBuf[cpu] == MAP_FAILED) { + logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + return true; +} + +void PerfBuffer::discard(const int cpu) { + if (mBuf[cpu] != MAP_FAILED) { + mDiscard[cpu] = true; + } +} + +bool PerfBuffer::isEmpty() { + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (mBuf[cpu] != MAP_FAILED) { + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); + const __u64 head = pemp->data_head; + const __u64 tail = pemp->data_tail; + + if (head != tail) { + return false; + } + } + } + + return true; +} + +bool PerfBuffer::send(Sender *const sender) { + for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) { + if (mBuf[cpu] == MAP_FAILED) { + continue; + } + + // Take a snapshot of the positions + struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]); + const __u64 head = pemp->data_head; + const __u64 tail = pemp->data_tail; + + if (head > tail) { + const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize; + const int offset = gSessionData->mLocalCapture ? 1 : 0; + unsigned char header[7]; + header[0] = RESPONSE_APC_DATA; + Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5); + // Should use real packing functions + header[5] = FRAME_PERF; + header[6] = cpu; + + // Write header + sender->writeData(reinterpret_cast<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA); + + // Write data + if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) { + // Not wrapped + sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA); + } else { + // Wrapped + sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA); + sender->writeData(reinterpret_cast<const char *>(b), head & BUF_MASK, RESPONSE_APC_DATA); + } + + // Update tail with the data read + pemp->data_tail = head; + } + + if (mDiscard[cpu]) { + munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE); + mBuf[cpu] = MAP_FAILED; + mDiscard[cpu] = false; + logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + } + } + + return true; +} diff --git a/daemon/PerfBuffer.h b/daemon/PerfBuffer.h new file mode 100644 index 0000000..278a3b9 --- /dev/null +++ b/daemon/PerfBuffer.h @@ -0,0 +1,39 @@ +/** + * 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. + */ + +#ifndef PERF_BUFFER +#define PERF_BUFFER + +#include "Config.h" + +#define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024) +#define BUF_MASK (BUF_SIZE - 1) + +class Sender; + +class PerfBuffer { +public: + PerfBuffer(); + ~PerfBuffer(); + + bool useFd(const int cpu, const int fd, const int groupFd); + void discard(const int cpu); + bool isEmpty(); + bool send(Sender *const sender); + +private: + void *mBuf[NR_CPUS]; + // After the buffer is flushed it should be unmaped + bool mDiscard[NR_CPUS]; + + // Intentionally undefined + PerfBuffer(const PerfBuffer &); + PerfBuffer &operator=(const PerfBuffer &); +}; + +#endif // PERF_BUFFER diff --git a/daemon/PerfDriver.cpp b/daemon/PerfDriver.cpp new file mode 100644 index 0000000..8e25c22 --- /dev/null +++ b/daemon/PerfDriver.cpp @@ -0,0 +1,355 @@ +/** + * 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 "PerfDriver.h" + +#include <dirent.h> +#include <sys/utsname.h> +#include <time.h> + +#include "Buffer.h" +#include "Config.h" +#include "ConfigurationXML.h" +#include "Counter.h" +#include "DriverSource.h" +#include "DynBuf.h" +#include "Logging.h" +#include "PerfGroup.h" +#include "SessionData.h" + +#define PERF_DEVICES "/sys/bus/event_source/devices" + +#define TYPE_DERIVED ~0U + +// From gator.h +struct gator_cpu { + const int cpuid; + // Human readable name + const char core_name[32]; + // gatorfs event and Perf PMU name + const char *const pmnc_name; + const int pmnc_counters; +}; + +// From gator_main.c +static const struct gator_cpu gator_cpus[] = { + { 0xb36, "ARM1136", "ARM_ARM11", 3 }, + { 0xb56, "ARM1156", "ARM_ARM11", 3 }, + { 0xb76, "ARM1176", "ARM_ARM11", 3 }, + { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 }, + { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 }, + { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 }, + { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 }, + { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 }, + { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 }, + { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 }, + { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 }, + { 0x00f, "Scorpion", "Scorpion", 4 }, + { 0x02d, "ScorpionMP", "ScorpionMP", 4 }, + { 0x049, "KraitSIM", "Krait", 4 }, + { 0x04d, "Krait", "Krait", 4 }, + { 0x06f, "Krait S4 Pro", "Krait", 4 }, + { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 }, + { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 }, + { 0xd0f, "AArch64", "ARM_AArch64", 6 }, +}; + +static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-"; +static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_"; + +class PerfCounter { +public: + PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false) {} + ~PerfCounter() { + delete [] mName; + } + + PerfCounter *getNext() const { return mNext; } + const char *getName() const { return mName; } + uint32_t getType() const { return mType; } + int getCount() const { return mCount; } + void setCount(const int count) { mCount = count; } + int getKey() const { return mKey; } + uint64_t getConfig() const { return mConfig; } + void setConfig(const uint64_t config) { mConfig = config; } + bool isEnabled() const { return mEnabled; } + void setEnabled(const bool enabled) { mEnabled = enabled; } + +private: + PerfCounter *const mNext; + const char *const mName; + const uint32_t mType; + int mCount; + const int mKey; + uint64_t mConfig; + bool mEnabled; +}; + +PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) { +} + +PerfDriver::~PerfDriver() { + while (mCounters != NULL) { + PerfCounter *counter = mCounters; + mCounters = counter->getNext(); + delete counter; + } +} + +void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) { + int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1; + char *name = new char[len]; + snprintf(name, len, "%s_ccnt", counterName); + mCounters = new PerfCounter(mCounters, name, type, -1); + + for (int j = 0; j < numCounters; ++j) { + len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1; + name = new char[len]; + snprintf(name, len, "%s_cnt%d", counterName, j); + mCounters = new PerfCounter(mCounters, name, type, -1); + } +} + +// From include/generated/uapi/linux/version.h +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +bool PerfDriver::setup() { + // Check the kernel version + struct utsname utsname; + if (uname(&utsname) != 0) { + logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + int release[3] = { 0, 0, 0 }; + int part = 0; + char *ch = utsname.release; + while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) { + release[part] = 10*release[part] + *ch - '0'; + + ++ch; + if (*ch == '.') { + ++part; + ++ch; + } + } + + if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0)) { + logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + // Add supported PMUs + bool foundCpu = false; + DIR *dir = opendir(PERF_DEVICES); + if (dir == NULL) { + logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { + // Do the names match exactly? + if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 && + // Do these names match but have the old vs new prefix? + (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 || + strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 || + strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) { + continue; + } + + int type; + char buf[256]; + snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name); + if (DriverSource::readIntDriver(buf, &type) != 0) { + continue; + } + + foundCpu = true; + addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters); + } + } + closedir(dir); + + if (!foundCpu) { + // If no cpu was found based on pmu names, try by cpuid + for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) { + if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) { + continue; + } + + foundCpu = true; + addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters); + } + } + + /* + if (!foundCpu) { + // If all else fails, use the perf architected counters + // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once + addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9); + } + */ + + // Add supported software counters + long long id; + DynBuf printb; + + id = getTracepointId("irq/softirq_exit", &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id); + } + + id = getTracepointId("irq/irq_handler_exit", &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id); + } + + //Linux_block_rq_wr + //Linux_block_rq_rd + //Linux_net_rx + //Linux_net_tx + + id = getTracepointId(SCHED_SWITCH, &printb); + if (id >= 0) { + mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id); + } + + //Linux_meminfo_memused + //Linux_meminfo_memfree + //Linux_meminfo_bufferram + //Linux_power_cpu_freq + //Linux_power_cpu_idle + + mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1); + + //Linux_cpu_wait_io + + mIsSetup = true; + return true; +} + +bool PerfDriver::summary(Buffer *const buffer) { + struct utsname utsname; + if (uname(&utsname) != 0) { + logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + char buf[512]; + snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine); + + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { + logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + const int64_t uptime = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec; + + buffer->summary(timestamp, uptime, 0, buf); + + for (int i = 0; i < gSessionData->mCores; ++i) { + int j; + for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) { + if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { + break; + } + } + if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) { + buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name); + } else { + snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]); + buffer->coreName(i, gSessionData->mCpuIds[i], buf); + } + } + buffer->commit(1); + + return true; +} + +PerfCounter *PerfDriver::findCounter(const Counter &counter) const { + for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) { + if (strcmp(perfCounter->getName(), counter.getType()) == 0) { + return perfCounter; + } + } + + return NULL; +} + +bool PerfDriver::claimCounter(const Counter &counter) const { + return findCounter(counter) != NULL; +} + +void PerfDriver::resetCounters() { + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + counter->setEnabled(false); + } +} + +void PerfDriver::setupCounter(Counter &counter) { + PerfCounter *const perfCounter = findCounter(counter); + if (perfCounter == NULL) { + counter.setEnabled(false); + return; + } + + // Don't use the config from counters XML if it's not set, ex: software counters + if (counter.getEvent() != -1) { + perfCounter->setConfig(counter.getEvent()); + } + perfCounter->setCount(counter.getCount()); + perfCounter->setEnabled(true); + counter.setKey(perfCounter->getKey()); +} + +int PerfDriver::writeCounters(mxml_node_t *root) const { + int count = 0; + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + mxml_node_t *node = mxmlNewElement(root, "counter"); + mxmlElementSetAttr(node, "name", counter->getName()); + ++count; + } + + return count; +} + +bool PerfDriver::enable(PerfGroup *group, Buffer *const buffer) const { + for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) { + if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) { + if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), 0, 0)) { + logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + return true; +} + +long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) { + if (!printb->printf(EVENTS_PATH "/%s/id", name)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return -1; + } + + int64_t result; + if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) { + logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__); + return -1; + } + + return result; +} diff --git a/daemon/PerfDriver.h b/daemon/PerfDriver.h new file mode 100644 index 0000000..3181b74 --- /dev/null +++ b/daemon/PerfDriver.h @@ -0,0 +1,56 @@ +/** + * 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. + */ + +#ifndef PERFDRIVER_H +#define PERFDRIVER_H + +#include "Driver.h" + +// If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH +#define DEBUGFS_PATH "/sys/kernel/debug" +#define EVENTS_PATH DEBUGFS_PATH "/tracing/events" + +#define SCHED_SWITCH "sched/sched_switch" + +class Buffer; +class DynBuf; +class PerfCounter; +class PerfGroup; + +class PerfDriver : public Driver { +public: + PerfDriver(); + ~PerfDriver(); + + bool setup(); + bool summary(Buffer *const buffer); + bool isSetup() const { return mIsSetup; } + + bool claimCounter(const Counter &counter) const; + void resetCounters(); + void setupCounter(Counter &counter); + + int writeCounters(mxml_node_t *root) const; + + bool enable(PerfGroup *group, Buffer *const buffer) const; + + static long long getTracepointId(const char *const name, DynBuf *const printb); + +private: + PerfCounter *findCounter(const Counter &counter) const; + void addCpuCounters(const char *const counterName, const int type, const int numCounters); + + PerfCounter *mCounters; + bool mIsSetup; + + // Intentionally undefined + PerfDriver(const PerfDriver &); + PerfDriver &operator=(const PerfDriver &); +}; + +#endif // PERFDRIVER_H diff --git a/daemon/PerfGroup.cpp b/daemon/PerfGroup.cpp new file mode 100644 index 0000000..faf5fca --- /dev/null +++ b/daemon/PerfGroup.cpp @@ -0,0 +1,206 @@ +/** + * 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 "PerfGroup.h" + +#include <errno.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include "Buffer.h" +#include "Logging.h" +#include "Monitor.h" +#include "PerfBuffer.h" +#include "SessionData.h" + +#define DEFAULT_PEA_ARGS(pea, additionalSampleType) \ + pea.size = sizeof(pea); \ + /* Emit time, read_format below, group leader id, and raw tracepoint info */ \ + pea.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER | additionalSampleType; \ + /* Emit emit value in group format */ \ + pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \ + /* start out disabled */ \ + pea.disabled = 1; \ + /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \ + pea.watermark = 1; \ + /* Be conservative in flush size as only one buffer set is monitored */ \ + pea.wakeup_watermark = 3 * BUF_SIZE / 4 + +static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) { + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); +} + +PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) { + memset(&mAttrs, 0, sizeof(mAttrs)); + memset(&mKeys, -1, sizeof(mKeys)); + memset(&mFds, -1, sizeof(mFds)); +} + +PerfGroup::~PerfGroup() { + for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { + if (mFds[pos] >= 0) { + close(mFds[pos]); + } + } +} + +bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) { + int i; + for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + break; + } + } + + if (i >= ARRAY_LENGTH(mKeys)) { + logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + DEFAULT_PEA_ARGS(mAttrs[i], sampleType); + mAttrs[i].type = type; + mAttrs[i].config = config; + mAttrs[i].sample_period = sample; + // always be on the CPU but only a group leader can be pinned + mAttrs[i].pinned = (i == 0 ? 1 : 0); + mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0); + mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0); + mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0); + mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0); + mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0); + + mKeys[i] = key; + + buffer->pea(&mAttrs[i], key); + + return true; +} + +bool PerfGroup::prepareCPU(const int cpu) { + logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + continue; + } + + const int offset = i * gSessionData->mCores; + if (mFds[cpu + offset] >= 0) { + logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: %lli", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type); + mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT); + if (mFds[cpu + offset] < 0) { + logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno)); + continue; + } + + if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) { + logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + return true; +} + +int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) { + __u64 ids[ARRAY_LENGTH(mKeys)]; + int coreKeys[ARRAY_LENGTH(mKeys)]; + int idCount = 0; + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + const int fd = mFds[cpu + i * gSessionData->mCores]; + if (fd < 0) { + continue; + } + + coreKeys[idCount] = mKeys[i]; + if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + ++idCount; + } + + if (!monitor->add(mFds[cpu])) { + logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + buffer->keys(idCount, ids, coreKeys); + + if (start) { + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + } + + return idCount; +} + +bool PerfGroup::offlineCPU(const int cpu) { + logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + } + + // Mark the buffer so that it will be released next time it's read + mPb->discard(cpu); + + for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) { + if (mKeys[i] < 0) { + continue; + } + + int offset = i * gSessionData->mCores + cpu; + if (mFds[offset] >= 0) { + close(mFds[offset]); + mFds[offset] = -1; + } + } + + return true; +} + +bool PerfGroup::start() { + for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) { + if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) { + logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + + return true; + + fail: + stop(); + + return false; +} + +void PerfGroup::stop() { + for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) { + if (mFds[pos] >= 0) { + ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE); + } + } +} diff --git a/daemon/PerfGroup.h b/daemon/PerfGroup.h new file mode 100644 index 0000000..af496d4 --- /dev/null +++ b/daemon/PerfGroup.h @@ -0,0 +1,55 @@ + /** + * 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. + */ + +#ifndef PERF_GROUP +#define PERF_GROUP + +// Use a snapshot of perf_event.h as it may be more recent than what is on the target and if not newer features won't be supported anyways +#include "k/perf_event.h" + +#include "Config.h" + +class Buffer; +class Monitor; +class PerfBuffer; + +enum PerfGroupFlags { + PERF_GROUP_MMAP = 1 << 0, + PERF_GROUP_COMM = 1 << 1, + PERF_GROUP_FREQ = 1 << 2, + PERF_GROUP_TASK = 1 << 3, + PERF_GROUP_SAMPLE_ID_ALL = 1 << 4, +}; + +class PerfGroup { +public: + PerfGroup(PerfBuffer *const pb); + ~PerfGroup(); + + bool add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags); + // Safe to call concurrently + bool prepareCPU(const int cpu); + // Not safe to call concurrently. Returns the number of events enabled + int onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor); + bool offlineCPU(int cpu); + bool start(); + void stop(); + +private: + // +1 for the group leader + struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1]; + int mKeys[MAX_PERFORMANCE_COUNTERS + 1]; + int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)]; + PerfBuffer *const mPb; + + // Intentionally undefined + PerfGroup(const PerfGroup &); + PerfGroup &operator=(const PerfGroup &); +}; + +#endif // PERF_GROUP diff --git a/daemon/PerfSource.cpp b/daemon/PerfSource.cpp new file mode 100644 index 0000000..1f1cb19 --- /dev/null +++ b/daemon/PerfSource.cpp @@ -0,0 +1,271 @@ +/** + * 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, 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; + + 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) + + // 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, 0)) + + || !gSessionData->perf.enable(&mCountersGroup, &mBuffer) + || 0) { + logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.12 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, &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); + } + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } + if (!mCountersBuf.send(sender)) { + logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed"); + handleException(); + } +} diff --git a/daemon/PerfSource.h b/daemon/PerfSource.h new file mode 100644 index 0000000..3f471c8 --- /dev/null +++ b/daemon/PerfSource.h @@ -0,0 +1,54 @@ +/** + * 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. + */ + +#ifndef PERFSOURCE_H +#define PERFSOURCE_H + +#include <semaphore.h> + +#include "Buffer.h" +#include "Monitor.h" +#include "PerfBuffer.h" +#include "PerfGroup.h" +#include "Source.h" +#include "UEvent.h" + +class Sender; + +class PerfSource : public Source { +public: + PerfSource(sem_t *senderSem, sem_t *startProfile); + ~PerfSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + bool handleUEvent(); + + Buffer mSummary; + Buffer mBuffer; + PerfBuffer mCountersBuf; + PerfGroup mCountersGroup; + Monitor mMonitor; + UEvent mUEvent; + sem_t *const mSenderSem; + sem_t *const mStartProfile; + int mInterruptFd; + bool mIsDone; + + // Intentionally undefined + PerfSource(const PerfSource &); + PerfSource &operator=(const PerfSource &); +}; + +#endif // PERFSOURCE_H diff --git a/daemon/Proc.cpp b/daemon/Proc.cpp new file mode 100644 index 0000000..e0b9e22 --- /dev/null +++ b/daemon/Proc.cpp @@ -0,0 +1,179 @@ +/** + * 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 "Proc.h" + +#include <dirent.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "Buffer.h" +#include "DynBuf.h" +#include "Logging.h" + +struct ProcStat { + // From linux-dev/include/linux/sched.h +#define TASK_COMM_LEN 16 + // TASK_COMM_LEN may grow, so be ready for it to get larger + char comm[2*TASK_COMM_LEN]; + long numThreads; +}; + +static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) { + if (!b->read(pathname)) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the thread just doesn't exist any more + return true; + } + + char *comm = strchr(b->getBuf(), '('); + if (comm == NULL) { + logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + ++comm; + char *const str = strrchr(comm, ')'); + if (str == NULL) { + logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + *str = '\0'; + strncpy(ps->comm, comm, sizeof(ps->comm) - 1); + ps->comm[sizeof(ps->comm) - 1] = '\0'; + + const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads); + if (count != 1) { + logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +static bool readProcTask(Buffer *const buffer, const int pid, const char *const image, DynBuf *const printb, DynBuf *const b) { + bool result = false; + + if (!b->printf("/proc/%i/task", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + DIR *task = opendir(b->getBuf()); + if (task == NULL) { + logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + + struct dirent *dirent; + while ((dirent = readdir(task)) != NULL) { + char *endptr; + const int tid = strtol(dirent->d_name, &endptr, 10); + if (*endptr != '\0') { + // Ignore task items that are not integers like ., etc... + continue; + } + + if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + ProcStat ps; + if (!readProcStat(&ps, printb->getBuf(), b)) { + logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + buffer->comm(pid, tid, image, ps.comm); + } + + result = true; + + fail: + closedir(task); + + return result; +} + +bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) { + bool result = false; + + DIR *proc = opendir("/proc"); + if (proc == NULL) { + logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__); + return result; + } + + struct dirent *dirent; + while ((dirent = readdir(proc)) != NULL) { + char *endptr; + const int pid = strtol(dirent->d_name, &endptr, 10); + if (*endptr != '\0') { + // Ignore proc items that are not integers like ., cpuinfo, etc... + continue; + } + + if (!printb->printf("/proc/%i/stat", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + ProcStat ps; + if (!readProcStat(&ps, printb->getBuf(), b1)) { + logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + if (!printb->printf("/proc/%i/exe", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + const int err = b1->readlink(printb->getBuf()); + const char *image; + if (err == 0) { + image = strrchr(b1->getBuf(), '/'); + if (image == NULL) { + image = b1->getBuf(); + } else { + ++image; + } + } else if (err == -ENOENT) { + // readlink /proc/[pid]/exe returns ENOENT for kernel threads + image = "\0"; + } else { + logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + + if (!printb->printf("/proc/%i/maps", pid)) { + logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + if (!b2->read(printb->getBuf())) { + logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__); + // This is not a fatal error - the process just doesn't exist any more + continue; + } + + buffer->maps(pid, pid, b2->getBuf()); + if (ps.numThreads <= 1) { + buffer->comm(pid, pid, image, ps.comm); + } else { + if (!readProcTask(buffer, pid, image, printb, b3)) { + logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__); + goto fail; + } + } + } + + result = true; + + fail: + closedir(proc); + + return result; +} diff --git a/daemon/Proc.h b/daemon/Proc.h new file mode 100644 index 0000000..057b610 --- /dev/null +++ b/daemon/Proc.h @@ -0,0 +1,17 @@ +/** + * 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. + */ + +#ifndef PROC_H +#define PROC_H + +class Buffer; +class DynBuf; + +bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3); + +#endif // PROC_H diff --git a/daemon/Sender.cpp b/daemon/Sender.cpp index 8eb348f..3a981a6 100644 --- a/daemon/Sender.cpp +++ b/daemon/Sender.cpp @@ -1,19 +1,18 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 <string.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <sys/types.h> -#include <arpa/inet.h> +#include "Sender.h" + #include <stdlib.h> +#include <string.h> #include <unistd.h> -#include "Sender.h" + +#include "Buffer.h" #include "Logging.h" #include "OlySocket.h" #include "SessionData.h" @@ -49,9 +48,12 @@ Sender::Sender(OlySocket* socket) { } Sender::~Sender() { - delete mDataSocket; - mDataSocket = NULL; - if (mDataFile) { + // Just close it as the client socket is on the stack + if (mDataSocket != NULL) { + mDataSocket->closeSocket(); + mDataSocket = NULL; + } + if (mDataFile != NULL) { fclose(mDataFile); } } @@ -95,10 +97,7 @@ void Sender::writeData(const char* data, int length, int type) { // type and length already added by the Collector for apc data unsigned char header[5]; header[0] = type; - header[1] = (length >> 0) & 0xff; - header[2] = (length >> 8) & 0xff; - header[3] = (length >> 16) & 0xff; - header[4] = (length >> 24) & 0xff; + Buffer::writeLEInt(header + 1, length); mDataSocket->send((char*)&header, sizeof(header)); } @@ -106,7 +105,7 @@ void Sender::writeData(const char* data, int length, int type) { const int chunkSize = 100*1000 * alarmDuration / 8; int pos = 0; while (true) { - mDataSocket->send((char*)data + pos, min(length - pos, chunkSize)); + mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize)); pos += chunkSize; if (pos >= length) { break; diff --git a/daemon/Sender.h b/daemon/Sender.h index b388f03..4c359db 100644 --- a/daemon/Sender.h +++ b/daemon/Sender.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/SessionData.cpp b/daemon/SessionData.cpp index cf84407..c169299 100644 --- a/daemon/SessionData.cpp +++ b/daemon/SessionData.cpp @@ -1,13 +1,15 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 <string.h> #include "SessionData.h" + +#include <string.h> + #include "SessionXML.h" #include "Logging.h" @@ -38,6 +40,7 @@ void SessionData::initialize() { mTotalBufferSize = 0; // sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module mCores = 1; + mPageSize = 0; } void SessionData::parseSessionXML(char* xmlString) { @@ -88,7 +91,8 @@ void SessionData::parseSessionXML(char* xmlString) { void SessionData::readCpuInfo() { char temp[256]; // arbitrarily large amount strcpy(mCoreName, "unknown"); - mCpuId = -1; + memset(&mCpuIds, -1, sizeof(mCpuIds)); + mMaxCpuId = -1; FILE* f = fopen("/proc/cpuinfo", "r"); if (f == NULL) { @@ -98,15 +102,16 @@ void SessionData::readCpuInfo() { } bool foundCoreName = false; - bool foundCpuId = false; - while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) { + int processor = 0; + while (fgets(temp, sizeof(temp), f)) { if (strlen(temp) > 0) { temp[strlen(temp) - 1] = 0; // Replace the line feed with a null } const bool foundHardware = strstr(temp, "Hardware") != 0; const bool foundCPUPart = strstr(temp, "CPU part") != 0; - if (foundHardware || foundCPUPart) { + const bool foundProcessor = strstr(temp, "processor") != 0; + if (foundHardware || foundCPUPart || foundProcessor) { char* position = strchr(temp, ':'); if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { logg->logMessage("Unknown format of /proc/cpuinfo\n" @@ -122,11 +127,15 @@ void SessionData::readCpuInfo() { } if (foundCPUPart) { - int cpuId = strtol(position, NULL, 16); - if (cpuId > mCpuId) { - mCpuId = cpuId; + mCpuIds[processor] = strtol(position, NULL, 0); + // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId + if (mCpuIds[processor] > mMaxCpuId) { + mMaxCpuId = mCpuIds[processor]; } - foundCpuId = true; + } + + if (foundProcessor) { + processor = strtol(position, NULL, 0); } } } diff --git a/daemon/SessionData.h b/daemon/SessionData.h index c834251..ea34240 100644 --- a/daemon/SessionData.h +++ b/daemon/SessionData.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -11,12 +11,12 @@ #include <stdint.h> +#include "Config.h" #include "Counter.h" #include "Hwmon.h" +#include "PerfDriver.h" -#define MAX_PERFORMANCE_COUNTERS 50 - -#define PROTOCOL_VERSION 17 +#define PROTOCOL_VERSION 18 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions struct ImageLinkList { @@ -34,6 +34,7 @@ public: void parseSessionXML(char* xmlString); Hwmon hwmon; + PerfDriver perf; char mCoreName[MAX_STRING_LEN]; struct ImageLinkList *mImages; @@ -47,6 +48,7 @@ public: bool mSessionIsActive; bool mLocalCapture; bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled + bool mIsEBS; int mBacktraceDepth; int mTotalBufferSize; // number of MB to use for the entire collection buffer @@ -54,7 +56,9 @@ public: int64_t mLiveRate; int mDuration; int mCores; - int mCpuId; + int mPageSize; + int mCpuIds[NR_CPUS]; + int mMaxCpuId; // PMU Counters int mCounterOverflow; diff --git a/daemon/SessionXML.cpp b/daemon/SessionXML.cpp index 0a0a027..55b2f92 100644 --- a/daemon/SessionXML.cpp +++ b/daemon/SessionXML.cpp @@ -1,15 +1,17 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 "SessionXML.h" + #include <string.h> #include <stdlib.h> #include <limits.h> -#include "SessionXML.h" + #include "Logging.h" #include "OlyUtility.h" #include "SessionData.h" @@ -25,7 +27,7 @@ static const char* ATTR_DURATION = "duration"; static const char* ATTR_PATH = "path"; static const char* ATTR_LIVE_RATE = "live_rate"; -SessionXML::SessionXML(const char* str) { +SessionXML::SessionXML(const char *str) { parameters.buffer_mode[0] = 0; parameters.sample_rate[0] = 0; parameters.duration = 0; @@ -33,13 +35,13 @@ SessionXML::SessionXML(const char* str) { parameters.live_rate = 0; parameters.images = NULL; mPath = 0; - mSessionXML = (char*)str; + mSessionXML = (const char *)str; logg->logMessage(mSessionXML); } SessionXML::~SessionXML() { if (mPath != 0) { - free(mSessionXML); + free((char *)mSessionXML); } } diff --git a/daemon/SessionXML.h b/daemon/SessionXML.h index 0fb03bd..e146094 100644 --- a/daemon/SessionXML.h +++ b/daemon/SessionXML.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -24,13 +24,13 @@ struct ConfigParameters { class SessionXML { public: - SessionXML(const char* str); + SessionXML(const char *str); ~SessionXML(); void parse(); ConfigParameters parameters; private: - char* mSessionXML; - char* mPath; + const char *mSessionXML; + const char *mPath; void sessionTag(mxml_node_t *tree, mxml_node_t *node); void sessionImage(mxml_node_t *node); diff --git a/daemon/Source.cpp b/daemon/Source.cpp new file mode 100644 index 0000000..60cf704 --- /dev/null +++ b/daemon/Source.cpp @@ -0,0 +1,33 @@ +/** + * 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 "Source.h" + +#include "Logging.h" + +Source::Source() : mThreadID() { +} + +Source::~Source() { +} + +void Source::start() { + if (pthread_create(&mThreadID, NULL, runStatic, this)) { + logg->logError(__FILE__, __LINE__, "Failed to create source thread"); + handleException(); + } +} + +void Source::join() { + pthread_join(mThreadID, NULL); +} + +void *Source::runStatic(void *arg) { + static_cast<Source *>(arg)->run(); + return NULL; +} diff --git a/daemon/Source.h b/daemon/Source.h new file mode 100644 index 0000000..56ac3d6 --- /dev/null +++ b/daemon/Source.h @@ -0,0 +1,40 @@ +/** + * 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. + */ + +#ifndef SOURCE_H +#define SOURCE_H + +#include <pthread.h> + +class Sender; + +class Source { +public: + Source(); + virtual ~Source(); + + virtual bool prepare() = 0; + void start(); + virtual void run() = 0; + virtual void interrupt() = 0; + void join(); + + virtual bool isDone() = 0; + virtual void write(Sender *sender) = 0; + +private: + static void *runStatic(void *arg); + + pthread_t mThreadID; + + // Intentionally undefined + Source(const Source &); + Source &operator=(const Source &); +}; + +#endif // SOURCE_H diff --git a/daemon/StreamlineSetup.cpp b/daemon/StreamlineSetup.cpp index 2faada2..caa665e 100644 --- a/daemon/StreamlineSetup.cpp +++ b/daemon/StreamlineSetup.cpp @@ -1,26 +1,23 @@ /** - * Copyright (C) ARM Limited 2011-2013. All rights reserved. + * Copyright (C) ARM Limited 2011-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 <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <arpa/inet.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include "Sender.h" -#include "Logging.h" -#include "OlyUtility.h" -#include "SessionData.h" -#include "CapturedXML.h" #include "StreamlineSetup.h" + +#include "Buffer.h" +#include "CapturedXML.h" #include "ConfigurationXML.h" #include "Driver.h" #include "EventsXML.h" +#include "Logging.h" +#include "OlySocket.h" +#include "OlyUtility.h" +#include "Sender.h" +#include "SessionData.h" static const char* TAG_SESSION = "session"; static const char* TAG_REQUEST = "request"; @@ -198,12 +195,9 @@ void StreamlineSetup::handleDeliver(char* xml) { void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { unsigned char header[5]; header[0] = type; - header[1] = (length >> 0) & 0xff; - header[2] = (length >> 8) & 0xff; - header[3] = (length >> 16) & 0xff; - header[4] = (length >> 24) & 0xff; + Buffer::writeLEInt(header + 1, length); mSocket->send((char*)&header, sizeof(header)); - mSocket->send((char*)data, length); + mSocket->send((const char*)data, length); } void StreamlineSetup::sendEvents() { @@ -241,8 +235,14 @@ void StreamlineSetup::sendCounters() { xml = mxmlNewXML("1.0"); counters = mxmlNewElement(xml, "counters"); + int count = 0; for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { - driver->writeCounters(counters); + count += driver->writeCounters(counters); + } + + if (count == 0) { + logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); + handleException(); } char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); diff --git a/daemon/StreamlineSetup.h b/daemon/StreamlineSetup.h index d6d9a6e..74bb197 100644 --- a/daemon/StreamlineSetup.h +++ b/daemon/StreamlineSetup.h @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 @@ -9,7 +9,10 @@ #ifndef __STREAMLINE_SETUP_H__ #define __STREAMLINE_SETUP_H__ -#include "OlySocket.h" +#include <stdint.h> +#include <string.h> + +class OlySocket; // Commands from Streamline enum { diff --git a/daemon/UEvent.cpp b/daemon/UEvent.cpp new file mode 100644 index 0000000..282e965 --- /dev/null +++ b/daemon/UEvent.cpp @@ -0,0 +1,75 @@ +/** + * 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 "UEvent.h" + +#include <linux/netlink.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "Logging.h" + +static const char EMPTY[] = ""; +static const char ACTION[] = "ACTION="; +static const char DEVPATH[] = "DEVPATH="; +static const char SUBSYSTEM[] = "SUBSYSTEM="; + +UEvent::UEvent() : mFd(-1) { +} + +UEvent::~UEvent() { + if (mFd >= 0) { + close(mFd); + } +} + +bool UEvent::init() { + mFd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); + if (mFd < 0) { + logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + struct sockaddr_nl sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.nl_family = AF_NETLINK; + sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events + sockaddr.nl_pid = 0; + if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) { + logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + return true; +} + +bool UEvent::read(UEventResult *const result) { + ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0); + if (bytes <= 0) { + logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__); + return false; + } + + result->mAction = EMPTY; + result->mDevPath = EMPTY; + result->mSubsystem = EMPTY; + + for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) { + char *const str = result->mBuf + pos; + if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) { + result->mAction = str + sizeof(ACTION) - 1; + } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) { + result->mDevPath = str + sizeof(DEVPATH) - 1; + } else if (strncmp(str, SUBSYSTEM, sizeof(SUBSYSTEM) - 1) == 0) { + result->mSubsystem = str + sizeof(SUBSYSTEM) - 1; + } + } + + return true; +} diff --git a/daemon/UEvent.h b/daemon/UEvent.h new file mode 100644 index 0000000..2f7ef2c --- /dev/null +++ b/daemon/UEvent.h @@ -0,0 +1,36 @@ +/** + * 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. + */ + +#ifndef UEVENT_H +#define UEVENT_H + +struct UEventResult { + const char *mAction; + const char *mDevPath; + const char *mSubsystem; + char mBuf[1<<13]; +}; + +class UEvent { +public: + UEvent(); + ~UEvent(); + + bool init(); + bool read(UEventResult *const result); + int getFd() const { return mFd; } + +private: + int mFd; + + // Intentionally undefined + UEvent(const UEvent &); + UEvent &operator=(const UEvent &); +}; + +#endif // UEVENT_H diff --git a/daemon/UserSpaceSource.cpp b/daemon/UserSpaceSource.cpp new file mode 100644 index 0000000..debe696 --- /dev/null +++ b/daemon/UserSpaceSource.cpp @@ -0,0 +1,97 @@ +/** + * 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 "UserSpaceSource.h" + +#include <sys/prctl.h> +#include <unistd.h> + +#include "Child.h" +#include "DriverSource.h" +#include "Logging.h" +#include "SessionData.h" + +#define NS_PER_S ((uint64_t)1000000000) +#define NS_PER_US 1000 + +extern Child *child; + +UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) { +} + +UserSpaceSource::~UserSpaceSource() { +} + +bool UserSpaceSource::prepare() { + return true; +} + +void UserSpaceSource::run() { + prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0); + + gSessionData->hwmon.start(); + + int64_t monotonic_started = 0; + while (monotonic_started <= 0) { + usleep(10); + + if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver start time"); + handleException(); + } + } + + uint64_t next_time = 0; + while (gSessionData->mSessionIsActive) { + struct timespec ts; +#ifndef CLOCK_MONOTONIC_RAW + // Android doesn't have this defined but it was added in Linux 2.6.28 +#define CLOCK_MONOTONIC_RAW 4 +#endif + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to get uptime"); + handleException(); + } + const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started; + // Sample ten times a second ignoring gSessionData->mSampleRate + next_time += NS_PER_S/10;//gSessionData->mSampleRate; + if (next_time < curr_time) { + logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time); + next_time = curr_time; + } + + if (mBuffer.eventHeader(curr_time)) { + gSessionData->hwmon.read(&mBuffer); + // Only check after writing all counters so that time and corresponding counters appear in the same frame + mBuffer.check(curr_time); + } + + if (mBuffer.bytesAvailable() <= 0) { + logg->logMessage("One shot (counters)"); + child->endSession(); + } + + usleep((next_time - curr_time)/NS_PER_US); + } + + mBuffer.setDone(); +} + +void UserSpaceSource::interrupt() { + // Do nothing +} + +bool UserSpaceSource::isDone() { + return mBuffer.isDone(); +} + +void UserSpaceSource::write(Sender *sender) { + if (!mBuffer.isDone()) { + mBuffer.write(sender); + } +} diff --git a/daemon/UserSpaceSource.h b/daemon/UserSpaceSource.h new file mode 100644 index 0000000..fb5889d --- /dev/null +++ b/daemon/UserSpaceSource.h @@ -0,0 +1,38 @@ +/** + * 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. + */ + +#ifndef USERSPACESOURCE_H +#define USERSPACESOURCE_H + +#include <semaphore.h> + +#include "Buffer.h" +#include "Source.h" + +// User space counters - currently just hwmon +class UserSpaceSource : public Source { +public: + UserSpaceSource(sem_t *senderSem); + ~UserSpaceSource(); + + bool prepare(); + void run(); + void interrupt(); + + bool isDone(); + void write(Sender *sender); + +private: + Buffer mBuffer; + + // Intentionally unimplemented + UserSpaceSource(const UserSpaceSource &); + UserSpaceSource &operator=(const UserSpaceSource &); +}; + +#endif // USERSPACESOURCE_H diff --git a/daemon/common.mk b/daemon/common.mk index 031d169..d9dc146 100644 --- a/daemon/common.mk +++ b/daemon/common.mk @@ -25,7 +25,7 @@ include $(wildcard *.d) include $(wildcard mxml/*.d) EventsXML.cpp: events_xml.h -ConfigurationXML.cpp: configuration_xml.h +ConfigurationXML.cpp: defaults_xml.h # Don't regenerate conf-lex.c or conf-parse.c libsensors/conf-lex.c: ; @@ -47,4 +47,4 @@ escape: escape.c gcc $^ -o $@ clean: - rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h + rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h diff --git a/daemon/configuration.xml b/daemon/defaults.xml index b44c00a..5bf096c 100644 --- a/daemon/configuration.xml +++ b/daemon/defaults.xml @@ -6,29 +6,34 @@ <configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/> <configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/> <configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/> - <configuration counter="ARM_Cortex-A5_ccnt" event="0xff"/> - <configuration counter="ARM_Cortex-A5_cnt0" event="0x8"/> - <configuration counter="ARM_Cortex-A5_cnt1" event="0x1"/> - <configuration counter="ARM_Cortex-A7_ccnt" event="0xff"/> - <configuration counter="ARM_Cortex-A7_cnt0" event="0x08"/> - <configuration counter="ARM_Cortex-A7_cnt1" event="0x10"/> - <configuration counter="ARM_Cortex-A7_cnt2" event="0x16"/> - <configuration counter="ARM_Cortex-A8_ccnt" event="0xff"/> - <configuration counter="ARM_Cortex-A8_cnt0" event="0x8"/> - <configuration counter="ARM_Cortex-A8_cnt1" event="0x44"/> - <configuration counter="ARM_Cortex-A8_cnt2" event="0x43"/> - <configuration counter="ARM_Cortex-A8_cnt3" event="0x10"/> - <configuration counter="ARM_Cortex-A9_ccnt" event="0xff"/> - <configuration counter="ARM_Cortex-A9_cnt0" event="0x68"/> - <configuration counter="ARM_Cortex-A9_cnt1" event="0x06"/> - <configuration counter="ARM_Cortex-A9_cnt2" event="0x07"/> - <configuration counter="ARM_Cortex-A9_cnt3" event="0x03"/> - <configuration counter="ARM_Cortex-A9_cnt4" event="0x04"/> - <configuration counter="ARM_Cortex-A15_ccnt" event="0xff"/> - <configuration counter="ARM_Cortex-A15_cnt0" event="0x8"/> - <configuration counter="ARM_Cortex-A15_cnt1" event="0x16"/> - <configuration counter="ARM_Cortex-A15_cnt2" event="0x10"/> - <configuration counter="ARM_Cortex-A15_cnt3" event="0x19"/> + <configuration counter="ARMv7_Cortex_A5_ccnt" event="0xff"/> + <configuration counter="ARMv7_Cortex_A5_cnt0" event="0x8"/> + <configuration counter="ARMv7_Cortex_A5_cnt1" event="0x1"/> + <configuration counter="ARMv7_Cortex_A7_ccnt" event="0xff"/> + <configuration counter="ARMv7_Cortex_A7_cnt0" event="0x08"/> + <configuration counter="ARMv7_Cortex_A7_cnt1" event="0x10"/> + <configuration counter="ARMv7_Cortex_A7_cnt2" event="0x16"/> + <configuration counter="ARMv7_Cortex_A8_ccnt" event="0xff"/> + <configuration counter="ARMv7_Cortex_A8_cnt0" event="0x8"/> + <configuration counter="ARMv7_Cortex_A8_cnt1" event="0x44"/> + <configuration counter="ARMv7_Cortex_A8_cnt2" event="0x43"/> + <configuration counter="ARMv7_Cortex_A8_cnt3" event="0x10"/> + <configuration counter="ARMv7_Cortex_A9_ccnt" event="0xff"/> + <configuration counter="ARMv7_Cortex_A9_cnt0" event="0x68"/> + <configuration counter="ARMv7_Cortex_A9_cnt1" event="0x06"/> + <configuration counter="ARMv7_Cortex_A9_cnt2" event="0x07"/> + <configuration counter="ARMv7_Cortex_A9_cnt3" event="0x03"/> + <configuration counter="ARMv7_Cortex_A9_cnt4" event="0x04"/> + <configuration counter="ARMv7_Cortex_A12_ccnt" event="0xff"/> + <configuration counter="ARMv7_Cortex_A12_cnt0" event="0x08"/> + <configuration counter="ARMv7_Cortex_A12_cnt1" event="0x16"/> + <configuration counter="ARMv7_Cortex_A12_cnt2" event="0x10"/> + <configuration counter="ARMv7_Cortex_A12_cnt3" event="0x19"/> + <configuration counter="ARMv7_Cortex_A15_ccnt" event="0xff"/> + <configuration counter="ARMv7_Cortex_A15_cnt0" event="0x8"/> + <configuration counter="ARMv7_Cortex_A15_cnt1" event="0x16"/> + <configuration counter="ARMv7_Cortex_A15_cnt2" event="0x10"/> + <configuration counter="ARMv7_Cortex_A15_cnt3" event="0x19"/> <configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/> <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/> <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/> diff --git a/daemon/escape.c b/daemon/escape.c index 3eec1f8..c54aa1c 100644 --- a/daemon/escape.c +++ b/daemon/escape.c @@ -1,5 +1,5 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 diff --git a/daemon/events-Cortex-A12.xml b/daemon/events-Cortex-A12.xml index 20a4772..9c04354 100644 --- a/daemon/events-Cortex-A12.xml +++ b/daemon/events-Cortex-A12.xml @@ -1,6 +1,6 @@ - <counter_set name="ARM_Cortex-A12_cnt" count="6"/> - <category name="Cortex-A12" counter_set="ARM_Cortex-A12_cnt" per_cpu="yes" supports_event_based_sampling="yes"> - <event counter="ARM_Cortex-A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <counter_set name="ARMv7_Cortex_A12_cnt" count="6"/> + <category name="Cortex-A12" counter_set="ARMv7_Cortex_A12_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="ARMv7_Cortex_A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> <event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/> diff --git a/daemon/events-Cortex-A15.xml b/daemon/events-Cortex-A15.xml index faa8b1c..f50e55d 100644 --- a/daemon/events-Cortex-A15.xml +++ b/daemon/events-Cortex-A15.xml @@ -1,6 +1,6 @@ - <counter_set name="ARM_Cortex-A15_cnt" count="6"/> - <category name="Cortex-A15" counter_set="ARM_Cortex-A15_cnt" per_cpu="yes" supports_event_based_sampling="yes"> - <event counter="ARM_Cortex-A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <counter_set name="ARMv7_Cortex_A15_cnt" count="6"/> + <category name="Cortex-A15" counter_set="ARMv7_Cortex_A15_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="ARMv7_Cortex_A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/> <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> diff --git a/daemon/events-Cortex-A5.xml b/daemon/events-Cortex-A5.xml index a5b1546..d67581d 100644 --- a/daemon/events-Cortex-A5.xml +++ b/daemon/events-Cortex-A5.xml @@ -1,6 +1,6 @@ - <counter_set name="ARM_Cortex-A5_cnt" count="2"/> - <category name="Cortex-A5" counter_set="ARM_Cortex-A5_cnt" per_cpu="yes" supports_event_based_sampling="yes"> - <event counter="ARM_Cortex-A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <counter_set name="ARMv7_Cortex_A5_cnt" count="2"/> + <category name="Cortex-A5" counter_set="ARMv7_Cortex_A5_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="ARMv7_Cortex_A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> diff --git a/daemon/events-Cortex-A7.xml b/daemon/events-Cortex-A7.xml index 54d7264..6e078b3 100644 --- a/daemon/events-Cortex-A7.xml +++ b/daemon/events-Cortex-A7.xml @@ -1,6 +1,6 @@ - <counter_set name="ARM_Cortex-A7_cnt" count="4"/> - <category name="Cortex-A7" counter_set="ARM_Cortex-A7_cnt" per_cpu="yes" supports_event_based_sampling="yes"> - <event counter="ARM_Cortex-A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <counter_set name="ARMv7_Cortex_A7_cnt" count="4"/> + <category name="Cortex-A7" counter_set="ARMv7_Cortex_A7_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="ARMv7_Cortex_A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> <event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/> <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> diff --git a/daemon/events-Cortex-A8.xml b/daemon/events-Cortex-A8.xml index f251823..a69e25a 100644 --- a/daemon/events-Cortex-A8.xml +++ b/daemon/events-Cortex-A8.xml @@ -1,6 +1,6 @@ - <counter_set name="ARM_Cortex-A8_cnt" count="4"/> - <category name="Cortex-A8" counter_set="ARM_Cortex-A8_cnt" per_cpu="yes" supports_event_based_sampling="yes"> - <event counter="ARM_Cortex-A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <counter_set name="ARMv7_Cortex_A8_cnt" count="4"/> + <category name="Cortex-A8" counter_set="ARMv7_Cortex_A8_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="ARMv7_Cortex_A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> diff --git a/daemon/events-Cortex-A9.xml b/daemon/events-Cortex-A9.xml index 75f09c8..3e7f828 100644 --- a/daemon/events-Cortex-A9.xml +++ b/daemon/events-Cortex-A9.xml @@ -1,6 +1,6 @@ - <counter_set name="ARM_Cortex-A9_cnt" count="6"/> - <category name="Cortex-A9" counter_set="ARM_Cortex-A9_cnt" per_cpu="yes" supports_event_based_sampling="yes"> - <event counter="ARM_Cortex-A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <counter_set name="ARMv7_Cortex_A9_cnt" count="6"/> + <category name="Cortex-A9" counter_set="ARMv7_Cortex_A9_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="ARMv7_Cortex_A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> <event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/> <event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/> <event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/> diff --git a/daemon/events-Linux.xml b/daemon/events-Linux.xml index 31a90a1..4d677e1 100644 --- a/daemon/events-Linux.xml +++ b/daemon/events-Linux.xml @@ -6,12 +6,12 @@ <event counter="Linux_net_rx" title="Network" name="Receive" units="B" description="Receive network traffic, including effect from Streamline"/> <event counter="Linux_net_tx" title="Network" name="Transmit" units="B" description="Transmit network traffic, including effect from Streamline"/> <event counter="Linux_sched_switch" title="Scheduler" name="Switch" per_cpu="yes" description="Context switch events"/> - <event counter="Linux_meminfo_memused" title="Memory" name="Used" display="maximum" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/> - <event counter="Linux_meminfo_memfree" title="Memory" name="Free" display="minimum" units="B" description="Available memory size"/> - <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" display="maximum" units="B" description="Memory used by OS disk buffers"/> - <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" display="maximum" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/> - <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" display="maximum" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/> - <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/> - <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/> + <event counter="Linux_meminfo_memused" title="Memory" name="Used" class="absolute" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/> + <event counter="Linux_meminfo_memfree" title="Memory" name="Free" class="absolute" display="minimum" units="B" description="Available memory size"/> + <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" class="absolute" units="B" description="Memory used by OS disk buffers"/> + <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" class="absolute" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/> + <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" class="absolute" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/> + <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/> + <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/> </category> diff --git a/daemon/events-Mali-4xx.xml b/daemon/events-Mali-4xx.xml index 8772ce4..5a71386 100644 --- a/daemon/events-Mali-4xx.xml +++ b/daemon/events-Mali-4xx.xml @@ -207,7 +207,7 @@ <event event="0x0400" option_set="fs" title="ARM Mali-4xx" name="Filmstrip" description="Scaled framebuffer"/> </category> <category name="ARM_Mali-4xx_Voltage" per_cpu="no"> - <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" display="average" average_selection="yes" units="mV" description="GPU core voltage."/> + <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" class="absolute" display="average" average_selection="yes" units="mV" description="GPU core voltage."/> </category> <category name="ARM_Mali-4xx_Frequency" per_cpu="no"> <event counter="ARM_Mali-4xx_Frequency" title="Mali GPU Frequency" name="Frequency" display="average" average_selection="yes" units="MHz" description="GPU core frequency."/> diff --git a/daemon/events-Mali-T6xx.xml b/daemon/events-Mali-T6xx.xml index 2465238..ec9ca00 100644 --- a/daemon/events-Mali-T6xx.xml +++ b/daemon/events-Mali-T6xx.xml @@ -4,14 +4,14 @@ </category> <category name="Mali-T6xx-PMShader" per_cpu="no"> - <event counter="ARM_Mali-T6xx_PM_SHADER_0" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_1" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_2" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_3" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_4" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_5" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_6" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/> - <event counter="ARM_Mali-T6xx_PM_SHADER_7" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_0" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_1" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_2" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_3" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_4" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_5" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_6" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/> + <event counter="ARM_Mali-T6xx_PM_SHADER_7" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/> </category> <category name="Mali-T6xx-PMTiler" per_cpu="no"> diff --git a/daemon/events-Perf-Hardware.xml b/daemon/events-Perf-Hardware.xml new file mode 100644 index 0000000..423696f --- /dev/null +++ b/daemon/events-Perf-Hardware.xml @@ -0,0 +1,12 @@ + <counter_set name="Perf_Hardware_cnt" count="6"/> + <category name="Perf Hardware" counter_set="Perf_Hardware_cnt" per_cpu="yes" supports_event_based_sampling="yes"> + <event counter="Perf_Hardware_ccnt" event="0" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/> + <event event="1" title="Instruction" name="Executed" description="Instruction executed"/> + <event event="2" title="Cache" name="References" description="Cache References"/> + <event event="3" title="Cache" name="Misses" description="Cache Misses"/> + <event event="4" title="Branch" name="Instructions" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/> + <event event="5" title="Branch" name="Misses" description="Branch mispredicted or not predicted"/> + <event event="6" title="Bus" name="Cycles" description="Bus Cycles"/> + <event event="7" title="Instruction" name="Stalled Frontend" description="Stalled Frontend Cycles"/> + <event event="8" title="Instruction" name="Stalled Backend" description="Stalled Backend Cycles"/> + </category> diff --git a/daemon/k/perf_event.3.12.h b/daemon/k/perf_event.3.12.h new file mode 100644 index 0000000..e886c48 --- /dev/null +++ b/daemon/k/perf_event.3.12.h @@ -0,0 +1,792 @@ +/* + * Performance events: + * + * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de> + * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra + * + * Data type definitions, declarations, prototypes. + * + * Started by: Thomas Gleixner and Ingo Molnar + * + * For licencing details see kernel-base/COPYING + */ +#ifndef _LINUX_PERF_EVENT_H +#define _LINUX_PERF_EVENT_H + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <asm/byteorder.h> + +/* + * User-space ABI bits: + */ + +/* + * attr.type + */ +enum perf_type_id { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_TRACEPOINT = 2, + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, + PERF_TYPE_BREAKPOINT = 5, + + PERF_TYPE_MAX, /* non-ABI */ +}; + +/* + * Generalized performance event event_id types, used by the + * attr.event_id parameter of the sys_perf_event_open() + * syscall: + */ +enum perf_hw_id { + /* + * Common hardware events, generalized by the kernel: + */ + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, + PERF_COUNT_HW_REF_CPU_CYCLES = 9, + + PERF_COUNT_HW_MAX, /* non-ABI */ +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + PERF_COUNT_HW_CACHE_NODE = 6, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +/* + * Special "software" events provided by the kernel, even if the hardware + * does not support performance events. These events measure various + * physical and sw events of the kernel (and allow the profiling of them as + * well): + */ +enum perf_sw_ids { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, + PERF_COUNT_SW_EMULATION_FAULTS = 8, + PERF_COUNT_SW_DUMMY = 9, + + PERF_COUNT_SW_MAX, /* non-ABI */ +}; + +/* + * Bits that can be set in attr.sample_type to request information + * in the overflow packets. + */ +enum perf_event_sample_format { + PERF_SAMPLE_IP = 1U << 0, + PERF_SAMPLE_TID = 1U << 1, + PERF_SAMPLE_TIME = 1U << 2, + PERF_SAMPLE_ADDR = 1U << 3, + PERF_SAMPLE_READ = 1U << 4, + PERF_SAMPLE_CALLCHAIN = 1U << 5, + PERF_SAMPLE_ID = 1U << 6, + PERF_SAMPLE_CPU = 1U << 7, + PERF_SAMPLE_PERIOD = 1U << 8, + PERF_SAMPLE_STREAM_ID = 1U << 9, + PERF_SAMPLE_RAW = 1U << 10, + PERF_SAMPLE_BRANCH_STACK = 1U << 11, + PERF_SAMPLE_REGS_USER = 1U << 12, + PERF_SAMPLE_STACK_USER = 1U << 13, + PERF_SAMPLE_WEIGHT = 1U << 14, + PERF_SAMPLE_DATA_SRC = 1U << 15, + PERF_SAMPLE_IDENTIFIER = 1U << 16, + + PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */ +}; + +/* + * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set + * + * If the user does not pass priv level information via branch_sample_type, + * the kernel uses the event's priv level. Branch and event priv levels do + * not have to match. Branch priv level is checked for permissions. + * + * The branch types can be combined, however BRANCH_ANY covers all types + * of branches and therefore it supersedes all the other types. + */ +enum perf_branch_sample_type { + PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */ + PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */ + PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */ + + PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */ + PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */ + PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */ + PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */ + PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */ + PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */ + PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */ + + PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */ +}; + +#define PERF_SAMPLE_BRANCH_PLM_ALL \ + (PERF_SAMPLE_BRANCH_USER|\ + PERF_SAMPLE_BRANCH_KERNEL|\ + PERF_SAMPLE_BRANCH_HV) + +/* + * Values to determine ABI of the registers dump. + */ +enum perf_sample_regs_abi { + PERF_SAMPLE_REGS_ABI_NONE = 0, + PERF_SAMPLE_REGS_ABI_32 = 1, + PERF_SAMPLE_REGS_ABI_64 = 2, +}; + +/* + * The format of the data returned by read() on a perf event fd, + * as specified by attr.read_format: + * + * struct read_format { + * { u64 value; + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING + * { u64 id; } && PERF_FORMAT_ID + * } && !PERF_FORMAT_GROUP + * + * { u64 nr; + * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED + * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING + * { u64 value; + * { u64 id; } && PERF_FORMAT_ID + * } cntr[nr]; + * } && PERF_FORMAT_GROUP + * }; + */ +enum perf_event_read_format { + PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, + PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, + PERF_FORMAT_ID = 1U << 2, + PERF_FORMAT_GROUP = 1U << 3, + + PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ +}; + +#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ +#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ +#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ +#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ + /* add: sample_stack_user */ + +/* + * Hardware event_id to monitor via a performance monitoring event: + */ +struct perf_event_attr { + + /* + * Major type: hardware/software/tracepoint/etc. + */ + __u32 type; + + /* + * Size of the attr structure, for fwd/bwd compat. + */ + __u32 size; + + /* + * Type specific configuration information. + */ + __u64 config; + + union { + __u64 sample_period; + __u64 sample_freq; + }; + + __u64 sample_type; + __u64 read_format; + + __u64 disabled : 1, /* off by default */ + inherit : 1, /* children inherit it */ + pinned : 1, /* must always be on PMU */ + exclusive : 1, /* only group on PMU */ + exclude_user : 1, /* don't count user */ + exclude_kernel : 1, /* ditto kernel */ + exclude_hv : 1, /* ditto hypervisor */ + exclude_idle : 1, /* don't count when idle */ + mmap : 1, /* include mmap data */ + comm : 1, /* include comm data */ + freq : 1, /* use freq, not period */ + inherit_stat : 1, /* per task counts */ + enable_on_exec : 1, /* next exec enables */ + task : 1, /* trace fork/exit */ + watermark : 1, /* wakeup_watermark */ + /* + * precise_ip: + * + * 0 - SAMPLE_IP can have arbitrary skid + * 1 - SAMPLE_IP must have constant skid + * 2 - SAMPLE_IP requested to have 0 skid + * 3 - SAMPLE_IP must have 0 skid + * + * See also PERF_RECORD_MISC_EXACT_IP + */ + precise_ip : 2, /* skid constraint */ + mmap_data : 1, /* non-exec mmap data */ + sample_id_all : 1, /* sample_type all events */ + + exclude_host : 1, /* don't count in host */ + exclude_guest : 1, /* don't count in guest */ + + exclude_callchain_kernel : 1, /* exclude kernel callchains */ + exclude_callchain_user : 1, /* exclude user callchains */ + mmap2 : 1, /* include mmap with inode data */ + + __reserved_1 : 40; + + union { + __u32 wakeup_events; /* wakeup every n events */ + __u32 wakeup_watermark; /* bytes before wakeup */ + }; + + __u32 bp_type; + union { + __u64 bp_addr; + __u64 config1; /* extension of config */ + }; + union { + __u64 bp_len; + __u64 config2; /* extension of config1 */ + }; + __u64 branch_sample_type; /* enum perf_branch_sample_type */ + + /* + * Defines set of user regs to dump on samples. + * See asm/perf_regs.h for details. + */ + __u64 sample_regs_user; + + /* + * Defines size of the user stack to dump on samples. + */ + __u32 sample_stack_user; + + /* Align to u64. */ + __u32 __reserved_2; +}; + +#define perf_flags(attr) (*(&(attr)->read_format + 1)) + +/* + * Ioctls that can be done on a perf event fd: + */ +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) +#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) +#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) + +enum perf_event_ioc_flags { + PERF_IOC_FLAG_GROUP = 1U << 0, +}; + +/* + * Structure of the page that can be mapped via mmap + */ +struct perf_event_mmap_page { + __u32 version; /* version number of this structure */ + __u32 compat_version; /* lowest version this is compat with */ + + /* + * Bits needed to read the hw events in user-space. + * + * u32 seq, time_mult, time_shift, idx, width; + * u64 count, enabled, running; + * u64 cyc, time_offset; + * s64 pmc = 0; + * + * do { + * seq = pc->lock; + * barrier() + * + * enabled = pc->time_enabled; + * running = pc->time_running; + * + * if (pc->cap_usr_time && enabled != running) { + * cyc = rdtsc(); + * time_offset = pc->time_offset; + * time_mult = pc->time_mult; + * time_shift = pc->time_shift; + * } + * + * idx = pc->index; + * count = pc->offset; + * if (pc->cap_usr_rdpmc && idx) { + * width = pc->pmc_width; + * pmc = rdpmc(idx - 1); + * } + * + * barrier(); + * } while (pc->lock != seq); + * + * NOTE: for obvious reason this only works on self-monitoring + * processes. + */ + __u32 lock; /* seqlock for synchronization */ + __u32 index; /* hardware event identifier */ + __s64 offset; /* add to hardware event value */ + __u64 time_enabled; /* time event active */ + __u64 time_running; /* time event on cpu */ + union { + __u64 capabilities; + struct { + __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */ + cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */ + + cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */ + cap_user_time : 1, /* The time_* fields are used */ + cap_user_time_zero : 1, /* The time_zero field is used */ + cap_____res : 59; + }; + }; + + /* + * If cap_usr_rdpmc this field provides the bit-width of the value + * read using the rdpmc() or equivalent instruction. This can be used + * to sign extend the result like: + * + * pmc <<= 64 - width; + * pmc >>= 64 - width; // signed shift right + * count += pmc; + */ + __u16 pmc_width; + + /* + * If cap_usr_time the below fields can be used to compute the time + * delta since time_enabled (in ns) using rdtsc or similar. + * + * u64 quot, rem; + * u64 delta; + * + * quot = (cyc >> time_shift); + * rem = cyc & ((1 << time_shift) - 1); + * delta = time_offset + quot * time_mult + + * ((rem * time_mult) >> time_shift); + * + * Where time_offset,time_mult,time_shift and cyc are read in the + * seqcount loop described above. This delta can then be added to + * enabled and possible running (if idx), improving the scaling: + * + * enabled += delta; + * if (idx) + * running += delta; + * + * quot = count / running; + * rem = count % running; + * count = quot * enabled + (rem * enabled) / running; + */ + __u16 time_shift; + __u32 time_mult; + __u64 time_offset; + /* + * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated + * from sample timestamps. + * + * time = timestamp - time_zero; + * quot = time / time_mult; + * rem = time % time_mult; + * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; + * + * And vice versa: + * + * quot = cyc >> time_shift; + * rem = cyc & ((1 << time_shift) - 1); + * timestamp = time_zero + quot * time_mult + + * ((rem * time_mult) >> time_shift); + */ + __u64 time_zero; + __u32 size; /* Header size up to __reserved[] fields. */ + + /* + * Hole for extension of the self monitor capabilities + */ + + __u8 __reserved[118*8+4]; /* align to 1k. */ + + /* + * Control data for the mmap() data buffer. + * + * User-space reading the @data_head value should issue an smp_rmb(), + * after reading this value. + * + * When the mapping is PROT_WRITE the @data_tail value should be + * written by userspace to reflect the last read data, after issueing + * an smp_mb() to separate the data read from the ->data_tail store. + * In this case the kernel will not over-write unread data. + * + * See perf_output_put_handle() for the data ordering. + */ + __u64 data_head; /* head in the data section */ + __u64 data_tail; /* user-space written tail */ +}; + +#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0) +#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) +#define PERF_RECORD_MISC_KERNEL (1 << 0) +#define PERF_RECORD_MISC_USER (2 << 0) +#define PERF_RECORD_MISC_HYPERVISOR (3 << 0) +#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) +#define PERF_RECORD_MISC_GUEST_USER (5 << 0) + +#define PERF_RECORD_MISC_MMAP_DATA (1 << 13) +/* + * Indicates that the content of PERF_SAMPLE_IP points to + * the actual instruction that triggered the event. See also + * perf_event_attr::precise_ip. + */ +#define PERF_RECORD_MISC_EXACT_IP (1 << 14) +/* + * Reserve the last bit to indicate some extended misc field + */ +#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15) + +struct perf_event_header { + __u32 type; + __u16 misc; + __u16 size; +}; + +enum perf_event_type { + + /* + * If perf_event_attr.sample_id_all is set then all event types will + * have the sample_type selected fields related to where/when + * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU, + * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed + * just after the perf_event_header and the fields already present for + * the existing fields, i.e. at the end of the payload. That way a newer + * perf.data file will be supported by older perf tools, with these new + * optional fields being ignored. + * + * struct sample_id { + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 id; } && PERF_SAMPLE_IDENTIFIER + * } && perf_event_attr::sample_id_all + * + * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The + * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed + * relative to header.size. + */ + + /* + * The MMAP events record the PROT_EXEC mappings so that we can + * correlate userspace IPs to code. They have the following structure: + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * char filename[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MMAP = 1, + + /* + * struct { + * struct perf_event_header header; + * u64 id; + * u64 lost; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_LOST = 2, + + /* + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * char comm[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_COMM = 3, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_EXIT = 4, + + /* + * struct { + * struct perf_event_header header; + * u64 time; + * u64 id; + * u64 stream_id; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_THROTTLE = 5, + PERF_RECORD_UNTHROTTLE = 6, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_FORK = 7, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, tid; + * + * struct read_format values; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_READ = 8, + + /* + * struct { + * struct perf_event_header header; + * + * # + * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. + * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position + * # is fixed relative to header. + * # + * + * { u64 id; } && PERF_SAMPLE_IDENTIFIER + * { u64 ip; } && PERF_SAMPLE_IP + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 addr; } && PERF_SAMPLE_ADDR + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 period; } && PERF_SAMPLE_PERIOD + * + * { struct read_format values; } && PERF_SAMPLE_READ + * + * { u64 nr, + * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN + * + * # + * # The RAW record below is opaque data wrt the ABI + * # + * # That is, the ABI doesn't make any promises wrt to + * # the stability of its content, it may vary depending + * # on event, hardware, kernel version and phase of + * # the moon. + * # + * # In other words, PERF_SAMPLE_RAW contents are not an ABI. + * # + * + * { u32 size; + * char data[size];}&& PERF_SAMPLE_RAW + * + * { u64 nr; + * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK + * + * { u64 abi; # enum perf_sample_regs_abi + * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER + * + * { u64 size; + * char data[size]; + * u64 dyn_size; } && PERF_SAMPLE_STACK_USER + * + * { u64 weight; } && PERF_SAMPLE_WEIGHT + * { u64 data_src; } && PERF_SAMPLE_DATA_SRC + * }; + */ + PERF_RECORD_SAMPLE = 9, + + /* + * The MMAP2 records are an augmented version of MMAP, they add + * maj, min, ino numbers to be used to uniquely identify each mapping + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * u32 maj; + * u32 min; + * u64 ino; + * u64 ino_generation; + * char filename[]; + * struct sample_id sample_id; + * }; + */ + PERF_RECORD_MMAP2 = 10, + + PERF_RECORD_MAX, /* non-ABI */ +}; + +#define PERF_MAX_STACK_DEPTH 127 + +enum perf_callchain_context { + PERF_CONTEXT_HV = (__u64)-32, + PERF_CONTEXT_KERNEL = (__u64)-128, + PERF_CONTEXT_USER = (__u64)-512, + + PERF_CONTEXT_GUEST = (__u64)-2048, + PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, + PERF_CONTEXT_GUEST_USER = (__u64)-2560, + + PERF_CONTEXT_MAX = (__u64)-4095, +}; + +#define PERF_FLAG_FD_NO_GROUP (1U << 0) +#define PERF_FLAG_FD_OUTPUT (1U << 1) +#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ + +union perf_mem_data_src { + __u64 val; + struct { + __u64 mem_op:5, /* type of opcode */ + mem_lvl:14, /* memory hierarchy level */ + mem_snoop:5, /* snoop mode */ + mem_lock:2, /* lock instr */ + mem_dtlb:7, /* tlb access */ + mem_rsvd:31; + }; +}; + +/* type of opcode (load/store/prefetch,code) */ +#define PERF_MEM_OP_NA 0x01 /* not available */ +#define PERF_MEM_OP_LOAD 0x02 /* load instruction */ +#define PERF_MEM_OP_STORE 0x04 /* store instruction */ +#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ +#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ +#define PERF_MEM_OP_SHIFT 0 + +/* memory hierarchy (memory level, hit or miss) */ +#define PERF_MEM_LVL_NA 0x01 /* not available */ +#define PERF_MEM_LVL_HIT 0x02 /* hit level */ +#define PERF_MEM_LVL_MISS 0x04 /* miss level */ +#define PERF_MEM_LVL_L1 0x08 /* L1 */ +#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ +#define PERF_MEM_LVL_L2 0x20 /* L2 */ +#define PERF_MEM_LVL_L3 0x40 /* L3 */ +#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ +#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ +#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ +#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ +#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ +#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ +#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ +#define PERF_MEM_LVL_SHIFT 5 + +/* snoop mode */ +#define PERF_MEM_SNOOP_NA 0x01 /* not available */ +#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ +#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ +#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ +#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ +#define PERF_MEM_SNOOP_SHIFT 19 + +/* locked instruction */ +#define PERF_MEM_LOCK_NA 0x01 /* not available */ +#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ +#define PERF_MEM_LOCK_SHIFT 24 + +/* TLB access */ +#define PERF_MEM_TLB_NA 0x01 /* not available */ +#define PERF_MEM_TLB_HIT 0x02 /* hit level */ +#define PERF_MEM_TLB_MISS 0x04 /* miss level */ +#define PERF_MEM_TLB_L1 0x08 /* L1 */ +#define PERF_MEM_TLB_L2 0x10 /* L2 */ +#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ +#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ +#define PERF_MEM_TLB_SHIFT 26 + +#define PERF_MEM_S(a, s) \ + (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) + +/* + * single taken branch record layout: + * + * from: source instruction (may not always be a branch insn) + * to: branch target + * mispred: branch target was mispredicted + * predicted: branch target was predicted + * + * support for mispred, predicted is optional. In case it + * is not supported mispred = predicted = 0. + * + * in_tx: running in a hardware transaction + * abort: aborting a hardware transaction + */ +struct perf_branch_entry { + __u64 from; + __u64 to; + __u64 mispred:1, /* target mispredicted */ + predicted:1,/* target predicted */ + in_tx:1, /* in transaction */ + abort:1, /* transaction abort */ + reserved:60; +}; + +#endif /* _LINUX_PERF_EVENT_H */ diff --git a/daemon/k/perf_event.h b/daemon/k/perf_event.h new file mode 120000 index 0000000..e5dff8c --- /dev/null +++ b/daemon/k/perf_event.h @@ -0,0 +1 @@ +perf_event.3.12.h
\ No newline at end of file diff --git a/daemon/main.cpp b/daemon/main.cpp index bfd36b9..1275aef 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -1,32 +1,30 @@ /** - * Copyright (C) ARM Limited 2010-2013. All rights reserved. + * 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 <stdlib.h> -#include <signal.h> -#include <sys/wait.h> -#include <unistd.h> -#include <sys/syscall.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mount.h> +#include <arpa/inet.h> #include <fcntl.h> +#include <pthread.h> #include <sys/mman.h> -#include <sys/time.h> +#include <sys/mount.h> +#include <sys/prctl.h> #include <sys/resource.h> -#include <arpa/inet.h> #include <sys/socket.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <unistd.h> + #include "Child.h" -#include "SessionData.h" -#include "OlySocket.h" +#include "KMod.h" #include "Logging.h" +#include "OlySocket.h" #include "OlyUtility.h" -#include "KMod.h" +#include "SessionData.h" #define DEBUG false @@ -34,7 +32,7 @@ extern Child* child; static int shutdownFilesystem(); static pthread_mutex_t numSessions_mutex; static int numSessions = 0; -static OlySocket* sock = NULL; +static OlyServerSocket* sock = NULL; static bool driverRunningAtStart = false; static bool driverMountedAtStart = false; @@ -157,6 +155,7 @@ typedef struct { static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 }; static void* answerThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned long)&"gatord-discover", 0, 0, 0); const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid; RVIConfigureInfo dstAns; int req = udpPort(UDP_REQ_PORT); @@ -231,16 +230,7 @@ static bool init_module (const char * const location) { return ret; } -static int setupFilesystem(char* module) { - int retval; - - // Verify root permissions - uid_t euid = geteuid(); - if (euid) { - logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); - handleException(); - } - +static bool setupFilesystem(char* module) { if (module) { // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running shutdownFilesystem(); @@ -252,7 +242,7 @@ static int setupFilesystem(char* module) { } } - retval = mountGatorFS(); + const int retval = mountGatorFS(); if (retval == 1) { logg->logMessage("Driver already running at startup"); driverRunningAtStart = true; @@ -274,8 +264,8 @@ static int setupFilesystem(char* module) { } if (access(location, F_OK) == -1) { - logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line"); - handleException(); + // The gator kernel is not already loaded and unable to locate gator.ko + return false; } // Load driver @@ -296,7 +286,7 @@ static int setupFilesystem(char* module) { } } - return 0; + return true; } static int shutdownFilesystem() { @@ -418,8 +408,28 @@ int main(int argc, char** argv) { // Parse the command line parameters struct cmdline_t cmdline = parseCommandLine(argc, argv); + // Verify root permissions + uid_t euid = geteuid(); + if (euid) { + logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); + handleException(); + } + // Call before setting up the SIGCHLD handler, as system() spawns child processes - setupFilesystem(cmdline.module); + if (!setupFilesystem(cmdline.module)) { + logg->logMessage("Unable to setup gatorfs, trying perf"); + if (!gSessionData->perf.setup()) { + logg->logError(__FILE__, __LINE__, + "Unable to locate gator.ko driver:\n" + " >>> gator.ko should be co-located with gatord in the same directory\n" + " >>> OR insmod gator.ko prior to launching gatord\n" + " >>> OR specify the location of gator.ko on the command line\n" + " >>> OR run Linux 3.12 or later with perf support to collect data via userspace only"); + handleException(); + } + } + + gSessionData->hwmon.setup(); // Handle child exit codes signal(SIGCHLD, child_exit); @@ -439,11 +449,11 @@ int main(int argc, char** argv) { logg->logError(__FILE__, __LINE__, "Failed to create answer thread"); handleException(); } - sock = new OlySocket(cmdline.port, true); + sock = new OlyServerSocket(cmdline.port); // Forever loop, can be exited via a signal or exception while (1) { logg->logMessage("Waiting on connection..."); - sock->acceptConnection(); + OlySocket client(sock->acceptConnection()); int pid = fork(); if (pid < 0) { @@ -452,13 +462,13 @@ int main(int argc, char** argv) { } else if (pid == 0) { // Child sock->closeServerSocket(); - child = new Child(sock, numSessions + 1); + child = new Child(&client, numSessions + 1); child->run(); delete child; exit(0); } else { // Parent - sock->closeSocket(); + client.closeSocket(); pthread_mutex_lock(&numSessions_mutex); numSessions++; |