gator: Version 5.18

Signed-off-by: Jon Medhurst <tixy@linaro.org>
diff --git a/tools/gator/daemon/Android.mk b/tools/gator/daemon/Android.mk
index a042971..045d028 100644
--- a/tools/gator/daemon/Android.mk
+++ b/tools/gator/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 @@
 	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/tools/gator/daemon/Buffer.cpp b/tools/gator/daemon/Buffer.cpp
index 090a715..93557da 100644
--- a/tools/gator/daemon/Buffer.cpp
+++ b/tools/gator/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)
+#define mask (mSize - 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) {
+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 @@
 		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 @@
 	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;
 }
 
-void Buffer::commit (const uint64_t time) {
+int Buffer::contiguousSpaceAvailable() const {
+	int remaining = bytesAvailable();
+	int contiguous = mSize - mWritePos;
+	if (remaining < contiguous) {
+		return remaining;
+	} else {
+		return contiguous;
+	}
+}
+
+void Buffer::commit(const uint64_t time) {
 	// post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
 	const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
-	int length = 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 @@
 			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 @@
 			b |= 0x80;
 		}
 
-		buf[(writePos + packedBytes) & mask] = b;
+		mBuf[(mWritePos + packedBytes) & mask] = b;
 		packedBytes++;
 	}
 
-	writePos = (writePos + packedBytes) & mask;
+	mWritePos = (mWritePos + packedBytes) & mask;
 }
 
-void Buffer::frame () {
+void Buffer::writeBytes(const void *const data, size_t count) {
+	size_t i;
+	for (i = 0; i < count; ++i) {
+		mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i];
+	}
+
+	mWritePos = (mWritePos + i) & mask;
+}
+
+void Buffer::writeString(const char *const str) {
+	const int len = strlen(str);
+	packInt(len);
+	writeBytes(str, len);
+}
+
+void Buffer::frame() {
 	if (!gSessionData->mLocalCapture) {
 		packInt(RESPONSE_APC_DATA);
 	}
 	// Reserve space for the length
-	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 @@
 	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 @@
 	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/tools/gator/daemon/Buffer.h b/tools/gator/daemon/Buffer.h
index b3c8d78..5023777 100644
--- a/tools/gator/daemon/Buffer.h
+++ b/tools/gator/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);
+	void write(Sender *sender);
 
-	int bytesAvailable () const;
-	void commit (const uint64_t time);
-	void check (const uint64_t time);
+	int bytesAvailable() const;
+	int contiguousSpaceAvailable() const;
+	void commit(const uint64_t time);
+	void check(const uint64_t time);
 
-	void frame ();
+	void frame();
 
-	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);
+	// 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 setDone ();
-	bool isDone () const;
+	// 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);
+
+	// 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;
+
+	// 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);
+	bool commitReady() const;
+	bool checkSpace(int bytes);
 
-	void packInt (int32_t x);
-	void packInt64 (int64_t x);
+	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 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;
+	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/tools/gator/daemon/CapturedXML.cpp b/tools/gator/daemon/CapturedXML.cpp
index 30c4c44..cf79b72 100644
--- a/tools/gator/daemon/CapturedXML.cpp
+++ b/tools/gator/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 @@
 
 	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 @@
 	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/tools/gator/daemon/CapturedXML.h b/tools/gator/daemon/CapturedXML.h
index b0482f5..efc1e52 100644
--- a/tools/gator/daemon/CapturedXML.h
+++ b/tools/gator/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/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp
index 9ee2ef8..ca33561 100644
--- a/tools/gator/daemon/Child.cpp
+++ b/tools/gator/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 @@
 	}
 	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 @@
 	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::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 @@
 	{ 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 @@
 		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 @@
 		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 @@
 	sem_wait(&senderThreadStarted);
 
 	// Start profiling
-	logg->logMessage("********** Profiling started **********");
-	collector->start();
-	sem_post(&startProfile);
+	primarySource->run();
 
-	// 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");
-
-	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 @@
 
 	logg->logMessage("Profiling ended.");
 
-	delete buffer;
-	delete collectorFifo;
+	delete externalSource;
+	delete userSpaceSource;
+	delete primarySource;
 	delete sender;
-	delete collector;
 	delete localCapture;
 }
