aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDrew Richardson <drew.richardson@arm.com>2014-04-07 12:00:00 -0700
committerDrew Richardson <drew.richardson@arm.com>2014-12-19 15:54:15 -0800
commit020278c1a682ea75ddb63fe4e0d373116935199f (patch)
tree697ac3d6fc26467eca30e00d0e6bed1d6f6f1c5b
parentf1f81bcde6ec1647ad617d68b4626951e3c1d019 (diff)
downloadgator-020278c1a682ea75ddb63fe4e0d373116935199f.tar.gz
gator: Version 5.185.18
Signed-off-by: Drew Richardson <drew.richardson@arm.com>
-rw-r--r--README_Streamline.txt35
-rw-r--r--daemon/Android.mk15
-rw-r--r--daemon/Buffer.cpp259
-rw-r--r--daemon/Buffer.h99
-rw-r--r--daemon/CapturedXML.cpp11
-rw-r--r--daemon/CapturedXML.h2
-rw-r--r--daemon/Child.cpp174
-rw-r--r--daemon/Child.h4
-rw-r--r--daemon/Collector.h38
-rw-r--r--daemon/Config.h17
-rw-r--r--daemon/ConfigurationXML.cpp16
-rw-r--r--daemon/ConfigurationXML.h2
-rw-r--r--daemon/Counter.h4
-rw-r--r--daemon/Driver.cpp2
-rw-r--r--daemon/Driver.h4
-rw-r--r--daemon/DriverSource.cpp (renamed from daemon/Collector.cpp)225
-rw-r--r--daemon/DriverSource.h52
-rw-r--r--daemon/DynBuf.cpp139
-rw-r--r--daemon/DynBuf.h52
-rw-r--r--daemon/EventsXML.cpp4
-rw-r--r--daemon/EventsXML.h2
-rw-r--r--daemon/ExternalSource.cpp56
-rw-r--r--daemon/ExternalSource.h40
-rw-r--r--daemon/Fifo.cpp2
-rw-r--r--daemon/Fifo.h4
-rw-r--r--daemon/Hwmon.cpp40
-rw-r--r--daemon/Hwmon.h6
-rw-r--r--daemon/KMod.cpp25
-rw-r--r--daemon/KMod.h4
-rw-r--r--daemon/LocalCapture.cpp6
-rw-r--r--daemon/LocalCapture.h2
-rw-r--r--daemon/Logging.cpp6
-rw-r--r--daemon/Logging.h13
-rw-r--r--daemon/Monitor.cpp61
-rw-r--r--daemon/Monitor.h32
-rw-r--r--daemon/OlySocket.cpp101
-rw-r--r--daemon/OlySocket.h43
-rw-r--r--daemon/OlyUtility.cpp2
-rw-r--r--daemon/OlyUtility.h2
-rw-r--r--daemon/PerfBuffer.cpp139
-rw-r--r--daemon/PerfBuffer.h39
-rw-r--r--daemon/PerfDriver.cpp355
-rw-r--r--daemon/PerfDriver.h56
-rw-r--r--daemon/PerfGroup.cpp206
-rw-r--r--daemon/PerfGroup.h55
-rw-r--r--daemon/PerfSource.cpp271
-rw-r--r--daemon/PerfSource.h54
-rw-r--r--daemon/Proc.cpp179
-rw-r--r--daemon/Proc.h17
-rw-r--r--daemon/Sender.cpp29
-rw-r--r--daemon/Sender.h2
-rw-r--r--daemon/SessionData.cpp29
-rw-r--r--daemon/SessionData.h14
-rw-r--r--daemon/SessionXML.cpp12
-rw-r--r--daemon/SessionXML.h8
-rw-r--r--daemon/Source.cpp33
-rw-r--r--daemon/Source.h40
-rw-r--r--daemon/StreamlineSetup.cpp36
-rw-r--r--daemon/StreamlineSetup.h7
-rw-r--r--daemon/UEvent.cpp75
-rw-r--r--daemon/UEvent.h36
-rw-r--r--daemon/UserSpaceSource.cpp97
-rw-r--r--daemon/UserSpaceSource.h38
-rw-r--r--daemon/common.mk4
-rw-r--r--daemon/defaults.xml (renamed from daemon/configuration.xml)51
-rw-r--r--daemon/escape.c2
-rw-r--r--daemon/events-Cortex-A12.xml6
-rw-r--r--daemon/events-Cortex-A15.xml6
-rw-r--r--daemon/events-Cortex-A5.xml6
-rw-r--r--daemon/events-Cortex-A7.xml6
-rw-r--r--daemon/events-Cortex-A8.xml6
-rw-r--r--daemon/events-Cortex-A9.xml6
-rw-r--r--daemon/events-Linux.xml14
-rw-r--r--daemon/events-Mali-4xx.xml2
-rw-r--r--daemon/events-Mali-T6xx.xml16
-rw-r--r--daemon/events-Perf-Hardware.xml12
-rw-r--r--daemon/k/perf_event.3.12.h792
l---------daemon/k/perf_event.h1
-rw-r--r--daemon/main.cpp80
-rw-r--r--driver/gator.h13
-rw-r--r--driver/gator_annotate.c2
-rw-r--r--driver/gator_annotate_kernel.c8
-rw-r--r--driver/gator_backtrace.c48
-rw-r--r--driver/gator_buffer.c168
-rw-r--r--driver/gator_buffer_write.c (renamed from driver/gator_pack.c)24
-rw-r--r--driver/gator_cookies.c82
-rw-r--r--driver/gator_events_armv6.c2
-rw-r--r--driver/gator_events_armv7.c18
-rw-r--r--driver/gator_events_block.c2
-rw-r--r--driver/gator_events_ccn-504.c2
-rw-r--r--driver/gator_events_irq.c2
-rw-r--r--driver/gator_events_l2c-310.c2
-rw-r--r--driver/gator_events_mali_4xx.c2
-rw-r--r--driver/gator_events_mali_4xx.h2
-rw-r--r--driver/gator_events_mali_common.c2
-rw-r--r--driver/gator_events_mali_common.h2
-rw-r--r--driver/gator_events_mali_t6xx.c8
-rw-r--r--driver/gator_events_mali_t6xx_hw.c10
-rw-r--r--driver/gator_events_mali_t6xx_hw_test.c2
-rw-r--r--driver/gator_events_meminfo.c28
-rw-r--r--driver/gator_events_mmapped.c2
-rw-r--r--driver/gator_events_net.c2
-rw-r--r--driver/gator_events_perf_pmu.c2
-rw-r--r--driver/gator_events_sched.c2
-rw-r--r--driver/gator_events_scorpion.c2
-rw-r--r--driver/gator_fs.c47
-rw-r--r--driver/gator_hrtimer_gator.c8
-rw-r--r--driver/gator_hrtimer_perf.c113
-rw-r--r--driver/gator_iks.c4
-rw-r--r--driver/gator_main.c286
-rw-r--r--driver/gator_marshaling.c97
-rw-r--r--driver/gator_trace_gpu.c13
-rw-r--r--driver/gator_trace_gpu.h2
-rw-r--r--driver/gator_trace_power.c6
-rw-r--r--driver/gator_trace_sched.c20
-rw-r--r--driver/mali/mali_mjollnir_profiling_gator_api.h2
-rw-r--r--driver/mali/mali_utgard_profiling_gator_api.h2
-rw-r--r--driver/mali_t6xx.mk9
118 files changed, 4342 insertions, 1144 deletions
diff --git a/README_Streamline.txt b/README_Streamline.txt
index 99b70bd..df3f923 100644
--- a/README_Streamline.txt
+++ b/README_Streamline.txt
@@ -2,7 +2,7 @@
*** Purpose ***
Instructions on setting up ARM Streamline on the target.
-The gator driver and gator daemon are required to run on the ARM Linux target in order for ARM Streamline to operate.
+The gator driver and gator daemon are required to run on the ARM Linux target in order for ARM Streamline to operate. A new early access feature allows the gator daemon can run without the gator driver by using userspace APIs with reduced functionality when using Linux 3.12 or later.
The driver should be built as a module and the daemon must run with root permissions on the target.
*** Introduction ***
@@ -14,6 +14,7 @@ A Linux development environment with cross compiling tools is most likely requir
-First, check if the kernel has the proper configuration options (see below). Profiling cannot occur using a kernel that is not configured properly, a new kernel must be created. See if /proc/config.gz exists on the target.
-Second, given a properly configured kernel, check if the filesystem contains the kernel source/headers, which can be used to re-create the gator driver. These files may be located in different areas, but common locations are /lib/modules/ and /usr/src.
-If the kernel is not properly configured or sources/headers are not available, the developer is on their own and kernel creation is beyond the scope of this document. Note: It is possible for a module to work when compiled against a similar kernel source code, though this is not guaranteed to work due to differences in kernel structures, exported symbols and incompatible configuration parameters.
+ -If the target is running Linux 3.12 or later the kernel driver is not required and userspace APIs will be used instead.
*** Kernel configuration ***
@@ -24,7 +25,7 @@ menuconfig options (depending on the kernel version, the location of these confi
- [*] Profiling Support (enables CONFIG_PROFILING)
- Kernel Features
- [*] High Resolution Timer Support (enables CONFIG_HIGH_RES_TIMERS)
- - [*] Use local timer interrupts (only required for SMP, enables CONFIG_LOCAL_TIMERS)
+ - [*] Use local timer interrupts (only required for SMP and for version before Linux 3.12, enables CONFIG_LOCAL_TIMERS)
- [*] Enable hardware performance counter support for perf events (enables CONFIG_HW_PERF_EVENTS)
- CPU Power Management
- CPU Frequency scaling
@@ -46,8 +47,8 @@ CONFIG_DEBUG_INFO (optional, used for analyzing the kernel)
CONFIG_CPU_FREQ (optional, provides frequency setting of the CPU)
These may be verified on a running system using /proc/config.gz (if this file exists) by running 'zcat /proc/config.gz | grep <option>'. For example, confirming that CONFIG_PROFILING is enabled
- > zcat /proc/config.gz | grep CONFIG_PROFILING
- CONFIG_PROFILING=y
+ > zcat /proc/config.gz | grep CONFIG_PROFILING
+ CONFIG_PROFILING=y
If a device tree is used it must include the pmu bindings, see Documentation/devicetree/bindings/arm/pmu.txt for details.
@@ -91,6 +92,9 @@ For Android targets (install the android ndk, see developer.android.com)
ndk-build
or execute /path/to/ndk/ndk-build if the ndk is not on your path
gatord should now be created and located in libs/armeabi
+ If you get an error like the following, upgrade to a more recent version of the android ndk
+ jni/PerfGroup.cpp: In function 'int sys_perf_event_open(perf_event_attr*, pid_t, int, int, long unsigned int)':
+ jni/PerfGroup.cpp:36:17: error: '__NR_perf_event_open' was not declared in this scope
*** Running gator ***
@@ -101,6 +105,7 @@ gator.ko must be located in the same directory as gatord on the target or the lo
With root privileges, run the daemon
sudo ./gatord &
Note: gatord requires libstdc++.so.6 which is usually supplied by the Linux distribution on the target. A copy of libstdc++.so.6 is available in the DS-5 Linux example distribution.
+If gator.ko is not loaded and is not in the same directory as gatord when using Linux 3.12 or later, gatord can run without gator.ko by using userspace APIs. Not all features are supported by userspace gator. If /dev/gator/version does not exist after starting gatord it is running userspace gator.
*** Customizing the l2c-310 Counter ***
@@ -130,12 +135,25 @@ Attempting to run an incompatible binary often results in the confusing error me
*** Bugs ***
There is a bug in some Linux kernels where perf misidentifies the CPU type. To see if you are affected by this, run ls /sys/bus/event_source/devices/ and verify the listed processor type matches what is expected. For example, an A9 should show the following.
-
-# ls /sys/bus/event_source/devices/
-ARMv7_Cortex_A9 breakpoint software tracepoint
-
+ # ls /sys/bus/event_source/devices/
+ ARMv7_Cortex_A9 breakpoint software tracepoint
To work around the issue try upgrading to a later kernel or comment out the gator_events_perf_pmu_cpu_init(gator_cpu, type); call in gator_events_perf_pmu.c
+There is a bug in some Linux kernels where an Oops may occurs when using userspace gator and a core is offlined. The fix was merged into mainline in 3.14-rc5, see http://git.kernel.org/tip/e3703f8cdfcf39c25c4338c3ad8e68891cca3731, and as been backported to older kernels.
+
+If you see this error when using SELinux, ex: Android 4.4 or later
+ # ./gatord
+ Unable to load (insmod) gator.ko driver:
+ >>> gator.ko must be built against the current kernel version & configuration
+ >>> See dmesg for more details
+ # dmesg
+ ...
+ <7>[ 6745.475110] SELinux: initialized (dev gatorfs, type gatorfs), not configured for labeling
+ <5>[ 6745.477434] type=1400 audit(1393005053.336:10): avc: denied { mount } for pid=1996 comm="gatord-main" name="/" dev="gatorfs" ino=8733 scontext=u:r:shell:s0 tcontext=u:object_r:unlabeled:s0 tclass=filesystem
+disable SELinux so that gatorfs can be mounted by running
+ # setenforce 0
+Once gator is started, SELinux can be reenabled
+
*** Profiling the kernel (optional) ***
CONFIG_DEBUG_INFO must be enabled, see "Kernel configuration" section above.
@@ -154,4 +172,3 @@ update-rc.d rungator.sh defaults
*** GPL License ***
For license information, please see the file LICENSE after unzipping driver-src/gator-driver.tar.gz.
-
diff --git a/daemon/Android.mk b/daemon/Android.mk
index a042971..045d028 100644
--- a/daemon/Android.mk
+++ b/daemon/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h configuration_xml.h)
+XML_H := $(shell cd $(LOCAL_PATH) && make events_xml.h defaults_xml.h)
LOCAL_CFLAGS += -Wall -O3 -mthumb-interwork -fno-exceptions -DETCDIR=\"/etc\" -Ilibsensors
@@ -9,22 +9,33 @@ LOCAL_SRC_FILES := \
Buffer.cpp \
CapturedXML.cpp \
Child.cpp \
- Collector.cpp \
ConfigurationXML.cpp \
Driver.cpp \
+ DriverSource.cpp \
+ DynBuf.cpp \
EventsXML.cpp \
+ ExternalSource.cpp \
Fifo.cpp \
Hwmon.cpp \
KMod.cpp \
LocalCapture.cpp \
Logging.cpp \
main.cpp \
+ Monitor.cpp \
OlySocket.cpp \
OlyUtility.cpp \
+ PerfBuffer.cpp \
+ PerfDriver.cpp \
+ PerfGroup.cpp \
+ PerfSource.cpp \
+ Proc.cpp \
Sender.cpp \
SessionData.cpp \
SessionXML.cpp \
+ Source.cpp \
StreamlineSetup.cpp \
+ UEvent.cpp \
+ UserSpaceSource.cpp \
libsensors/access.c \
libsensors/conf-lex.c \
libsensors/conf-parse.c \
diff --git a/daemon/Buffer.cpp b/daemon/Buffer.cpp
index 090a715..93557da 100644
--- a/daemon/Buffer.cpp
+++ b/daemon/Buffer.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -12,33 +12,60 @@
#include "Sender.h"
#include "SessionData.h"
-#define mask (size - 1)
-
-Buffer::Buffer (const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : core(core), buftype(buftype), size(size), readPos(0), writePos(0), commitPos(0), available(true), done(false), buf(new char[size]), commitTime(gSessionData->mLiveRate), readerSem(readerSem) {
- if ((size & mask) != 0) {
+#define mask (mSize - 1)
+
+enum {
+ CODE_PEA = 1,
+ CODE_KEYS = 2,
+ CODE_FORMAT = 3,
+ CODE_MAPS = 4,
+ CODE_COMM = 5,
+};
+
+// Summary Frame Messages
+enum {
+ MESSAGE_SUMMARY = 1,
+ MESSAGE_CORE_NAME = 3,
+};
+
+// From gator_marshaling.c
+#define NEWLINE_CANARY \
+ /* Unix */ \
+ "1\n" \
+ /* Windows */ \
+ "2\r\n" \
+ /* Mac OS */ \
+ "3\r" \
+ /* RISC OS */ \
+ "4\n\r" \
+ /* Add another character so the length isn't 0x0a bytes */ \
+ "5"
+
+Buffer::Buffer(const int32_t core, const int32_t buftype, const int size, sem_t *const readerSem) : mCore(core), mBufType(buftype), mSize(size), mReadPos(0), mWritePos(0), mCommitPos(0), mAvailable(true), mIsDone(false), mBuf(new char[mSize]), mCommitTime(gSessionData->mLiveRate), mReaderSem(readerSem) {
+ if ((mSize & mask) != 0) {
logg->logError(__FILE__, __LINE__, "Buffer size is not a power of 2");
handleException();
}
frame();
}
-Buffer::~Buffer () {
- delete [] buf;
+Buffer::~Buffer() {
+ delete [] mBuf;
}
-void Buffer::write (Sender * const sender) {
+void Buffer::write(Sender *const sender) {
if (!commitReady()) {
return;
}
// determine the size of two halves
- int length1 = commitPos - readPos;
- char * buffer1 = buf + readPos;
+ int length1 = mCommitPos - mReadPos;
+ char *buffer1 = mBuf + mReadPos;
int length2 = 0;
- char * buffer2 = buf;
+ char *buffer2 = mBuf;
if (length1 < 0) {
- length1 = size - readPos;
- length2 = commitPos;
+ length1 = mSize - mReadPos;
+ length2 = mCommitPos;
}
logg->logMessage("Sending data length1: %i length2: %i", length1, length2);
@@ -53,22 +80,22 @@ void Buffer::write (Sender * const sender) {
sender->writeData(buffer2, length2, RESPONSE_APC_DATA);
}
- readPos = commitPos;
+ mReadPos = mCommitPos;
}
-bool Buffer::commitReady () const {
- return commitPos != readPos;
+bool Buffer::commitReady() const {
+ return mCommitPos != mReadPos;
}
-int Buffer::bytesAvailable () const {
- int filled = writePos - readPos;
+int Buffer::bytesAvailable() const {
+ int filled = mWritePos - mReadPos;
if (filled < 0) {
- filled += size;
+ filled += mSize;
}
- int remaining = size - filled;
+ int remaining = mSize - filled;
- if (available) {
+ if (mAvailable) {
// Give some extra room; also allows space to insert the overflow error packet
remaining -= 200;
} else {
@@ -79,58 +106,68 @@ int Buffer::bytesAvailable () const {
return remaining;
}
-bool Buffer::checkSpace (const int bytes) {
+bool Buffer::checkSpace(const int bytes) {
const int remaining = bytesAvailable();
if (remaining < bytes) {
- available = false;
+ mAvailable = false;
} else {
- available = true;
+ mAvailable = true;
}
- return available;
+ return mAvailable;
+}
+
+int Buffer::contiguousSpaceAvailable() const {
+ int remaining = bytesAvailable();
+ int contiguous = mSize - mWritePos;
+ if (remaining < contiguous) {
+ return remaining;
+ } else {
+ return contiguous;
+ }
}
-void Buffer::commit (const uint64_t time) {
+void Buffer::commit(const uint64_t time) {
// post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
- int length = writePos - commitPos;
+ int length = mWritePos - mCommitPos;
if (length < 0) {
- length += size;
+ length += mSize;
}
length = length - typeLength - sizeof(int32_t);
for (size_t byte = 0; byte < sizeof(int32_t); byte++) {
- buf[(commitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
+ mBuf[(mCommitPos + typeLength + byte) & mask] = (length >> byte * 8) & 0xFF;
}
- logg->logMessage("Committing data readPos: %i writePos: %i commitPos: %i", readPos, writePos, commitPos);
- commitPos = writePos;
+ logg->logMessage("Committing data mReadPos: %i mWritePos: %i mCommitPos: %i", mReadPos, mWritePos, mCommitPos);
+ mCommitPos = mWritePos;
if (gSessionData->mLiveRate > 0) {
- while (time > commitTime) {
- commitTime += gSessionData->mLiveRate;
+ while (time > mCommitTime) {
+ mCommitTime += gSessionData->mLiveRate;
}
}
- if (!done) {
+ if (!mIsDone) {
frame();
}
// send a notification that data is ready
- sem_post(readerSem);
+ sem_post(mReaderSem);
}
-void Buffer::check (const uint64_t time) {
- int filled = writePos - commitPos;
+void Buffer::check(const uint64_t time) {
+ int filled = mWritePos - mCommitPos;
if (filled < 0) {
- filled += size;
+ filled += mSize;
}
- if (filled >= ((size * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= commitTime)) {
+ if (filled >= ((mSize * 3) / 4) || (gSessionData->mLiveRate > 0 && time >= mCommitTime)) {
commit(time);
}
}
-void Buffer::packInt (int32_t x) {
+void Buffer::packInt(int32_t x) {
int packedBytes = 0;
int more = true;
while (more) {
@@ -144,14 +181,14 @@ void Buffer::packInt (int32_t x) {
b |= 0x80;
}
- buf[(writePos + packedBytes) & mask] = b;
+ mBuf[(mWritePos + packedBytes) & mask] = b;
packedBytes++;
}
- writePos = (writePos + packedBytes) & mask;
+ mWritePos = (mWritePos + packedBytes) & mask;
}
-void Buffer::packInt64 (int64_t x) {
+void Buffer::packInt64(int64_t x) {
int packedBytes = 0;
int more = true;
while (more) {
@@ -165,24 +202,61 @@ void Buffer::packInt64 (int64_t x) {
b |= 0x80;
}
- buf[(writePos + packedBytes) & mask] = b;
+ mBuf[(mWritePos + packedBytes) & mask] = b;
packedBytes++;
}
- writePos = (writePos + packedBytes) & mask;
+ mWritePos = (mWritePos + packedBytes) & mask;
+}
+
+void Buffer::writeBytes(const void *const data, size_t count) {
+ size_t i;
+ for (i = 0; i < count; ++i) {
+ mBuf[(mWritePos + i) & mask] = static_cast<const char *>(data)[i];
+ }
+
+ mWritePos = (mWritePos + i) & mask;
}
-void Buffer::frame () {
+void Buffer::writeString(const char *const str) {
+ const int len = strlen(str);
+ packInt(len);
+ writeBytes(str, len);
+}
+
+void Buffer::frame() {
if (!gSessionData->mLocalCapture) {
packInt(RESPONSE_APC_DATA);
}
// Reserve space for the length
- writePos += sizeof(int32_t);
- packInt(buftype);
- packInt(core);
+ mWritePos += sizeof(int32_t);
+ packInt(mBufType);
+ packInt(mCore);
}
-bool Buffer::eventHeader (const uint64_t curr_time) {
+void Buffer::summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname) {
+ packInt(MESSAGE_SUMMARY);
+ writeString(NEWLINE_CANARY);
+ packInt64(timestamp);
+ packInt64(uptime);
+ packInt64(monotonicDelta);
+ writeString("uname");
+ writeString(uname);
+ writeString("");
+ check(1);
+}
+
+void Buffer::coreName(const int core, const int cpuid, const char *const name) {
+ if (checkSpace(3 * MAXSIZE_PACK32 + 0x100)) {
+ packInt(MESSAGE_CORE_NAME);
+ packInt(core);
+ packInt(cpuid);
+ writeString(name);
+ }
+ check(1);
+}
+
+bool Buffer::eventHeader(const uint64_t curr_time) {
bool retval = false;
if (checkSpace(MAXSIZE_PACK32 + MAXSIZE_PACK64)) {
packInt(0); // key of zero indicates a timestamp
@@ -193,9 +267,9 @@ bool Buffer::eventHeader (const uint64_t curr_time) {
return retval;
}
-bool Buffer::eventTid (const int tid) {
+bool Buffer::eventTid(const int tid) {
bool retval = false;
- if (checkSpace(2*MAXSIZE_PACK32)) {
+ if (checkSpace(2 * MAXSIZE_PACK32)) {
packInt(1); // key of 1 indicates a tid
packInt(tid);
retval = true;
@@ -204,25 +278,94 @@ bool Buffer::eventTid (const int tid) {
return retval;
}
-void Buffer::event (const int32_t key, const int32_t value) {
+void Buffer::event(const int32_t key, const int32_t value) {
if (checkSpace(2 * MAXSIZE_PACK32)) {
packInt(key);
packInt(value);
}
}
-void Buffer::event64 (const int64_t key, const int64_t value) {
+void Buffer::event64(const int64_t key, const int64_t value) {
if (checkSpace(2 * MAXSIZE_PACK64)) {
packInt64(key);
packInt64(value);
}
}
-void Buffer::setDone () {
- done = true;
+void Buffer::pea(const struct perf_event_attr *const pea, int key) {
+ if (checkSpace(2 * MAXSIZE_PACK32 + pea->size)) {
+ packInt(CODE_PEA);
+ writeBytes(pea, pea->size);
+ packInt(key);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ // Don't know the real perf time so use 1 as it will work for now
+ check(1);
+}
+
+void Buffer::keys(const int count, const __u64 *const ids, const int *const keys) {
+ if (checkSpace(2 * MAXSIZE_PACK32 + count * (MAXSIZE_PACK32 + MAXSIZE_PACK64))) {
+ packInt(CODE_KEYS);
+ packInt(count);
+ for (int i = 0; i < count; ++i) {
+ packInt64(ids[i]);
+ packInt(keys[i]);
+ }
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::format(const int length, const char *const format) {
+ if (checkSpace(MAXSIZE_PACK32 + length + 1)) {
+ packInt(CODE_FORMAT);
+ writeBytes(format, length + 1);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::maps(const int pid, const int tid, const char *const maps) {
+ const int mapsLen = strlen(maps) + 1;
+ if (checkSpace(3 * MAXSIZE_PACK32 + mapsLen)) {
+ packInt(CODE_MAPS);
+ packInt(pid);
+ packInt(tid);
+ writeBytes(maps, mapsLen);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::comm(const int pid, const int tid, const char *const image, const char *const comm) {
+ const int imageLen = strlen(image) + 1;
+ const int commLen = strlen(comm) + 1;
+ if (checkSpace(3 * MAXSIZE_PACK32 + imageLen + commLen)) {
+ packInt(CODE_COMM);
+ packInt(pid);
+ packInt(tid);
+ writeBytes(image, imageLen);
+ writeBytes(comm, commLen);
+ } else {
+ logg->logError(__FILE__, __LINE__, "Ran out of buffer space for perf attrs");
+ handleException();
+ }
+ check(1);
+}
+
+void Buffer::setDone() {
+ mIsDone = true;
commit(0);
}
-bool Buffer::isDone () const {
- return done && readPos == commitPos && commitPos == writePos;
+bool Buffer::isDone() const {
+ return mIsDone && mReadPos == mCommitPos && mCommitPos == mWritePos;
}
diff --git a/daemon/Buffer.h b/daemon/Buffer.h
index b3c8d78..5023777 100644
--- a/daemon/Buffer.h
+++ b/daemon/Buffer.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -9,54 +9,89 @@
#ifndef BUFFER_H
#define BUFFER_H
-#include <stddef.h>
#include <stdint.h>
#include <semaphore.h>
+#include "k/perf_event.h"
+
class Sender;
+enum {
+ FRAME_SUMMARY = 1,
+ FRAME_BLOCK_COUNTER = 5,
+ FRAME_EXTERNAL = 10,
+ FRAME_PERF_ATTRS = 11,
+ FRAME_PERF = 12,
+};
+
class Buffer {
public:
static const size_t MAXSIZE_PACK32 = 5;
static const size_t MAXSIZE_PACK64 = 10;
- Buffer (int32_t core, int32_t buftype, const int size, sem_t *const readerSem);
- ~Buffer ();
+ Buffer(int32_t core, int32_t buftype, const int size, sem_t *const readerSem);
+ ~Buffer();
+
+ void write(Sender *sender);
+
+ int bytesAvailable() const;
+ int contiguousSpaceAvailable() const;
+ void commit(const uint64_t time);
+ void check(const uint64_t time);
- void write (Sender * sender);
+ void frame();
- int bytesAvailable () const;
- void commit (const uint64_t time);
- void check (const uint64_t time);
+ // Summary messages
+ void summary(const int64_t timestamp, const int64_t uptime, const int64_t monotonicDelta, const char *const uname);
+ void coreName(const int core, const int cpuid, const char *const name);
- void frame ();
+ // Block Counter messages
+ bool eventHeader(uint64_t curr_time);
+ bool eventTid(int tid);
+ void event(int32_t key, int32_t value);
+ void event64(int64_t key, int64_t value);
- bool eventHeader (uint64_t curr_time);
- bool eventTid (int tid);
- void event (int32_t key, int32_t value);
- void event64 (int64_t key, int64_t value);
+ // Perf Attrs messages
+ void pea(const struct perf_event_attr *const pea, int key);
+ void keys(const int count, const __u64 *const ids, const int *const keys);
+ void format(const int length, const char *const format);
+ void maps(const int pid, const int tid, const char *const maps);
+ void comm(const int pid, const int tid, const char *const image, const char *const comm);
- void setDone ();
- bool isDone () const;
+ void setDone();
+ bool isDone() const;
+
+ // Prefer a new member to using these functions if possible
+ char *getWritePos() { return mBuf + mWritePos; }
+ void advanceWrite(int bytes) { mWritePos = (mWritePos + bytes) & /*mask*/(mSize - 1); }
+
+ static void writeLEInt(unsigned char *buf, int v) {
+ buf[0] = (v >> 0) & 0xFF;
+ buf[1] = (v >> 8) & 0xFF;
+ buf[2] = (v >> 16) & 0xFF;
+ buf[3] = (v >> 24) & 0xFF;
+ }
private:
- bool commitReady () const;
- bool checkSpace (int bytes);
-
- void packInt (int32_t x);
- void packInt64 (int64_t x);
-
- const int32_t core;
- const int32_t buftype;
- const int size;
- int readPos;
- int writePos;
- int commitPos;
- bool available;
- bool done;
- char *const buf;
- uint64_t commitTime;
- sem_t *const readerSem;
+ bool commitReady() const;
+ bool checkSpace(int bytes);
+
+ void packInt(int32_t x);
+ void packInt64(int64_t x);
+ void writeBytes(const void *const data, size_t count);
+ void writeString(const char *const str);
+
+ const int32_t mCore;
+ const int32_t mBufType;
+ const int mSize;
+ int mReadPos;
+ int mWritePos;
+ int mCommitPos;
+ bool mAvailable;
+ bool mIsDone;
+ char *const mBuf;
+ uint64_t mCommitTime;
+ sem_t *const mReaderSem;
// Intentionally unimplemented
Buffer(const Buffer &);
diff --git a/daemon/CapturedXML.cpp b/daemon/CapturedXML.cpp
index 30c4c44..cf79b72 100644
--- a/daemon/CapturedXML.cpp
+++ b/daemon/CapturedXML.cpp
@@ -1,16 +1,18 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include "CapturedXML.h"
+
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
+
#include "SessionData.h"
-#include "CapturedXML.h"
#include "Logging.h"
#include "OlyUtility.h"
@@ -30,6 +32,9 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) {
captured = mxmlNewElement(xml, "captured");
mxmlElementSetAttr(captured, "version", "1");
+ if (gSessionData->perf.isSetup()) {
+ mxmlElementSetAttr(captured, "type", "Perf");
+ }
mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION);
if (includeTime) { // Send the following only after the capture is complete
if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010)
@@ -41,7 +46,7 @@ mxml_node_t* CapturedXML::getTree(bool includeTime) {
mxmlElementSetAttr(target, "name", gSessionData->mCoreName);
mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate);
mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores);
- mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mCpuId);
+ mxmlElementSetAttrf(target, "cpuid", "0x%x", gSessionData->mMaxCpuId);
if (!gSessionData->mOneShot && (gSessionData->mSampleRate > 0)) {
mxmlElementSetAttr(target, "supports_live", "yes");
diff --git a/daemon/CapturedXML.h b/daemon/CapturedXML.h
index b0482f5..efc1e52 100644
--- a/daemon/CapturedXML.h
+++ b/daemon/CapturedXML.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/Child.cpp b/daemon/Child.cpp
index 9ee2ef8..ca33561 100644
--- a/daemon/Child.cpp
+++ b/daemon/Child.cpp
@@ -1,38 +1,39 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include "Child.h"
+
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
+
#include "Logging.h"
#include "CapturedXML.h"
#include "SessionData.h"
-#include "Child.h"
#include "LocalCapture.h"
-#include "Collector.h"
#include "Sender.h"
#include "OlyUtility.h"
+#include "OlySocket.h"
#include "StreamlineSetup.h"
#include "ConfigurationXML.h"
#include "Driver.h"
-#include "Fifo.h"
-#include "Buffer.h"
-
-#define NS_PER_S ((uint64_t)1000000000)
-#define NS_PER_US 1000
+#include "PerfSource.h"
+#include "DriverSource.h"
+#include "UserSpaceSource.h"
+#include "ExternalSource.h"
static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
-static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads
-static Buffer* buffer = NULL;
+static Source *primarySource = NULL;
+static Source *userSpaceSource = NULL;
+static Source *externalSource = NULL;
static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
-static Collector* collector = NULL;
Child* child = NULL; // shared by Child.cpp and main.cpp
extern void cleanUp();
@@ -78,7 +79,7 @@ static void child_handler(int signum) {
}
beenHere = true;
logg->logMessage("Gator is shutting down.");
- if (signum == SIGALRM || !collector) {
+ if (signum == SIGALRM || !primarySource) {
exit(1);
} else {
child->endSession();
@@ -139,77 +140,22 @@ static void *stopThread(void *) {
return 0;
}
-static void *countersThread(void *) {
- prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
-
- gSessionData->hwmon.start();
-
- int64_t monotonic_started = 0;
- while (monotonic_started <= 0) {
- usleep(10);
-
- if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
- logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
- handleException();
- }
- }
-
- uint64_t next_time = 0;
- while (gSessionData->mSessionIsActive) {
- struct timespec ts;
-#ifndef CLOCK_MONOTONIC_RAW
- // Android doesn't have this defined but it was added in Linux 2.6.28
-#define CLOCK_MONOTONIC_RAW 4
-#endif
- if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
- logg->logError(__FILE__, __LINE__, "Failed to get uptime");
- handleException();
- }
- const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started;
- // Sample ten times a second ignoring gSessionData->mSampleRate
- next_time += NS_PER_S/10;//gSessionData->mSampleRate;
- if (next_time < curr_time) {
- logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
- next_time = curr_time;
- }
-
- if (buffer->eventHeader(curr_time)) {
- gSessionData->hwmon.read(buffer);
- // Only check after writing all counters so that time and corresponding counters appear in the same frame
- buffer->check(curr_time);
- }
-
- if (buffer->bytesAvailable() <= 0) {
- logg->logMessage("One shot (counters)");
- child->endSession();
- }
-
- usleep((next_time - curr_time)/NS_PER_US);
- }
-
- buffer->setDone();
-
- return NULL;
-}
-
static void *senderThread(void *) {
- int length = 1;
- char* data;
char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
sem_post(&senderThreadStarted);
prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
sem_wait(&haltPipeline);
- while (length > 0 || !buffer->isDone()) {
+ while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) {
sem_wait(&senderSem);
- data = collectorFifo->read(&length);
- if (data != NULL) {
- sender->writeData(data, length, RESPONSE_APC_DATA);
- collectorFifo->release();
+
+ primarySource->write(sender);
+ if (userSpaceSource != NULL) {
+ userSpaceSource->write(sender);
}
- if (!buffer->isDone()) {
- buffer->write(sender);
+ if (externalSource != NULL) {
+ externalSource->write(sender);
}
}
@@ -255,15 +201,13 @@ void Child::initialization() {
void Child::endSession() {
gSessionData->mSessionIsActive = false;
- collector->stop();
+ primarySource->interrupt();
sem_post(&haltPipeline);
}
void Child::run() {
- char* collectBuffer;
- int bytesCollected = 0;
LocalCapture* localCapture = NULL;
- pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID;
+ pthread_t durationThreadID, stopThreadID, senderThreadID;
prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
@@ -282,7 +226,11 @@ void Child::run() {
{ ConfigurationXML configuration; }
// Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
- collector = new Collector();
+ if (!gSessionData->perf.isSetup()) {
+ primarySource = new DriverSource(&senderSem, &startProfile);
+ } else {
+ primarySource = new PerfSource(&senderSem, &startProfile);
+ }
// Initialize all drivers
for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
@@ -317,15 +265,11 @@ void Child::run() {
free(xmlString);
}
- // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
- logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
- collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
-
- // Get the initial pointer to the collect buffer
- collectBuffer = collectorFifo->start();
-
- // Create a new Block Counter Buffer
- buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
+ // Must be after session XML is parsed
+ if (!primarySource->prepare()) {
+ logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
+ handleException();
+ }
// Sender thread shall be halted until it is signaled for one shot mode
sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
@@ -340,14 +284,21 @@ void Child::run() {
thread_creation_success = false;
}
- bool startcountersThread = gSessionData->hwmon.countersEnabled();
- if (startcountersThread) {
- if (pthread_create(&countersThreadID, NULL, countersThread, this)) {
- thread_creation_success = false;
+ if (gSessionData->hwmon.countersEnabled()) {
+ userSpaceSource = new UserSpaceSource(&senderSem);
+ if (!userSpaceSource->prepare()) {
+ logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
+ handleException();
}
- } else {
- // Let senderThread know there is no buffer data to send
- buffer->setDone();
+ userSpaceSource->start();
+ }
+ if (access("/tmp/gator", F_OK) == 0) {
+ externalSource = new ExternalSource(&senderSem);
+ if (!externalSource->prepare()) {
+ logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
+ handleException();
+ }
+ externalSource->start();
}
if (!thread_creation_success) {
@@ -359,28 +310,13 @@ void Child::run() {
sem_wait(&senderThreadStarted);
// Start profiling
- logg->logMessage("********** Profiling started **********");
- collector->start();
- sem_post(&startProfile);
-
- // Collect Data
- do {
- // This command will stall until data is received from the driver
- bytesCollected = collector->collect(collectBuffer);
-
- // In one shot mode, stop collection once all the buffers are filled
- if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
- if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
- logg->logMessage("One shot");
- endSession();
- }
- }
- collectBuffer = collectorFifo->write(bytesCollected);
- } while (bytesCollected > 0);
- logg->logMessage("Exit collect data loop");
+ primarySource->run();
- if (startcountersThread) {
- pthread_join(countersThreadID, NULL);
+ if (externalSource != NULL) {
+ externalSource->join();
+ }
+ if (userSpaceSource != NULL) {
+ userSpaceSource->join();
}
// Wait for the other threads to exit
@@ -401,9 +337,9 @@ void Child::run() {
logg->logMessage("Profiling ended.");
- delete buffer;
- delete collectorFifo;
+ delete externalSource;
+ delete userSpaceSource;
+ delete primarySource;
delete sender;
- delete collector;
delete localCapture;
}
diff --git a/daemon/Child.h b/daemon/Child.h
index 0330e9d..9e206d7 100644
--- a/daemon/Child.h
+++ b/daemon/Child.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -9,8 +9,6 @@
#ifndef __CHILD_H__
#define __CHILD_H__
-#include <pthread.h>
-
class OlySocket;
class Child {
diff --git a/daemon/Collector.h b/daemon/Collector.h
deleted file mode 100644
index c5e9eac..0000000
--- a/daemon/Collector.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __COLLECTOR_H__
-#define __COLLECTOR_H__
-
-#include <stdio.h>
-
-class Collector {
-public:
- Collector();
- ~Collector();
- void start();
- void stop();
- int collect(char* buffer);
- int getBufferSize() {return mBufferSize;}
-
- static int readIntDriver(const char* path, int* value);
- static int readInt64Driver(const char* path, int64_t* value);
- static int writeDriver(const char* path, int value);
- static int writeDriver(const char* path, int64_t value);
- static int writeDriver(const char* path, const char* data);
- static int writeReadDriver(const char* path, int* value);
- static int writeReadDriver(const char* path, int64_t* value);
-
-private:
- int mBufferSize;
- int mBufferFD;
-
- void checkVersion();
-};
-
-#endif //__COLLECTOR_H__
diff --git a/daemon/Config.h b/daemon/Config.h
new file mode 100644
index 0000000..6f5e2aa
--- /dev/null
+++ b/daemon/Config.h
@@ -0,0 +1,17 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define ARRAY_LENGTH(A) static_cast<int>(sizeof(A)/sizeof((A)[0]))
+
+#define MAX_PERFORMANCE_COUNTERS 50
+#define NR_CPUS 16
+
+#endif // CONFIG_H
diff --git a/daemon/ConfigurationXML.cpp b/daemon/ConfigurationXML.cpp
index 2a5252a..fd479f2 100644
--- a/daemon/ConfigurationXML.cpp
+++ b/daemon/ConfigurationXML.cpp
@@ -1,15 +1,17 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include "ConfigurationXML.h"
+
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
-#include "ConfigurationXML.h"
+
#include "Driver.h"
#include "Logging.h"
#include "OlyUtility.h"
@@ -67,6 +69,7 @@ int ConfigurationXML::parse(const char* configurationXML) {
// clear counter overflow
gSessionData->mCounterOverflow = 0;
+ gSessionData->mIsEBS = false;
mIndex = 0;
// disable all counters prior to parsing the configuration xml
@@ -155,6 +158,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) {
if (mxmlElementGetAttr(node, ATTR_COUNTER)) counter.setType(mxmlElementGetAttr(node, ATTR_COUNTER));
if (mxmlElementGetAttr(node, ATTR_EVENT)) counter.setEvent(strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16));
if (mxmlElementGetAttr(node, ATTR_COUNT)) counter.setCount(strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10));
+ if (counter.getCount() > 0) {
+ gSessionData->mIsEBS = true;
+ }
counter.setEnabled(true);
// Associate a driver with each counter
@@ -181,9 +187,9 @@ void ConfigurationXML::configurationTag(mxml_node_t *node) {
}
void ConfigurationXML::getDefaultConfigurationXml(const char * & xml, unsigned int & len) {
-#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len
- xml = (const char *)configuration_xml;
- len = configuration_xml_len;
+#include "defaults_xml.h" // defines and initializes char defaults_xml[] and int defaults_xml_len
+ xml = (const char *)defaults_xml;
+ len = defaults_xml_len;
}
void ConfigurationXML::getPath(char* path) {
diff --git a/daemon/ConfigurationXML.h b/daemon/ConfigurationXML.h
index 5650f48..efa415e 100644
--- a/daemon/ConfigurationXML.h
+++ b/daemon/ConfigurationXML.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/Counter.h b/daemon/Counter.h
index 231a85d..6891745 100644
--- a/daemon/Counter.h
+++ b/daemon/Counter.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,7 +25,7 @@ public:
void clear () {
mType[0] = '\0';
mEnabled = false;
- mEvent = 0;
+ mEvent = -1;
mCount = 0;
mKey = 0;
mDriver = NULL;
diff --git a/daemon/Driver.cpp b/daemon/Driver.cpp
index c262467..09e0401 100644
--- a/daemon/Driver.cpp
+++ b/daemon/Driver.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/Driver.h b/daemon/Driver.h
index f3a932f..e5ed7b6 100644
--- a/daemon/Driver.h
+++ b/daemon/Driver.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -27,7 +27,7 @@ public:
virtual void setupCounter(Counter &counter) = 0;
// Emits available counters
- virtual void writeCounters(mxml_node_t *root) const = 0;
+ virtual int writeCounters(mxml_node_t *root) const = 0;
// Emits possible dynamically generated events/counters
virtual void writeEvents(mxml_node_t *) const {}
diff --git a/daemon/Collector.cpp b/daemon/DriverSource.cpp
index bf73534..f78ec6b 100644
--- a/daemon/Collector.cpp
+++ b/daemon/DriverSource.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -8,23 +8,47 @@
#define __STDC_FORMAT_MACROS
+#include "DriverSource.h"
+
#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/time.h>
#include <inttypes.h>
-#include "Collector.h"
-#include "SessionData.h"
+#include <unistd.h>
+
+#include "Child.h"
+#include "Fifo.h"
#include "Logging.h"
#include "Sender.h"
+#include "SessionData.h"
+
+extern Child *child;
-// Driver initialization independent of session settings
-Collector::Collector() {
- mBufferFD = 0;
+DriverSource::DriverSource(sem_t *senderSem, sem_t *startProfile) : mFifo(NULL), mSenderSem(senderSem), mStartProfile(startProfile), mBufferSize(0), mBufferFD(0), mLength(1) {
+ int driver_version = 0;
- checkVersion();
+ if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
+ logg->logError(__FILE__, __LINE__, "Error reading gator driver version");
+ handleException();
+ }
+
+ // Verify the driver version matches the daemon version
+ if (driver_version != PROTOCOL_VERSION) {
+ if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
+ // One of the mismatched versions is development version
+ logg->logError(__FILE__, __LINE__,
+ "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
+ ">> The following must be synchronized from engineering repository:\n"
+ ">> * gator driver\n"
+ ">> * gator daemon\n"
+ ">> * Streamline", driver_version, PROTOCOL_VERSION);
+ handleException();
+ } else {
+ // Release version mismatch
+ logg->logError(__FILE__, __LINE__,
+ "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
+ ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION);
+ handleException();
+ }
+ }
int enable = -1;
if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) {
@@ -37,14 +61,15 @@ Collector::Collector() {
gSessionData->mCores = 1;
}
- mBufferSize = 0;
if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) {
logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size");
handleException();
}
}
-Collector::~Collector() {
+DriverSource::~DriverSource() {
+ delete mFifo;
+
// Write zero for safety, as a zero should have already been written
writeDriver("/dev/gator/enable", "0");
@@ -54,36 +79,21 @@ Collector::~Collector() {
}
}
-void Collector::checkVersion() {
- int driver_version = 0;
-
- if (readIntDriver("/dev/gator/version", &driver_version) == -1) {
- logg->logError(__FILE__, __LINE__, "Error reading gator driver version");
- handleException();
- }
+bool DriverSource::prepare() {
+ // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
+ logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, mBufferSize);
+ mFifo = new Fifo(mBufferSize + 5, gSessionData->mTotalBufferSize*1024*1024, mSenderSem);
- // Verify the driver version matches the daemon version
- if (driver_version != PROTOCOL_VERSION) {
- if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) {
- // One of the mismatched versions is development version
- logg->logError(__FILE__, __LINE__,
- "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n"
- ">> The following must be synchronized from engineering repository:\n"
- ">> * gator driver\n"
- ">> * gator daemon\n"
- ">> * Streamline", driver_version, PROTOCOL_VERSION);
- handleException();
- } else {
- // Release version mismatch
- logg->logError(__FILE__, __LINE__,
- "gator driver version \"%d\" is different than gator daemon version \"%d\".\n"
- ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION);
- handleException();
- }
- }
+ return true;
}
-void Collector::start() {
+void DriverSource::run() {
+ // Get the initial pointer to the collect buffer
+ char *collectBuffer = mFifo->start();
+ int bytesCollected = 0;
+
+ logg->logMessage("********** Profiling started **********");
+
// Set the maximum backtrace depth
if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) {
logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth");
@@ -125,79 +135,112 @@ void Collector::start() {
}
lseek(mBufferFD, 0, SEEK_SET);
+
+ sem_post(mStartProfile);
+
+ // Collect Data
+ do {
+ // This command will stall until data is received from the driver
+ // Calls event_buffer_read in the driver
+ errno = 0;
+ bytesCollected = read(mBufferFD, collectBuffer, mBufferSize);
+
+ // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
+ if (bytesCollected == -1 && errno == EINTR) {
+ bytesCollected = read(mBufferFD, collectBuffer, mBufferSize);
+ }
+
+ // return the total bytes written
+ logg->logMessage("Driver read of %d bytes", bytesCollected);
+
+ // In one shot mode, stop collection once all the buffers are filled
+ if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
+ if (bytesCollected == -1 || mFifo->willFill(bytesCollected)) {
+ logg->logMessage("One shot");
+ child->endSession();
+ }
+ }
+ collectBuffer = mFifo->write(bytesCollected);
+ } while (bytesCollected > 0);
+
+ logg->logMessage("Exit collect data loop");
}
-// These commands should cause the read() function in collect() to return
-void Collector::stop() {
- // This will stop the driver from profiling
+void DriverSource::interrupt() {
+ // This command should cause the read() function in collect() to return and stop the driver from profiling
if (writeDriver("/dev/gator/enable", "0") != 0) {
logg->logMessage("Stopping kernel failed");
}
}
-int Collector::collect(char* buffer) {
- // Calls event_buffer_read in the driver
- int bytesRead;
-
- errno = 0;
- bytesRead = read(mBufferFD, buffer, mBufferSize);
+bool DriverSource::isDone() {
+ return mLength <= 0;
+}
- // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data
- if (bytesRead == -1 && errno == EINTR) {
- bytesRead = read(mBufferFD, buffer, mBufferSize);
+void DriverSource::write(Sender *sender) {
+ char *data = mFifo->read(&mLength);
+ if (data != NULL) {
+ sender->writeData(data, mLength, RESPONSE_APC_DATA);
+ mFifo->release();
}
-
- // return the total bytes written
- logg->logMessage("Driver read of %d bytes", bytesRead);
- return bytesRead;
}
-int Collector::readIntDriver(const char* fullpath, int* value) {
- FILE* file = fopen(fullpath, "r");
- if (file == NULL) {
+int DriverSource::readIntDriver(const char *fullpath, int *value) {
+ char data[40]; // Sufficiently large to hold any integer
+ const int fd = open(fullpath, O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ const ssize_t bytes = read(fd, data, sizeof(data) - 1);
+ close(fd);
+ if (bytes < 0) {
return -1;
}
- if (fscanf(file, "%u", value) != 1) {
- fclose(file);
+ data[bytes] = '\0';
+
+ char *endptr;
+ errno = 0;
+ *value = strtol(data, &endptr, 10);
+ if (errno != 0 || *endptr != '\n') {
logg->logMessage("Invalid value in file %s", fullpath);
return -1;
}
- fclose(file);
+
return 0;
}
-int Collector::readInt64Driver(const char* fullpath, int64_t* value) {
- FILE* file = fopen(fullpath, "r");
- if (file == NULL) {
+int DriverSource::readInt64Driver(const char *fullpath, int64_t *value) {
+ char data[40]; // Sufficiently large to hold any integer
+ const int fd = open(fullpath, O_RDONLY);
+ if (fd < 0) {
return -1;
}
- if (fscanf(file, "%" SCNi64, value) != 1) {
- fclose(file);
- logg->logMessage("Invalid value in file %s", fullpath);
+
+ const ssize_t bytes = read(fd, data, sizeof(data) - 1);
+ close(fd);
+ if (bytes < 0) {
return -1;
}
- fclose(file);
- return 0;
-}
+ data[bytes] = '\0';
-int Collector::writeDriver(const char* path, int value) {
- char data[40]; // Sufficiently large to hold any integer
- snprintf(data, sizeof(data), "%d", value);
- return writeDriver(path, data);
-}
+ char *endptr;
+ errno = 0;
+ *value = strtoll(data, &endptr, 10);
+ if (errno != 0 || *endptr != '\n') {
+ logg->logMessage("Invalid value in file %s", fullpath);
+ return -1;
+ }
-int Collector::writeDriver(const char* path, int64_t value) {
- char data[40]; // Sufficiently large to hold any integer
- snprintf(data, sizeof(data), "%" PRIi64, value);
- return writeDriver(path, data);
+ return 0;
}
-int Collector::writeDriver(const char* fullpath, const char* data) {
+int DriverSource::writeDriver(const char *fullpath, const char *data) {
int fd = open(fullpath, O_WRONLY);
if (fd < 0) {
return -1;
}
- if (write(fd, data, strlen(data)) < 0) {
+ if (::write(fd, data, strlen(data)) < 0) {
close(fd);
logg->logMessage("Opened but could not write to %s", fullpath);
return -1;
@@ -206,14 +249,26 @@ int Collector::writeDriver(const char* fullpath, const char* data) {
return 0;
}
-int Collector::writeReadDriver(const char* path, int* value) {
+int DriverSource::writeDriver(const char *path, int value) {
+ char data[40]; // Sufficiently large to hold any integer
+ snprintf(data, sizeof(data), "%d", value);
+ return writeDriver(path, data);
+}
+
+int DriverSource::writeDriver(const char *path, int64_t value) {
+ char data[40]; // Sufficiently large to hold any integer
+ snprintf(data, sizeof(data), "%" PRIi64, value);
+ return writeDriver(path, data);
+}
+
+int DriverSource::writeReadDriver(const char *path, int *value) {
if (writeDriver(path, *value) || readIntDriver(path, value)) {
return -1;
}
return 0;
}
-int Collector::writeReadDriver(const char* path, int64_t* value) {
+int DriverSource::writeReadDriver(const char *path, int64_t *value) {
if (writeDriver(path, *value) || readInt64Driver(path, value)) {
return -1;
}
diff --git a/daemon/DriverSource.h b/daemon/DriverSource.h
new file mode 100644
index 0000000..dcf1078
--- /dev/null
+++ b/daemon/DriverSource.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DRIVERSOURCE_H
+#define DRIVERSOURCE_H
+
+#include <semaphore.h>
+#include <stdint.h>
+
+#include "Source.h"
+
+class Fifo;
+
+class DriverSource : public Source {
+public:
+ DriverSource(sem_t *senderSem, sem_t *startProfile);
+ ~DriverSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+ static int readIntDriver(const char *fullpath, int *value);
+ static int readInt64Driver(const char *fullpath, int64_t *value);
+ static int writeDriver(const char *fullpath, const char *data);
+ static int writeDriver(const char *path, int value);
+ static int writeDriver(const char *path, int64_t value);
+ static int writeReadDriver(const char *path, int *value);
+ static int writeReadDriver(const char *path, int64_t *value);
+
+private:
+ Fifo *mFifo;
+ sem_t *const mSenderSem;
+ sem_t *const mStartProfile;
+ int mBufferSize;
+ int mBufferFD;
+ int mLength;
+
+ // Intentionally unimplemented
+ DriverSource(const DriverSource &);
+ DriverSource &operator=(const DriverSource &);
+};
+
+#endif // DRIVERSOURCE_H
diff --git a/daemon/DynBuf.cpp b/daemon/DynBuf.cpp
new file mode 100644
index 0000000..6f92b33
--- /dev/null
+++ b/daemon/DynBuf.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "DynBuf.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "Logging.h"
+
+// Pick an aggressive size as buffer is primarily used for disk IO
+#define MIN_BUFFER_FREE (1 << 12)
+
+int DynBuf::resize(const size_t minCapacity) {
+ size_t scaledCapacity = 2 * capacity;
+ if (scaledCapacity < minCapacity) {
+ scaledCapacity = minCapacity;
+ }
+ if (scaledCapacity < 2 * MIN_BUFFER_FREE) {
+ scaledCapacity = 2 * MIN_BUFFER_FREE;
+ }
+ capacity = scaledCapacity;
+
+ buf = static_cast<char *>(realloc(buf, capacity));
+ if (buf == NULL) {
+ return -errno;
+ }
+
+ return 0;
+}
+
+bool DynBuf::read(const char *const path) {
+ int result = false;
+
+ const int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ logg->logMessage("%s(%s:%i): open failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ length = 0;
+
+ for (;;) {
+ const size_t minCapacity = length + MIN_BUFFER_FREE + 1;
+ if (capacity < minCapacity) {
+ if (resize(minCapacity) != 0) {
+ logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ }
+
+ const ssize_t bytes = ::read(fd, buf + length, capacity - length - 1);
+ if (bytes < 0) {
+ logg->logMessage("%s(%s:%i): read failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ } else if (bytes == 0) {
+ break;
+ }
+ length += bytes;
+ }
+
+ buf[length] = '\0';
+ result = true;
+
+ fail:
+ close(fd);
+
+ return result;
+}
+
+int DynBuf::readlink(const char *const path) {
+ ssize_t bytes = MIN_BUFFER_FREE;
+
+ for (;;) {
+ if (static_cast<size_t>(bytes) >= capacity) {
+ const int err = resize(2 * bytes);
+ if (err != 0) {
+ return err;
+ }
+ }
+ bytes = ::readlink(path, buf, capacity);
+ if (bytes < 0) {
+ return -errno;
+ } else if (static_cast<size_t>(bytes) < capacity) {
+ break;
+ }
+ }
+
+ length = bytes;
+ buf[bytes] = '\0';
+
+ return 0;
+}
+
+bool DynBuf::printf(const char *format, ...) {
+ va_list ap;
+
+ if (capacity <= 0) {
+ if (resize(2 * MIN_BUFFER_FREE) != 0) {
+ logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ va_start(ap, format);
+ int bytes = vsnprintf(buf, capacity, format, ap);
+ va_end(ap);
+ if (bytes < 0) {
+ logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (static_cast<size_t>(bytes) > capacity) {
+ if (resize(bytes + 1) != 0) {
+ logg->logMessage("%s(%s:%i): DynBuf::resize failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ va_start(ap, format);
+ bytes = vsnprintf(buf, capacity, format, ap);
+ va_end(ap);
+ if (bytes < 0) {
+ logg->logMessage("%s(%s:%i): fsnprintf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ length = bytes;
+
+ return true;
+}
diff --git a/daemon/DynBuf.h b/daemon/DynBuf.h
new file mode 100644
index 0000000..2f4554a
--- /dev/null
+++ b/daemon/DynBuf.h
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DYNBUF_H
+#define DYNBUF_H
+
+#include <stdlib.h>
+
+class DynBuf {
+public:
+ DynBuf() : capacity(0), length(0), buf(NULL) {}
+ ~DynBuf() {
+ reset();
+ }
+
+ inline void reset() {
+ capacity = 0;
+ length = 0;
+ if (buf != NULL) {
+ free(buf);
+ buf = NULL;
+ }
+ }
+
+ bool read(const char *const path);
+ // On error instead of printing the error and returning false, this returns -errno
+ int readlink(const char *const path);
+ __attribute__ ((format(printf, 2, 3)))
+ bool printf(const char *format, ...);
+
+ size_t getLength() const { return length; }
+ const char *getBuf() const { return buf; }
+ char *getBuf() { return buf; }
+
+private:
+ int resize(const size_t minCapacity);
+
+ size_t capacity;
+ size_t length;
+ char *buf;
+
+ // Intentionally undefined
+ DynBuf(const DynBuf &);
+ DynBuf &operator=(const DynBuf &);
+};
+
+#endif // DYNBUF_H
diff --git a/daemon/EventsXML.cpp b/daemon/EventsXML.cpp
index 2a80482..a07a046 100644
--- a/daemon/EventsXML.cpp
+++ b/daemon/EventsXML.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -35,7 +35,7 @@ char* EventsXML::getXML() {
fclose(fl);
} else {
logg->logMessage("Unable to locate events.xml, using default");
- xml = mxmlLoadString(NULL, (char *)events_xml, MXML_NO_CALLBACK);
+ xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK);
}
// Add dynamic events from the drivers
diff --git a/daemon/EventsXML.h b/daemon/EventsXML.h
index 8e693ef..6cd1560 100644
--- a/daemon/EventsXML.h
+++ b/daemon/EventsXML.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/ExternalSource.cpp b/daemon/ExternalSource.cpp
new file mode 100644
index 0000000..fe5824b
--- /dev/null
+++ b/daemon/ExternalSource.cpp
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "ExternalSource.h"
+
+#include <sys/prctl.h>
+
+#include "Logging.h"
+#include "OlySocket.h"
+#include "SessionData.h"
+
+ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 1024, senderSem), mSock("/tmp/gator") {
+}
+
+ExternalSource::~ExternalSource() {
+}
+
+bool ExternalSource::prepare() {
+ return true;
+}
+
+void ExternalSource::run() {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-uds", 0, 0, 0);
+
+ while (gSessionData->mSessionIsActive) {
+ // Will be aborted when the socket is closed at the end of the capture
+ int length = mSock.receive(mBuffer.getWritePos(), mBuffer.contiguousSpaceAvailable());
+ if (length <= 0) {
+ break;
+ }
+
+ mBuffer.advanceWrite(length);
+ mBuffer.check(0);
+ }
+
+ mBuffer.setDone();
+}
+
+void ExternalSource::interrupt() {
+ // Do nothing
+}
+
+bool ExternalSource::isDone() {
+ return mBuffer.isDone();
+}
+
+void ExternalSource::write(Sender *sender) {
+ if (!mBuffer.isDone()) {
+ mBuffer.write(sender);
+ }
+}
diff --git a/daemon/ExternalSource.h b/daemon/ExternalSource.h
new file mode 100644
index 0000000..2052bdf
--- /dev/null
+++ b/daemon/ExternalSource.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef EXTERNALSOURCE_H
+#define EXTERNALSOURCE_H
+
+#include <semaphore.h>
+
+#include "Buffer.h"
+#include "OlySocket.h"
+#include "Source.h"
+
+// Unix domain socket counters from external sources like graphics drivers
+class ExternalSource : public Source {
+public:
+ ExternalSource(sem_t *senderSem);
+ ~ExternalSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+private:
+ Buffer mBuffer;
+ OlySocket mSock;
+
+ // Intentionally unimplemented
+ ExternalSource(const ExternalSource &);
+ ExternalSource &operator=(const ExternalSource &);
+};
+
+#endif // EXTERNALSOURCE_H
diff --git a/daemon/Fifo.cpp b/daemon/Fifo.cpp
index 250a4d0..f672e92 100644
--- a/daemon/Fifo.cpp
+++ b/daemon/Fifo.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/Fifo.h b/daemon/Fifo.h
index d25cd68..7dd7426 100644
--- a/daemon/Fifo.h
+++ b/daemon/Fifo.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -12,7 +12,7 @@
#ifdef WIN32
#include <windows.h>
#define sem_t HANDLE
-#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, INFINITE, NULL)) == NULL)
+#define sem_init(sem, pshared, value) ((*(sem) = CreateSemaphore(NULL, value, LONG_MAX, NULL)) == NULL)
#define sem_wait(sem) WaitForSingleObject(*(sem), INFINITE)
#define sem_post(sem) ReleaseSemaphore(*(sem), 1, NULL)
#define sem_destroy(sem) CloseHandle(*(sem))
diff --git a/daemon/Hwmon.cpp b/daemon/Hwmon.cpp
index 1d7c0da..778f307 100644
--- a/daemon/Hwmon.cpp
+++ b/daemon/Hwmon.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -17,7 +17,7 @@
class HwmonCounter {
public:
- HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature);
+ HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature);
~HwmonCounter();
HwmonCounter *getNext() const { return next; }
@@ -69,7 +69,7 @@ private:
HwmonCounter &operator=(const HwmonCounter &);
};
-HwmonCounter::HwmonCounter(HwmonCounter *next, int key, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(key), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) {
+HwmonCounter::HwmonCounter(HwmonCounter *next, const sensors_chip_name *chip, const sensors_feature *feature) : next(next), key(getEventKey()), polled(false), readable(false), enabled(false), duplicate(false), chip(chip), feature(feature) {
int len = sensors_snprintf_chip_name(NULL, 0, chip) + 1;
char *chip_name = new char[len];
@@ -205,6 +205,23 @@ bool HwmonCounter::canRead() {
}
Hwmon::Hwmon() : counters(NULL) {
+}
+
+Hwmon::~Hwmon() {
+ while (counters != NULL) {
+ HwmonCounter * counter = counters;
+ counters = counter->getNext();
+ delete counter;
+ }
+ sensors_cleanup();
+}
+
+void Hwmon::setup() {
+ // hwmon does not currently work with perf
+ if (gSessionData->perf.isSetup()) {
+ return;
+ }
+
int err = sensors_init(NULL);
if (err) {
logg->logMessage("Failed to initialize libsensors! (%d)", err);
@@ -218,20 +235,11 @@ Hwmon::Hwmon() : counters(NULL) {
int feature_nr = 0;
const sensors_feature *feature;
while ((feature = sensors_get_features(chip, &feature_nr))) {
- counters = new HwmonCounter(counters, getEventKey(), chip, feature);
+ counters = new HwmonCounter(counters, chip, feature);
}
}
}
-Hwmon::~Hwmon() {
- while (counters != NULL) {
- HwmonCounter * counter = counters;
- counters = counter->getNext();
- delete counter;
- }
- sensors_cleanup();
-}
-
HwmonCounter *Hwmon::findCounter(const Counter &counter) const {
for (HwmonCounter * hwmonCounter = counters; hwmonCounter != NULL; hwmonCounter = hwmonCounter->getNext()) {
if (hwmonCounter->canRead() && strcmp(hwmonCounter->getName(), counter.getType()) == 0) {
@@ -271,14 +279,18 @@ void Hwmon::setupCounter(Counter &counter) {
counter.setKey(hwmonCounter->getKey());
}
-void Hwmon::writeCounters(mxml_node_t *root) const {
+int Hwmon::writeCounters(mxml_node_t *root) const {
+ int count = 0;
for (HwmonCounter * counter = counters; counter != NULL; counter = counter->getNext()) {
if (!counter->canRead()) {
continue;
}
mxml_node_t *node = mxmlNewElement(root, "counter");
mxmlElementSetAttr(node, "name", counter->getName());
+ ++count;
}
+
+ return count;
}
void Hwmon::writeEvents(mxml_node_t *root) const {
diff --git a/daemon/Hwmon.h b/daemon/Hwmon.h
index 46bb42e..a22a360 100644
--- a/daemon/Hwmon.h
+++ b/daemon/Hwmon.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -19,12 +19,14 @@ public:
Hwmon();
~Hwmon();
+ void setup();
+
bool claimCounter(const Counter &counter) const;
bool countersEnabled() const;
void resetCounters();
void setupCounter(Counter &counter);
- void writeCounters(mxml_node_t *root) const;
+ int writeCounters(mxml_node_t *root) const;
void writeEvents(mxml_node_t *root) const;
void start();
diff --git a/daemon/KMod.cpp b/daemon/KMod.cpp
index 559297f..9300002 100644
--- a/daemon/KMod.cpp
+++ b/daemon/KMod.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -12,9 +12,9 @@
#include <dirent.h>
#include <unistd.h>
-#include "Collector.h"
#include "ConfigurationXML.h"
#include "Counter.h"
+#include "DriverSource.h"
#include "Logging.h"
// Claim all the counters in /dev/gator/events
@@ -38,9 +38,9 @@ void KMod::resetCounters() {
continue;
snprintf(base, sizeof(base), "/dev/gator/events/%s", ent->d_name);
snprintf(text, sizeof(text), "%s/enabled", base);
- Collector::writeDriver(text, 0);
+ DriverSource::writeDriver(text, 0);
snprintf(text, sizeof(text), "%s/count", base);
- Collector::writeDriver(text, 0);
+ DriverSource::writeDriver(text, 0);
}
closedir(dir);
}
@@ -53,22 +53,22 @@ void KMod::setupCounter(Counter &counter) {
snprintf(text, sizeof(text), "%s/enabled", base);
int enabled = true;
- if (Collector::writeReadDriver(text, &enabled) || !enabled) {
+ if (DriverSource::writeReadDriver(text, &enabled) || !enabled) {
counter.setEnabled(false);
return;
}
snprintf(text, sizeof(text), "%s/key", base);
int key = 0;
- Collector::readIntDriver(text, &key);
+ DriverSource::readIntDriver(text, &key);
counter.setKey(key);
snprintf(text, sizeof(text), "%s/event", base);
- Collector::writeDriver(text, counter.getEvent());
+ DriverSource::writeDriver(text, counter.getEvent());
snprintf(text, sizeof(text), "%s/count", base);
if (access(text, F_OK) == 0) {
int count = counter.getCount();
- if (Collector::writeReadDriver(text, &count) && counter.getCount() > 0) {
+ if (DriverSource::writeReadDriver(text, &count) && counter.getCount() > 0) {
logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%i with a count of %d\n", counter.getType(), counter.getEvent(), counter.getCount());
handleException();
}
@@ -80,23 +80,26 @@ void KMod::setupCounter(Counter &counter) {
}
}
-void KMod::writeCounters(mxml_node_t *root) const {
+int KMod::writeCounters(mxml_node_t *root) const {
struct dirent *ent;
mxml_node_t *counter;
// counters.xml is simply a file listing of /dev/gator/events
DIR* dir = opendir("/dev/gator/events");
if (dir == NULL) {
- logg->logError(__FILE__, __LINE__, "Cannot create counters.xml since unable to read /dev/gator/events");
- handleException();
+ return 0;
}
+ int count = 0;
while ((ent = readdir(dir)) != NULL) {
// skip hidden files, current dir, and parent dir
if (ent->d_name[0] == '.')
continue;
counter = mxmlNewElement(root, "counter");
mxmlElementSetAttr(counter, "name", ent->d_name);
+ ++count;
}
closedir(dir);
+
+ return count;
}
diff --git a/daemon/KMod.h b/daemon/KMod.h
index 7974262..fb7fc8a 100644
--- a/daemon/KMod.h
+++ b/daemon/KMod.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2013. All rights reserved.
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -21,7 +21,7 @@ public:
void resetCounters();
void setupCounter(Counter &counter);
- void writeCounters(mxml_node_t *root) const;
+ int writeCounters(mxml_node_t *root) const;
};
#endif // KMOD_H
diff --git a/daemon/LocalCapture.cpp b/daemon/LocalCapture.cpp
index 3235a34..d2a4b79 100644
--- a/daemon/LocalCapture.cpp
+++ b/daemon/LocalCapture.cpp
@@ -1,18 +1,20 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include "LocalCapture.h"
+
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
-#include "LocalCapture.h"
+
#include "SessionData.h"
#include "Logging.h"
#include "OlyUtility.h"
diff --git a/daemon/LocalCapture.h b/daemon/LocalCapture.h
index 8042d6a..aadecce 100644
--- a/daemon/LocalCapture.h
+++ b/daemon/LocalCapture.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/Logging.cpp b/daemon/Logging.cpp
index 5fd45b5..b8d3178 100644
--- a/daemon/Logging.cpp
+++ b/daemon/Logging.cpp
@@ -1,11 +1,13 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include "Logging.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -23,8 +25,6 @@
#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex)
#endif
-#include "Logging.h"
-
// Global thread-safe logging
Logging* logg = NULL;
diff --git a/daemon/Logging.h b/daemon/Logging.h
index 8f960de..6ae3280 100644
--- a/daemon/Logging.h
+++ b/daemon/Logging.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -9,14 +9,7 @@
#ifndef __LOGGING_H__
#define __LOGGING_H__
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-#ifdef WIN32
-#include <windows.h>
-#else
#include <pthread.h>
-#endif
#define DRIVER_ERROR "\n Driver issue:\n >> gator.ko must be built against the current kernel version & configuration\n >> gator.ko should be co-located with gatord in the same directory\n >> OR insmod gator.ko prior to launching gatord"
@@ -33,11 +26,7 @@ private:
char mErrBuf[4096]; // Arbitrarily large buffer to hold a string
char mLogBuf[4096]; // Arbitrarily large buffer to hold a string
bool mDebug;
-#ifdef WIN32
- HANDLE mLoggingMutex;
-#else
pthread_mutex_t mLoggingMutex;
-#endif
};
extern Logging* logg;
diff --git a/daemon/Monitor.cpp b/daemon/Monitor.cpp
new file mode 100644
index 0000000..90d5c47
--- /dev/null
+++ b/daemon/Monitor.cpp
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Monitor.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "Logging.h"
+
+Monitor::Monitor() : mFd(-1) {
+}
+
+Monitor::~Monitor() {
+ if (mFd >= -1) {
+ close(mFd);
+ }
+}
+
+bool Monitor::init() {
+ mFd = epoll_create(16);
+ if (mFd < 0) {
+ logg->logMessage("%s(%s:%i): epoll_create1 failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+bool Monitor::add(const int fd) {
+ struct epoll_event event;
+ memset(&event, 0, sizeof(event));
+ event.data.fd = fd;
+ event.events = EPOLLIN;
+ if (epoll_ctl(mFd, EPOLL_CTL_ADD, fd, &event) != 0) {
+ logg->logMessage("%s(%s:%i): epoll_ctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+int Monitor::wait(struct epoll_event *const events, int maxevents, int timeout) {
+ int result = epoll_wait(mFd, events, maxevents, timeout);
+ if (result < 0) {
+ // Ignore if the call was interrupted as this will happen when SIGINT is received
+ if (errno == EINTR) {
+ result = 0;
+ } else {
+ logg->logMessage("%s(%s:%i): epoll_wait failed", __FUNCTION__, __FILE__, __LINE__);
+ }
+ }
+
+ return result;
+}
diff --git a/daemon/Monitor.h b/daemon/Monitor.h
new file mode 100644
index 0000000..6e268b6
--- /dev/null
+++ b/daemon/Monitor.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef MONITOR_H
+#define MONITOR_H
+
+#include <sys/epoll.h>
+
+class Monitor {
+public:
+ Monitor();
+ ~Monitor();
+
+ bool init();
+ bool add(const int fd);
+ int wait(struct epoll_event *const events, int maxevents, int timeout);
+
+private:
+
+ int mFd;
+
+ // Intentionally unimplemented
+ Monitor(const Monitor &);
+ Monitor &operator=(const Monitor &);
+};
+
+#endif // MONITOR_H
diff --git a/daemon/OlySocket.cpp b/daemon/OlySocket.cpp
index ab5c3c2..26e4768 100644
--- a/daemon/OlySocket.cpp
+++ b/daemon/OlySocket.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -15,6 +15,7 @@
#else
#include <netinet/in.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <unistd.h>
#include <netdb.h>
#endif
@@ -30,7 +31,7 @@
#define SHUTDOWN_RX_TX SHUT_RDWR
#endif
-OlySocket::OlySocket(int port, bool multiple) {
+OlyServerSocket::OlyServerSocket(int port) {
#ifdef WIN32
WSADATA wsaData;
if (WSAStartup(0x0202, &wsaData) != 0) {
@@ -39,24 +40,82 @@ OlySocket::OlySocket(int port, bool multiple) {
}
#endif
- if (multiple) {
- createServerSocket(port);
- } else {
- createSingleServerConnection(port);
- }
+ createServerSocket(port);
}
-OlySocket::OlySocket(int port, char* host) {
- mFDServer = 0;
+OlySocket::OlySocket(int port, const char* host) {
createClientSocket(host, port);
}
+OlySocket::OlySocket(int socketID) : mSocketID(socketID) {
+}
+
+#ifndef WIN32
+
+OlyServerSocket::OlyServerSocket(const char* path) {
+ // Create socket
+ mFDServer = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (mFDServer < 0) {
+ logg->logError(__FILE__, __LINE__, "Error creating server socket");
+ handleException();
+ }
+
+ unlink(path);
+
+ // Create sockaddr_in structure, ensuring non-populated fields are zero
+ struct sockaddr_un sockaddr;
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sun_family = AF_UNIX;
+ strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
+ sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0';
+
+ // Bind the socket to an address
+ if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
+ logg->logError(__FILE__, __LINE__, "Binding of server socket failed.");
+ handleException();
+ }
+
+ // Listen for connections on this socket
+ if (listen(mFDServer, 1) < 0) {
+ logg->logError(__FILE__, __LINE__, "Listening of server socket failed");
+ handleException();
+ }
+}
+
+OlySocket::OlySocket(const char* path) {
+ mSocketID = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (mSocketID < 0) {
+ return;
+ }
+
+ // Create sockaddr_in structure, ensuring non-populated fields are zero
+ struct sockaddr_un sockaddr;
+ memset((void*)&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sun_family = AF_UNIX;
+ strncpy(sockaddr.sun_path, path, sizeof(sockaddr.sun_path) - 1);
+ sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0';
+
+ if (connect(mSocketID, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
+ close(mSocketID);
+ mSocketID = -1;
+ return;
+ }
+}
+
+#endif
+
OlySocket::~OlySocket() {
if (mSocketID > 0) {
CLOSE_SOCKET(mSocketID);
}
}
+OlyServerSocket::~OlyServerSocket() {
+ if (mFDServer > 0) {
+ CLOSE_SOCKET(mFDServer);
+ }
+}
+
void OlySocket::shutdownConnection() {
// Shutdown is primarily used to unblock other threads that are blocking on send/receive functions
shutdown(mSocketID, SHUTDOWN_RX_TX);
@@ -70,7 +129,7 @@ void OlySocket::closeSocket() {
}
}
-void OlySocket::closeServerSocket() {
+void OlyServerSocket::closeServerSocket() {
if (CLOSE_SOCKET(mFDServer) != 0) {
logg->logError(__FILE__, __LINE__, "Failed to close server socket.");
handleException();
@@ -78,7 +137,7 @@ void OlySocket::closeServerSocket() {
mFDServer = 0;
}
-void OlySocket::createClientSocket(char* hostname, int portno) {
+void OlySocket::createClientSocket(const char* hostname, int portno) {
#ifdef WIN32
// TODO: Implement for Windows
#else
@@ -119,14 +178,7 @@ void OlySocket::createClientSocket(char* hostname, int portno) {
#endif
}
-void OlySocket::createSingleServerConnection(int port) {
- createServerSocket(port);
-
- mSocketID = acceptConnection();
- closeServerSocket();
-}
-
-void OlySocket::createServerSocket(int port) {
+void OlyServerSocket::createServerSocket(int port) {
int family = AF_INET6;
// Create socket
@@ -169,22 +221,23 @@ void OlySocket::createServerSocket(int port) {
// mSocketID is always set to the most recently accepted connection
// The user of this class should maintain the different socket connections, e.g. by forking the process
-int OlySocket::acceptConnection() {
+int OlyServerSocket::acceptConnection() {
+ int socketID;
if (mFDServer <= 0) {
logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket");
handleException();
}
// Accept a connection, note that this call blocks until a client connects
- mSocketID = accept(mFDServer, NULL, NULL);
- if (mSocketID < 0) {
+ socketID = accept(mFDServer, NULL, NULL);
+ if (socketID < 0) {
logg->logError(__FILE__, __LINE__, "Socket acceptance failed");
handleException();
}
- return mSocketID;
+ return socketID;
}
-void OlySocket::send(char* buffer, int size) {
+void OlySocket::send(const char* buffer, int size) {
if (size <= 0 || buffer == NULL) {
return;
}
diff --git a/daemon/OlySocket.h b/daemon/OlySocket.h
index 5bab7d1..eab786b 100644
--- a/daemon/OlySocket.h
+++ b/daemon/OlySocket.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -9,27 +9,44 @@
#ifndef __OLY_SOCKET_H__
#define __OLY_SOCKET_H__
-#include <string.h>
-
class OlySocket {
public:
- OlySocket(int port, bool multipleConnections = false);
- OlySocket(int port, char* hostname);
+ OlySocket(int port, const char* hostname);
+ OlySocket(int socketID);
+#ifndef WIN32
+ OlySocket(const char* path);
+#endif
~OlySocket();
- int acceptConnection();
+
void closeSocket();
- void closeServerSocket();
void shutdownConnection();
- void send(char* buffer, int size);
- void sendString(const char* string) {send((char*)string, strlen(string));}
+ void send(const char* buffer, int size);
int receive(char* buffer, int size);
int receiveNBytes(char* buffer, int size);
int receiveString(char* buffer, int size);
- int getSocketID() {return mSocketID;}
+
+ bool isValid() const { return mSocketID >= 0; }
+
+private:
+ int mSocketID;
+
+ void createClientSocket(const char* hostname, int port);
+};
+
+class OlyServerSocket {
+public:
+ OlyServerSocket(int port);
+#ifndef WIN32
+ OlyServerSocket(const char* path);
+#endif
+ ~OlyServerSocket();
+
+ int acceptConnection();
+ void closeServerSocket();
+
private:
- int mSocketID, mFDServer;
- void createClientSocket(char* hostname, int port);
- void createSingleServerConnection(int port);
+ int mFDServer;
+
void createServerSocket(int port);
};
diff --git a/daemon/OlyUtility.cpp b/daemon/OlyUtility.cpp
index 0b22d6e..45340a2 100644
--- a/daemon/OlyUtility.cpp
+++ b/daemon/OlyUtility.cpp
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/OlyUtility.h b/daemon/OlyUtility.h
index abab0a5..1d26beb 100644
--- a/daemon/OlyUtility.h
+++ b/daemon/OlyUtility.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/PerfBuffer.cpp b/daemon/PerfBuffer.cpp
new file mode 100644
index 0000000..5fad583
--- /dev/null
+++ b/daemon/PerfBuffer.cpp
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "PerfBuffer.h"
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "Buffer.h"
+#include "Logging.h"
+#include "Sender.h"
+#include "SessionData.h"
+
+PerfBuffer::PerfBuffer() {
+ for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) {
+ mBuf[cpu] = MAP_FAILED;
+ mDiscard[cpu] = false;
+ }
+}
+
+PerfBuffer::~PerfBuffer() {
+ for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
+ }
+ }
+}
+
+bool PerfBuffer::useFd(const int cpu, const int fd, const int groupFd) {
+ if (fd == groupFd) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu);
+ return false;
+ }
+
+ // The buffer isn't mapped yet
+ mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mBuf[cpu] == MAP_FAILED) {
+ logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ // Check the version
+ struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
+ if (pemp->compat_version != 0) {
+ logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ } else {
+ if (mBuf[cpu] == MAP_FAILED) {
+ logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, groupFd) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void PerfBuffer::discard(const int cpu) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ mDiscard[cpu] = true;
+ }
+}
+
+bool PerfBuffer::isEmpty() {
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ if (mBuf[cpu] != MAP_FAILED) {
+ // Take a snapshot of the positions
+ struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
+ const __u64 head = pemp->data_head;
+ const __u64 tail = pemp->data_tail;
+
+ if (head != tail) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool PerfBuffer::send(Sender *const sender) {
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ if (mBuf[cpu] == MAP_FAILED) {
+ continue;
+ }
+
+ // Take a snapshot of the positions
+ struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
+ const __u64 head = pemp->data_head;
+ const __u64 tail = pemp->data_tail;
+
+ if (head > tail) {
+ const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize;
+ const int offset = gSessionData->mLocalCapture ? 1 : 0;
+ unsigned char header[7];
+ header[0] = RESPONSE_APC_DATA;
+ Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5);
+ // Should use real packing functions
+ header[5] = FRAME_PERF;
+ header[6] = cpu;
+
+ // Write header
+ sender->writeData(reinterpret_cast<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA);
+
+ // Write data
+ if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) {
+ // Not wrapped
+ sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA);
+ } else {
+ // Wrapped
+ sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA);
+ sender->writeData(reinterpret_cast<const char *>(b), head & BUF_MASK, RESPONSE_APC_DATA);
+ }
+
+ // Update tail with the data read
+ pemp->data_tail = head;
+ }
+
+ if (mDiscard[cpu]) {
+ munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
+ mBuf[cpu] = MAP_FAILED;
+ mDiscard[cpu] = false;
+ logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
+ }
+ }
+
+ return true;
+}
diff --git a/daemon/PerfBuffer.h b/daemon/PerfBuffer.h
new file mode 100644
index 0000000..278a3b9
--- /dev/null
+++ b/daemon/PerfBuffer.h
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PERF_BUFFER
+#define PERF_BUFFER
+
+#include "Config.h"
+
+#define BUF_SIZE (gSessionData->mTotalBufferSize * 1024 * 1024)
+#define BUF_MASK (BUF_SIZE - 1)
+
+class Sender;
+
+class PerfBuffer {
+public:
+ PerfBuffer();
+ ~PerfBuffer();
+
+ bool useFd(const int cpu, const int fd, const int groupFd);
+ void discard(const int cpu);
+ bool isEmpty();
+ bool send(Sender *const sender);
+
+private:
+ void *mBuf[NR_CPUS];
+ // After the buffer is flushed it should be unmaped
+ bool mDiscard[NR_CPUS];
+
+ // Intentionally undefined
+ PerfBuffer(const PerfBuffer &);
+ PerfBuffer &operator=(const PerfBuffer &);
+};
+
+#endif // PERF_BUFFER
diff --git a/daemon/PerfDriver.cpp b/daemon/PerfDriver.cpp
new file mode 100644
index 0000000..8e25c22
--- /dev/null
+++ b/daemon/PerfDriver.cpp
@@ -0,0 +1,355 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "PerfDriver.h"
+
+#include <dirent.h>
+#include <sys/utsname.h>
+#include <time.h>
+
+#include "Buffer.h"
+#include "Config.h"
+#include "ConfigurationXML.h"
+#include "Counter.h"
+#include "DriverSource.h"
+#include "DynBuf.h"
+#include "Logging.h"
+#include "PerfGroup.h"
+#include "SessionData.h"
+
+#define PERF_DEVICES "/sys/bus/event_source/devices"
+
+#define TYPE_DERIVED ~0U
+
+// From gator.h
+struct gator_cpu {
+ const int cpuid;
+ // Human readable name
+ const char core_name[32];
+ // gatorfs event and Perf PMU name
+ const char *const pmnc_name;
+ const int pmnc_counters;
+};
+
+// From gator_main.c
+static const struct gator_cpu gator_cpus[] = {
+ { 0xb36, "ARM1136", "ARM_ARM11", 3 },
+ { 0xb56, "ARM1156", "ARM_ARM11", 3 },
+ { 0xb76, "ARM1176", "ARM_ARM11", 3 },
+ { 0xb02, "ARM11MPCore", "ARM_ARM11MPCore", 3 },
+ { 0xc05, "Cortex-A5", "ARMv7_Cortex_A5", 2 },
+ { 0xc07, "Cortex-A7", "ARMv7_Cortex_A7", 4 },
+ { 0xc08, "Cortex-A8", "ARMv7_Cortex_A8", 4 },
+ { 0xc09, "Cortex-A9", "ARMv7_Cortex_A9", 6 },
+ { 0xc0d, "Cortex-A12", "ARMv7_Cortex_A12", 6 },
+ { 0xc0f, "Cortex-A15", "ARMv7_Cortex_A15", 6 },
+ { 0xc0e, "Cortex-A17", "ARMv7_Cortex_A17", 6 },
+ { 0x00f, "Scorpion", "Scorpion", 4 },
+ { 0x02d, "ScorpionMP", "ScorpionMP", 4 },
+ { 0x049, "KraitSIM", "Krait", 4 },
+ { 0x04d, "Krait", "Krait", 4 },
+ { 0x06f, "Krait S4 Pro", "Krait", 4 },
+ { 0xd03, "Cortex-A53", "ARM_Cortex-A53", 6 },
+ { 0xd07, "Cortex-A57", "ARM_Cortex-A57", 6 },
+ { 0xd0f, "AArch64", "ARM_AArch64", 6 },
+};
+
+static const char OLD_PMU_PREFIX[] = "ARMv7 Cortex-";
+static const char NEW_PMU_PREFIX[] = "ARMv7_Cortex_";
+
+class PerfCounter {
+public:
+ PerfCounter(PerfCounter *next, const char *name, uint32_t type, uint64_t config) : mNext(next), mName(name), mType(type), mCount(0), mKey(getEventKey()), mConfig(config), mEnabled(false) {}
+ ~PerfCounter() {
+ delete [] mName;
+ }
+
+ PerfCounter *getNext() const { return mNext; }
+ const char *getName() const { return mName; }
+ uint32_t getType() const { return mType; }
+ int getCount() const { return mCount; }
+ void setCount(const int count) { mCount = count; }
+ int getKey() const { return mKey; }
+ uint64_t getConfig() const { return mConfig; }
+ void setConfig(const uint64_t config) { mConfig = config; }
+ bool isEnabled() const { return mEnabled; }
+ void setEnabled(const bool enabled) { mEnabled = enabled; }
+
+private:
+ PerfCounter *const mNext;
+ const char *const mName;
+ const uint32_t mType;
+ int mCount;
+ const int mKey;
+ uint64_t mConfig;
+ bool mEnabled;
+};
+
+PerfDriver::PerfDriver() : mCounters(NULL), mIsSetup(false) {
+}
+
+PerfDriver::~PerfDriver() {
+ while (mCounters != NULL) {
+ PerfCounter *counter = mCounters;
+ mCounters = counter->getNext();
+ delete counter;
+ }
+}
+
+void PerfDriver::addCpuCounters(const char *const counterName, const int type, const int numCounters) {
+ int len = snprintf(NULL, 0, "%s_ccnt", counterName) + 1;
+ char *name = new char[len];
+ snprintf(name, len, "%s_ccnt", counterName);
+ mCounters = new PerfCounter(mCounters, name, type, -1);
+
+ for (int j = 0; j < numCounters; ++j) {
+ len = snprintf(NULL, 0, "%s_cnt%d", counterName, j) + 1;
+ name = new char[len];
+ snprintf(name, len, "%s_cnt%d", counterName, j);
+ mCounters = new PerfCounter(mCounters, name, type, -1);
+ }
+}
+
+// From include/generated/uapi/linux/version.h
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+bool PerfDriver::setup() {
+ // Check the kernel version
+ struct utsname utsname;
+ if (uname(&utsname) != 0) {
+ logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ int release[3] = { 0, 0, 0 };
+ int part = 0;
+ char *ch = utsname.release;
+ while (*ch >= '0' && *ch <= '9' && part < ARRAY_LENGTH(release)) {
+ release[part] = 10*release[part] + *ch - '0';
+
+ ++ch;
+ if (*ch == '.') {
+ ++part;
+ ++ch;
+ }
+ }
+
+ if (KERNEL_VERSION(release[0], release[1], release[2]) < KERNEL_VERSION(3, 12, 0)) {
+ logg->logMessage("%s(%s:%i): Unsupported kernel version", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ // Add supported PMUs
+ bool foundCpu = false;
+ DIR *dir = opendir(PERF_DEVICES);
+ if (dir == NULL) {
+ logg->logMessage("%s(%s:%i): opendif failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(dir)) != NULL) {
+ for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
+ // Do the names match exactly?
+ if (strcmp(dirent->d_name, gator_cpus[i].pmnc_name) != 0 &&
+ // Do these names match but have the old vs new prefix?
+ (strncmp(dirent->d_name, OLD_PMU_PREFIX, sizeof(OLD_PMU_PREFIX) - 1) != 0 ||
+ strncmp(gator_cpus[i].pmnc_name, NEW_PMU_PREFIX, sizeof(NEW_PMU_PREFIX) - 1) != 0 ||
+ strcmp(dirent->d_name + sizeof(OLD_PMU_PREFIX) - 1, gator_cpus[i].pmnc_name + sizeof(NEW_PMU_PREFIX) - 1) != 0)) {
+ continue;
+ }
+
+ int type;
+ char buf[256];
+ snprintf(buf, sizeof(buf), PERF_DEVICES "/%s/type", dirent->d_name);
+ if (DriverSource::readIntDriver(buf, &type) != 0) {
+ continue;
+ }
+
+ foundCpu = true;
+ addCpuCounters(gator_cpus[i].pmnc_name, type, gator_cpus[i].pmnc_counters);
+ }
+ }
+ closedir(dir);
+
+ if (!foundCpu) {
+ // If no cpu was found based on pmu names, try by cpuid
+ for (int i = 0; i < ARRAY_LENGTH(gator_cpus); ++i) {
+ if (gSessionData->mMaxCpuId != gator_cpus[i].cpuid) {
+ continue;
+ }
+
+ foundCpu = true;
+ addCpuCounters(gator_cpus[i].pmnc_name, PERF_TYPE_RAW, gator_cpus[i].pmnc_counters);
+ }
+ }
+
+ /*
+ if (!foundCpu) {
+ // If all else fails, use the perf architected counters
+ // 9 because that's how many are in events-Perf-Hardware.xml - assume they can all be enabled at once
+ addCpuCounters("Perf_Hardware", PERF_TYPE_HARDWARE, 9);
+ }
+ */
+
+ // Add supported software counters
+ long long id;
+ DynBuf printb;
+
+ id = getTracepointId("irq/softirq_exit", &printb);
+ if (id >= 0) {
+ mCounters = new PerfCounter(mCounters, "Linux_irq_softirq", PERF_TYPE_TRACEPOINT, id);
+ }
+
+ id = getTracepointId("irq/irq_handler_exit", &printb);
+ if (id >= 0) {
+ mCounters = new PerfCounter(mCounters, "Linux_irq_irq", PERF_TYPE_TRACEPOINT, id);
+ }
+
+ //Linux_block_rq_wr
+ //Linux_block_rq_rd
+ //Linux_net_rx
+ //Linux_net_tx
+
+ id = getTracepointId(SCHED_SWITCH, &printb);
+ if (id >= 0) {
+ mCounters = new PerfCounter(mCounters, "Linux_sched_switch", PERF_TYPE_TRACEPOINT, id);
+ }
+
+ //Linux_meminfo_memused
+ //Linux_meminfo_memfree
+ //Linux_meminfo_bufferram
+ //Linux_power_cpu_freq
+ //Linux_power_cpu_idle
+
+ mCounters = new PerfCounter(mCounters, "Linux_cpu_wait_contention", TYPE_DERIVED, -1);
+
+ //Linux_cpu_wait_io
+
+ mIsSetup = true;
+ return true;
+}
+
+bool PerfDriver::summary(Buffer *const buffer) {
+ struct utsname utsname;
+ if (uname(&utsname) != 0) {
+ logg->logMessage("%s(%s:%i): uname failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%s %s %s %s %s GNU/Linux", utsname.sysname, utsname.nodename, utsname.release, utsname.version, utsname.machine);
+
+ struct timespec ts;
+ if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
+ logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ const int64_t timestamp = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+ logg->logMessage("%s(%s:%i): clock_gettime failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ const int64_t uptime = (int64_t)ts.tv_sec * 1000000000L + ts.tv_nsec;
+
+ buffer->summary(timestamp, uptime, 0, buf);
+
+ for (int i = 0; i < gSessionData->mCores; ++i) {
+ int j;
+ for (j = 0; j < ARRAY_LENGTH(gator_cpus); ++j) {
+ if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) {
+ break;
+ }
+ }
+ if (gator_cpus[j].cpuid == gSessionData->mCpuIds[i]) {
+ buffer->coreName(i, gSessionData->mCpuIds[i], gator_cpus[j].core_name);
+ } else {
+ snprintf(buf, sizeof(buf), "Unknown (0x%.3x)", gSessionData->mCpuIds[i]);
+ buffer->coreName(i, gSessionData->mCpuIds[i], buf);
+ }
+ }
+ buffer->commit(1);
+
+ return true;
+}
+
+PerfCounter *PerfDriver::findCounter(const Counter &counter) const {
+ for (PerfCounter * perfCounter = mCounters; perfCounter != NULL; perfCounter = perfCounter->getNext()) {
+ if (strcmp(perfCounter->getName(), counter.getType()) == 0) {
+ return perfCounter;
+ }
+ }
+
+ return NULL;
+}
+
+bool PerfDriver::claimCounter(const Counter &counter) const {
+ return findCounter(counter) != NULL;
+}
+
+void PerfDriver::resetCounters() {
+ for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ counter->setEnabled(false);
+ }
+}
+
+void PerfDriver::setupCounter(Counter &counter) {
+ PerfCounter *const perfCounter = findCounter(counter);
+ if (perfCounter == NULL) {
+ counter.setEnabled(false);
+ return;
+ }
+
+ // Don't use the config from counters XML if it's not set, ex: software counters
+ if (counter.getEvent() != -1) {
+ perfCounter->setConfig(counter.getEvent());
+ }
+ perfCounter->setCount(counter.getCount());
+ perfCounter->setEnabled(true);
+ counter.setKey(perfCounter->getKey());
+}
+
+int PerfDriver::writeCounters(mxml_node_t *root) const {
+ int count = 0;
+ for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ mxml_node_t *node = mxmlNewElement(root, "counter");
+ mxmlElementSetAttr(node, "name", counter->getName());
+ ++count;
+ }
+
+ return count;
+}
+
+bool PerfDriver::enable(PerfGroup *group, Buffer *const buffer) const {
+ for (PerfCounter * counter = mCounters; counter != NULL; counter = counter->getNext()) {
+ if (counter->isEnabled() && (counter->getType() != TYPE_DERIVED)) {
+ if (!group->add(buffer, counter->getKey(), counter->getType(), counter->getConfig(), counter->getCount(), 0, 0)) {
+ logg->logMessage("%s(%s:%i): PerfGroup::add failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+long long PerfDriver::getTracepointId(const char *const name, DynBuf *const printb) {
+ if (!printb->printf(EVENTS_PATH "/%s/id", name)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return -1;
+ }
+
+ int64_t result;
+ if (DriverSource::readInt64Driver(printb->getBuf(), &result) != 0) {
+ logg->logMessage("%s(%s:%i): DriverSource::readInt64Driver failed", __FUNCTION__, __FILE__, __LINE__);
+ return -1;
+ }
+
+ return result;
+}
diff --git a/daemon/PerfDriver.h b/daemon/PerfDriver.h
new file mode 100644
index 0000000..3181b74
--- /dev/null
+++ b/daemon/PerfDriver.h
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PERFDRIVER_H
+#define PERFDRIVER_H
+
+#include "Driver.h"
+
+// If debugfs is not mounted at /sys/kernel/debug, update DEBUGFS_PATH
+#define DEBUGFS_PATH "/sys/kernel/debug"
+#define EVENTS_PATH DEBUGFS_PATH "/tracing/events"
+
+#define SCHED_SWITCH "sched/sched_switch"
+
+class Buffer;
+class DynBuf;
+class PerfCounter;
+class PerfGroup;
+
+class PerfDriver : public Driver {
+public:
+ PerfDriver();
+ ~PerfDriver();
+
+ bool setup();
+ bool summary(Buffer *const buffer);
+ bool isSetup() const { return mIsSetup; }
+
+ bool claimCounter(const Counter &counter) const;
+ void resetCounters();
+ void setupCounter(Counter &counter);
+
+ int writeCounters(mxml_node_t *root) const;
+
+ bool enable(PerfGroup *group, Buffer *const buffer) const;
+
+ static long long getTracepointId(const char *const name, DynBuf *const printb);
+
+private:
+ PerfCounter *findCounter(const Counter &counter) const;
+ void addCpuCounters(const char *const counterName, const int type, const int numCounters);
+
+ PerfCounter *mCounters;
+ bool mIsSetup;
+
+ // Intentionally undefined
+ PerfDriver(const PerfDriver &);
+ PerfDriver &operator=(const PerfDriver &);
+};
+
+#endif // PERFDRIVER_H
diff --git a/daemon/PerfGroup.cpp b/daemon/PerfGroup.cpp
new file mode 100644
index 0000000..faf5fca
--- /dev/null
+++ b/daemon/PerfGroup.cpp
@@ -0,0 +1,206 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "PerfGroup.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "Buffer.h"
+#include "Logging.h"
+#include "Monitor.h"
+#include "PerfBuffer.h"
+#include "SessionData.h"
+
+#define DEFAULT_PEA_ARGS(pea, additionalSampleType) \
+ pea.size = sizeof(pea); \
+ /* Emit time, read_format below, group leader id, and raw tracepoint info */ \
+ pea.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_READ | PERF_SAMPLE_IDENTIFIER | additionalSampleType; \
+ /* Emit emit value in group format */ \
+ pea.read_format = PERF_FORMAT_ID | PERF_FORMAT_GROUP; \
+ /* start out disabled */ \
+ pea.disabled = 1; \
+ /* have a sampling interrupt happen when we cross the wakeup_watermark boundary */ \
+ pea.watermark = 1; \
+ /* Be conservative in flush size as only one buffer set is monitored */ \
+ pea.wakeup_watermark = 3 * BUF_SIZE / 4
+
+static int sys_perf_event_open(struct perf_event_attr *const attr, const pid_t pid, const int cpu, const int group_fd, const unsigned long flags) {
+ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+PerfGroup::PerfGroup(PerfBuffer *const pb) : mPb(pb) {
+ memset(&mAttrs, 0, sizeof(mAttrs));
+ memset(&mKeys, -1, sizeof(mKeys));
+ memset(&mFds, -1, sizeof(mFds));
+}
+
+PerfGroup::~PerfGroup() {
+ for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
+ if (mFds[pos] >= 0) {
+ close(mFds[pos]);
+ }
+ }
+}
+
+bool PerfGroup::add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags) {
+ int i;
+ for (i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ if (mKeys[i] < 0) {
+ break;
+ }
+ }
+
+ if (i >= ARRAY_LENGTH(mKeys)) {
+ logg->logMessage("%s(%s:%i): Too many counters", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ DEFAULT_PEA_ARGS(mAttrs[i], sampleType);
+ mAttrs[i].type = type;
+ mAttrs[i].config = config;
+ mAttrs[i].sample_period = sample;
+ // always be on the CPU but only a group leader can be pinned
+ mAttrs[i].pinned = (i == 0 ? 1 : 0);
+ mAttrs[i].mmap = (flags & PERF_GROUP_MMAP ? 1 : 0);
+ mAttrs[i].comm = (flags & PERF_GROUP_COMM ? 1 : 0);
+ mAttrs[i].freq = (flags & PERF_GROUP_FREQ ? 1 : 0);
+ mAttrs[i].task = (flags & PERF_GROUP_TASK ? 1 : 0);
+ mAttrs[i].sample_id_all = (flags & PERF_GROUP_SAMPLE_ID_ALL ? 1 : 0);
+
+ mKeys[i] = key;
+
+ buffer->pea(&mAttrs[i], key);
+
+ return true;
+}
+
+bool PerfGroup::prepareCPU(const int cpu) {
+ logg->logMessage("%s(%s:%i): Onlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ if (mKeys[i] < 0) {
+ continue;
+ }
+
+ const int offset = i * gSessionData->mCores;
+ if (mFds[cpu + offset] >= 0) {
+ logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ logg->logMessage("%s(%s:%i): perf_event_open cpu: %i type: %lli config: %lli sample: %lli sample_type: %lli", __FUNCTION__, __FILE__, __LINE__, cpu, (long long)mAttrs[i].type, (long long)mAttrs[i].config, (long long)mAttrs[i].sample_period, (long long)mAttrs[i].sample_type);
+ mFds[cpu + offset] = sys_perf_event_open(&mAttrs[i], -1, cpu, i == 0 ? -1 : mFds[cpu], i == 0 ? 0 : PERF_FLAG_FD_OUTPUT);
+ if (mFds[cpu + offset] < 0) {
+ logg->logMessage("%s(%s:%i): failed %s", __FUNCTION__, __FILE__, __LINE__, strerror(errno));
+ continue;
+ }
+
+ if (!mPb->useFd(cpu, mFds[cpu + offset], mFds[cpu])) {
+ logg->logMessage("%s(%s:%i): PerfBuffer::useFd failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int PerfGroup::onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor) {
+ __u64 ids[ARRAY_LENGTH(mKeys)];
+ int coreKeys[ARRAY_LENGTH(mKeys)];
+ int idCount = 0;
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ const int fd = mFds[cpu + i * gSessionData->mCores];
+ if (fd < 0) {
+ continue;
+ }
+
+ coreKeys[idCount] = mKeys[i];
+ if (ioctl(fd, PERF_EVENT_IOC_ID, &ids[idCount]) != 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ ++idCount;
+ }
+
+ if (!monitor->add(mFds[cpu])) {
+ logg->logMessage("%s(%s:%i): Monitor::add failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ buffer->keys(idCount, ids, coreKeys);
+
+ if (start) {
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ int offset = i * gSessionData->mCores + cpu;
+ if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_ENABLE) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ }
+
+ return idCount;
+}
+
+bool PerfGroup::offlineCPU(const int cpu) {
+ logg->logMessage("%s(%s:%i): Offlining cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ int offset = i * gSessionData->mCores + cpu;
+ if (mFds[offset] >= 0 && ioctl(mFds[offset], PERF_EVENT_IOC_DISABLE) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+
+ // Mark the buffer so that it will be released next time it's read
+ mPb->discard(cpu);
+
+ for (int i = 0; i < ARRAY_LENGTH(mKeys); ++i) {
+ if (mKeys[i] < 0) {
+ continue;
+ }
+
+ int offset = i * gSessionData->mCores + cpu;
+ if (mFds[offset] >= 0) {
+ close(mFds[offset]);
+ mFds[offset] = -1;
+ }
+ }
+
+ return true;
+}
+
+bool PerfGroup::start() {
+ for (int pos = 0; pos < ARRAY_LENGTH(mFds); ++pos) {
+ if (mFds[pos] >= 0 && ioctl(mFds[pos], PERF_EVENT_IOC_ENABLE) < 0) {
+ logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ }
+
+ return true;
+
+ fail:
+ stop();
+
+ return false;
+}
+
+void PerfGroup::stop() {
+ for (int pos = ARRAY_LENGTH(mFds) - 1; pos >= 0; --pos) {
+ if (mFds[pos] >= 0) {
+ ioctl(mFds[pos], PERF_EVENT_IOC_DISABLE);
+ }
+ }
+}
diff --git a/daemon/PerfGroup.h b/daemon/PerfGroup.h
new file mode 100644
index 0000000..af496d4
--- /dev/null
+++ b/daemon/PerfGroup.h
@@ -0,0 +1,55 @@
+ /**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PERF_GROUP
+#define PERF_GROUP
+
+// Use a snapshot of perf_event.h as it may be more recent than what is on the target and if not newer features won't be supported anyways
+#include "k/perf_event.h"
+
+#include "Config.h"
+
+class Buffer;
+class Monitor;
+class PerfBuffer;
+
+enum PerfGroupFlags {
+ PERF_GROUP_MMAP = 1 << 0,
+ PERF_GROUP_COMM = 1 << 1,
+ PERF_GROUP_FREQ = 1 << 2,
+ PERF_GROUP_TASK = 1 << 3,
+ PERF_GROUP_SAMPLE_ID_ALL = 1 << 4,
+};
+
+class PerfGroup {
+public:
+ PerfGroup(PerfBuffer *const pb);
+ ~PerfGroup();
+
+ bool add(Buffer *const buffer, const int key, const __u32 type, const __u64 config, const __u64 sample, const __u64 sampleType, const int flags);
+ // Safe to call concurrently
+ bool prepareCPU(const int cpu);
+ // Not safe to call concurrently. Returns the number of events enabled
+ int onlineCPU(const int cpu, const bool start, Buffer *const buffer, Monitor *const monitor);
+ bool offlineCPU(int cpu);
+ bool start();
+ void stop();
+
+private:
+ // +1 for the group leader
+ struct perf_event_attr mAttrs[MAX_PERFORMANCE_COUNTERS + 1];
+ int mKeys[MAX_PERFORMANCE_COUNTERS + 1];
+ int mFds[NR_CPUS * (MAX_PERFORMANCE_COUNTERS + 1)];
+ PerfBuffer *const mPb;
+
+ // Intentionally undefined
+ PerfGroup(const PerfGroup &);
+ PerfGroup &operator=(const PerfGroup &);
+};
+
+#endif // PERF_GROUP
diff --git a/daemon/PerfSource.cpp b/daemon/PerfSource.cpp
new file mode 100644
index 0000000..1f1cb19
--- /dev/null
+++ b/daemon/PerfSource.cpp
@@ -0,0 +1,271 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "PerfSource.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "Child.h"
+#include "DynBuf.h"
+#include "Logging.h"
+#include "PerfDriver.h"
+#include "Proc.h"
+#include "SessionData.h"
+
+#define MS_PER_US 1000000
+
+extern Child *child;
+
+static bool sendTracepointFormat(Buffer *const buffer, const char *const name, DynBuf *const printb, DynBuf *const b) {
+ if (!printb->printf(EVENTS_PATH "/%s/format", name)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ if (!b->read(printb->getBuf())) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ buffer->format(b->getLength(), b->getBuf());
+
+ return true;
+}
+
+PerfSource::PerfSource(sem_t *senderSem, sem_t *startProfile) : mSummary(0, FRAME_SUMMARY, 1024, senderSem), mBuffer(0, FRAME_PERF_ATTRS, 1024*1024, senderSem), mCountersBuf(), mCountersGroup(&mCountersBuf), mMonitor(), mUEvent(), mSenderSem(senderSem), mStartProfile(startProfile), mInterruptFd(-1), mIsDone(false) {
+ long l = sysconf(_SC_PAGE_SIZE);
+ if (l < 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to obtain the page size");
+ handleException();
+ }
+ gSessionData->mPageSize = static_cast<int>(l);
+
+ l = sysconf(_SC_NPROCESSORS_CONF);
+ if (l < 0) {
+ logg->logError(__FILE__, __LINE__, "Unable to obtain the number of cores");
+ handleException();
+ }
+ gSessionData->mCores = static_cast<int>(l);
+}
+
+PerfSource::~PerfSource() {
+}
+
+struct PrepareParallelArgs {
+ PerfGroup *pg;
+ int cpu;
+};
+
+void *prepareParallel(void *arg) {
+ const PrepareParallelArgs *const args = (PrepareParallelArgs *)arg;
+ args->pg->prepareCPU(args->cpu);
+ return NULL;
+}
+
+bool PerfSource::prepare() {
+ DynBuf printb;
+ DynBuf b1;
+ DynBuf b2;
+ DynBuf b3;
+ long long schedSwitchId;
+
+ if (0
+ || !mMonitor.init()
+ || !mUEvent.init()
+ || !mMonitor.add(mUEvent.getFd())
+
+ || (schedSwitchId = PerfDriver::getTracepointId(SCHED_SWITCH, &printb)) < 0
+ || !sendTracepointFormat(&mBuffer, SCHED_SWITCH, &printb, &b1)
+
+ // Only want RAW but not IP on sched_switch and don't want TID on SAMPLE_ID
+ || !mCountersGroup.add(&mBuffer, 100/**/, PERF_TYPE_TRACEPOINT, schedSwitchId, 1, PERF_SAMPLE_RAW, PERF_GROUP_MMAP | PERF_GROUP_COMM | PERF_GROUP_TASK | PERF_GROUP_SAMPLE_ID_ALL)
+
+ // Only want TID and IP but not RAW on timer
+ || (gSessionData->mSampleRate > 0 && !gSessionData->mIsEBS && !mCountersGroup.add(&mBuffer, 99/**/, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, 1000000000UL / gSessionData->mSampleRate, PERF_SAMPLE_TID | PERF_SAMPLE_IP, 0))
+
+ || !gSessionData->perf.enable(&mCountersGroup, &mBuffer)
+ || 0) {
+ logg->logMessage("%s(%s:%i): perf setup failed, are you running Linux 3.12 or later?", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!gSessionData->perf.summary(&mSummary)) {
+ logg->logMessage("%s(%s:%i): PerfDriver::summary failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ {
+ // Run prepareCPU in parallel as perf_event_open can take more than 1 sec in some cases
+ pthread_t threads[NR_CPUS];
+ PrepareParallelArgs args[NR_CPUS];
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ args[cpu].pg = &mCountersGroup;
+ args[cpu].cpu = cpu;
+ if (pthread_create(&threads[cpu], NULL, prepareParallel, &args[cpu]) != 0) {
+ logg->logMessage("%s(%s:%i): pthread_create failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ if (pthread_join(threads[cpu], NULL) != 0) {
+ logg->logMessage("%s(%s:%i): pthread_join failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ }
+
+ int numEvents = 0;
+ for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
+ numEvents += mCountersGroup.onlineCPU(cpu, false, &mBuffer, &mMonitor);
+ }
+ if (numEvents <= 0) {
+ logg->logMessage("%s(%s:%i): PerfGroup::onlineCPU failed on all cores", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ // Start events before reading proc to avoid race conditions
+ if (!mCountersGroup.start()) {
+ logg->logMessage("%s(%s:%i): PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (!readProc(&mBuffer, &printb, &b1, &b2, &b3)) {
+ logg->logMessage("%s(%s:%i): readProc failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ mBuffer.commit(1);
+
+ return true;
+}
+
+static const char CPU_DEVPATH[] = "/devices/system/cpu/cpu";
+
+void PerfSource::run() {
+ int pipefd[2];
+
+ if (pipe(pipefd) != 0) {
+ logg->logError(__FILE__, __LINE__, "pipe failed");
+ handleException();
+ }
+ mInterruptFd = pipefd[1];
+
+ if (!mMonitor.add(pipefd[0])) {
+ logg->logError(__FILE__, __LINE__, "Monitor::add failed");
+ handleException();
+ }
+
+ int timeout = -1;
+ if (gSessionData->mLiveRate > 0) {
+ timeout = gSessionData->mLiveRate/MS_PER_US;
+ }
+
+ sem_post(mStartProfile);
+
+ while (gSessionData->mSessionIsActive) {
+ // +1 for uevents, +1 for pipe
+ struct epoll_event events[NR_CPUS + 2];
+ int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout);
+ if (ready < 0) {
+ logg->logError(__FILE__, __LINE__, "Monitor::wait failed");
+ handleException();
+ }
+
+ for (int i = 0; i < ready; ++i) {
+ if (events[i].data.fd == mUEvent.getFd()) {
+ if (!handleUEvent()) {
+ logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed");
+ handleException();
+ }
+ break;
+ }
+ }
+
+ // send a notification that data is ready
+ sem_post(mSenderSem);
+
+ // In one shot mode, stop collection once all the buffers are filled
+ // Assume timeout == 0 in this case
+ if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
+ logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__);
+ child->endSession();
+ }
+ }
+
+ mCountersGroup.stop();
+ mBuffer.setDone();
+ mIsDone = true;
+
+ // send a notification that data is ready
+ sem_post(mSenderSem);
+
+ mInterruptFd = -1;
+ close(pipefd[0]);
+ close(pipefd[1]);
+}
+
+bool PerfSource::handleUEvent() {
+ UEventResult result;
+ if (!mUEvent.read(&result)) {
+ logg->logMessage("%s(%s:%i): UEvent::Read failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ if (strcmp(result.mSubsystem, "cpu") == 0) {
+ if (strncmp(result.mDevPath, CPU_DEVPATH, sizeof(CPU_DEVPATH) - 1) != 0) {
+ logg->logMessage("%s(%s:%i): Unexpected cpu DEVPATH format", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ char *endptr;
+ errno = 0;
+ int cpu = strtol(result.mDevPath + sizeof(CPU_DEVPATH) - 1, &endptr, 10);
+ if (errno != 0 || *endptr != '\0') {
+ logg->logMessage("%s(%s:%i): strtol failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ if (strcmp(result.mAction, "online") == 0) {
+ // Only call onlineCPU if prepareCPU succeeded
+ const bool result = mCountersGroup.prepareCPU(cpu) &&
+ mCountersGroup.onlineCPU(cpu, true, &mBuffer, &mMonitor);
+ mBuffer.commit(1);
+ return result;
+ } else if (strcmp(result.mAction, "offline") == 0) {
+ return mCountersGroup.offlineCPU(cpu);
+ }
+ }
+
+ return true;
+}
+
+void PerfSource::interrupt() {
+ if (mInterruptFd >= 0) {
+ int8_t c = 0;
+ // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
+ if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
+ logg->logError(__FILE__, __LINE__, "write failed");
+ handleException();
+ }
+ }
+}
+
+bool PerfSource::isDone () {
+ return mBuffer.isDone() && mIsDone && mCountersBuf.isEmpty();
+}
+
+void PerfSource::write (Sender *sender) {
+ if (!mSummary.isDone()) {
+ mSummary.write(sender);
+ }
+ if (!mBuffer.isDone()) {
+ mBuffer.write(sender);
+ }
+ if (!mCountersBuf.send(sender)) {
+ logg->logError(__FILE__, __LINE__, "PerfBuffer::send failed");
+ handleException();
+ }
+}
diff --git a/daemon/PerfSource.h b/daemon/PerfSource.h
new file mode 100644
index 0000000..3f471c8
--- /dev/null
+++ b/daemon/PerfSource.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PERFSOURCE_H
+#define PERFSOURCE_H
+
+#include <semaphore.h>
+
+#include "Buffer.h"
+#include "Monitor.h"
+#include "PerfBuffer.h"
+#include "PerfGroup.h"
+#include "Source.h"
+#include "UEvent.h"
+
+class Sender;
+
+class PerfSource : public Source {
+public:
+ PerfSource(sem_t *senderSem, sem_t *startProfile);
+ ~PerfSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+private:
+ bool handleUEvent();
+
+ Buffer mSummary;
+ Buffer mBuffer;
+ PerfBuffer mCountersBuf;
+ PerfGroup mCountersGroup;
+ Monitor mMonitor;
+ UEvent mUEvent;
+ sem_t *const mSenderSem;
+ sem_t *const mStartProfile;
+ int mInterruptFd;
+ bool mIsDone;
+
+ // Intentionally undefined
+ PerfSource(const PerfSource &);
+ PerfSource &operator=(const PerfSource &);
+};
+
+#endif // PERFSOURCE_H
diff --git a/daemon/Proc.cpp b/daemon/Proc.cpp
new file mode 100644
index 0000000..e0b9e22
--- /dev/null
+++ b/daemon/Proc.cpp
@@ -0,0 +1,179 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Proc.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Buffer.h"
+#include "DynBuf.h"
+#include "Logging.h"
+
+struct ProcStat {
+ // From linux-dev/include/linux/sched.h
+#define TASK_COMM_LEN 16
+ // TASK_COMM_LEN may grow, so be ready for it to get larger
+ char comm[2*TASK_COMM_LEN];
+ long numThreads;
+};
+
+static bool readProcStat(ProcStat *const ps, const char *const pathname, DynBuf *const b) {
+ if (!b->read(pathname)) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the thread exited", __FUNCTION__, __FILE__, __LINE__);
+ // This is not a fatal error - the thread just doesn't exist any more
+ return true;
+ }
+
+ char *comm = strchr(b->getBuf(), '(');
+ if (comm == NULL) {
+ logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ ++comm;
+ char *const str = strrchr(comm, ')');
+ if (str == NULL) {
+ logg->logMessage("%s(%s:%i): parsing stat failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+ *str = '\0';
+ strncpy(ps->comm, comm, sizeof(ps->comm) - 1);
+ ps->comm[sizeof(ps->comm) - 1] = '\0';
+
+ const int count = sscanf(str + 2, " %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %ld", &ps->numThreads);
+ if (count != 1) {
+ logg->logMessage("%s(%s:%i): sscanf failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+static bool readProcTask(Buffer *const buffer, const int pid, const char *const image, DynBuf *const printb, DynBuf *const b) {
+ bool result = false;
+
+ if (!b->printf("/proc/%i/task", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ return result;
+ }
+ DIR *task = opendir(b->getBuf());
+ if (task == NULL) {
+ logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
+ return result;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(task)) != NULL) {
+ char *endptr;
+ const int tid = strtol(dirent->d_name, &endptr, 10);
+ if (*endptr != '\0') {
+ // Ignore task items that are not integers like ., etc...
+ continue;
+ }
+
+ if (!printb->printf("/proc/%i/task/%i/stat", pid, tid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ ProcStat ps;
+ if (!readProcStat(&ps, printb->getBuf(), b)) {
+ logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ buffer->comm(pid, tid, image, ps.comm);
+ }
+
+ result = true;
+
+ fail:
+ closedir(task);
+
+ return result;
+}
+
+bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3) {
+ bool result = false;
+
+ DIR *proc = opendir("/proc");
+ if (proc == NULL) {
+ logg->logMessage("%s(%s:%i): opendir failed", __FUNCTION__, __FILE__, __LINE__);
+ return result;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(proc)) != NULL) {
+ char *endptr;
+ const int pid = strtol(dirent->d_name, &endptr, 10);
+ if (*endptr != '\0') {
+ // Ignore proc items that are not integers like ., cpuinfo, etc...
+ continue;
+ }
+
+ if (!printb->printf("/proc/%i/stat", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ ProcStat ps;
+ if (!readProcStat(&ps, printb->getBuf(), b1)) {
+ logg->logMessage("%s(%s:%i): readProcStat failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ if (!printb->printf("/proc/%i/exe", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ const int err = b1->readlink(printb->getBuf());
+ const char *image;
+ if (err == 0) {
+ image = strrchr(b1->getBuf(), '/');
+ if (image == NULL) {
+ image = b1->getBuf();
+ } else {
+ ++image;
+ }
+ } else if (err == -ENOENT) {
+ // readlink /proc/[pid]/exe returns ENOENT for kernel threads
+ image = "\0";
+ } else {
+ logg->logMessage("%s(%s:%i): DynBuf::readlink failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+
+ if (!printb->printf("/proc/%i/maps", pid)) {
+ logg->logMessage("%s(%s:%i): DynBuf::printf failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ if (!b2->read(printb->getBuf())) {
+ logg->logMessage("%s(%s:%i): DynBuf::read failed, likely because the process exited", __FUNCTION__, __FILE__, __LINE__);
+ // This is not a fatal error - the process just doesn't exist any more
+ continue;
+ }
+
+ buffer->maps(pid, pid, b2->getBuf());
+ if (ps.numThreads <= 1) {
+ buffer->comm(pid, pid, image, ps.comm);
+ } else {
+ if (!readProcTask(buffer, pid, image, printb, b3)) {
+ logg->logMessage("%s(%s:%i): readProcTask failed", __FUNCTION__, __FILE__, __LINE__);
+ goto fail;
+ }
+ }
+ }
+
+ result = true;
+
+ fail:
+ closedir(proc);
+
+ return result;
+}
diff --git a/daemon/Proc.h b/daemon/Proc.h
new file mode 100644
index 0000000..057b610
--- /dev/null
+++ b/daemon/Proc.h
@@ -0,0 +1,17 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PROC_H
+#define PROC_H
+
+class Buffer;
+class DynBuf;
+
+bool readProc(Buffer *const buffer, DynBuf *const printb, DynBuf *const b1, DynBuf *const b2, DynBuf *const b3);
+
+#endif // PROC_H
diff --git a/daemon/Sender.cpp b/daemon/Sender.cpp
index 8eb348f..3a981a6 100644
--- a/daemon/Sender.cpp
+++ b/daemon/Sender.cpp
@@ -1,19 +1,18 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
+#include "Sender.h"
+
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include "Sender.h"
+
+#include "Buffer.h"
#include "Logging.h"
#include "OlySocket.h"
#include "SessionData.h"
@@ -49,9 +48,12 @@ Sender::Sender(OlySocket* socket) {
}
Sender::~Sender() {
- delete mDataSocket;
- mDataSocket = NULL;
- if (mDataFile) {
+ // Just close it as the client socket is on the stack
+ if (mDataSocket != NULL) {
+ mDataSocket->closeSocket();
+ mDataSocket = NULL;
+ }
+ if (mDataFile != NULL) {
fclose(mDataFile);
}
}
@@ -95,10 +97,7 @@ void Sender::writeData(const char* data, int length, int type) {
// type and length already added by the Collector for apc data
unsigned char header[5];
header[0] = type;
- header[1] = (length >> 0) & 0xff;
- header[2] = (length >> 8) & 0xff;
- header[3] = (length >> 16) & 0xff;
- header[4] = (length >> 24) & 0xff;
+ Buffer::writeLEInt(header + 1, length);
mDataSocket->send((char*)&header, sizeof(header));
}
@@ -106,7 +105,7 @@ void Sender::writeData(const char* data, int length, int type) {
const int chunkSize = 100*1000 * alarmDuration / 8;
int pos = 0;
while (true) {
- mDataSocket->send((char*)data + pos, min(length - pos, chunkSize));
+ mDataSocket->send((const char*)data + pos, min(length - pos, chunkSize));
pos += chunkSize;
if (pos >= length) {
break;
diff --git a/daemon/Sender.h b/daemon/Sender.h
index b388f03..4c359db 100644
--- a/daemon/Sender.h
+++ b/daemon/Sender.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/SessionData.cpp b/daemon/SessionData.cpp
index cf84407..c169299 100644
--- a/daemon/SessionData.cpp
+++ b/daemon/SessionData.cpp
@@ -1,13 +1,15 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <string.h>
#include "SessionData.h"
+
+#include <string.h>
+
#include "SessionXML.h"
#include "Logging.h"
@@ -38,6 +40,7 @@ void SessionData::initialize() {
mTotalBufferSize = 0;
// sysconf(_SC_NPROCESSORS_CONF) is unreliable on 2.6 Android, get the value from the kernel module
mCores = 1;
+ mPageSize = 0;
}
void SessionData::parseSessionXML(char* xmlString) {
@@ -88,7 +91,8 @@ void SessionData::parseSessionXML(char* xmlString) {
void SessionData::readCpuInfo() {
char temp[256]; // arbitrarily large amount
strcpy(mCoreName, "unknown");
- mCpuId = -1;
+ memset(&mCpuIds, -1, sizeof(mCpuIds));
+ mMaxCpuId = -1;
FILE* f = fopen("/proc/cpuinfo", "r");
if (f == NULL) {
@@ -98,15 +102,16 @@ void SessionData::readCpuInfo() {
}
bool foundCoreName = false;
- bool foundCpuId = false;
- while (fgets(temp, sizeof(temp), f) && (!foundCoreName || !foundCpuId)) {
+ int processor = 0;
+ while (fgets(temp, sizeof(temp), f)) {
if (strlen(temp) > 0) {
temp[strlen(temp) - 1] = 0; // Replace the line feed with a null
}
const bool foundHardware = strstr(temp, "Hardware") != 0;
const bool foundCPUPart = strstr(temp, "CPU part") != 0;
- if (foundHardware || foundCPUPart) {
+ const bool foundProcessor = strstr(temp, "processor") != 0;
+ if (foundHardware || foundCPUPart || foundProcessor) {
char* position = strchr(temp, ':');
if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) {
logg->logMessage("Unknown format of /proc/cpuinfo\n"
@@ -122,11 +127,15 @@ void SessionData::readCpuInfo() {
}
if (foundCPUPart) {
- int cpuId = strtol(position, NULL, 16);
- if (cpuId > mCpuId) {
- mCpuId = cpuId;
+ mCpuIds[processor] = strtol(position, NULL, 0);
+ // If this does not have the full topology in /proc/cpuinfo, mCpuIds[0] may not have the 1 CPU part emitted - this guarantees it's in mMaxCpuId
+ if (mCpuIds[processor] > mMaxCpuId) {
+ mMaxCpuId = mCpuIds[processor];
}
- foundCpuId = true;
+ }
+
+ if (foundProcessor) {
+ processor = strtol(position, NULL, 0);
}
}
}
diff --git a/daemon/SessionData.h b/daemon/SessionData.h
index c834251..ea34240 100644
--- a/daemon/SessionData.h
+++ b/daemon/SessionData.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -11,12 +11,12 @@
#include <stdint.h>
+#include "Config.h"
#include "Counter.h"
#include "Hwmon.h"
+#include "PerfDriver.h"
-#define MAX_PERFORMANCE_COUNTERS 50
-
-#define PROTOCOL_VERSION 17
+#define PROTOCOL_VERSION 18
#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions
struct ImageLinkList {
@@ -34,6 +34,7 @@ public:
void parseSessionXML(char* xmlString);
Hwmon hwmon;
+ PerfDriver perf;
char mCoreName[MAX_STRING_LEN];
struct ImageLinkList *mImages;
@@ -47,6 +48,7 @@ public:
bool mSessionIsActive;
bool mLocalCapture;
bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled
+ bool mIsEBS;
int mBacktraceDepth;
int mTotalBufferSize; // number of MB to use for the entire collection buffer
@@ -54,7 +56,9 @@ public:
int64_t mLiveRate;
int mDuration;
int mCores;
- int mCpuId;
+ int mPageSize;
+ int mCpuIds[NR_CPUS];
+ int mMaxCpuId;
// PMU Counters
int mCounterOverflow;
diff --git a/daemon/SessionXML.cpp b/daemon/SessionXML.cpp
index 0a0a027..55b2f92 100644
--- a/daemon/SessionXML.cpp
+++ b/daemon/SessionXML.cpp
@@ -1,15 +1,17 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include "SessionXML.h"
+
#include <string.h>
#include <stdlib.h>
#include <limits.h>
-#include "SessionXML.h"
+
#include "Logging.h"
#include "OlyUtility.h"
#include "SessionData.h"
@@ -25,7 +27,7 @@ static const char* ATTR_DURATION = "duration";
static const char* ATTR_PATH = "path";
static const char* ATTR_LIVE_RATE = "live_rate";
-SessionXML::SessionXML(const char* str) {
+SessionXML::SessionXML(const char *str) {
parameters.buffer_mode[0] = 0;
parameters.sample_rate[0] = 0;
parameters.duration = 0;
@@ -33,13 +35,13 @@ SessionXML::SessionXML(const char* str) {
parameters.live_rate = 0;
parameters.images = NULL;
mPath = 0;
- mSessionXML = (char*)str;
+ mSessionXML = (const char *)str;
logg->logMessage(mSessionXML);
}
SessionXML::~SessionXML() {
if (mPath != 0) {
- free(mSessionXML);
+ free((char *)mSessionXML);
}
}
diff --git a/daemon/SessionXML.h b/daemon/SessionXML.h
index 0fb03bd..e146094 100644
--- a/daemon/SessionXML.h
+++ b/daemon/SessionXML.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -24,13 +24,13 @@ struct ConfigParameters {
class SessionXML {
public:
- SessionXML(const char* str);
+ SessionXML(const char *str);
~SessionXML();
void parse();
ConfigParameters parameters;
private:
- char* mSessionXML;
- char* mPath;
+ const char *mSessionXML;
+ const char *mPath;
void sessionTag(mxml_node_t *tree, mxml_node_t *node);
void sessionImage(mxml_node_t *node);
diff --git a/daemon/Source.cpp b/daemon/Source.cpp
new file mode 100644
index 0000000..60cf704
--- /dev/null
+++ b/daemon/Source.cpp
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "Source.h"
+
+#include "Logging.h"
+
+Source::Source() : mThreadID() {
+}
+
+Source::~Source() {
+}
+
+void Source::start() {
+ if (pthread_create(&mThreadID, NULL, runStatic, this)) {
+ logg->logError(__FILE__, __LINE__, "Failed to create source thread");
+ handleException();
+ }
+}
+
+void Source::join() {
+ pthread_join(mThreadID, NULL);
+}
+
+void *Source::runStatic(void *arg) {
+ static_cast<Source *>(arg)->run();
+ return NULL;
+}
diff --git a/daemon/Source.h b/daemon/Source.h
new file mode 100644
index 0000000..56ac3d6
--- /dev/null
+++ b/daemon/Source.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SOURCE_H
+#define SOURCE_H
+
+#include <pthread.h>
+
+class Sender;
+
+class Source {
+public:
+ Source();
+ virtual ~Source();
+
+ virtual bool prepare() = 0;
+ void start();
+ virtual void run() = 0;
+ virtual void interrupt() = 0;
+ void join();
+
+ virtual bool isDone() = 0;
+ virtual void write(Sender *sender) = 0;
+
+private:
+ static void *runStatic(void *arg);
+
+ pthread_t mThreadID;
+
+ // Intentionally undefined
+ Source(const Source &);
+ Source &operator=(const Source &);
+};
+
+#endif // SOURCE_H
diff --git a/daemon/StreamlineSetup.cpp b/daemon/StreamlineSetup.cpp
index 2faada2..caa665e 100644
--- a/daemon/StreamlineSetup.cpp
+++ b/daemon/StreamlineSetup.cpp
@@ -1,26 +1,23 @@
/**
- * Copyright (C) ARM Limited 2011-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2011-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include "Sender.h"
-#include "Logging.h"
-#include "OlyUtility.h"
-#include "SessionData.h"
-#include "CapturedXML.h"
#include "StreamlineSetup.h"
+
+#include "Buffer.h"
+#include "CapturedXML.h"
#include "ConfigurationXML.h"
#include "Driver.h"
#include "EventsXML.h"
+#include "Logging.h"
+#include "OlySocket.h"
+#include "OlyUtility.h"
+#include "Sender.h"
+#include "SessionData.h"
static const char* TAG_SESSION = "session";
static const char* TAG_REQUEST = "request";
@@ -198,12 +195,9 @@ void StreamlineSetup::handleDeliver(char* xml) {
void StreamlineSetup::sendData(const char* data, uint32_t length, char type) {
unsigned char header[5];
header[0] = type;
- header[1] = (length >> 0) & 0xff;
- header[2] = (length >> 8) & 0xff;
- header[3] = (length >> 16) & 0xff;
- header[4] = (length >> 24) & 0xff;
+ Buffer::writeLEInt(header + 1, length);
mSocket->send((char*)&header, sizeof(header));
- mSocket->send((char*)data, length);
+ mSocket->send((const char*)data, length);
}
void StreamlineSetup::sendEvents() {
@@ -241,8 +235,14 @@ void StreamlineSetup::sendCounters() {
xml = mxmlNewXML("1.0");
counters = mxmlNewElement(xml, "counters");
+ int count = 0;
for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
- driver->writeCounters(counters);
+ count += driver->writeCounters(counters);
+ }
+
+ if (count == 0) {
+ logg->logError(__FILE__, __LINE__, "No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly");
+ handleException();
}
char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
diff --git a/daemon/StreamlineSetup.h b/daemon/StreamlineSetup.h
index d6d9a6e..74bb197 100644
--- a/daemon/StreamlineSetup.h
+++ b/daemon/StreamlineSetup.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -9,7 +9,10 @@
#ifndef __STREAMLINE_SETUP_H__
#define __STREAMLINE_SETUP_H__
-#include "OlySocket.h"
+#include <stdint.h>
+#include <string.h>
+
+class OlySocket;
// Commands from Streamline
enum {
diff --git a/daemon/UEvent.cpp b/daemon/UEvent.cpp
new file mode 100644
index 0000000..282e965
--- /dev/null
+++ b/daemon/UEvent.cpp
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "UEvent.h"
+
+#include <linux/netlink.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "Logging.h"
+
+static const char EMPTY[] = "";
+static const char ACTION[] = "ACTION=";
+static const char DEVPATH[] = "DEVPATH=";
+static const char SUBSYSTEM[] = "SUBSYSTEM=";
+
+UEvent::UEvent() : mFd(-1) {
+}
+
+UEvent::~UEvent() {
+ if (mFd >= 0) {
+ close(mFd);
+ }
+}
+
+bool UEvent::init() {
+ mFd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+ if (mFd < 0) {
+ logg->logMessage("%s(%s:%i): socket failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ struct sockaddr_nl sockaddr;
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.nl_family = AF_NETLINK;
+ sockaddr.nl_groups = 1; // bitmask: (1 << 0) == kernel events, (1 << 1) == udev events
+ sockaddr.nl_pid = 0;
+ if (bind(mFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) {
+ logg->logMessage("%s(%s:%i): bind failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ return true;
+}
+
+bool UEvent::read(UEventResult *const result) {
+ ssize_t bytes = recv(mFd, result->mBuf, sizeof(result->mBuf), 0);
+ if (bytes <= 0) {
+ logg->logMessage("%s(%s:%i): recv failed", __FUNCTION__, __FILE__, __LINE__);
+ return false;
+ }
+
+ result->mAction = EMPTY;
+ result->mDevPath = EMPTY;
+ result->mSubsystem = EMPTY;
+
+ for (int pos = 0; pos < bytes; pos += strlen(result->mBuf + pos) + 1) {
+ char *const str = result->mBuf + pos;
+ if (strncmp(str, ACTION, sizeof(ACTION) - 1) == 0) {
+ result->mAction = str + sizeof(ACTION) - 1;
+ } else if (strncmp(str, DEVPATH, sizeof(DEVPATH) - 1) == 0) {
+ result->mDevPath = str + sizeof(DEVPATH) - 1;
+ } else if (strncmp(str, SUBSYSTEM, sizeof(SUBSYSTEM) - 1) == 0) {
+ result->mSubsystem = str + sizeof(SUBSYSTEM) - 1;
+ }
+ }
+
+ return true;
+}
diff --git a/daemon/UEvent.h b/daemon/UEvent.h
new file mode 100644
index 0000000..2f7ef2c
--- /dev/null
+++ b/daemon/UEvent.h
@@ -0,0 +1,36 @@
+/**
+ * Copyright (C) ARM Limited 2013-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef UEVENT_H
+#define UEVENT_H
+
+struct UEventResult {
+ const char *mAction;
+ const char *mDevPath;
+ const char *mSubsystem;
+ char mBuf[1<<13];
+};
+
+class UEvent {
+public:
+ UEvent();
+ ~UEvent();
+
+ bool init();
+ bool read(UEventResult *const result);
+ int getFd() const { return mFd; }
+
+private:
+ int mFd;
+
+ // Intentionally undefined
+ UEvent(const UEvent &);
+ UEvent &operator=(const UEvent &);
+};
+
+#endif // UEVENT_H
diff --git a/daemon/UserSpaceSource.cpp b/daemon/UserSpaceSource.cpp
new file mode 100644
index 0000000..debe696
--- /dev/null
+++ b/daemon/UserSpaceSource.cpp
@@ -0,0 +1,97 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "UserSpaceSource.h"
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
+#include "Child.h"
+#include "DriverSource.h"
+#include "Logging.h"
+#include "SessionData.h"
+
+#define NS_PER_S ((uint64_t)1000000000)
+#define NS_PER_US 1000
+
+extern Child *child;
+
+UserSpaceSource::UserSpaceSource(sem_t *senderSem) : mBuffer(0, FRAME_BLOCK_COUNTER, gSessionData->mTotalBufferSize*1024*1024, senderSem) {
+}
+
+UserSpaceSource::~UserSpaceSource() {
+}
+
+bool UserSpaceSource::prepare() {
+ return true;
+}
+
+void UserSpaceSource::run() {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
+
+ gSessionData->hwmon.start();
+
+ int64_t monotonic_started = 0;
+ while (monotonic_started <= 0) {
+ usleep(10);
+
+ if (DriverSource::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
+ logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
+ handleException();
+ }
+ }
+
+ uint64_t next_time = 0;
+ while (gSessionData->mSessionIsActive) {
+ struct timespec ts;
+#ifndef CLOCK_MONOTONIC_RAW
+ // Android doesn't have this defined but it was added in Linux 2.6.28
+#define CLOCK_MONOTONIC_RAW 4
+#endif
+ if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
+ logg->logError(__FILE__, __LINE__, "Failed to get uptime");
+ handleException();
+ }
+ const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started;
+ // Sample ten times a second ignoring gSessionData->mSampleRate
+ next_time += NS_PER_S/10;//gSessionData->mSampleRate;
+ if (next_time < curr_time) {
+ logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
+ next_time = curr_time;
+ }
+
+ if (mBuffer.eventHeader(curr_time)) {
+ gSessionData->hwmon.read(&mBuffer);
+ // Only check after writing all counters so that time and corresponding counters appear in the same frame
+ mBuffer.check(curr_time);
+ }
+
+ if (mBuffer.bytesAvailable() <= 0) {
+ logg->logMessage("One shot (counters)");
+ child->endSession();
+ }
+
+ usleep((next_time - curr_time)/NS_PER_US);
+ }
+
+ mBuffer.setDone();
+}
+
+void UserSpaceSource::interrupt() {
+ // Do nothing
+}
+
+bool UserSpaceSource::isDone() {
+ return mBuffer.isDone();
+}
+
+void UserSpaceSource::write(Sender *sender) {
+ if (!mBuffer.isDone()) {
+ mBuffer.write(sender);
+ }
+}
diff --git a/daemon/UserSpaceSource.h b/daemon/UserSpaceSource.h
new file mode 100644
index 0000000..fb5889d
--- /dev/null
+++ b/daemon/UserSpaceSource.h
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef USERSPACESOURCE_H
+#define USERSPACESOURCE_H
+
+#include <semaphore.h>
+
+#include "Buffer.h"
+#include "Source.h"
+
+// User space counters - currently just hwmon
+class UserSpaceSource : public Source {
+public:
+ UserSpaceSource(sem_t *senderSem);
+ ~UserSpaceSource();
+
+ bool prepare();
+ void run();
+ void interrupt();
+
+ bool isDone();
+ void write(Sender *sender);
+
+private:
+ Buffer mBuffer;
+
+ // Intentionally unimplemented
+ UserSpaceSource(const UserSpaceSource &);
+ UserSpaceSource &operator=(const UserSpaceSource &);
+};
+
+#endif // USERSPACESOURCE_H
diff --git a/daemon/common.mk b/daemon/common.mk
index 031d169..d9dc146 100644
--- a/daemon/common.mk
+++ b/daemon/common.mk
@@ -25,7 +25,7 @@ include $(wildcard *.d)
include $(wildcard mxml/*.d)
EventsXML.cpp: events_xml.h
-ConfigurationXML.cpp: configuration_xml.h
+ConfigurationXML.cpp: defaults_xml.h
# Don't regenerate conf-lex.c or conf-parse.c
libsensors/conf-lex.c: ;
@@ -47,4 +47,4 @@ escape: escape.c
gcc $^ -o $@
clean:
- rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h configuration_xml.h
+ rm -f *.d *.o mxml/*.d mxml/*.o libsensors/*.d libsensors/*.o $(TARGET) escape events.xml events_xml.h defaults_xml.h
diff --git a/daemon/configuration.xml b/daemon/defaults.xml
index b44c00a..5bf096c 100644
--- a/daemon/configuration.xml
+++ b/daemon/defaults.xml
@@ -6,29 +6,34 @@
<configuration counter="ARM_ARM11MPCore_ccnt" event="0xff"/>
<configuration counter="ARM_ARM11MPCore_cnt0" event="0x08"/>
<configuration counter="ARM_ARM11MPCore_cnt1" event="0x0b"/>
- <configuration counter="ARM_Cortex-A5_ccnt" event="0xff"/>
- <configuration counter="ARM_Cortex-A5_cnt0" event="0x8"/>
- <configuration counter="ARM_Cortex-A5_cnt1" event="0x1"/>
- <configuration counter="ARM_Cortex-A7_ccnt" event="0xff"/>
- <configuration counter="ARM_Cortex-A7_cnt0" event="0x08"/>
- <configuration counter="ARM_Cortex-A7_cnt1" event="0x10"/>
- <configuration counter="ARM_Cortex-A7_cnt2" event="0x16"/>
- <configuration counter="ARM_Cortex-A8_ccnt" event="0xff"/>
- <configuration counter="ARM_Cortex-A8_cnt0" event="0x8"/>
- <configuration counter="ARM_Cortex-A8_cnt1" event="0x44"/>
- <configuration counter="ARM_Cortex-A8_cnt2" event="0x43"/>
- <configuration counter="ARM_Cortex-A8_cnt3" event="0x10"/>
- <configuration counter="ARM_Cortex-A9_ccnt" event="0xff"/>
- <configuration counter="ARM_Cortex-A9_cnt0" event="0x68"/>
- <configuration counter="ARM_Cortex-A9_cnt1" event="0x06"/>
- <configuration counter="ARM_Cortex-A9_cnt2" event="0x07"/>
- <configuration counter="ARM_Cortex-A9_cnt3" event="0x03"/>
- <configuration counter="ARM_Cortex-A9_cnt4" event="0x04"/>
- <configuration counter="ARM_Cortex-A15_ccnt" event="0xff"/>
- <configuration counter="ARM_Cortex-A15_cnt0" event="0x8"/>
- <configuration counter="ARM_Cortex-A15_cnt1" event="0x16"/>
- <configuration counter="ARM_Cortex-A15_cnt2" event="0x10"/>
- <configuration counter="ARM_Cortex-A15_cnt3" event="0x19"/>
+ <configuration counter="ARMv7_Cortex_A5_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A5_cnt0" event="0x8"/>
+ <configuration counter="ARMv7_Cortex_A5_cnt1" event="0x1"/>
+ <configuration counter="ARMv7_Cortex_A7_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A7_cnt0" event="0x08"/>
+ <configuration counter="ARMv7_Cortex_A7_cnt1" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A7_cnt2" event="0x16"/>
+ <configuration counter="ARMv7_Cortex_A8_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt0" event="0x8"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt1" event="0x44"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt2" event="0x43"/>
+ <configuration counter="ARMv7_Cortex_A8_cnt3" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A9_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt0" event="0x68"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt1" event="0x06"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt2" event="0x07"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt3" event="0x03"/>
+ <configuration counter="ARMv7_Cortex_A9_cnt4" event="0x04"/>
+ <configuration counter="ARMv7_Cortex_A12_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt0" event="0x08"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt1" event="0x16"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt2" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A12_cnt3" event="0x19"/>
+ <configuration counter="ARMv7_Cortex_A15_ccnt" event="0xff"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt0" event="0x8"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt1" event="0x16"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt2" event="0x10"/>
+ <configuration counter="ARMv7_Cortex_A15_cnt3" event="0x19"/>
<configuration counter="ARM_Cortex-A53_ccnt" event="0x11"/>
<configuration counter="ARM_Cortex-A53_cnt0" event="0x8"/>
<configuration counter="ARM_Cortex-A53_cnt1" event="0x16"/>
diff --git a/daemon/escape.c b/daemon/escape.c
index 3eec1f8..c54aa1c 100644
--- a/daemon/escape.c
+++ b/daemon/escape.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/daemon/events-Cortex-A12.xml b/daemon/events-Cortex-A12.xml
index 20a4772..9c04354 100644
--- a/daemon/events-Cortex-A12.xml
+++ b/daemon/events-Cortex-A12.xml
@@ -1,6 +1,6 @@
- <counter_set name="ARM_Cortex-A12_cnt" count="6"/>
- <category name="Cortex-A12" counter_set="ARM_Cortex-A12_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv7_Cortex_A12_cnt" count="6"/>
+ <category name="Cortex-A12" counter_set="ARMv7_Cortex_A12_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A12_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
<event event="0x03" title="Cache" name="Data refill" description="Memory Read or Write operation that causes a refill of at least the level of data or unified cache closest to the processor"/>
diff --git a/daemon/events-Cortex-A15.xml b/daemon/events-Cortex-A15.xml
index faa8b1c..f50e55d 100644
--- a/daemon/events-Cortex-A15.xml
+++ b/daemon/events-Cortex-A15.xml
@@ -1,6 +1,6 @@
- <counter_set name="ARM_Cortex-A15_cnt" count="6"/>
- <category name="Cortex-A15" counter_set="ARM_Cortex-A15_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv7_Cortex_A15_cnt" count="6"/>
+ <category name="Cortex-A15" counter_set="ARMv7_Cortex_A15_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A15_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
diff --git a/daemon/events-Cortex-A5.xml b/daemon/events-Cortex-A5.xml
index a5b1546..d67581d 100644
--- a/daemon/events-Cortex-A5.xml
+++ b/daemon/events-Cortex-A5.xml
@@ -1,6 +1,6 @@
- <counter_set name="ARM_Cortex-A5_cnt" count="2"/>
- <category name="Cortex-A5" counter_set="ARM_Cortex-A5_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv7_Cortex_A5_cnt" count="2"/>
+ <category name="Cortex-A5" counter_set="ARMv7_Cortex_A5_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A5_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
diff --git a/daemon/events-Cortex-A7.xml b/daemon/events-Cortex-A7.xml
index 54d7264..6e078b3 100644
--- a/daemon/events-Cortex-A7.xml
+++ b/daemon/events-Cortex-A7.xml
@@ -1,6 +1,6 @@
- <counter_set name="ARM_Cortex-A7_cnt" count="4"/>
- <category name="Cortex-A7" counter_set="ARM_Cortex-A7_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv7_Cortex_A7_cnt" count="4"/>
+ <category name="Cortex-A7" counter_set="ARMv7_Cortex_A7_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A7_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Software increment architecturally executed"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
diff --git a/daemon/events-Cortex-A8.xml b/daemon/events-Cortex-A8.xml
index f251823..a69e25a 100644
--- a/daemon/events-Cortex-A8.xml
+++ b/daemon/events-Cortex-A8.xml
@@ -1,6 +1,6 @@
- <counter_set name="ARM_Cortex-A8_cnt" count="4"/>
- <category name="Cortex-A8" counter_set="ARM_Cortex-A8_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv7_Cortex_A8_cnt" count="4"/>
+ <category name="Cortex-A8" counter_set="ARMv7_Cortex_A8_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A8_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
diff --git a/daemon/events-Cortex-A9.xml b/daemon/events-Cortex-A9.xml
index 75f09c8..3e7f828 100644
--- a/daemon/events-Cortex-A9.xml
+++ b/daemon/events-Cortex-A9.xml
@@ -1,6 +1,6 @@
- <counter_set name="ARM_Cortex-A9_cnt" count="6"/>
- <category name="Cortex-A9" counter_set="ARM_Cortex-A9_cnt" per_cpu="yes" supports_event_based_sampling="yes">
- <event counter="ARM_Cortex-A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <counter_set name="ARMv7_Cortex_A9_cnt" count="6"/>
+ <category name="Cortex-A9" counter_set="ARMv7_Cortex_A9_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="ARMv7_Cortex_A9_ccnt" event="0xff" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
<event event="0x00" title="Software" name="Increment" description="Incremented only on writes to the Software Increment Register"/>
<event event="0x01" title="Cache" name="Instruction refill" description="Instruction fetch that causes a refill of at least the level of instruction or unified cache closest to the processor"/>
<event event="0x02" title="Cache" name="Inst TLB refill" description="Instruction fetch that causes a TLB refill of at least the level of TLB closest to the processor"/>
diff --git a/daemon/events-Linux.xml b/daemon/events-Linux.xml
index 31a90a1..4d677e1 100644
--- a/daemon/events-Linux.xml
+++ b/daemon/events-Linux.xml
@@ -6,12 +6,12 @@
<event counter="Linux_net_rx" title="Network" name="Receive" units="B" description="Receive network traffic, including effect from Streamline"/>
<event counter="Linux_net_tx" title="Network" name="Transmit" units="B" description="Transmit network traffic, including effect from Streamline"/>
<event counter="Linux_sched_switch" title="Scheduler" name="Switch" per_cpu="yes" description="Context switch events"/>
- <event counter="Linux_meminfo_memused" title="Memory" name="Used" display="maximum" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/>
- <event counter="Linux_meminfo_memfree" title="Memory" name="Free" display="minimum" units="B" description="Available memory size"/>
- <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" display="maximum" units="B" description="Memory used by OS disk buffers"/>
- <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" display="maximum" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/>
- <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" display="maximum" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/>
- <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/>
- <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" display="average" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/>
+ <event counter="Linux_meminfo_memused" title="Memory" name="Used" class="absolute" units="B" proc="yes" description="Total used memory size. Note: a process' used memory includes shared memory that may be counted more than once (equivalent to RES from top). Kernel threads are not filterable."/>
+ <event counter="Linux_meminfo_memfree" title="Memory" name="Free" class="absolute" display="minimum" units="B" description="Available memory size"/>
+ <event counter="Linux_meminfo_bufferram" title="Memory" name="Buffer" class="absolute" units="B" description="Memory used by OS disk buffers"/>
+ <event counter="Linux_power_cpu_freq" title="Clock" name="Frequency" per_cpu="yes" class="absolute" units="Hz" series_composition="overlay" average_cores="yes" description="Frequency setting of the CPU"/>
+ <event counter="Linux_power_cpu_idle" title="Idle" name="State" per_cpu="yes" class="absolute" description="CPU Idle State + 1, set the Sample Rate to None to prevent the hrtimer from interrupting the system"/>
+ <event counter="Linux_cpu_wait_contention" title="CPU Contention" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on contended resource"/>
+ <event counter="Linux_cpu_wait_io" title="CPU I/O" name="Wait" per_cpu="no" class="activity" derived="yes" rendering_type="bar" average_selection="yes" percentage="yes" modifier="10000" description="Thread waiting on I/O resource"/>
</category>
diff --git a/daemon/events-Mali-4xx.xml b/daemon/events-Mali-4xx.xml
index 8772ce4..5a71386 100644
--- a/daemon/events-Mali-4xx.xml
+++ b/daemon/events-Mali-4xx.xml
@@ -207,7 +207,7 @@
<event event="0x0400" option_set="fs" title="ARM Mali-4xx" name="Filmstrip" description="Scaled framebuffer"/>
</category>
<category name="ARM_Mali-4xx_Voltage" per_cpu="no">
- <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" display="average" average_selection="yes" units="mV" description="GPU core voltage."/>
+ <event counter="ARM_Mali-4xx_Voltage" title="Mali GPU Voltage" name="Voltage" class="absolute" display="average" average_selection="yes" units="mV" description="GPU core voltage."/>
</category>
<category name="ARM_Mali-4xx_Frequency" per_cpu="no">
<event counter="ARM_Mali-4xx_Frequency" title="Mali GPU Frequency" name="Frequency" display="average" average_selection="yes" units="MHz" description="GPU core frequency."/>
diff --git a/daemon/events-Mali-T6xx.xml b/daemon/events-Mali-T6xx.xml
index 2465238..ec9ca00 100644
--- a/daemon/events-Mali-T6xx.xml
+++ b/daemon/events-Mali-T6xx.xml
@@ -4,14 +4,14 @@
</category>
<category name="Mali-T6xx-PMShader" per_cpu="no">
- <event counter="ARM_Mali-T6xx_PM_SHADER_0" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_1" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_2" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_3" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_4" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_5" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_6" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/>
- <event counter="ARM_Mali-T6xx_PM_SHADER_7" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_0" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 0" description="Mali PM Shader: PM Shader Core 0."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_1" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 1" description="Mali PM Shader: PM Shader Core 1."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_2" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 2" description="Mali PM Shader: PM Shader Core 2."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_3" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 3" description="Mali PM Shader: PM Shader Core 3."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_4" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 4" description="Mali PM Shader: PM Shader Core 4."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_5" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 5" description="Mali PM Shader: PM Shader Core 5."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_6" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 6" description="Mali PM Shader: PM Shader Core 6."/>
+ <event counter="ARM_Mali-T6xx_PM_SHADER_7" class="absolute" display="average" average_selection="yes" percentage="yes" title="Mali PM Shader" name="PM Shader Core 7" description="Mali PM Shader: PM Shader Core 7."/>
</category>
<category name="Mali-T6xx-PMTiler" per_cpu="no">
diff --git a/daemon/events-Perf-Hardware.xml b/daemon/events-Perf-Hardware.xml
new file mode 100644
index 0000000..423696f
--- /dev/null
+++ b/daemon/events-Perf-Hardware.xml
@@ -0,0 +1,12 @@
+ <counter_set name="Perf_Hardware_cnt" count="6"/>
+ <category name="Perf Hardware" counter_set="Perf_Hardware_cnt" per_cpu="yes" supports_event_based_sampling="yes">
+ <event counter="Perf_Hardware_ccnt" event="0" title="Clock" name="Cycles" display="hertz" units="Hz" average_selection="yes" average_cores="yes" description="The number of core clock cycles"/>
+ <event event="1" title="Instruction" name="Executed" description="Instruction executed"/>
+ <event event="2" title="Cache" name="References" description="Cache References"/>
+ <event event="3" title="Cache" name="Misses" description="Cache Misses"/>
+ <event event="4" title="Branch" name="Instructions" description="Branch or other change in program flow that could have been predicted by the branch prediction resources of the processor"/>
+ <event event="5" title="Branch" name="Misses" description="Branch mispredicted or not predicted"/>
+ <event event="6" title="Bus" name="Cycles" description="Bus Cycles"/>
+ <event event="7" title="Instruction" name="Stalled Frontend" description="Stalled Frontend Cycles"/>
+ <event event="8" title="Instruction" name="Stalled Backend" description="Stalled Backend Cycles"/>
+ </category>
diff --git a/daemon/k/perf_event.3.12.h b/daemon/k/perf_event.3.12.h
new file mode 100644
index 0000000..e886c48
--- /dev/null
+++ b/daemon/k/perf_event.3.12.h
@@ -0,0 +1,792 @@
+/*
+ * Performance events:
+ *
+ * Copyright (C) 2008-2009, Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2011, Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2008-2011, Red Hat, Inc., Peter Zijlstra
+ *
+ * Data type definitions, declarations, prototypes.
+ *
+ * Started by: Thomas Gleixner and Ingo Molnar
+ *
+ * For licencing details see kernel-base/COPYING
+ */
+#ifndef _LINUX_PERF_EVENT_H
+#define _LINUX_PERF_EVENT_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <asm/byteorder.h>
+
+/*
+ * User-space ABI bits:
+ */
+
+/*
+ * attr.type
+ */
+enum perf_type_id {
+ PERF_TYPE_HARDWARE = 0,
+ PERF_TYPE_SOFTWARE = 1,
+ PERF_TYPE_TRACEPOINT = 2,
+ PERF_TYPE_HW_CACHE = 3,
+ PERF_TYPE_RAW = 4,
+ PERF_TYPE_BREAKPOINT = 5,
+
+ PERF_TYPE_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized performance event event_id types, used by the
+ * attr.event_id parameter of the sys_perf_event_open()
+ * syscall:
+ */
+enum perf_hw_id {
+ /*
+ * Common hardware events, generalized by the kernel:
+ */
+ PERF_COUNT_HW_CPU_CYCLES = 0,
+ PERF_COUNT_HW_INSTRUCTIONS = 1,
+ PERF_COUNT_HW_CACHE_REFERENCES = 2,
+ PERF_COUNT_HW_CACHE_MISSES = 3,
+ PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4,
+ PERF_COUNT_HW_BRANCH_MISSES = 5,
+ PERF_COUNT_HW_BUS_CYCLES = 6,
+ PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7,
+ PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8,
+ PERF_COUNT_HW_REF_CPU_CYCLES = 9,
+
+ PERF_COUNT_HW_MAX, /* non-ABI */
+};
+
+/*
+ * Generalized hardware cache events:
+ *
+ * { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
+ * { read, write, prefetch } x
+ * { accesses, misses }
+ */
+enum perf_hw_cache_id {
+ PERF_COUNT_HW_CACHE_L1D = 0,
+ PERF_COUNT_HW_CACHE_L1I = 1,
+ PERF_COUNT_HW_CACHE_LL = 2,
+ PERF_COUNT_HW_CACHE_DTLB = 3,
+ PERF_COUNT_HW_CACHE_ITLB = 4,
+ PERF_COUNT_HW_CACHE_BPU = 5,
+ PERF_COUNT_HW_CACHE_NODE = 6,
+
+ PERF_COUNT_HW_CACHE_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_id {
+ PERF_COUNT_HW_CACHE_OP_READ = 0,
+ PERF_COUNT_HW_CACHE_OP_WRITE = 1,
+ PERF_COUNT_HW_CACHE_OP_PREFETCH = 2,
+
+ PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */
+};
+
+enum perf_hw_cache_op_result_id {
+ PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0,
+ PERF_COUNT_HW_CACHE_RESULT_MISS = 1,
+
+ PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */
+};
+
+/*
+ * Special "software" events provided by the kernel, even if the hardware
+ * does not support performance events. These events measure various
+ * physical and sw events of the kernel (and allow the profiling of them as
+ * well):
+ */
+enum perf_sw_ids {
+ PERF_COUNT_SW_CPU_CLOCK = 0,
+ PERF_COUNT_SW_TASK_CLOCK = 1,
+ PERF_COUNT_SW_PAGE_FAULTS = 2,
+ PERF_COUNT_SW_CONTEXT_SWITCHES = 3,
+ PERF_COUNT_SW_CPU_MIGRATIONS = 4,
+ PERF_COUNT_SW_PAGE_FAULTS_MIN = 5,
+ PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
+ PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
+ PERF_COUNT_SW_EMULATION_FAULTS = 8,
+ PERF_COUNT_SW_DUMMY = 9,
+
+ PERF_COUNT_SW_MAX, /* non-ABI */
+};
+
+/*
+ * Bits that can be set in attr.sample_type to request information
+ * in the overflow packets.
+ */
+enum perf_event_sample_format {
+ PERF_SAMPLE_IP = 1U << 0,
+ PERF_SAMPLE_TID = 1U << 1,
+ PERF_SAMPLE_TIME = 1U << 2,
+ PERF_SAMPLE_ADDR = 1U << 3,
+ PERF_SAMPLE_READ = 1U << 4,
+ PERF_SAMPLE_CALLCHAIN = 1U << 5,
+ PERF_SAMPLE_ID = 1U << 6,
+ PERF_SAMPLE_CPU = 1U << 7,
+ PERF_SAMPLE_PERIOD = 1U << 8,
+ PERF_SAMPLE_STREAM_ID = 1U << 9,
+ PERF_SAMPLE_RAW = 1U << 10,
+ PERF_SAMPLE_BRANCH_STACK = 1U << 11,
+ PERF_SAMPLE_REGS_USER = 1U << 12,
+ PERF_SAMPLE_STACK_USER = 1U << 13,
+ PERF_SAMPLE_WEIGHT = 1U << 14,
+ PERF_SAMPLE_DATA_SRC = 1U << 15,
+ PERF_SAMPLE_IDENTIFIER = 1U << 16,
+
+ PERF_SAMPLE_MAX = 1U << 17, /* non-ABI */
+};
+
+/*
+ * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
+ *
+ * If the user does not pass priv level information via branch_sample_type,
+ * the kernel uses the event's priv level. Branch and event priv levels do
+ * not have to match. Branch priv level is checked for permissions.
+ *
+ * The branch types can be combined, however BRANCH_ANY covers all types
+ * of branches and therefore it supersedes all the other types.
+ */
+enum perf_branch_sample_type {
+ PERF_SAMPLE_BRANCH_USER = 1U << 0, /* user branches */
+ PERF_SAMPLE_BRANCH_KERNEL = 1U << 1, /* kernel branches */
+ PERF_SAMPLE_BRANCH_HV = 1U << 2, /* hypervisor branches */
+
+ PERF_SAMPLE_BRANCH_ANY = 1U << 3, /* any branch types */
+ PERF_SAMPLE_BRANCH_ANY_CALL = 1U << 4, /* any call branch */
+ PERF_SAMPLE_BRANCH_ANY_RETURN = 1U << 5, /* any return branch */
+ PERF_SAMPLE_BRANCH_IND_CALL = 1U << 6, /* indirect calls */
+ PERF_SAMPLE_BRANCH_ABORT_TX = 1U << 7, /* transaction aborts */
+ PERF_SAMPLE_BRANCH_IN_TX = 1U << 8, /* in transaction */
+ PERF_SAMPLE_BRANCH_NO_TX = 1U << 9, /* not in transaction */
+
+ PERF_SAMPLE_BRANCH_MAX = 1U << 10, /* non-ABI */
+};
+
+#define PERF_SAMPLE_BRANCH_PLM_ALL \
+ (PERF_SAMPLE_BRANCH_USER|\
+ PERF_SAMPLE_BRANCH_KERNEL|\
+ PERF_SAMPLE_BRANCH_HV)
+
+/*
+ * Values to determine ABI of the registers dump.
+ */
+enum perf_sample_regs_abi {
+ PERF_SAMPLE_REGS_ABI_NONE = 0,
+ PERF_SAMPLE_REGS_ABI_32 = 1,
+ PERF_SAMPLE_REGS_ABI_64 = 2,
+};
+
+/*
+ * The format of the data returned by read() on a perf event fd,
+ * as specified by attr.read_format:
+ *
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ *
+ * { u64 nr;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 value;
+ * { u64 id; } && PERF_FORMAT_ID
+ * } cntr[nr];
+ * } && PERF_FORMAT_GROUP
+ * };
+ */
+enum perf_event_read_format {
+ PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ PERF_FORMAT_ID = 1U << 2,
+ PERF_FORMAT_GROUP = 1U << 3,
+
+ PERF_FORMAT_MAX = 1U << 4, /* non-ABI */
+};
+
+#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
+#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
+#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
+#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
+ /* add: sample_stack_user */
+
+/*
+ * Hardware event_id to monitor via a performance monitoring event:
+ */
+struct perf_event_attr {
+
+ /*
+ * Major type: hardware/software/tracepoint/etc.
+ */
+ __u32 type;
+
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ __u32 size;
+
+ /*
+ * Type specific configuration information.
+ */
+ __u64 config;
+
+ union {
+ __u64 sample_period;
+ __u64 sample_freq;
+ };
+
+ __u64 sample_type;
+ __u64 read_format;
+
+ __u64 disabled : 1, /* off by default */
+ inherit : 1, /* children inherit it */
+ pinned : 1, /* must always be on PMU */
+ exclusive : 1, /* only group on PMU */
+ exclude_user : 1, /* don't count user */
+ exclude_kernel : 1, /* ditto kernel */
+ exclude_hv : 1, /* ditto hypervisor */
+ exclude_idle : 1, /* don't count when idle */
+ mmap : 1, /* include mmap data */
+ comm : 1, /* include comm data */
+ freq : 1, /* use freq, not period */
+ inherit_stat : 1, /* per task counts */
+ enable_on_exec : 1, /* next exec enables */
+ task : 1, /* trace fork/exit */
+ watermark : 1, /* wakeup_watermark */
+ /*
+ * precise_ip:
+ *
+ * 0 - SAMPLE_IP can have arbitrary skid
+ * 1 - SAMPLE_IP must have constant skid
+ * 2 - SAMPLE_IP requested to have 0 skid
+ * 3 - SAMPLE_IP must have 0 skid
+ *
+ * See also PERF_RECORD_MISC_EXACT_IP
+ */
+ precise_ip : 2, /* skid constraint */
+ mmap_data : 1, /* non-exec mmap data */
+ sample_id_all : 1, /* sample_type all events */
+
+ exclude_host : 1, /* don't count in host */
+ exclude_guest : 1, /* don't count in guest */
+
+ exclude_callchain_kernel : 1, /* exclude kernel callchains */
+ exclude_callchain_user : 1, /* exclude user callchains */
+ mmap2 : 1, /* include mmap with inode data */
+
+ __reserved_1 : 40;
+
+ union {
+ __u32 wakeup_events; /* wakeup every n events */
+ __u32 wakeup_watermark; /* bytes before wakeup */
+ };
+
+ __u32 bp_type;
+ union {
+ __u64 bp_addr;
+ __u64 config1; /* extension of config */
+ };
+ union {
+ __u64 bp_len;
+ __u64 config2; /* extension of config1 */
+ };
+ __u64 branch_sample_type; /* enum perf_branch_sample_type */
+
+ /*
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ __u64 sample_regs_user;
+
+ /*
+ * Defines size of the user stack to dump on samples.
+ */
+ __u32 sample_stack_user;
+
+ /* Align to u64. */
+ __u32 __reserved_2;
+};
+
+#define perf_flags(attr) (*(&(attr)->read_format + 1))
+
+/*
+ * Ioctls that can be done on a perf event fd:
+ */
+#define PERF_EVENT_IOC_ENABLE _IO ('$', 0)
+#define PERF_EVENT_IOC_DISABLE _IO ('$', 1)
+#define PERF_EVENT_IOC_REFRESH _IO ('$', 2)
+#define PERF_EVENT_IOC_RESET _IO ('$', 3)
+#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64)
+#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5)
+#define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *)
+#define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *)
+
+enum perf_event_ioc_flags {
+ PERF_IOC_FLAG_GROUP = 1U << 0,
+};
+
+/*
+ * Structure of the page that can be mapped via mmap
+ */
+struct perf_event_mmap_page {
+ __u32 version; /* version number of this structure */
+ __u32 compat_version; /* lowest version this is compat with */
+
+ /*
+ * Bits needed to read the hw events in user-space.
+ *
+ * u32 seq, time_mult, time_shift, idx, width;
+ * u64 count, enabled, running;
+ * u64 cyc, time_offset;
+ * s64 pmc = 0;
+ *
+ * do {
+ * seq = pc->lock;
+ * barrier()
+ *
+ * enabled = pc->time_enabled;
+ * running = pc->time_running;
+ *
+ * if (pc->cap_usr_time && enabled != running) {
+ * cyc = rdtsc();
+ * time_offset = pc->time_offset;
+ * time_mult = pc->time_mult;
+ * time_shift = pc->time_shift;
+ * }
+ *
+ * idx = pc->index;
+ * count = pc->offset;
+ * if (pc->cap_usr_rdpmc && idx) {
+ * width = pc->pmc_width;
+ * pmc = rdpmc(idx - 1);
+ * }
+ *
+ * barrier();
+ * } while (pc->lock != seq);
+ *
+ * NOTE: for obvious reason this only works on self-monitoring
+ * processes.
+ */
+ __u32 lock; /* seqlock for synchronization */
+ __u32 index; /* hardware event identifier */
+ __s64 offset; /* add to hardware event value */
+ __u64 time_enabled; /* time event active */
+ __u64 time_running; /* time event on cpu */
+ union {
+ __u64 capabilities;
+ struct {
+ __u64 cap_bit0 : 1, /* Always 0, deprecated, see commit 860f085b74e9 */
+ cap_bit0_is_deprecated : 1, /* Always 1, signals that bit 0 is zero */
+
+ cap_user_rdpmc : 1, /* The RDPMC instruction can be used to read counts */
+ cap_user_time : 1, /* The time_* fields are used */
+ cap_user_time_zero : 1, /* The time_zero field is used */
+ cap_____res : 59;
+ };
+ };
+
+ /*
+ * If cap_usr_rdpmc this field provides the bit-width of the value
+ * read using the rdpmc() or equivalent instruction. This can be used
+ * to sign extend the result like:
+ *
+ * pmc <<= 64 - width;
+ * pmc >>= 64 - width; // signed shift right
+ * count += pmc;
+ */
+ __u16 pmc_width;
+
+ /*
+ * If cap_usr_time the below fields can be used to compute the time
+ * delta since time_enabled (in ns) using rdtsc or similar.
+ *
+ * u64 quot, rem;
+ * u64 delta;
+ *
+ * quot = (cyc >> time_shift);
+ * rem = cyc & ((1 << time_shift) - 1);
+ * delta = time_offset + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ *
+ * Where time_offset,time_mult,time_shift and cyc are read in the
+ * seqcount loop described above. This delta can then be added to
+ * enabled and possible running (if idx), improving the scaling:
+ *
+ * enabled += delta;
+ * if (idx)
+ * running += delta;
+ *
+ * quot = count / running;
+ * rem = count % running;
+ * count = quot * enabled + (rem * enabled) / running;
+ */
+ __u16 time_shift;
+ __u32 time_mult;
+ __u64 time_offset;
+ /*
+ * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
+ * from sample timestamps.
+ *
+ * time = timestamp - time_zero;
+ * quot = time / time_mult;
+ * rem = time % time_mult;
+ * cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
+ *
+ * And vice versa:
+ *
+ * quot = cyc >> time_shift;
+ * rem = cyc & ((1 << time_shift) - 1);
+ * timestamp = time_zero + quot * time_mult +
+ * ((rem * time_mult) >> time_shift);
+ */
+ __u64 time_zero;
+ __u32 size; /* Header size up to __reserved[] fields. */
+
+ /*
+ * Hole for extension of the self monitor capabilities
+ */
+
+ __u8 __reserved[118*8+4]; /* align to 1k. */
+
+ /*
+ * Control data for the mmap() data buffer.
+ *
+ * User-space reading the @data_head value should issue an smp_rmb(),
+ * after reading this value.
+ *
+ * When the mapping is PROT_WRITE the @data_tail value should be
+ * written by userspace to reflect the last read data, after issueing
+ * an smp_mb() to separate the data read from the ->data_tail store.
+ * In this case the kernel will not over-write unread data.
+ *
+ * See perf_output_put_handle() for the data ordering.
+ */
+ __u64 data_head; /* head in the data section */
+ __u64 data_tail; /* user-space written tail */
+};
+
+#define PERF_RECORD_MISC_CPUMODE_MASK (7 << 0)
+#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0)
+#define PERF_RECORD_MISC_KERNEL (1 << 0)
+#define PERF_RECORD_MISC_USER (2 << 0)
+#define PERF_RECORD_MISC_HYPERVISOR (3 << 0)
+#define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0)
+#define PERF_RECORD_MISC_GUEST_USER (5 << 0)
+
+#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
+/*
+ * Indicates that the content of PERF_SAMPLE_IP points to
+ * the actual instruction that triggered the event. See also
+ * perf_event_attr::precise_ip.
+ */
+#define PERF_RECORD_MISC_EXACT_IP (1 << 14)
+/*
+ * Reserve the last bit to indicate some extended misc field
+ */
+#define PERF_RECORD_MISC_EXT_RESERVED (1 << 15)
+
+struct perf_event_header {
+ __u32 type;
+ __u16 misc;
+ __u16 size;
+};
+
+enum perf_event_type {
+
+ /*
+ * If perf_event_attr.sample_id_all is set then all event types will
+ * have the sample_type selected fields related to where/when
+ * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+ * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+ * just after the perf_event_header and the fields already present for
+ * the existing fields, i.e. at the end of the payload. That way a newer
+ * perf.data file will be supported by older perf tools, with these new
+ * optional fields being ignored.
+ *
+ * struct sample_id {
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * } && perf_event_attr::sample_id_all
+ *
+ * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
+ * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+ * relative to header.size.
+ */
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP = 1,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_LOST = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_COMM = 3,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_FORK = 7,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_READ = 8,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * #
+ * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+ * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+ * # is fixed relative to header.
+ * #
+ *
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values; } && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];}&& PERF_SAMPLE_RAW
+ *
+ * { u64 nr;
+ * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ *
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ *
+ * { u64 size;
+ * char data[size];
+ * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
+ *
+ * { u64 weight; } && PERF_SAMPLE_WEIGHT
+ * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
+ * };
+ */
+ PERF_RECORD_SAMPLE = 9,
+
+ /*
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * u64 ino_generation;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP2 = 10,
+
+ PERF_RECORD_MAX, /* non-ABI */
+};
+
+#define PERF_MAX_STACK_DEPTH 127
+
+enum perf_callchain_context {
+ PERF_CONTEXT_HV = (__u64)-32,
+ PERF_CONTEXT_KERNEL = (__u64)-128,
+ PERF_CONTEXT_USER = (__u64)-512,
+
+ PERF_CONTEXT_GUEST = (__u64)-2048,
+ PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176,
+ PERF_CONTEXT_GUEST_USER = (__u64)-2560,
+
+ PERF_CONTEXT_MAX = (__u64)-4095,
+};
+
+#define PERF_FLAG_FD_NO_GROUP (1U << 0)
+#define PERF_FLAG_FD_OUTPUT (1U << 1)
+#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */
+
+union perf_mem_data_src {
+ __u64 val;
+ struct {
+ __u64 mem_op:5, /* type of opcode */
+ mem_lvl:14, /* memory hierarchy level */
+ mem_snoop:5, /* snoop mode */
+ mem_lock:2, /* lock instr */
+ mem_dtlb:7, /* tlb access */
+ mem_rsvd:31;
+ };
+};
+
+/* type of opcode (load/store/prefetch,code) */
+#define PERF_MEM_OP_NA 0x01 /* not available */
+#define PERF_MEM_OP_LOAD 0x02 /* load instruction */
+#define PERF_MEM_OP_STORE 0x04 /* store instruction */
+#define PERF_MEM_OP_PFETCH 0x08 /* prefetch */
+#define PERF_MEM_OP_EXEC 0x10 /* code (execution) */
+#define PERF_MEM_OP_SHIFT 0
+
+/* memory hierarchy (memory level, hit or miss) */
+#define PERF_MEM_LVL_NA 0x01 /* not available */
+#define PERF_MEM_LVL_HIT 0x02 /* hit level */
+#define PERF_MEM_LVL_MISS 0x04 /* miss level */
+#define PERF_MEM_LVL_L1 0x08 /* L1 */
+#define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */
+#define PERF_MEM_LVL_L2 0x20 /* L2 */
+#define PERF_MEM_LVL_L3 0x40 /* L3 */
+#define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */
+#define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */
+#define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */
+#define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */
+#define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */
+#define PERF_MEM_LVL_IO 0x1000 /* I/O memory */
+#define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */
+#define PERF_MEM_LVL_SHIFT 5
+
+/* snoop mode */
+#define PERF_MEM_SNOOP_NA 0x01 /* not available */
+#define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */
+#define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */
+#define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */
+#define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */
+#define PERF_MEM_SNOOP_SHIFT 19
+
+/* locked instruction */
+#define PERF_MEM_LOCK_NA 0x01 /* not available */
+#define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */
+#define PERF_MEM_LOCK_SHIFT 24
+
+/* TLB access */
+#define PERF_MEM_TLB_NA 0x01 /* not available */
+#define PERF_MEM_TLB_HIT 0x02 /* hit level */
+#define PERF_MEM_TLB_MISS 0x04 /* miss level */
+#define PERF_MEM_TLB_L1 0x08 /* L1 */
+#define PERF_MEM_TLB_L2 0x10 /* L2 */
+#define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/
+#define PERF_MEM_TLB_OS 0x40 /* OS fault handler */
+#define PERF_MEM_TLB_SHIFT 26
+
+#define PERF_MEM_S(a, s) \
+ (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT)
+
+/*
+ * single taken branch record layout:
+ *
+ * from: source instruction (may not always be a branch insn)
+ * to: branch target
+ * mispred: branch target was mispredicted
+ * predicted: branch target was predicted
+ *
+ * support for mispred, predicted is optional. In case it
+ * is not supported mispred = predicted = 0.
+ *
+ * in_tx: running in a hardware transaction
+ * abort: aborting a hardware transaction
+ */
+struct perf_branch_entry {
+ __u64 from;
+ __u64 to;
+ __u64 mispred:1, /* target mispredicted */
+ predicted:1,/* target predicted */
+ in_tx:1, /* in transaction */
+ abort:1, /* transaction abort */
+ reserved:60;
+};
+
+#endif /* _LINUX_PERF_EVENT_H */
diff --git a/daemon/k/perf_event.h b/daemon/k/perf_event.h
new file mode 120000
index 0000000..e5dff8c
--- /dev/null
+++ b/daemon/k/perf_event.h
@@ -0,0 +1 @@
+perf_event.3.12.h \ No newline at end of file
diff --git a/daemon/main.cpp b/daemon/main.cpp
index bfd36b9..1275aef 100644
--- a/daemon/main.cpp
+++ b/daemon/main.cpp
@@ -1,32 +1,30 @@
/**
- * Copyright (C) ARM Limited 2010-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2010-2014. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
+#include <arpa/inet.h>
#include <fcntl.h>
+#include <pthread.h>
#include <sys/mman.h>
-#include <sys/time.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
#include <sys/resource.h>
-#include <arpa/inet.h>
#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
#include "Child.h"
-#include "SessionData.h"
-#include "OlySocket.h"
+#include "KMod.h"
#include "Logging.h"
+#include "OlySocket.h"
#include "OlyUtility.h"
-#include "KMod.h"
+#include "SessionData.h"
#define DEBUG false
@@ -34,7 +32,7 @@ extern Child* child;
static int shutdownFilesystem();
static pthread_mutex_t numSessions_mutex;
static int numSessions = 0;
-static OlySocket* sock = NULL;
+static OlyServerSocket* sock = NULL;
static bool driverRunningAtStart = false;
static bool driverMountedAtStart = false;
@@ -157,6 +155,7 @@ typedef struct {
static const char DST_REQ[] = { 'D', 'S', 'T', '_', 'R', 'E', 'Q', ' ', 0, 0, 0, 0x64 };
static void* answerThread(void* pVoid) {
+ prctl(PR_SET_NAME, (unsigned long)&"gatord-discover", 0, 0, 0);
const struct cmdline_t * const cmdline = (struct cmdline_t *)pVoid;
RVIConfigureInfo dstAns;
int req = udpPort(UDP_REQ_PORT);
@@ -231,16 +230,7 @@ static bool init_module (const char * const location) {
return ret;
}
-static int setupFilesystem(char* module) {
- int retval;
-
- // Verify root permissions
- uid_t euid = geteuid();
- if (euid) {
- logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
- handleException();
- }
-
+static bool setupFilesystem(char* module) {
if (module) {
// unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running
shutdownFilesystem();
@@ -252,7 +242,7 @@ static int setupFilesystem(char* module) {
}
}
- retval = mountGatorFS();
+ const int retval = mountGatorFS();
if (retval == 1) {
logg->logMessage("Driver already running at startup");
driverRunningAtStart = true;
@@ -274,8 +264,8 @@ static int setupFilesystem(char* module) {
}
if (access(location, F_OK) == -1) {
- logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line");
- handleException();
+ // The gator kernel is not already loaded and unable to locate gator.ko
+ return false;
}
// Load driver
@@ -296,7 +286,7 @@ static int setupFilesystem(char* module) {
}
}
- return 0;
+ return true;
}
static int shutdownFilesystem() {
@@ -418,8 +408,28 @@ int main(int argc, char** argv) {
// Parse the command line parameters
struct cmdline_t cmdline = parseCommandLine(argc, argv);
+ // Verify root permissions
+ uid_t euid = geteuid();
+ if (euid) {
+ logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges");
+ handleException();
+ }
+
// Call before setting up the SIGCHLD handler, as system() spawns child processes
- setupFilesystem(cmdline.module);
+ if (!setupFilesystem(cmdline.module)) {
+ logg->logMessage("Unable to setup gatorfs, trying perf");
+ if (!gSessionData->perf.setup()) {
+ logg->logError(__FILE__, __LINE__,
+ "Unable to locate gator.ko driver:\n"
+ " >>> gator.ko should be co-located with gatord in the same directory\n"
+ " >>> OR insmod gator.ko prior to launching gatord\n"
+ " >>> OR specify the location of gator.ko on the command line\n"
+ " >>> OR run Linux 3.12 or later with perf support to collect data via userspace only");
+ handleException();
+ }
+ }
+
+ gSessionData->hwmon.setup();
// Handle child exit codes
signal(SIGCHLD, child_exit);
@@ -439,11 +449,11 @@ int main(int argc, char** argv) {
logg->logError(__FILE__, __LINE__, "Failed to create answer thread");
handleException();
}
- sock = new OlySocket(cmdline.port, true);
+ sock = new OlyServerSocket(cmdline.port);
// Forever loop, can be exited via a signal or exception
while (1) {
logg->logMessage("Waiting on connection...");
- sock->acceptConnection();
+ OlySocket client(sock->acceptConnection());
int pid = fork();
if (pid < 0) {
@@ -452,13 +462,13 @@ int main(int argc, char** argv) {
} else if (pid == 0) {
// Child
sock->closeServerSocket();
- child = new Child(sock, numSessions + 1);
+ child = new Child(&client, numSessions + 1);
child->run();
delete child;
exit(0);
} else {
// Parent
- sock->closeSocket();
+ client.closeSocket();
pthread_mutex_lock(&numSessions_mutex);
numSessions++;
diff --git a/driver/gator.h b/driver/gator.h
index d8981ed..586cd9e 100644
--- a/driver/gator.h
+++ b/driver/gator.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
@@ -31,6 +31,7 @@
#define CORTEX_A9 0xc09
#define CORTEX_A12 0xc0d
#define CORTEX_A15 0xc0f
+#define CORTEX_A17 0xc0e
#define SCORPION 0x00f
#define SCORPIONMP 0x02d
#define KRAITSIM 0x049
@@ -47,9 +48,7 @@ struct gator_cpu {
const int cpuid;
// Human readable name
const char core_name[MAXSIZE_CORE_NAME];
- // Perf PMU name
- const char * const pmu_name;
- // gatorfs event name
+ // gatorfs event and Perf PMU name
const char * const pmnc_name;
// compatible from Documentation/devicetree/bindings/arm/cpus.txt
const char * const dt_name;
@@ -62,10 +61,6 @@ const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name);
/******************************************************************************
* Filesystem
******************************************************************************/
-int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
- char const *name,
- const struct file_operations *fops, int perm);
-
struct dentry *gatorfs_mkdir(struct super_block *sb, struct dentry *root,
char const *name);
@@ -75,8 +70,6 @@ int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
char const *name, unsigned long *val);
-void gator_op_create_files(struct super_block *sb, struct dentry *root);
-
/******************************************************************************
* Tracepoints
******************************************************************************/
diff --git a/driver/gator_annotate.c b/driver/gator_annotate.c
index 5b9399b..7e2c6e5 100644
--- a/driver/gator_annotate.c
+++ b/driver/gator_annotate.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/driver/gator_annotate_kernel.c b/driver/gator_annotate_kernel.c
index a406e48..0108068 100644
--- a/driver/gator_annotate_kernel.c
+++ b/driver/gator_annotate_kernel.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2012-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
@@ -29,12 +29,14 @@ static void kannotate_write(const char *ptr, unsigned int size)
}
}
-static void marshal_u16(char *buf, u16 val) {
+static void marshal_u16(char *buf, u16 val)
+{
buf[0] = val & 0xff;
buf[1] = (val >> 8) & 0xff;
}
-static void marshal_u32(char *buf, u32 val) {
+static void marshal_u32(char *buf, u32 val)
+{
buf[0] = val & 0xff;
buf[1] = (val >> 8) & 0xff;
buf[2] = (val >> 16) & 0xff;
diff --git a/driver/gator_backtrace.c b/driver/gator_backtrace.c
index ffacb49..9f305cf 100644
--- a/driver/gator_backtrace.c
+++ b/driver/gator_backtrace.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
@@ -30,6 +30,18 @@ struct stack_frame_eabi {
};
};
+static void gator_add_trace(int cpu, unsigned long address)
+{
+ off_t offset = 0;
+ unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset);
+
+ if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) {
+ offset = address;
+ }
+
+ marshal_backtrace(offset & ~1, cookie, 0);
+}
+
static void arm_backtrace_eabi(int cpu, struct pt_regs *const regs, unsigned int depth)
{
#if defined(__arm__) || defined(__aarch64__)
@@ -122,7 +134,7 @@ static int report_trace(struct stackframe *frame, void *d)
addr = addr - (unsigned long)mod->module_core;
}
#endif
- marshal_backtrace(addr & ~1, cookie);
+ marshal_backtrace(addr & ~1, cookie, 1);
(*depth)--;
}
@@ -136,7 +148,7 @@ static int report_trace(struct stackframe *frame, void *d)
#if (defined(__arm__) || defined(__aarch64__)) && !defined(GATOR_KERNEL_STACK_UNWINDING)
// Disabled by default
MODULE_PARM_DESC(kernel_stack_unwinding, "Allow kernel stack unwinding.");
-bool kernel_stack_unwinding = 0;
+static bool kernel_stack_unwinding = 0;
module_param(kernel_stack_unwinding, bool, 0644);
#endif
@@ -163,6 +175,34 @@ static void kernel_backtrace(int cpu, struct pt_regs *const regs)
#endif
walk_stackframe(&frame, report_trace, &depth);
#else
- marshal_backtrace(PC_REG & ~1, NO_COOKIE);
+ marshal_backtrace(PC_REG & ~1, NO_COOKIE, 1);
#endif
}
+
+static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time)
+{
+ bool in_kernel;
+ unsigned long exec_cookie;
+
+ if (!regs)
+ return;
+
+ in_kernel = !user_mode(regs);
+ exec_cookie = get_exec_cookie(cpu, current);
+
+ if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, time))
+ return;
+
+ if (in_kernel) {
+ kernel_backtrace(cpu, regs);
+ } else {
+ // Cookie+PC
+ gator_add_trace(cpu, PC_REG);
+
+ // Backtrace
+ if (gator_backtrace_depth)
+ arm_backtrace_eabi(cpu, regs, gator_backtrace_depth);
+ }
+
+ marshal_backtrace_footer(time);
+}
diff --git a/driver/gator_buffer.c b/driver/gator_buffer.c
new file mode 100644
index 0000000..eba22df
--- /dev/null
+++ b/driver/gator_buffer.c
@@ -0,0 +1,168 @@
+/**
+ * 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.
+ *
+ */
+
+static void marshal_frame(int cpu, int buftype)
+{
+ int frame;
+
+ if (!per_cpu(gator_buffer, cpu)[buftype]) {
+ return;
+ }
+
+ switch (buftype) {
+ case SUMMARY_BUF:
+ frame = FRAME_SUMMARY;
+ break;
+ case BACKTRACE_BUF:
+ frame = FRAME_BACKTRACE;
+ break;
+ case NAME_BUF:
+ frame = FRAME_NAME;
+ break;
+ case COUNTER_BUF:
+ frame = FRAME_COUNTER;
+ break;
+ case BLOCK_COUNTER_BUF:
+ frame = FRAME_BLOCK_COUNTER;
+ break;
+ case ANNOTATE_BUF:
+ frame = FRAME_ANNOTATE;
+ break;
+ case SCHED_TRACE_BUF:
+ frame = FRAME_SCHED_TRACE;
+ break;
+ case GPU_TRACE_BUF:
+ frame = FRAME_GPU_TRACE;
+ break;
+ case IDLE_BUF:
+ frame = FRAME_IDLE;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ // add response type
+ if (gator_response_type > 0) {
+ gator_buffer_write_packed_int(cpu, buftype, gator_response_type);
+ }
+
+ // leave space for 4-byte unpacked length
+ per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype];
+
+ // add frame type and core number
+ gator_buffer_write_packed_int(cpu, buftype, frame);
+ gator_buffer_write_packed_int(cpu, buftype, cpu);
+}
+
+static int buffer_bytes_available(int cpu, int buftype)
+{
+ int remaining, filled;
+
+ filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype];
+ if (filled < 0) {
+ filled += gator_buffer_size[buftype];
+ }
+
+ remaining = gator_buffer_size[buftype] - filled;
+
+ if (per_cpu(buffer_space_available, cpu)[buftype]) {
+ // Give some extra room; also allows space to insert the overflow error packet
+ remaining -= 200;
+ } else {
+ // Hysteresis, prevents multiple overflow messages
+ remaining -= 2000;
+ }
+
+ return remaining;
+}
+
+static bool buffer_check_space(int cpu, int buftype, int bytes)
+{
+ int remaining = buffer_bytes_available(cpu, buftype);
+
+ if (remaining < bytes) {
+ per_cpu(buffer_space_available, cpu)[buftype] = false;
+ } else {
+ per_cpu(buffer_space_available, cpu)[buftype] = true;
+ }
+
+ return per_cpu(buffer_space_available, cpu)[buftype];
+}
+
+static int contiguous_space_available(int cpu, int buftype)
+{
+ int remaining = buffer_bytes_available(cpu, buftype);
+ int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype];
+ if (remaining < contiguous)
+ return remaining;
+ else
+ return contiguous;
+}
+
+static void gator_commit_buffer(int cpu, int buftype, u64 time)
+{
+ int type_length, commit, length, byte;
+ unsigned long flags;
+
+ if (!per_cpu(gator_buffer, cpu)[buftype])
+ return;
+
+ // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
+ local_irq_save(flags);
+ type_length = gator_response_type ? 1 : 0;
+ commit = per_cpu(gator_buffer_commit, cpu)[buftype];
+ length = per_cpu(gator_buffer_write, cpu)[buftype] - commit;
+ if (length < 0) {
+ length += gator_buffer_size[buftype];
+ }
+ length = length - type_length - sizeof(s32);
+
+ if (length <= FRAME_HEADER_SIZE) {
+ // Nothing to write, only the frame header is present
+ local_irq_restore(flags);
+ return;
+ }
+
+ for (byte = 0; byte < sizeof(s32); byte++) {
+ per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF;
+ }
+
+ per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype];
+
+ if (gator_live_rate > 0) {
+ while (time > per_cpu(gator_buffer_commit_time, cpu)) {
+ per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate;
+ }
+ }
+
+ marshal_frame(cpu, buftype);
+ local_irq_restore(flags);
+
+ // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
+ if (per_cpu(in_scheduler_context, cpu)) {
+#ifndef CONFIG_PREEMPT_RT_FULL
+ // mod_timer can not be used in interrupt context in RT-Preempt full
+ mod_timer(&gator_buffer_wake_up_timer, jiffies + 1);
+#endif
+ } else {
+ up(&gator_buffer_wake_sem);
+ }
+}
+
+static void buffer_check(int cpu, int buftype, u64 time)
+{
+ int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype];
+ if (filled < 0) {
+ filled += gator_buffer_size[buftype];
+ }
+ if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) {
+ gator_commit_buffer(cpu, buftype, time);
+ }
+}
diff --git a/driver/gator_pack.c b/driver/gator_buffer_write.c
index 2c082f2..b621ba9 100644
--- a/driver/gator_pack.c
+++ b/driver/gator_buffer_write.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
@@ -56,3 +56,25 @@ static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x)
per_cpu(gator_buffer_write, cpu)[buftype] = (write + packedBytes) & mask;
}
+
+static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len)
+{
+ int i;
+ u32 write = per_cpu(gator_buffer_write, cpu)[buftype];
+ u32 mask = gator_buffer_mask[buftype];
+ char *buffer = per_cpu(gator_buffer, cpu)[buftype];
+
+ for (i = 0; i < len; i++) {
+ buffer[write] = x[i];
+ write = (write + 1) & mask;
+ }
+
+ per_cpu(gator_buffer_write, cpu)[buftype] = write;
+}
+
+static void gator_buffer_write_string(int cpu, int buftype, const char *x)
+{
+ int len = strlen(x);
+ gator_buffer_write_packed_int(cpu, buftype, len);
+ gator_buffer_write_bytes(cpu, buftype, x, len);
+}
diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c
index 91adfdd..5c7d842 100644
--- a/driver/gator_cookies.c
+++ b/driver/gator_cookies.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
@@ -15,17 +15,22 @@
static uint32_t *gator_crc32_table;
static unsigned int translate_buffer_mask;
+struct cookie_args {
+ struct task_struct *task;
+ const char *text;
+};
+
static DEFINE_PER_CPU(char *, translate_text);
static DEFINE_PER_CPU(uint32_t, cookie_next_key);
static DEFINE_PER_CPU(uint64_t *, cookie_keys);
static DEFINE_PER_CPU(uint32_t *, cookie_values);
static DEFINE_PER_CPU(int, translate_buffer_read);
static DEFINE_PER_CPU(int, translate_buffer_write);
-static DEFINE_PER_CPU(void **, translate_buffer);
+static DEFINE_PER_CPU(struct cookie_args *, translate_buffer);
static uint32_t get_cookie(int cpu, struct task_struct *task, const char *text, bool from_wq);
static void wq_cookie_handler(struct work_struct *unused);
-DECLARE_WORK(cookie_work, wq_cookie_handler);
+static DECLARE_WORK(cookie_work, wq_cookie_handler);
static struct timer_list app_process_wake_up_timer;
static void app_process_wake_up_handler(unsigned long unused_data);
@@ -109,36 +114,62 @@ static void cookiemap_add(uint64_t key, uint32_t value)
}
#ifndef CONFIG_PREEMPT_RT_FULL
-static void translate_buffer_write_ptr(int cpu, void *x)
+static void translate_buffer_write_args(int cpu, struct task_struct *task, const char *text)
{
- per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x;
- per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask;
+ unsigned long flags;
+ int write;
+ int next_write;
+ struct cookie_args *args;
+
+ local_irq_save(flags);
+
+ write = per_cpu(translate_buffer_write, cpu);
+ next_write = (write + 1) & translate_buffer_mask;
+
+ // At least one entry must always remain available as when read == write, the queue is empty not full
+ if (next_write != per_cpu(translate_buffer_read, cpu)) {
+ args = &per_cpu(translate_buffer, cpu)[write];
+ args->task = task;
+ args->text = text;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ get_task_struct(task);
+#endif
+ per_cpu(translate_buffer_write, cpu) = next_write;
+ }
+
+ local_irq_restore(flags);
}
#endif
-static void *translate_buffer_read_ptr(int cpu)
+static void translate_buffer_read_args(int cpu, struct cookie_args *args)
{
- void *value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++];
- per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask;
- return value;
+ unsigned long flags;
+ int read;
+
+ local_irq_save(flags);
+
+ read = per_cpu(translate_buffer_read, cpu);
+ *args = per_cpu(translate_buffer, cpu)[read];
+ per_cpu(translate_buffer_read, cpu) = (read + 1) & translate_buffer_mask;
+
+ local_irq_restore(flags);
}
static void wq_cookie_handler(struct work_struct *unused)
{
- struct task_struct *task;
- char *text;
+ struct cookie_args args;
int cpu = get_physical_cpu(), cookie;
- unsigned int commit;
mutex_lock(&start_mutex);
if (gator_started != 0) {
- commit = per_cpu(translate_buffer_write, cpu);
- while (per_cpu(translate_buffer_read, cpu) != commit) {
- task = (struct task_struct *)translate_buffer_read_ptr(cpu);
- text = (char *)translate_buffer_read_ptr(cpu);
- cookie = get_cookie(cpu, task, text, true);
- marshal_link(cookie, task->tgid, task->pid);
+ while (per_cpu(translate_buffer_read, cpu) != per_cpu(translate_buffer_write, cpu)) {
+ translate_buffer_read_args(cpu, &args);
+ cookie = get_cookie(cpu, args.task, args.text, true);
+ marshal_link(cookie, args.task->tgid, args.task->pid);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
+ put_task_struct(args.task);
+#endif
}
}
@@ -169,15 +200,14 @@ static int translate_app_process(const char **text, int cpu, struct task_struct
// inconsistent during a context switch between android/linux versions
if (!from_wq) {
// Check if already in buffer
- int ptr = per_cpu(translate_buffer_read, cpu);
- while (ptr != per_cpu(translate_buffer_write, cpu)) {
- if (per_cpu(translate_buffer, cpu)[ptr] == (void *)task)
+ int pos = per_cpu(translate_buffer_read, cpu);
+ while (pos != per_cpu(translate_buffer_write, cpu)) {
+ if (per_cpu(translate_buffer, cpu)[pos].task == task)
goto out;
- ptr = (ptr + 2) & translate_buffer_mask;
+ pos = (pos + 1) & translate_buffer_mask;
}
- translate_buffer_write_ptr(cpu, (void *)task);
- translate_buffer_write_ptr(cpu, (void *)*text);
+ translate_buffer_write_args(cpu, task, *text);
// Not safe to call in RT-Preempt full in schedule switch context
mod_timer(&app_process_wake_up_timer, jiffies + 1);
@@ -340,7 +370,7 @@ static int cookies_initialize(void)
}
memset(per_cpu(cookie_values, cpu), 0, size);
- per_cpu(translate_buffer, cpu) = (void **)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL);
+ per_cpu(translate_buffer, cpu) = (struct cookie_args *)kmalloc(TRANSLATE_BUFFER_SIZE, GFP_KERNEL);
if (!per_cpu(translate_buffer, cpu)) {
err = -ENOMEM;
goto cookie_setup_error;
diff --git a/driver/gator_events_armv6.c b/driver/gator_events_armv6.c
index dd79740..3536456 100644
--- a/driver/gator_events_armv6.c
+++ b/driver/gator_events_armv6.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/driver/gator_events_armv7.c b/driver/gator_events_armv7.c
index 30881c8..153119b 100644
--- a/driver/gator_events_armv7.c
+++ b/driver/gator_events_armv7.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
@@ -141,9 +141,9 @@ static int gator_events_armv7_create_files(struct super_block *sb, struct dentry
for (i = 0; i < pmnc_counters; i++) {
char buf[40];
if (i == 0) {
- snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name);
+ snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name);
} else {
- snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i - 1);
+ snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i - 1);
}
dir = gatorfs_mkdir(sb, root, buf);
if (!dir) {
@@ -275,25 +275,27 @@ int gator_events_armv7_init(void)
switch (gator_cpuid()) {
case CORTEX_A5:
- pmnc_name = "Cortex-A5";
+ pmnc_name = "ARMv7_Cortex_A5";
pmnc_counters = 2;
break;
case CORTEX_A7:
- pmnc_name = "Cortex-A7";
+ pmnc_name = "ARMv7_Cortex_A7";
pmnc_counters = 4;
break;
case CORTEX_A8:
- pmnc_name = "Cortex-A8";
+ pmnc_name = "ARMv7_Cortex_A8";
pmnc_counters = 4;
break;
case CORTEX_A9:
- pmnc_name = "Cortex-A9";
+ pmnc_name = "ARMv7_Cortex_A9";
pmnc_counters = 6;
break;
+ // ARM Cortex A12 is not supported by version of Linux before 3.0
case CORTEX_A15:
- pmnc_name = "Cortex-A15";
+ pmnc_name = "ARMv7_Cortex_A15";
pmnc_counters = 6;
break;
+ // ARM Cortex A17 is not supported by version of Linux before 3.0
default:
return -1;
}
diff --git a/driver/gator_events_block.c b/driver/gator_events_block.c
index 691ef25..b2bc414 100644
--- a/driver/gator_events_block.c
+++ b/driver/gator_events_block.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/driver/gator_events_ccn-504.c b/driver/gator_events_ccn-504.c
index b892319..024ffc2 100644
--- a/driver/gator_events_ccn-504.c
+++ b/driver/gator_events_ccn-504.c
@@ -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/driver/gator_events_irq.c b/driver/gator_events_irq.c
index b11879a..facbdd6 100644
--- a/driver/gator_events_irq.c
+++ b/driver/gator_events_irq.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/driver/gator_events_l2c-310.c b/driver/gator_events_l2c-310.c
index ee521af..553f970 100644
--- a/driver/gator_events_l2c-310.c
+++ b/driver/gator_events_l2c-310.c
@@ -1,7 +1,7 @@
/**
* l2c310 (L2 Cache Controller) event counters for gator
*
- * 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/driver/gator_events_mali_4xx.c b/driver/gator_events_mali_4xx.c
index 6719c1e..85d4764 100644
--- a/driver/gator_events_mali_4xx.c
+++ b/driver/gator_events_mali_4xx.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/driver/gator_events_mali_4xx.h b/driver/gator_events_mali_4xx.h
index 413ad0f..976ca8c 100644
--- a/driver/gator_events_mali_4xx.h
+++ b/driver/gator_events_mali_4xx.h
@@ -1,5 +1,5 @@
/**
- * 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
diff --git a/driver/gator_events_mali_common.c b/driver/gator_events_mali_common.c
index 466ca16..dc58dcf 100644
--- a/driver/gator_events_mali_common.c
+++ b/driver/gator_events_mali_common.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2012-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/driver/gator_events_mali_common.h b/driver/gator_events_mali_common.h
index 509f9b6..41c2a3c 100644
--- a/driver/gator_events_mali_common.h
+++ b/driver/gator_events_mali_common.h
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2012-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/driver/gator_events_mali_t6xx.c b/driver/gator_events_mali_t6xx.c
index 7bf7d6a..76f14ee 100644
--- a/driver/gator_events_mali_t6xx.c
+++ b/driver/gator_events_mali_t6xx.c
@@ -1,5 +1,5 @@
/**
- * 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
@@ -15,7 +15,13 @@
#include <linux/slab.h>
#include <asm/io.h>
+#ifdef MALI_DIR_MIDGARD
+/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
+#include "mali_linux_trace.h"
+#else
+/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
#include "linux/mali_linux_trace.h"
+#endif
#include "gator_events_mali_common.h"
diff --git a/driver/gator_events_mali_t6xx_hw.c b/driver/gator_events_mali_t6xx_hw.c
index e406991..dfbc91f 100644
--- a/driver/gator_events_mali_t6xx_hw.c
+++ b/driver/gator_events_mali_t6xx_hw.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2012-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
@@ -16,9 +16,17 @@
#include <asm/io.h>
/* Mali T6xx DDK includes */
+#ifdef MALI_DIR_MIDGARD
+/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
+#include "mali_linux_trace.h"
+#include "mali_kbase.h"
+#include "mali_kbase_mem_linux.h"
+#else
+/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
#include "linux/mali_linux_trace.h"
#include "kbase/src/common/mali_kbase.h"
#include "kbase/src/linux/mali_kbase_mem_linux.h"
+#endif
#include "gator_events_mali_common.h"
diff --git a/driver/gator_events_mali_t6xx_hw_test.c b/driver/gator_events_mali_t6xx_hw_test.c
index efb32dd..ba6553f 100644
--- a/driver/gator_events_mali_t6xx_hw_test.c
+++ b/driver/gator_events_mali_t6xx_hw_test.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2012-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/driver/gator_events_meminfo.c b/driver/gator_events_meminfo.c
index 451290d..c633dfd 100644
--- a/driver/gator_events_meminfo.c
+++ b/driver/gator_events_meminfo.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
@@ -274,6 +274,28 @@ static int gator_events_meminfo_read(long long **buffer)
return meminfo_length;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+
+static inline unsigned long gator_get_mm_counter(struct mm_struct *mm, int member)
+{
+#ifdef SPLIT_RSS_COUNTING
+ long val = atomic_long_read(&mm->rss_stat.count[member]);
+ if (val < 0)
+ val = 0;
+ return (unsigned long)val;
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ return mm->rss_stat.count[member];
+#else
+ return atomic_long_read(&mm->rss_stat.count[member]);
+#endif
+#endif
+}
+
+#define get_mm_counter(mm, member) gator_get_mm_counter(mm, member)
+
+#endif
+
static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task)
{
struct mm_struct *mm;
@@ -302,7 +324,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct
// Derived from task_statm in fs/proc/task_mmu.c
if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) {
share = get_mm_counter(mm,
-#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
file_rss
#else
MM_FILEPAGES
@@ -338,7 +360,7 @@ static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct
if (meminfo_enabled[MEMINFO_MEMUSED]) {
value = share + get_mm_counter(mm,
-#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
anon_rss
#else
MM_ANONPAGES
diff --git a/driver/gator_events_mmapped.c b/driver/gator_events_mmapped.c
index f055e48..3b248ec 100644
--- a/driver/gator_events_mmapped.c
+++ b/driver/gator_events_mmapped.c
@@ -1,7 +1,7 @@
/*
* Example events provider
*
- * 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/driver/gator_events_net.c b/driver/gator_events_net.c
index 9c8d3a4..11c10e3 100644
--- a/driver/gator_events_net.c
+++ b/driver/gator_events_net.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/driver/gator_events_perf_pmu.c b/driver/gator_events_perf_pmu.c
index d472df9..8b2d67a 100644
--- a/driver/gator_events_perf_pmu.c
+++ b/driver/gator_events_perf_pmu.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/driver/gator_events_sched.c b/driver/gator_events_sched.c
index 29f4e39..9e39158 100644
--- a/driver/gator_events_sched.c
+++ b/driver/gator_events_sched.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/driver/gator_events_scorpion.c b/driver/gator_events_scorpion.c
index c91db12..8ca251a 100644
--- a/driver/gator_events_scorpion.c
+++ b/driver/gator_events_scorpion.c
@@ -1,5 +1,5 @@
/**
- * 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
diff --git a/driver/gator_fs.c b/driver/gator_fs.c
index fe6f83d..166cfe7 100644
--- a/driver/gator_fs.c
+++ b/driver/gator_fs.c
@@ -39,12 +39,7 @@ static const struct super_operations s_ops = {
.drop_inode = generic_delete_inode,
};
-ssize_t gatorfs_str_to_user(char const *str, char __user *buf, size_t count, loff_t *offset)
-{
- return simple_read_from_buffer(buf, count, offset, str, strlen(str));
-}
-
-ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
+static ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count, loff_t *offset)
{
char tmpbuf[TMPBUFSIZE];
size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val);
@@ -53,7 +48,7 @@ ssize_t gatorfs_ulong_to_user(unsigned long val, char __user *buf, size_t count,
return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
}
-ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset)
+static ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *offset)
{
char tmpbuf[TMPBUFSIZE];
size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%llu\n", val);
@@ -62,7 +57,7 @@ ssize_t gatorfs_u64_to_user(u64 val, char __user *buf, size_t count, loff_t *off
return simple_read_from_buffer(buf, count, offset, tmpbuf, maxlen);
}
-int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
+static int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t count)
{
char tmpbuf[TMPBUFSIZE];
unsigned long flags;
@@ -84,7 +79,7 @@ int gatorfs_ulong_from_user(unsigned long *val, char const __user *buf, size_t c
return 0;
}
-int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count)
+static int gatorfs_u64_from_user(u64 *val, char const __user *buf, size_t count)
{
char tmpbuf[TMPBUFSIZE];
unsigned long flags;
@@ -211,8 +206,8 @@ int gatorfs_create_ulong(struct super_block *sb, struct dentry *root,
return 0;
}
-int gatorfs_create_u64(struct super_block *sb, struct dentry *root,
- char const *name, u64 *val)
+static int gatorfs_create_u64(struct super_block *sb, struct dentry *root,
+ char const *name, u64 *val)
{
struct dentry *d = __gatorfs_create_file(sb, root, name,
&u64_fops, 0644);
@@ -235,8 +230,8 @@ int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root,
return 0;
}
-int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root,
- char const *name, u64 * val)
+static int gatorfs_create_ro_u64(struct super_block *sb, struct dentry *root,
+ char const *name, u64 * val)
{
struct dentry *d =
__gatorfs_create_file(sb, root, name, &u64_ro_fops, 0444);
@@ -258,29 +253,17 @@ static const struct file_operations atomic_ro_fops = {
.open = default_open,
};
-int gatorfs_create_ro_atomic(struct super_block *sb, struct dentry *root,
- char const *name, atomic_t *val)
-{
- struct dentry *d = __gatorfs_create_file(sb, root, name,
- &atomic_ro_fops, 0444);
- if (!d)
- return -EFAULT;
-
- d->d_inode->i_private = val;
- return 0;
-}
-
-int gatorfs_create_file(struct super_block *sb, struct dentry *root,
- char const *name, const struct file_operations *fops)
+static int gatorfs_create_file(struct super_block *sb, struct dentry *root,
+ char const *name, const struct file_operations *fops)
{
if (!__gatorfs_create_file(sb, root, name, fops, 0644))
return -EFAULT;
return 0;
}
-int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
- char const *name,
- const struct file_operations *fops, int perm)
+static int gatorfs_create_file_perm(struct super_block *sb, struct dentry *root,
+ char const *name,
+ const struct file_operations *fops, int perm)
{
if (!__gatorfs_create_file(sb, root, name, fops, perm))
return -EFAULT;
@@ -371,12 +354,12 @@ static struct file_system_type gatorfs_type = {
.kill_sb = kill_litter_super,
};
-int __init gatorfs_register(void)
+static int __init gatorfs_register(void)
{
return register_filesystem(&gatorfs_type);
}
-void gatorfs_unregister(void)
+static void gatorfs_unregister(void)
{
unregister_filesystem(&gatorfs_type);
}
diff --git a/driver/gator_hrtimer_gator.c b/driver/gator_hrtimer_gator.c
index b0c947a..7658455 100644
--- a/driver/gator_hrtimer_gator.c
+++ b/driver/gator_hrtimer_gator.c
@@ -1,5 +1,5 @@
/**
- * 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
@@ -7,10 +7,6 @@
*
*/
-// gator_hrtimer_perf.c is used if perf is supported
-// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers
-#if 1
-
void (*callback)(void);
DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer);
DEFINE_PER_CPU(ktime_t, hrtimer_expire);
@@ -82,5 +78,3 @@ static void gator_hrtimer_shutdown(void)
{
/* empty */
}
-
-#endif
diff --git a/driver/gator_hrtimer_perf.c b/driver/gator_hrtimer_perf.c
deleted file mode 100644
index 7b95399..0000000
--- a/driver/gator_hrtimer_perf.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * Copyright (C) ARM Limited 2011-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.
- *
- */
-
-// gator_hrtimer_gator.c is used if perf is not supported
-// update, gator_hrtimer_gator.c always used until issues resolved with perf hrtimers
-#if 0
-
-// Note: perf Cortex support added in 2.6.35 and PERF_COUNT_SW_CPU_CLOCK/hrtimer broken on 2.6.35 and 2.6.36
-// not relevant as this code is not active until 3.0.0, but wanted to document the issue
-
-void (*callback)(void);
-static int profiling_interval;
-static DEFINE_PER_CPU(struct perf_event *, perf_hrtimer);
-static DEFINE_PER_CPU(struct perf_event_attr *, perf_hrtimer_attr);
-
-static void gator_hrtimer_shutdown(void);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
-static void hrtimer_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
-#else
-static void hrtimer_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
-#endif
-{
- (*callback)();
-}
-
-static int gator_online_single_hrtimer(int cpu)
-{
- if (per_cpu(perf_hrtimer, cpu) != 0 || per_cpu(perf_hrtimer_attr, cpu) == 0)
- return 0;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
- per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler);
-#else
- per_cpu(perf_hrtimer, cpu) = perf_event_create_kernel_counter(per_cpu(perf_hrtimer_attr, cpu), cpu, 0, hrtimer_overflow_handler, 0);
-#endif
- if (IS_ERR(per_cpu(perf_hrtimer, cpu))) {
- per_cpu(perf_hrtimer, cpu) = NULL;
- return -1;
- }
-
- if (per_cpu(perf_hrtimer, cpu)->state != PERF_EVENT_STATE_ACTIVE) {
- perf_event_release_kernel(per_cpu(perf_hrtimer, cpu));
- per_cpu(perf_hrtimer, cpu) = NULL;
- return -1;
- }
-
- return 0;
-}
-
-static void gator_hrtimer_online(int cpu)
-{
- if (gator_online_single_hrtimer(cpu) < 0) {
- pr_debug("gator: unable to online the hrtimer on cpu%d\n", cpu);
- }
-}
-
-static void gator_hrtimer_offline(int cpu)
-{
- if (per_cpu(perf_hrtimer, cpu)) {
- perf_event_release_kernel(per_cpu(perf_hrtimer, cpu));
- per_cpu(perf_hrtimer, cpu) = NULL;
- }
-}
-
-static int gator_hrtimer_init(int interval, void (*func)(void))
-{
- u32 size = sizeof(struct perf_event_attr);
- int cpu;
-
- callback = func;
-
- // calculate profiling interval
- profiling_interval = 1000000000 / interval;
-
- for_each_present_cpu(cpu) {
- per_cpu(perf_hrtimer, cpu) = 0;
- per_cpu(perf_hrtimer_attr, cpu) = kmalloc(size, GFP_KERNEL);
- if (per_cpu(perf_hrtimer_attr, cpu) == 0) {
- gator_hrtimer_shutdown();
- return -1;
- }
-
- memset(per_cpu(perf_hrtimer_attr, cpu), 0, size);
- per_cpu(perf_hrtimer_attr, cpu)->type = PERF_TYPE_SOFTWARE;
- per_cpu(perf_hrtimer_attr, cpu)->size = size;
- per_cpu(perf_hrtimer_attr, cpu)->config = PERF_COUNT_SW_CPU_CLOCK;
- per_cpu(perf_hrtimer_attr, cpu)->sample_period = profiling_interval;
- per_cpu(perf_hrtimer_attr, cpu)->pinned = 1;
- }
-
- return 0;
-}
-
-static void gator_hrtimer_shutdown(void)
-{
- int cpu;
-
- for_each_present_cpu(cpu) {
- if (per_cpu(perf_hrtimer_attr, cpu)) {
- kfree(per_cpu(perf_hrtimer_attr, cpu));
- per_cpu(perf_hrtimer_attr, cpu) = NULL;
- }
- }
-}
-
-#endif
diff --git a/driver/gator_iks.c b/driver/gator_iks.c
index 24233d7..e90dfcc 100644
--- a/driver/gator_iks.c
+++ b/driver/gator_iks.c
@@ -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
@@ -147,11 +147,13 @@ static void gator_send_iks_core_names(void)
{
int cpu;
// Send the cpu names
+ preempt_disable();
for (cpu = 0; cpu < nr_cpu_ids; ++cpu) {
if (mpidr_cpus[cpu] != NULL) {
gator_send_core_name(cpu, mpidr_cpus[cpu]->cpuid, mpidr_cpus[cpu]);
}
}
+ preempt_enable();
}
static int gator_migrate_start(void)
diff --git a/driver/gator_main.c b/driver/gator_main.c
index 9773ae2..e67f7c5 100644
--- a/driver/gator_main.c
+++ b/driver/gator_main.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
@@ -8,7 +8,7 @@
*/
// This version must match the gator daemon version
-#define PROTOCOL_VERSION 17
+#define PROTOCOL_VERSION 18
static unsigned long gator_protocol_version = PROTOCOL_VERSION;
#include <linux/slab.h>
@@ -89,20 +89,27 @@ static unsigned long gator_protocol_version = PROTOCOL_VERSION;
#define MESSAGE_END_BACKTRACE 1
+// Name Frame Messages
#define MESSAGE_COOKIE 1
#define MESSAGE_THREAD_NAME 2
-#define HRTIMER_CORE_NAME 3
#define MESSAGE_LINK 4
+// GPU Trace Frame Messages
#define MESSAGE_GPU_START 1
#define MESSAGE_GPU_STOP 2
+// Scheduler Trace Frame Messages
#define MESSAGE_SCHED_SWITCH 1
#define MESSAGE_SCHED_EXIT 2
#define MESSAGE_SCHED_START 3
+// Idle Frame Messages
#define MESSAGE_IDLE_ENTER 1
-#define MESSAGE_IDLE_EXIT 2
+#define MESSAGE_IDLE_EXIT 2
+
+// Summary Frame Messages
+#define MESSAGE_SUMMARY 1
+#define MESSAGE_CORE_NAME 3
#define MAXSIZE_PACK32 5
#define MAXSIZE_PACK64 10
@@ -154,7 +161,13 @@ bool event_based_sampling;
static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait);
static DECLARE_WAIT_QUEUE_HEAD(gator_annotate_wait);
static struct timer_list gator_buffer_wake_up_timer;
-static bool gator_buffer_wake_stop;
+static bool gator_buffer_wake_run;
+// Initialize semaphore unlocked to initialize memory values
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+static DECLARE_MUTEX(gator_buffer_wake_sem);
+#else
+static DEFINE_SEMAPHORE(gator_buffer_wake_sem);
+#endif
static struct task_struct *gator_buffer_wake_thread;
static LIST_HEAD(gator_events);
@@ -164,21 +177,19 @@ static bool printed_monotonic_warning;
static bool sent_core_name[NR_CPUS];
+static DEFINE_PER_CPU(bool, in_scheduler_context);
+
/******************************************************************************
* Prototypes
******************************************************************************/
-static void buffer_check(int cpu, int buftype, u64 time);
-static void gator_commit_buffer(int cpu, int buftype, u64 time);
-static int buffer_bytes_available(int cpu, int buftype);
-static bool buffer_check_space(int cpu, int buftype, int bytes);
-static int contiguous_space_available(int cpu, int bufytpe);
-static void gator_buffer_write_packed_int(int cpu, int buftype, int x);
-static void gator_buffer_write_packed_int64(int cpu, int buftype, long long x);
-static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len);
-static void gator_buffer_write_string(int cpu, int buftype, const char *x);
-static void gator_add_trace(int cpu, unsigned long address);
-static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time);
static u64 gator_get_time(void);
+static void gator_op_create_files(struct super_block *sb, struct dentry *root);
+
+// gator_buffer is protected by being per_cpu and by having IRQs disabled when writing to it.
+// Most marshal_* calls take care of this except for marshal_cookie*, marshal_backtrace* and marshal_frame where the caller is responsible for doing so.
+// No synchronization is needed with the backtrace buffer as it is per cpu and is only used from the hrtimer.
+// The annotate_lock must be held when using the annotation buffer as it is not per cpu.
+// collect_counters which is the sole writer to the block counter frame is additionally protected by the per cpu collecting flag
// Size of the buffer, must be a power of 2. Effectively constant, set in gator_op_setup.
static uint32_t gator_buffer_size[NUM_GATOR_BUFS];
@@ -229,8 +240,10 @@ GATOR_EVENTS_LIST
/******************************************************************************
* Application Includes
******************************************************************************/
+#include "gator_fs.c"
+#include "gator_buffer_write.c"
+#include "gator_buffer.c"
#include "gator_marshaling.c"
-#include "gator_hrtimer_perf.c"
#include "gator_hrtimer_gator.c"
#include "gator_cookies.c"
#include "gator_annotate.c"
@@ -238,14 +251,12 @@ GATOR_EVENTS_LIST
#include "gator_trace_power.c"
#include "gator_trace_gpu.c"
#include "gator_backtrace.c"
-#include "gator_fs.c"
-#include "gator_pack.c"
/******************************************************************************
* Misc
******************************************************************************/
-const struct gator_cpu gator_cpus[] = {
+static const struct gator_cpu gator_cpus[] = {
{
.cpuid = ARM1136,
.core_name = "ARM1136",
@@ -277,52 +288,53 @@ const struct gator_cpu gator_cpus[] = {
{
.cpuid = CORTEX_A5,
.core_name = "Cortex-A5",
- .pmu_name = "ARMv7_Cortex_A5",
- .pmnc_name = "ARM_Cortex-A5",
+ .pmnc_name = "ARMv7_Cortex_A5",
.dt_name = "arm,cortex-a5",
.pmnc_counters = 2,
},
{
.cpuid = CORTEX_A7,
.core_name = "Cortex-A7",
- .pmu_name = "ARMv7_Cortex_A7",
- .pmnc_name = "ARM_Cortex-A7",
+ .pmnc_name = "ARMv7_Cortex_A7",
.dt_name = "arm,cortex-a7",
.pmnc_counters = 4,
},
{
.cpuid = CORTEX_A8,
.core_name = "Cortex-A8",
- .pmu_name = "ARMv7_Cortex_A8",
- .pmnc_name = "ARM_Cortex-A8",
+ .pmnc_name = "ARMv7_Cortex_A8",
.dt_name = "arm,cortex-a8",
.pmnc_counters = 4,
},
{
.cpuid = CORTEX_A9,
.core_name = "Cortex-A9",
- .pmu_name = "ARMv7_Cortex_A9",
- .pmnc_name = "ARM_Cortex-A9",
+ .pmnc_name = "ARMv7_Cortex_A9",
.dt_name = "arm,cortex-a9",
.pmnc_counters = 6,
},
{
.cpuid = CORTEX_A12,
.core_name = "Cortex-A12",
- .pmu_name = "ARMv7_Cortex_A12",
- .pmnc_name = "ARM_Cortex-A12",
+ .pmnc_name = "ARMv7_Cortex_A12",
.dt_name = "arm,cortex-a12",
.pmnc_counters = 6,
},
{
.cpuid = CORTEX_A15,
.core_name = "Cortex-A15",
- .pmu_name = "ARMv7_Cortex_A15",
- .pmnc_name = "ARM_Cortex-A15",
+ .pmnc_name = "ARMv7_Cortex_A15",
.dt_name = "arm,cortex-a15",
.pmnc_counters = 6,
},
{
+ .cpuid = CORTEX_A17,
+ .core_name = "Cortex-A17",
+ .pmnc_name = "ARMv7_Cortex_A17",
+ .dt_name = "arm,cortex-a17",
+ .pmnc_counters = 6,
+ },
+ {
.cpuid = SCORPION,
.core_name = "Scorpion",
.pmnc_name = "Scorpion",
@@ -401,7 +413,7 @@ const struct gator_cpu *gator_find_cpu_by_pmu_name(const char *const name)
for (i = 0; gator_cpus[i].cpuid != 0; ++i) {
const struct gator_cpu *const gator_cpu = &gator_cpus[i];
- if (gator_cpu->pmu_name != NULL && strcmp(gator_cpu->pmu_name, name) == 0) {
+ if (gator_cpu->pmnc_name != NULL && strcmp(gator_cpu->pmnc_name, name) == 0) {
return gator_cpu;
}
}
@@ -431,10 +443,15 @@ static void gator_buffer_wake_up(unsigned long data)
static int gator_buffer_wake_func(void *data)
{
- while (!gator_buffer_wake_stop) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- if (gator_buffer_wake_stop) {
+ for (;;) {
+ if (down_killable(&gator_buffer_wake_sem)) {
+ break;
+ }
+
+ // Eat up any pending events
+ while (!down_trylock(&gator_buffer_wake_sem));
+
+ if (!gator_buffer_wake_run) {
break;
}
@@ -464,173 +481,6 @@ static bool buffer_commit_ready(int *cpu, int *buftype)
}
/******************************************************************************
- * Buffer management
- ******************************************************************************/
-static int buffer_bytes_available(int cpu, int buftype)
-{
- int remaining, filled;
-
- filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype];
- if (filled < 0) {
- filled += gator_buffer_size[buftype];
- }
-
- remaining = gator_buffer_size[buftype] - filled;
-
- if (per_cpu(buffer_space_available, cpu)[buftype]) {
- // Give some extra room; also allows space to insert the overflow error packet
- remaining -= 200;
- } else {
- // Hysteresis, prevents multiple overflow messages
- remaining -= 2000;
- }
-
- return remaining;
-}
-
-static int contiguous_space_available(int cpu, int buftype)
-{
- int remaining = buffer_bytes_available(cpu, buftype);
- int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype];
- if (remaining < contiguous)
- return remaining;
- else
- return contiguous;
-}
-
-static bool buffer_check_space(int cpu, int buftype, int bytes)
-{
- int remaining = buffer_bytes_available(cpu, buftype);
-
- if (remaining < bytes) {
- per_cpu(buffer_space_available, cpu)[buftype] = false;
- } else {
- per_cpu(buffer_space_available, cpu)[buftype] = true;
- }
-
- return per_cpu(buffer_space_available, cpu)[buftype];
-}
-
-static void gator_buffer_write_bytes(int cpu, int buftype, const char *x, int len)
-{
- int i;
- u32 write = per_cpu(gator_buffer_write, cpu)[buftype];
- u32 mask = gator_buffer_mask[buftype];
- char *buffer = per_cpu(gator_buffer, cpu)[buftype];
-
- for (i = 0; i < len; i++) {
- buffer[write] = x[i];
- write = (write + 1) & mask;
- }
-
- per_cpu(gator_buffer_write, cpu)[buftype] = write;
-}
-
-static void gator_buffer_write_string(int cpu, int buftype, const char *x)
-{
- int len = strlen(x);
- gator_buffer_write_packed_int(cpu, buftype, len);
- gator_buffer_write_bytes(cpu, buftype, x, len);
-}
-
-static void gator_commit_buffer(int cpu, int buftype, u64 time)
-{
- int type_length, commit, length, byte;
-
- if (!per_cpu(gator_buffer, cpu)[buftype])
- return;
-
- // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
- type_length = gator_response_type ? 1 : 0;
- commit = per_cpu(gator_buffer_commit, cpu)[buftype];
- length = per_cpu(gator_buffer_write, cpu)[buftype] - commit;
- if (length < 0) {
- length += gator_buffer_size[buftype];
- }
- length = length - type_length - sizeof(s32);
-
- if (length <= FRAME_HEADER_SIZE) {
- // Nothing to write, only the frame header is present
- return;
- }
-
- for (byte = 0; byte < sizeof(s32); byte++) {
- per_cpu(gator_buffer, cpu)[buftype][(commit + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF;
- }
-
- per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype];
-
- if (gator_live_rate > 0) {
- while (time > per_cpu(gator_buffer_commit_time, cpu)) {
- per_cpu(gator_buffer_commit_time, cpu) += gator_live_rate;
- }
- }
-
- marshal_frame(cpu, buftype);
-
- // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater
- if (per_cpu(in_scheduler_context, cpu)) {
-#ifndef CONFIG_PREEMPT_RT_FULL
- // mod_timer can not be used in interrupt context in RT-Preempt full
- mod_timer(&gator_buffer_wake_up_timer, jiffies + 1);
-#endif
- } else {
- wake_up_process(gator_buffer_wake_thread);
- }
-}
-
-static void buffer_check(int cpu, int buftype, u64 time)
-{
- int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype];
- if (filled < 0) {
- filled += gator_buffer_size[buftype];
- }
- if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) {
- gator_commit_buffer(cpu, buftype, time);
- }
-}
-
-static void gator_add_trace(int cpu, unsigned long address)
-{
- off_t offset = 0;
- unsigned long cookie = get_address_cookie(cpu, current, address & ~1, &offset);
-
- if (cookie == NO_COOKIE || cookie == UNRESOLVED_COOKIE) {
- offset = address;
- }
-
- marshal_backtrace(offset & ~1, cookie);
-}
-
-static void gator_add_sample(int cpu, struct pt_regs *const regs, u64 time)
-{
- bool inKernel;
- unsigned long exec_cookie;
-
- if (!regs)
- return;
-
- inKernel = !user_mode(regs);
- exec_cookie = get_exec_cookie(cpu, current);
-
- if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel, time))
- return;
-
- if (inKernel) {
- kernel_backtrace(cpu, regs);
- } else {
- // Cookie+PC
- gator_add_trace(cpu, PC_REG);
-
- // Backtrace
- if (gator_backtrace_depth)
- arm_backtrace_eabi(cpu, regs, gator_backtrace_depth);
- }
-
- marshal_backtrace_footer(time);
-}
-
-/******************************************************************************
* hrtimer interrupt processing
******************************************************************************/
static void gator_timer_interrupt(void)
@@ -721,7 +571,8 @@ static void gator_timer_stop(void)
}
#if defined(__arm__) || defined(__aarch64__)
-static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu) {
+static void gator_send_core_name(int cpu, const u32 cpuid, const struct gator_cpu *const gator_cpu)
+{
const char *core_name = NULL;
char core_name_buf[32];
@@ -788,7 +639,7 @@ static void gator_timer_online_dispatch(int cpu, bool migrate)
#include "gator_iks.c"
-int gator_timer_start(unsigned long sample_rate)
+static int gator_timer_start(unsigned long sample_rate)
{
int cpu;
@@ -944,7 +795,6 @@ static void gator_summary(void)
struct timespec ts;
char uname_buf[512];
void (*m2b)(struct timespec *ts);
- unsigned long flags;
snprintf(uname_buf, sizeof(uname_buf), "%s %s %s %s %s GNU/Linux", utsname()->sysname, utsname()->nodename, utsname()->release, utsname()->version, utsname()->machine);
@@ -959,14 +809,14 @@ static void gator_summary(void)
}
uptime = timespec_to_ns(&ts);
- // Disable interrupts as gator_get_time calls smp_processor_id to verify time is monotonic
- local_irq_save(flags);
+ // Disable preemption as gator_get_time calls smp_processor_id to verify time is monotonic
+ preempt_disable();
// Set monotonic_started to zero as gator_get_time is uptime minus monotonic_started
gator_monotonic_started = 0;
gator_monotonic_started = gator_get_time();
- local_irq_restore(flags);
marshal_summary(timestamp, uptime, gator_monotonic_started, uname_buf);
+ preempt_enable();
}
int gator_events_install(struct gator_interface *interface)
@@ -1019,7 +869,7 @@ static int gator_start(void)
unsigned long cpu, i;
struct gator_interface *gi;
- gator_buffer_wake_stop = false;
+ gator_buffer_wake_run = true;
if (IS_ERR(gator_buffer_wake_thread = kthread_run(gator_buffer_wake_func, NULL, "gator_bwake"))) {
goto bwake_failure;
}
@@ -1094,8 +944,9 @@ cookies_failure:
events_failure:
gator_migrate_stop();
migrate_failure:
- gator_buffer_wake_stop = true;
- wake_up_process(gator_buffer_wake_thread);
+ gator_buffer_wake_run = false;
+ up(&gator_buffer_wake_sem);
+ gator_buffer_wake_thread = NULL;
bwake_failure:
return -1;
@@ -1121,8 +972,9 @@ static void gator_stop(void)
gator_migrate_stop();
- gator_buffer_wake_stop = true;
- wake_up_process(gator_buffer_wake_thread);
+ gator_buffer_wake_run = false;
+ up(&gator_buffer_wake_sem);
+ gator_buffer_wake_thread = NULL;
}
/******************************************************************************
@@ -1417,7 +1269,7 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, size_t
return written > 0 ? written : -EFAULT;
}
-const struct file_operations gator_event_buffer_fops = {
+static const struct file_operations gator_event_buffer_fops = {
.open = userspace_buffer_open,
.release = userspace_buffer_release,
.read = userspace_buffer_read,
@@ -1452,7 +1304,7 @@ static const struct file_operations depth_fops = {
.write = depth_write
};
-void gator_op_create_files(struct super_block *sb, struct dentry *root)
+static void gator_op_create_files(struct super_block *sb, struct dentry *root)
{
struct dentry *dir;
struct gator_interface *gi;
diff --git a/driver/gator_marshaling.c b/driver/gator_marshaling.c
index af80ff6..fd413ad 100644
--- a/driver/gator_marshaling.c
+++ b/driver/gator_marshaling.c
@@ -1,5 +1,5 @@
/**
- * Copyright (C) ARM Limited 2012-2013. All rights reserved.
+ * Copyright (C) ARM Limited 2012-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
@@ -29,6 +29,7 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon
int cpu = 0;
local_irq_save(flags);
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_SUMMARY);
gator_buffer_write_string(cpu, SUMMARY_BUF, NEWLINE_CANARY);
gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, timestamp);
gator_buffer_write_packed_int64(cpu, SUMMARY_BUF, uptime);
@@ -52,8 +53,8 @@ static void marshal_summary(long long timestamp, long long uptime, long long mon
#endif
gator_buffer_write_string(cpu, SUMMARY_BUF, "");
// Commit the buffer now so it can be one of the first frames read by Streamline
- gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time());
local_irq_restore(flags);
+ gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time());
}
static bool marshal_cookie_header(const char *text)
@@ -85,8 +86,8 @@ static void marshal_thread_name(int pid, char *name)
gator_buffer_write_packed_int(cpu, NAME_BUF, pid);
gator_buffer_write_string(cpu, NAME_BUF, name);
}
- buffer_check(cpu, NAME_BUF, time);
local_irq_restore(flags);
+ buffer_check(cpu, NAME_BUF, time);
}
static void marshal_link(int cookie, int tgid, int pid)
@@ -103,12 +104,12 @@ static void marshal_link(int cookie, int tgid, int pid)
gator_buffer_write_packed_int(cpu, NAME_BUF, tgid);
gator_buffer_write_packed_int(cpu, NAME_BUF, pid);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, NAME_BUF, time);
- local_irq_restore(flags);
}
-static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel, u64 time)
+static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, u64 time)
{
int cpu = get_physical_cpu();
if (!buffer_check_space(cpu, BACKTRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32 + gator_backtrace_depth * 2 * MAXSIZE_PACK32)) {
@@ -122,14 +123,16 @@ static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inK
gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie);
gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid);
gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid);
- gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, inKernel);
return true;
}
-static void marshal_backtrace(unsigned long address, int cookie)
+static void marshal_backtrace(unsigned long address, int cookie, int in_kernel)
{
int cpu = get_physical_cpu();
+ if (cookie == 0 && !in_kernel) {
+ cookie = UNRESOLVED_COOKIE;
+ }
gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie);
gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, address);
}
@@ -224,9 +227,9 @@ static void marshal_event_single(int core, int key, int value)
gator_buffer_write_packed_int(cpu, COUNTER_BUF, key);
gator_buffer_write_packed_int(cpu, COUNTER_BUF, value);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, COUNTER_BUF, time);
- local_irq_restore(flags);
}
#endif
@@ -248,9 +251,9 @@ static void marshal_sched_gpu_start(int unit, int core, int tgid, int pid)
gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid);
gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, GPU_TRACE_BUF, time);
- local_irq_restore(flags);
}
static void marshal_sched_gpu_stop(int unit, int core)
@@ -269,9 +272,9 @@ static void marshal_sched_gpu_stop(int unit, int core)
gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit);
gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, GPU_TRACE_BUF, time);
- local_irq_restore(flags);
}
static void marshal_sched_trace_start(int tgid, int pid, int cookie)
@@ -291,9 +294,9 @@ static void marshal_sched_trace_start(int tgid, int pid, int cookie)
gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, SCHED_TRACE_BUF, time);
- local_irq_restore(flags);
}
static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state)
@@ -314,9 +317,9 @@ static void marshal_sched_trace_switch(int tgid, int pid, int cookie, int state)
gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie);
gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, SCHED_TRACE_BUF, time);
- local_irq_restore(flags);
}
static void marshal_sched_trace_exit(int tgid, int pid)
@@ -334,9 +337,9 @@ static void marshal_sched_trace_exit(int tgid, int pid)
gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, time);
gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, SCHED_TRACE_BUF, time);
- local_irq_restore(flags);
}
#if GATOR_CPU_FREQ_SUPPORT
@@ -353,80 +356,26 @@ static void marshal_idle(int core, int state)
gator_buffer_write_packed_int64(cpu, IDLE_BUF, time);
gator_buffer_write_packed_int(cpu, IDLE_BUF, core);
}
+ local_irq_restore(flags);
// Check and commit; commit is set to occur once buffer is 3/4 full
buffer_check(cpu, IDLE_BUF, time);
- local_irq_restore(flags);
}
#endif
-static void marshal_frame(int cpu, int buftype)
-{
- int frame;
-
- if (!per_cpu(gator_buffer, cpu)[buftype]) {
- return;
- }
-
- switch (buftype) {
- case SUMMARY_BUF:
- frame = FRAME_SUMMARY;
- break;
- case BACKTRACE_BUF:
- frame = FRAME_BACKTRACE;
- break;
- case NAME_BUF:
- frame = FRAME_NAME;
- break;
- case COUNTER_BUF:
- frame = FRAME_COUNTER;
- break;
- case BLOCK_COUNTER_BUF:
- frame = FRAME_BLOCK_COUNTER;
- break;
- case ANNOTATE_BUF:
- frame = FRAME_ANNOTATE;
- break;
- case SCHED_TRACE_BUF:
- frame = FRAME_SCHED_TRACE;
- break;
- case GPU_TRACE_BUF:
- frame = FRAME_GPU_TRACE;
- break;
- case IDLE_BUF:
- frame = FRAME_IDLE;
- break;
- default:
- frame = -1;
- break;
- }
-
- // add response type
- if (gator_response_type > 0) {
- gator_buffer_write_packed_int(cpu, buftype, gator_response_type);
- }
-
- // leave space for 4-byte unpacked length
- per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + sizeof(s32)) & gator_buffer_mask[buftype];
-
- // add frame type and core number
- gator_buffer_write_packed_int(cpu, buftype, frame);
- gator_buffer_write_packed_int(cpu, buftype, cpu);
-}
-
#if defined(__arm__) || defined(__aarch64__)
static void marshal_core_name(const int core, const int cpuid, const char *name)
{
int cpu = get_physical_cpu();
unsigned long flags;
local_irq_save(flags);
- if (buffer_check_space(cpu, NAME_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) {
- gator_buffer_write_packed_int(cpu, NAME_BUF, HRTIMER_CORE_NAME);
- gator_buffer_write_packed_int(cpu, NAME_BUF, core);
- gator_buffer_write_packed_int(cpu, NAME_BUF, cpuid);
- gator_buffer_write_string(cpu, NAME_BUF, name);
+ if (buffer_check_space(cpu, SUMMARY_BUF, MAXSIZE_PACK32 + MAXSIZE_CORE_NAME)) {
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, MESSAGE_CORE_NAME);
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, core);
+ gator_buffer_write_packed_int(cpu, SUMMARY_BUF, cpuid);
+ gator_buffer_write_string(cpu, SUMMARY_BUF, name);
}
// Commit core names now so that they can show up in live
- gator_commit_buffer(cpu, NAME_BUF, gator_get_time());
local_irq_restore(flags);
+ gator_commit_buffer(cpu, SUMMARY_BUF, gator_get_time());
}
#endif
diff --git a/driver/gator_trace_gpu.c b/driver/gator_trace_gpu.c
index be135b4..6332098 100644
--- a/driver/gator_trace_gpu.c
+++ b/driver/gator_trace_gpu.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
@@ -14,8 +14,15 @@
#include <linux/math64.h>
#ifdef MALI_SUPPORT
+#ifdef MALI_DIR_MIDGARD
+/* New DDK Directory structure with kernel/drivers/gpu/arm/midgard*/
+#include "mali_linux_trace.h"
+#else
+/* Old DDK Directory structure with kernel/drivers/gpu/arm/t6xx*/
#include "linux/mali_linux_trace.h"
#endif
+#endif
+
#include "gator_trace_gpu.h"
/*
@@ -235,7 +242,7 @@ GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core))
mali_gpu_stop(gpu_unit, gpu_core);
}
-int gator_trace_gpu_start(void)
+static int gator_trace_gpu_start(void)
{
/*
* Returns nonzero for installation failed
@@ -271,7 +278,7 @@ int gator_trace_gpu_start(void)
return 0;
}
-void gator_trace_gpu_stop(void)
+static void gator_trace_gpu_stop(void)
{
#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx)
if (mali_timeline_trace_registered) {
diff --git a/driver/gator_trace_gpu.h b/driver/gator_trace_gpu.h
index bb0f42d..5113d45 100644
--- a/driver/gator_trace_gpu.h
+++ b/driver/gator_trace_gpu.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/driver/gator_trace_power.c b/driver/gator_trace_power.c
index 272e056..1895bb9 100644
--- a/driver/gator_trace_power.c
+++ b/driver/gator_trace_power.c
@@ -1,5 +1,5 @@
/**
- * 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
@@ -166,7 +166,7 @@ static void gator_trace_power_stop(void)
}
}
-void gator_trace_power_init(void)
+static void gator_trace_power_init(void)
{
int i;
for (i = 0; i < POWER_TOTAL; i++) {
@@ -197,7 +197,7 @@ static void gator_trace_power_stop(void)
{
}
-void gator_trace_power_init(void)
+static void gator_trace_power_init(void)
{
}
#endif
diff --git a/driver/gator_trace_sched.c b/driver/gator_trace_sched.c
index 332b3f6..52990e9 100644
--- a/driver/gator_trace_sched.c
+++ b/driver/gator_trace_sched.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
@@ -22,7 +22,6 @@ enum {
static DEFINE_PER_CPU(uint64_t *, taskname_keys);
static DEFINE_PER_CPU(int, collecting);
-static DEFINE_PER_CPU(bool, in_scheduler_context);
// this array is never read as the cpu wait charts are derived counters
// the files are needed, nonetheless, to show that these counters are available
@@ -52,7 +51,7 @@ static int sched_trace_create_files(struct super_block *sb, struct dentry *root)
return 0;
}
-void emit_pid_name(struct task_struct *task)
+static void emit_pid_name(struct task_struct *task)
{
bool found = false;
char taskcomm[TASK_COMM_LEN + 3];
@@ -116,20 +115,21 @@ static void collect_counters(u64 time, struct task_struct *task)
// Commit buffers on timeout
if (gator_live_rate > 0 && time >= per_cpu(gator_buffer_commit_time, cpu)) {
static const int buftypes[] = { NAME_BUF, COUNTER_BUF, BLOCK_COUNTER_BUF, SCHED_TRACE_BUF };
- unsigned long flags;
int i;
- local_irq_save(flags);
for (i = 0; i < ARRAY_SIZE(buftypes); ++i) {
gator_commit_buffer(cpu, buftypes[i], time);
}
- local_irq_restore(flags);
+ // spinlocks are noops on uniprocessor machines and mutexes do not work in sched_switch context in
+ // RT-Preempt full, so disable proactive flushing of the annotate frame on uniprocessor machines.
+#ifdef CONFIG_SMP
// Try to preemptively flush the annotate buffer to reduce the chance of the buffer being full
if (on_primary_core() && spin_trylock(&annotate_lock)) {
gator_commit_buffer(0, ANNOTATE_BUF, time);
spin_unlock(&annotate_lock);
}
+#endif
}
}
}
@@ -222,7 +222,7 @@ fail_sched_process_fork:
return -1;
}
-int gator_trace_sched_start(void)
+static int gator_trace_sched_start(void)
{
int cpu, size;
@@ -237,7 +237,7 @@ int gator_trace_sched_start(void)
return register_scheduler_tracepoints();
}
-void gator_trace_sched_offline(void)
+static void gator_trace_sched_offline(void)
{
trace_sched_insert_idle();
}
@@ -250,7 +250,7 @@ static void unregister_scheduler_tracepoints(void)
pr_debug("gator: unregistered tracepoints\n");
}
-void gator_trace_sched_stop(void)
+static void gator_trace_sched_stop(void)
{
int cpu;
unregister_scheduler_tracepoints();
@@ -260,7 +260,7 @@ void gator_trace_sched_stop(void)
}
}
-void gator_trace_sched_init(void)
+static void gator_trace_sched_init(void)
{
int i;
for (i = 0; i < CPU_WAIT_TOTAL; i++) {
diff --git a/driver/mali/mali_mjollnir_profiling_gator_api.h b/driver/mali/mali_mjollnir_profiling_gator_api.h
index 347a4fe..ff00d90 100644
--- a/driver/mali/mali_mjollnir_profiling_gator_api.h
+++ b/driver/mali/mali_mjollnir_profiling_gator_api.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/driver/mali/mali_utgard_profiling_gator_api.h b/driver/mali/mali_utgard_profiling_gator_api.h
index 559647a..43c5760 100644
--- a/driver/mali/mali_utgard_profiling_gator_api.h
+++ b/driver/mali/mali_utgard_profiling_gator_api.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/driver/mali_t6xx.mk b/driver/mali_t6xx.mk
index 1a98c1c..059d47a 100644
--- a/driver/mali_t6xx.mk
+++ b/driver/mali_t6xx.mk
@@ -10,8 +10,17 @@ EXTRA_CFLAGS += -DMALI_USE_UMP=1 \
-DMALI_NO_MALI=0
DDK_DIR ?= .
+ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/t6xx),)
KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase
OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/t6xx/kbase/osk
+endif
+
+ifneq ($(wildcard $(DDK_DIR)/drivers/gpu/arm/midgard),)
+KBASE_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard
+OSK_DIR = $(DDK_DIR)/drivers/gpu/arm/midgard/osk
+EXTRA_CFLAGS += -DMALI_DIR_MIDGARD=1
+endif
+
UMP_DIR = $(DDK_DIR)/include/linux
# Include directories in the DDK