diff --git a/tools/gator/daemon/Child.h b/tools/gator/daemon/Child.h
index 0330e9d7..9e206d7 100644
--- a/tools/gator/daemon/Child.h
+++ b/tools/gator/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/tools/gator/daemon/Collector.cpp b/tools/gator/daemon/Collector.cpp
deleted file mode 100644
index bf73534..0000000
--- a/tools/gator/daemon/Collector.cpp
+++ /dev/null
@@ -1,221 +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.
- */
-
-#define __STDC_FORMAT_MACROS
-
-#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 "Logging.h"
-#include "Sender.h"
-
-// Driver initialization independent of session settings
-Collector::Collector() {
-	mBufferFD = 0;
-
-	checkVersion();
-
-	int enable = -1;
-	if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
-		logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
-		handleException();
-	}
-
-	readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
-	if (gSessionData->mCores == 0) {
-		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() {
-	// Write zero for safety, as a zero should have already been written
-	writeDriver("/dev/gator/enable", "0");
-
-	// Calls event_buffer_release in the driver
-	if (mBufferFD) {
-		close(mBufferFD);
-	}
-}
-
-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();
-	}
-
-	// 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();
-		}
-	}
-}
-
-void Collector::start() {
-	// Set the maximum backtrace depth
-	if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) {
-		logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth");
-		handleException();
-	}
-
-	// open the buffer which calls userspace_buffer_open() in the driver
-	mBufferFD = open("/dev/gator/buffer", O_RDONLY);
-	if (mBufferFD < 0) {
-		logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure.");
-		handleException();
-	}
-
-	// set the tick rate of the profiling timer
-	if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) {
-		logg->logError(__FILE__, __LINE__, "Unable to set the driver tick");
-		handleException();
-	}
-
-	// notify the kernel of the response type
-	int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA;
-	if (writeDriver("/dev/gator/response_type", response_type)) {
-		logg->logError(__FILE__, __LINE__, "Unable to write the response type");
-		handleException();
-	}
-
-	// Set the live rate
-	if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) {
-		logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate");
-		handleException();
-	}
-
-	logg->logMessage("Start the driver");
-
-	// This command makes the driver start profiling by calling gator_op_start() in the driver
-	if (writeDriver("/dev/gator/enable", "1") != 0) {
-		logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure.");
-		handleException();
-	}
-
-	lseek(mBufferFD, 0, SEEK_SET);
-}
-
-// These commands should cause the read() function in collect() to return
-void Collector::stop() {
-	// This will 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);
-
-	// 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);
-	}
-
-	// 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) {
-		return -1;
-	}
-	if (fscanf(file, "%u", value) != 1) {
-		fclose(file);
-		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) {
-		return -1;
-	}
-	if (fscanf(file, "%" SCNi64, value) != 1) {
-		fclose(file);
-		logg->logMessage("Invalid value in file %s", fullpath);
-		return -1;
-	}
-	fclose(file);
-	return 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);
-}
-
-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);
-}
-
-int Collector::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) {
-		close(fd);
-		logg->logMessage("Opened but could not write to %s", fullpath);
-		return -1;
-	}
-	close(fd);
-	return 0;
-}
-
-int Collector::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) {
-	if (writeDriver(path, *value) || readInt64Driver(path, value)) {
-		return -1;
-	}
-	return 0;
-}
diff --git a/tools/gator/daemon/Collector.h b/tools/gator/daemon/Collector.h
deleted file mode 100644
index c5e9eac..0000000
--- a/tools/gator/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/tools/gator/daemon/Config.h b/tools/gator/daemon/Config.h
new file mode 100644
index 0000000..6f5e2aa
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/ConfigurationXML.cpp b/tools/gator/daemon/ConfigurationXML.cpp
index 2a5252a..fd479f2 100644
--- a/tools/gator/daemon/ConfigurationXML.cpp
+++ b/tools/gator/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 @@
 
 	// clear counter overflow
 	gSessionData->mCounterOverflow = 0;
+	gSessionData->mIsEBS = false;
 	mIndex = 0;
 
 	// disable all counters prior to parsing the configuration xml
@@ -155,6 +158,9 @@
 	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::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/tools/gator/daemon/ConfigurationXML.h b/tools/gator/daemon/ConfigurationXML.h
index 5650f48..efa415e 100644
--- a/tools/gator/daemon/ConfigurationXML.h
+++ b/tools/gator/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/tools/gator/daemon/Counter.h b/tools/gator/daemon/Counter.h
index 231a85d..6891745 100644
--- a/tools/gator/daemon/Counter.h
+++ b/tools/gator/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 @@
 	void clear () {
 		mType[0] = '\0';
 		mEnabled = false;
-		mEvent = 0;
+		mEvent = -1;
 		mCount = 0;
 		mKey = 0;
 		mDriver = NULL;
diff --git a/tools/gator/daemon/Driver.cpp b/tools/gator/daemon/Driver.cpp
index c262467..09e0401 100644
--- a/tools/gator/daemon/Driver.cpp
+++ b/tools/gator/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/tools/gator/daemon/Driver.h b/tools/gator/daemon/Driver.h
index f3a932f..e5ed7b6 100644
--- a/tools/gator/daemon/Driver.h
+++ b/tools/gator/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 @@
 	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/tools/gator/daemon/DriverSource.cpp b/tools/gator/daemon/DriverSource.cpp
new file mode 100644
index 0000000..f78ec6b7
--- /dev/null
+++ b/tools/gator/daemon/DriverSource.cpp
@@ -0,0 +1,276 @@
+/**
+ * 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.
+ */
+
+#define __STDC_FORMAT_MACROS
+
+#include "DriverSource.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#include "Child.h"
+#include "Fifo.h"
+#include "Logging.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+extern Child *child;
+
+DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) {
+	int driver_version = 0;
+
+	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) {
+		logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress.");
+		handleException();
+	}
+
+	readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores);
+	if (gSessionData->mCores == 0) {
+		gSessionData->mCores = 1;
+	}
+
+	if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
+		logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
+		handleException();
+	}
+}
+
+DriverSource::~DriverSource() {
+	delete mFifo;
+
+	// Write zero for safety, as a zero should have already been written
+	writeDriver("/dev/gator/enable", "0");
+
+	// Calls event_buffer_release in the driver
+	if (mBufferFD) {
+		close(mBufferFD);
+	}
+}
+
+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);
+
+	return true;
+}
+
+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");
+		handleException();
+	}
+
+	// open the buffer which calls userspace_buffer_open() in the driver
+	mBufferFD = open("/dev/gator/buffer", O_RDONLY);
+	if (mBufferFD < 0) {
+		logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure.");
+		handleException();
+	}
+
+	// set the tick rate of the profiling timer
+	if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) {
+		logg->logError(__FILE__, __LINE__, "Unable to set the driver tick");
+		handleException();
+	}
+
+	// notify the kernel of the response type
+	int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA;
+	if (writeDriver("/dev/gator/response_type", response_type)) {
+		logg->logError(__FILE__, __LINE__, "Unable to write the response type");
+		handleException();
+	}
+
+	// Set the live rate
+	if (writeReadDriver("/dev/gator/live_rate", &gSessionData->mLiveRate)) {
+		logg->logError(__FILE__, __LINE__, "Unable to set the driver live rate");
+		handleException();
+	}
+
+	logg->logMessage("Start the driver");
+
+	// This command makes the driver start profiling by calling gator_op_start() in the driver
+	if (writeDriver("/dev/gator/enable", "1") != 0) {
+		logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure.");
+		handleException();
+	}
+
+	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");
+}
+
+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");
+	}
+}
+
+bool DriverSource::isDone() {
+	return mLength <= 0;
+}
+
+void DriverSource::write(Sender *sender) {
+	char *data = mFifo->read(&mLength);
+	if (data != NULL) {
+		sender->writeData(data, mLength, RESPONSE_APC_DATA);
+		mFifo->release();
+	}
+}
+
+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;
+	}
+	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;
+	}
+
+	return 0;
+}
+
+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;
+	}
+
+	const ssize_t bytes = read(fd, data, sizeof(data) - 1);
+	close(fd);
+	if (bytes < 0) {
+		return -1;
+	}
+	data[bytes] = '\0';
+
+	char *endptr;
+	errno = 0;
+	*value = strtoll(data, &endptr, 10);
+	if (errno != 0 || *endptr != '\n') {
+		logg->logMessage("Invalid value in file %s", fullpath);
+		return -1;
+	}
+
+	return 0;
+}
+
+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) {
+		close(fd);
+		logg->logMessage("Opened but could not write to %s", fullpath);
+		return -1;
+	}
+	close(fd);
+	return 0;
+}
+
+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 DriverSource::writeReadDriver(const char *path, int64_t *value) {
+	if (writeDriver(path, *value) || readInt64Driver(path, value)) {
+		return -1;
+	}
+	return 0;
+}
diff --git a/tools/gator/daemon/DriverSource.h b/tools/gator/daemon/DriverSource.h
new file mode 100644
index 0000000..dcf1078
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/DynBuf.cpp b/tools/gator/daemon/DynBuf.cpp
new file mode 100644
index 0000000..6f92b33
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/DynBuf.h b/tools/gator/daemon/DynBuf.h
new file mode 100644
index 0000000..2f4554a
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/EventsXML.cpp b/tools/gator/daemon/EventsXML.cpp
index 2a80482..a07a046 100644
--- a/tools/gator/daemon/EventsXML.cpp
+++ b/tools/gator/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 @@
 		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/tools/gator/daemon/EventsXML.h b/tools/gator/daemon/EventsXML.h
index 8e693ef..6cd1560f 100644
--- a/tools/gator/daemon/EventsXML.h
+++ b/tools/gator/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/tools/gator/daemon/ExternalSource.cpp b/tools/gator/daemon/ExternalSource.cpp
new file mode 100644
index 0000000..fe5824b
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/ExternalSource.h b/tools/gator/daemon/ExternalSource.h
new file mode 100644
index 0000000..2052bdf
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/Fifo.cpp b/tools/gator/daemon/Fifo.cpp
index 250a4d0..f672e92 100644
--- a/tools/gator/daemon/Fifo.cpp
+++ b/tools/gator/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/tools/gator/daemon/Fifo.h b/tools/gator/daemon/Fifo.h
index d25cd68..7dd7426 100644
--- a/tools/gator/daemon/Fifo.h
+++ b/tools/gator/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/tools/gator/daemon/Hwmon.cpp b/tools/gator/daemon/Hwmon.cpp
index 1d7c0da..778f307 100644
--- a/tools/gator/daemon/Hwmon.cpp
+++ b/tools/gator/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 @@
 	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 @@
 }
 
 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 @@
 		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 @@
 	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/tools/gator/daemon/Hwmon.h b/tools/gator/daemon/Hwmon.h
index 46bb42e..a22a360 100644
--- a/tools/gator/daemon/Hwmon.h
+++ b/tools/gator/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 @@
 	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/tools/gator/daemon/KMod.cpp b/tools/gator/daemon/KMod.cpp
index 559297f..9300002 100644
--- a/tools/gator/daemon/KMod.cpp
+++ b/tools/gator/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 @@
 				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 @@
 
 	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::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/tools/gator/daemon/KMod.h b/tools/gator/daemon/KMod.h
index 7974262..fb7fc8a 100644
--- a/tools/gator/daemon/KMod.h
+++ b/tools/gator/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 @@
 	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/tools/gator/daemon/LocalCapture.cpp b/tools/gator/daemon/LocalCapture.cpp
index 3235a34..d2a4b79 100644
--- a/tools/gator/daemon/LocalCapture.cpp
+++ b/tools/gator/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/tools/gator/daemon/LocalCapture.h b/tools/gator/daemon/LocalCapture.h
index 8042d6a..aadecce 100644
--- a/tools/gator/daemon/LocalCapture.h
+++ b/tools/gator/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/tools/gator/daemon/Logging.cpp b/tools/gator/daemon/Logging.cpp
index 5fd45b5..b8d3178 100644
--- a/tools/gator/daemon/Logging.cpp
+++ b/tools/gator/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/tools/gator/daemon/Logging.h b/tools/gator/daemon/Logging.h
index 8f960de..6ae3280 100644
--- a/tools/gator/daemon/Logging.h
+++ b/tools/gator/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 @@
 	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/tools/gator/daemon/Monitor.cpp b/tools/gator/daemon/Monitor.cpp
new file mode 100644
index 0000000..90d5c47
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/Monitor.h b/tools/gator/daemon/Monitor.h
new file mode 100644
index 0000000..6e268b6
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/OlySocket.cpp b/tools/gator/daemon/OlySocket.cpp
index ab5c3c2..26e4768 100644
--- a/tools/gator/daemon/OlySocket.cpp
+++ b/tools/gator/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 @@
   }
 #endif
 
-  if (multiple) {
-    createServerSocket(port);
-  } else {
-    createSingleServerConnection(port);
+  createServerSocket(port);
+}
+
+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(int port, char* host) {
-  mFDServer = 0;
-  createClientSocket(host, port);
+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::closeServerSocket() {
+void OlyServerSocket::closeServerSocket() {
   if (CLOSE_SOCKET(mFDServer) != 0) {
     logg->logError(__FILE__, __LINE__, "Failed to close server socket.");
     handleException();
@@ -78,7 +137,7 @@
   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 @@
 #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 @@
 
 // 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/tools/gator/daemon/OlySocket.h b/tools/gator/daemon/OlySocket.h
index 5bab7d1..eab786b 100644
--- a/tools/gator/daemon/OlySocket.h
+++ b/tools/gator/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, mFDServer;
-  void createClientSocket(char* hostname, int port);
-  void createSingleServerConnection(int port);
+  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 mFDServer;
+
   void createServerSocket(int port);
 };
 
diff --git a/tools/gator/daemon/OlyUtility.cpp b/tools/gator/daemon/OlyUtility.cpp
index 0b22d6e..45340a2 100644
--- a/tools/gator/daemon/OlyUtility.cpp
+++ b/tools/gator/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/tools/gator/daemon/OlyUtility.h b/tools/gator/daemon/OlyUtility.h
index abab0a5..1d26beb 100644
--- a/tools/gator/daemon/OlyUtility.h
+++ b/tools/gator/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/tools/gator/daemon/PerfBuffer.cpp b/tools/gator/daemon/PerfBuffer.cpp
new file mode 100644
index 0000000..5fad583
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfBuffer.h b/tools/gator/daemon/PerfBuffer.h
new file mode 100644
index 0000000..278a3b9
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfDriver.cpp b/tools/gator/daemon/PerfDriver.cpp
new file mode 100644
index 0000000..8e25c22
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfDriver.h b/tools/gator/daemon/PerfDriver.h
new file mode 100644
index 0000000..3181b74
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfGroup.cpp b/tools/gator/daemon/PerfGroup.cpp
new file mode 100644
index 0000000..faf5fca
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfGroup.h b/tools/gator/daemon/PerfGroup.h
new file mode 100644
index 0000000..af496d4
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfSource.cpp b/tools/gator/daemon/PerfSource.cpp
new file mode 100644
index 0000000..1f1cb19
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/PerfSource.h b/tools/gator/daemon/PerfSource.h
new file mode 100644
index 0000000..3f471c8
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/Proc.cpp b/tools/gator/daemon/Proc.cpp
new file mode 100644
index 0000000..e0b9e22
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/Proc.h b/tools/gator/daemon/Proc.h
new file mode 100644
index 0000000..057b610
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/Sender.cpp b/tools/gator/daemon/Sender.cpp
index 8eb348f..3a981a6 100644
--- a/tools/gator/daemon/Sender.cpp
+++ b/tools/gator/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 <stdlib.h>
-#include <unistd.h>
 #include "Sender.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "Buffer.h"
 #include "Logging.h"
 #include "OlySocket.h"
 #include "SessionData.h"
@@ -49,9 +48,12 @@
 }
 
 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 @@
 			// 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 @@
 		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/tools/gator/daemon/Sender.h b/tools/gator/daemon/Sender.h
index b388f03..4c359db 100644
--- a/tools/gator/daemon/Sender.h
+++ b/tools/gator/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/tools/gator/daemon/SessionData.cpp b/tools/gator/daemon/SessionData.cpp
index cf84407..c169299a 100644
--- a/tools/gator/daemon/SessionData.cpp
+++ b/tools/gator/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 @@
 	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::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 @@
 	}
 
 	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 @@
 			}
 
 			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/tools/gator/daemon/SessionData.h b/tools/gator/daemon/SessionData.h
index c834251..ea34240 100644
--- a/tools/gator/daemon/SessionData.h
+++ b/tools/gator/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 @@
 	void parseSessionXML(char* xmlString);
 
 	Hwmon hwmon;
+	PerfDriver perf;
 
 	char mCoreName[MAX_STRING_LEN];
 	struct ImageLinkList *mImages;
@@ -47,6 +48,7 @@
 	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 @@
 	int64_t mLiveRate;
 	int mDuration;
 	int mCores;
-	int mCpuId;
+	int mPageSize;
+	int mCpuIds[NR_CPUS];
+	int mMaxCpuId;
 
 	// PMU Counters
 	int mCounterOverflow;
diff --git a/tools/gator/daemon/SessionXML.cpp b/tools/gator/daemon/SessionXML.cpp
index 0a0a027..55b2f92 100644
--- a/tools/gator/daemon/SessionXML.cpp
+++ b/tools/gator/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_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 @@
 	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/tools/gator/daemon/SessionXML.h b/tools/gator/daemon/SessionXML.h
index 0fb03bd..e146094 100644
--- a/tools/gator/daemon/SessionXML.h
+++ b/tools/gator/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 @@
 
 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/tools/gator/daemon/Source.cpp b/tools/gator/daemon/Source.cpp
new file mode 100644
index 0000000..60cf704
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/Source.h b/tools/gator/daemon/Source.h
new file mode 100644
index 0000000..56ac3d6
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/StreamlineSetup.cpp b/tools/gator/daemon/StreamlineSetup.cpp
index 2faada2..caa665e 100644
--- a/tools/gator/daemon/StreamlineSetup.cpp
+++ b/tools/gator/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::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 @@
 
 	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/tools/gator/daemon/StreamlineSetup.h b/tools/gator/daemon/StreamlineSetup.h
index d6d9a6e..74bb197 100644
--- a/tools/gator/daemon/StreamlineSetup.h
+++ b/tools/gator/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/tools/gator/daemon/UEvent.cpp b/tools/gator/daemon/UEvent.cpp
new file mode 100644
index 0000000..282e965
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/UEvent.h b/tools/gator/daemon/UEvent.h
new file mode 100644
index 0000000..2f7ef2c
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/UserSpaceSource.cpp b/tools/gator/daemon/UserSpaceSource.cpp
new file mode 100644
index 0000000..debe696
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/UserSpaceSource.h b/tools/gator/daemon/UserSpaceSource.h
new file mode 100644
index 0000000..fb5889d
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/common.mk b/tools/gator/daemon/common.mk
index 031d169..d9dc146 100644
--- a/tools/gator/daemon/common.mk
+++ b/tools/gator/daemon/common.mk
@@ -25,7 +25,7 @@
 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 @@
 	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/tools/gator/daemon/configuration.xml b/tools/gator/daemon/configuration.xml
deleted file mode 100644
index b44c00a..0000000
--- a/tools/gator/daemon/configuration.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<configurations revision="3">
-  <configuration counter="ARM_ARM11_ccnt" event="0xff"/>
-  <configuration counter="ARM_ARM11_cnt0" event="0x7"/>
-  <configuration counter="ARM_ARM11_cnt1" event="0xb"/>
-  <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="ARM_Cortex-A53_ccnt" event="0x11"/>
-  <configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/>
-  <configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/>
-  <configuration counter="ARM_Cortex-A53_cnt2" event="0x10"/>
-  <configuration counter="ARM_Cortex-A53_cnt3" event="0x19"/>
-  <configuration counter="ARM_Cortex-A57_ccnt" event="0x11"/>
-  <configuration counter="ARM_Cortex-A57_cnt0" event="0x8"/>
-  <configuration counter="ARM_Cortex-A57_cnt1" event="0x16"/>
-  <configuration counter="ARM_Cortex-A57_cnt2" event="0x10"/>
-  <configuration counter="ARM_Cortex-A57_cnt3" event="0x19"/>
-  <configuration counter="Scorpion_ccnt" event="0xff"/>
-  <configuration counter="Scorpion_cnt0" event="0x08"/>
-  <configuration counter="Scorpion_cnt1" event="0x10"/>
-  <configuration counter="ScorpionMP_ccnt" event="0xff"/>
-  <configuration counter="ScorpionMP_cnt0" event="0x08"/>
-  <configuration counter="ScorpionMP_cnt1" event="0x10"/>
-  <configuration counter="Krait_ccnt" event="0xff"/>
-  <configuration counter="Krait_cnt0" event="0x08"/>
-  <configuration counter="Krait_cnt1" event="0x10"/>
-  <configuration counter="Linux_block_rq_wr"/>
-  <configuration counter="Linux_block_rq_rd"/>
-  <configuration counter="Linux_meminfo_memused"/>
-  <configuration counter="Linux_meminfo_memfree"/>
-  <configuration counter="Linux_power_cpu_freq"/>
-  <configuration counter="L2C-310_cnt0" event="0x1"/>
-</configurations>
diff --git a/tools/gator/daemon/defaults.xml b/tools/gator/daemon/defaults.xml
new file mode 100644
index 0000000..5bf096c
--- /dev/null
+++ b/tools/gator/daemon/defaults.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configurations revision="3">
+  <configuration counter="ARM_ARM11_ccnt" event="0xff"/>
+  <configuration counter="ARM_ARM11_cnt0" event="0x7"/>
+  <configuration counter="ARM_ARM11_cnt1" event="0xb"/>
+  <configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/>
+  <configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/>
+  <configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/>
+  <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"/>
+  <configuration counter="ARM_Cortex-A53_cnt2" event="0x10"/>
+  <configuration counter="ARM_Cortex-A53_cnt3" event="0x19"/>
+  <configuration counter="ARM_Cortex-A57_ccnt" event="0x11"/>
+  <configuration counter="ARM_Cortex-A57_cnt0" event="0x8"/>
+  <configuration counter="ARM_Cortex-A57_cnt1" event="0x16"/>
+  <configuration counter="ARM_Cortex-A57_cnt2" event="0x10"/>
+  <configuration counter="ARM_Cortex-A57_cnt3" event="0x19"/>
+  <configuration counter="Scorpion_ccnt" event="0xff"/>
+  <configuration counter="Scorpion_cnt0" event="0x08"/>
+  <configuration counter="Scorpion_cnt1" event="0x10"/>
+  <configuration counter="ScorpionMP_ccnt" event="0xff"/>
+  <configuration counter="ScorpionMP_cnt0" event="0x08"/>
+  <configuration counter="ScorpionMP_cnt1" event="0x10"/>
+  <configuration counter="Krait_ccnt" event="0xff"/>
+  <configuration counter="Krait_cnt0" event="0x08"/>
+  <configuration counter="Krait_cnt1" event="0x10"/>
+  <configuration counter="Linux_block_rq_wr"/>
+  <configuration counter="Linux_block_rq_rd"/>
+  <configuration counter="Linux_meminfo_memused"/>
+  <configuration counter="Linux_meminfo_memfree"/>
+  <configuration counter="Linux_power_cpu_freq"/>
+  <configuration counter="L2C-310_cnt0" event="0x1"/>
+</configurations>
diff --git a/tools/gator/daemon/escape.c b/tools/gator/daemon/escape.c
index 3eec1f8..c54aa1c 100644
--- a/tools/gator/daemon/escape.c
+++ b/tools/gator/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/tools/gator/daemon/events-Cortex-A12.xml b/tools/gator/daemon/events-Cortex-A12.xml
index 20a4772..9c04354 100644
--- a/tools/gator/daemon/events-Cortex-A12.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Cortex-A15.xml b/tools/gator/daemon/events-Cortex-A15.xml
index faa8b1c..f50e55d 100644
--- a/tools/gator/daemon/events-Cortex-A15.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Cortex-A5.xml b/tools/gator/daemon/events-Cortex-A5.xml
index a5b1546..d67581d 100644
--- a/tools/gator/daemon/events-Cortex-A5.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Cortex-A7.xml b/tools/gator/daemon/events-Cortex-A7.xml
index 54d7264..6e078b3 100644
--- a/tools/gator/daemon/events-Cortex-A7.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Cortex-A8.xml b/tools/gator/daemon/events-Cortex-A8.xml
index f251823..a69e25a 100644
--- a/tools/gator/daemon/events-Cortex-A8.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Cortex-A9.xml b/tools/gator/daemon/events-Cortex-A9.xml
index 75f09c8..3e7f828 100644
--- a/tools/gator/daemon/events-Cortex-A9.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Linux.xml b/tools/gator/daemon/events-Linux.xml
index 31a90a1..4d677e1 100644
--- a/tools/gator/daemon/events-Linux.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Mali-4xx.xml b/tools/gator/daemon/events-Mali-4xx.xml
index 8772ce4..5a71386 100644
--- a/tools/gator/daemon/events-Mali-4xx.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Mali-T6xx.xml b/tools/gator/daemon/events-Mali-T6xx.xml
index 2465238..ec9ca00 100644
--- a/tools/gator/daemon/events-Mali-T6xx.xml
+++ b/tools/gator/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/tools/gator/daemon/events-Perf-Hardware.xml b/tools/gator/daemon/events-Perf-Hardware.xml
new file mode 100644
index 0000000..423696f
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/k/perf_event.3.12.h b/tools/gator/daemon/k/perf_event.3.12.h
new file mode 100644
index 0000000..e886c48
--- /dev/null
+++ b/tools/gator/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/tools/gator/daemon/k/perf_event.h b/tools/gator/daemon/k/perf_event.h
new file mode 120000
index 0000000..e5dff8c
--- /dev/null
+++ b/tools/gator/daemon/k/perf_event.h
@@ -0,0 +1 @@
+perf_event.3.12.h
\ No newline at end of file
diff --git a/tools/gator/daemon/main.cpp b/tools/gator/daemon/main.cpp
index bfd36b9..1275aef 100644
--- a/tools/gator/daemon/main.cpp
+++ b/tools/gator/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 <arpa/inet.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.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 <fcntl.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
+
 #include "Child.h"
-#include "SessionData.h"
-#include "OlySocket.h"
-#include "Logging.h"
-#include "OlyUtility.h"
 #include "KMod.h"
+#include "Logging.h"
+#include "OlySocket.h"
+#include "OlyUtility.h"
+#include "SessionData.h"
 
 #define DEBUG false
 
@@ -34,7 +32,7 @@
 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 @@
 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 @@
 	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 @@
 		}
 	}
 
-	retval = mountGatorFS();
+	const int retval = mountGatorFS();
 	if (retval == 1) {
 		logg->logMessage("Driver already running at startup");
 		driverRunningAtStart = true;
@@ -274,8 +264,8 @@
 		}
 
 		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 @@
 		}
 	}
 
-	return 0;
+	return true;
 }
 
 static int shutdownFilesystem() {
@@ -418,8 +408,28 @@
 	// 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 @@
 			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 @@
 			} 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++;