From 685fec4d4f4fb6bca850be6659e795e8368ce613 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Sat, 17 Mar 2012 10:25:47 +0000 Subject: gator/daemon: Version 5.9 Signed-off-by: Jon Medhurst --- drivers/gator/daemon/Android.mk | 34 ++ drivers/gator/daemon/CapturedXML.cpp | 97 ++++++ drivers/gator/daemon/CapturedXML.h | 23 ++ drivers/gator/daemon/Child.cpp | 304 +++++++++++++++++ drivers/gator/daemon/Child.h | 35 ++ drivers/gator/daemon/Collector.cpp | 266 +++++++++++++++ drivers/gator/daemon/Collector.h | 38 +++ drivers/gator/daemon/ConfigurationXML.cpp | 156 +++++++++ drivers/gator/daemon/ConfigurationXML.h | 29 ++ drivers/gator/daemon/Fifo.cpp | 103 ++++++ drivers/gator/daemon/Fifo.h | 41 +++ drivers/gator/daemon/LocalCapture.cpp | 208 ++++++++++++ drivers/gator/daemon/LocalCapture.h | 27 ++ drivers/gator/daemon/Logging.cpp | 79 +++++ drivers/gator/daemon/Logging.h | 45 +++ drivers/gator/daemon/Makefile | 40 +++ drivers/gator/daemon/OlySocket.cpp | 257 ++++++++++++++ drivers/gator/daemon/OlySocket.h | 38 +++ drivers/gator/daemon/OlyUtility.cpp | 167 +++++++++ drivers/gator/daemon/OlyUtility.h | 35 ++ drivers/gator/daemon/RequestXML.cpp | 61 ++++ drivers/gator/daemon/RequestXML.h | 33 ++ drivers/gator/daemon/Sender.cpp | 106 ++++++ drivers/gator/daemon/Sender.h | 38 +++ drivers/gator/daemon/SessionData.cpp | 103 ++++++ drivers/gator/daemon/SessionData.h | 68 ++++ drivers/gator/daemon/SessionXML.cpp | 119 +++++++ drivers/gator/daemon/SessionXML.h | 40 +++ drivers/gator/daemon/StreamlineSetup.cpp | 321 ++++++++++++++++++ drivers/gator/daemon/StreamlineSetup.h | 46 +++ drivers/gator/daemon/XMLOut.cpp | 175 ++++++++++ drivers/gator/daemon/XMLOut.h | 45 +++ drivers/gator/daemon/XMLReader.cpp | 164 +++++++++ drivers/gator/daemon/XMLReader.h | 27 ++ drivers/gator/daemon/configuration.xml | 39 +++ drivers/gator/daemon/events-ARM11.xml | 43 +++ drivers/gator/daemon/events-ARM11MPCore.xml | 30 ++ drivers/gator/daemon/events-Cortex-A15.xml | 77 +++++ drivers/gator/daemon/events-Cortex-A5.xml | 39 +++ drivers/gator/daemon/events-Cortex-A7.xml | 50 +++ drivers/gator/daemon/events-Cortex-A8.xml | 58 ++++ drivers/gator/daemon/events-Cortex-A9.xml | 67 ++++ drivers/gator/daemon/events-Krait-architected.xml | 28 ++ drivers/gator/daemon/events-L2C-310.xml | 21 ++ drivers/gator/daemon/events-Linux.xml | 16 + drivers/gator/daemon/events-Mali-400.xml | 395 ++++++++++++++++++++++ drivers/gator/daemon/events-Scorpion.xml | 113 +++++++ drivers/gator/daemon/events-ScorpionMP.xml | 96 ++++++ drivers/gator/daemon/events_footer.xml | 1 + drivers/gator/daemon/events_header.xml | 2 + drivers/gator/daemon/main.cpp | 306 +++++++++++++++++ 51 files changed, 4749 insertions(+) create mode 100644 drivers/gator/daemon/Android.mk create mode 100644 drivers/gator/daemon/CapturedXML.cpp create mode 100644 drivers/gator/daemon/CapturedXML.h create mode 100644 drivers/gator/daemon/Child.cpp create mode 100644 drivers/gator/daemon/Child.h create mode 100644 drivers/gator/daemon/Collector.cpp create mode 100644 drivers/gator/daemon/Collector.h create mode 100644 drivers/gator/daemon/ConfigurationXML.cpp create mode 100644 drivers/gator/daemon/ConfigurationXML.h create mode 100644 drivers/gator/daemon/Fifo.cpp create mode 100644 drivers/gator/daemon/Fifo.h create mode 100644 drivers/gator/daemon/LocalCapture.cpp create mode 100644 drivers/gator/daemon/LocalCapture.h create mode 100644 drivers/gator/daemon/Logging.cpp create mode 100644 drivers/gator/daemon/Logging.h create mode 100644 drivers/gator/daemon/Makefile create mode 100644 drivers/gator/daemon/OlySocket.cpp create mode 100644 drivers/gator/daemon/OlySocket.h create mode 100644 drivers/gator/daemon/OlyUtility.cpp create mode 100644 drivers/gator/daemon/OlyUtility.h create mode 100644 drivers/gator/daemon/RequestXML.cpp create mode 100644 drivers/gator/daemon/RequestXML.h create mode 100644 drivers/gator/daemon/Sender.cpp create mode 100644 drivers/gator/daemon/Sender.h create mode 100644 drivers/gator/daemon/SessionData.cpp create mode 100644 drivers/gator/daemon/SessionData.h create mode 100644 drivers/gator/daemon/SessionXML.cpp create mode 100644 drivers/gator/daemon/SessionXML.h create mode 100644 drivers/gator/daemon/StreamlineSetup.cpp create mode 100644 drivers/gator/daemon/StreamlineSetup.h create mode 100644 drivers/gator/daemon/XMLOut.cpp create mode 100644 drivers/gator/daemon/XMLOut.h create mode 100644 drivers/gator/daemon/XMLReader.cpp create mode 100644 drivers/gator/daemon/XMLReader.h create mode 100644 drivers/gator/daemon/configuration.xml create mode 100644 drivers/gator/daemon/events-ARM11.xml create mode 100644 drivers/gator/daemon/events-ARM11MPCore.xml create mode 100644 drivers/gator/daemon/events-Cortex-A15.xml create mode 100644 drivers/gator/daemon/events-Cortex-A5.xml create mode 100644 drivers/gator/daemon/events-Cortex-A7.xml create mode 100644 drivers/gator/daemon/events-Cortex-A8.xml create mode 100644 drivers/gator/daemon/events-Cortex-A9.xml create mode 100644 drivers/gator/daemon/events-Krait-architected.xml create mode 100644 drivers/gator/daemon/events-L2C-310.xml create mode 100644 drivers/gator/daemon/events-Linux.xml create mode 100644 drivers/gator/daemon/events-Mali-400.xml create mode 100644 drivers/gator/daemon/events-Scorpion.xml create mode 100644 drivers/gator/daemon/events-ScorpionMP.xml create mode 100644 drivers/gator/daemon/events_footer.xml create mode 100644 drivers/gator/daemon/events_header.xml create mode 100644 drivers/gator/daemon/main.cpp diff --git a/drivers/gator/daemon/Android.mk b/drivers/gator/daemon/Android.mk new file mode 100644 index 00000000000..14aeda2d114 --- /dev/null +++ b/drivers/gator/daemon/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +$(shell cd $(LOCAL_PATH);cat events_header.xml events-*\.xml events_footer.xml > events.xml;xxd -i events.xml > events_xml.h;xxd -i configuration.xml > configuration_xml.h) + +LOCAL_CFLAGS += -Wall -O3 -ftree-vectorize + +LOCAL_SRC_FILES:= \ + CapturedXML.cpp \ + Child.cpp \ + Collector.cpp \ + ConfigurationXML.cpp \ + Fifo.cpp \ + LocalCapture.cpp \ + Logging.cpp \ + main.cpp \ + OlySocket.cpp \ + OlyUtility.cpp \ + RequestXML.cpp \ + Sender.cpp \ + SessionData.cpp \ + SessionXML.cpp \ + StreamlineSetup.cpp \ + XMLOut.cpp \ + XMLReader.cpp + +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_MODULE:= gatord +LOCAL_MODULE_TAGS:= optional + +LOCAL_LDLIBS := -lz -llog + +include $(BUILD_EXECUTABLE) diff --git a/drivers/gator/daemon/CapturedXML.cpp b/drivers/gator/daemon/CapturedXML.cpp new file mode 100644 index 00000000000..51076dd4871 --- /dev/null +++ b/drivers/gator/daemon/CapturedXML.cpp @@ -0,0 +1,97 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include "SessionData.h" +#include "CapturedXML.h" +#include "Logging.h" +#include "OlyUtility.h" + +extern void handleException(); + +CapturedXML::CapturedXML() { +} + +CapturedXML::~CapturedXML() { +} + +const char* CapturedXML::getXML() { + bool perfCounters = false; + int x; + + clearXmlString(); + xmlHeader(); + + for (x=0; xmPerfCounterEnabled[x]) { + perfCounters = true; + break; + } + } + + startElement("captured"); + attributeInt("version", 1); + attributeInt("protocol", PROTOCOL_VERSION); + if (gSessionData->mBytes > 0) { // Send the following only after the capture is complete + if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) + attributeUInt("created", time(NULL)); // Valid until the year 2038 + } + attributeUInt("bytes", gSessionData->mBytes); + } + startElement("target"); + attributeString("name", gSessionData->mCoreName); + attributeInt("sample_rate", gSessionData->mSampleRate); + attributeInt("cores", gSessionData->mCores); + endElement("target"); + if (perfCounters) { + startElement("counters"); + for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { + if (gSessionData->mPerfCounterEnabled[x]) { + startElement("counter"); + attributeString("title", gSessionData->mPerfCounterTitle[x]); + attributeString("name", gSessionData->mPerfCounterName[x]); + attributeHex8("color", gSessionData->mPerfCounterColor[x]); + attributeHex8("key", gSessionData->mPerfCounterKey[x]); + attributeString("type", gSessionData->mPerfCounterType[x]); + attributeHex8("event", gSessionData->mPerfCounterEvent[x]); + if (gSessionData->mPerfCounterPerCPU[x]) { + attributeBool("per_cpu", true); + } + if (strlen(gSessionData->mPerfCounterOperation[x]) > 0) { + attributeString("operation", gSessionData->mPerfCounterOperation[x]); + } + if (gSessionData->mPerfCounterCount[x] > 0) { + attributeInt("count", gSessionData->mPerfCounterCount[x]); + } + attributeString("description", gSessionData->mPerfCounterDescription[x]); + endElement("counter"); + } + } + endElement("counters"); + } + endElement("captured"); + return getXmlString(); +} + +void CapturedXML::write(char* path) { + char* file = (char*)malloc(PATH_MAX); + + // Set full path + snprintf(file, PATH_MAX, "%s/captured.xml", path); + + // Write the file + const char* xml = getXML(); + if (util->writeToDisk(file, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(file); +} diff --git a/drivers/gator/daemon/CapturedXML.h b/drivers/gator/daemon/CapturedXML.h new file mode 100644 index 00000000000..d37c59e37ac --- /dev/null +++ b/drivers/gator/daemon/CapturedXML.h @@ -0,0 +1,23 @@ +/** + + * Copyright (C) ARM Limited 2010-2012. 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 __CAPTURED_XML_H__ +#define __CAPTURED_XML_H__ + +#include "XMLOut.h" + +class CapturedXML : XMLOut { +public: + CapturedXML(); + ~CapturedXML(); + const char* getXML(); + void write(char* path); +}; + +#endif //__CAPTURED_XML_H__ diff --git a/drivers/gator/daemon/Child.cpp b/drivers/gator/daemon/Child.cpp new file mode 100644 index 00000000000..fe5a1f061f9 --- /dev/null +++ b/drivers/gator/daemon/Child.cpp @@ -0,0 +1,304 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include +#include +#include +#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 "StreamlineSetup.h" +#include "ConfigurationXML.h" + +static sem_t haltPipeline, senderThreadStarted, startProfile; // Shared by Child and spawned threads +static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads +static Sender* sender = NULL; // Shared by Child.cpp and spawned threads +Collector* collector = NULL; +Child* child = NULL; // shared by Child.cpp and main.cpp + +extern void cleanUp(); +void handleException() { + if (child && child->numExceptions++ > 0) { + // it is possible one of the below functions itself can cause an exception, thus allow only one exception + logg->logMessage("Received multiple exceptions, terminating the child"); + exit(1); + } + fprintf(stderr, "%s", logg->getLastError()); + + if (child && child->socket) { + if (sender) { + // send the error, regardless of the command sent by Streamline + sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR); + + // cannot close the socket before Streamline issues the command, so wait for the command before exiting + if (gSessionData->mWaitingOnCommand) { + char discard; + child->socket->receiveNBytes(&discard, 1); + } + + // this indirectly calls close socket which will ensure the data has been sent + delete sender; + } + } + + if (gSessionData->mLocalCapture) + cleanUp(); + + exit(1); +} + +// CTRL C Signal Handler for child process +void child_handler(int signum) { + static bool beenHere = false; + if (beenHere == true) { + logg->logMessage("Gator is being forced to shut down."); + exit(1); + } + beenHere = true; + logg->logMessage("Gator is shutting down."); + if (signum == SIGALRM || !collector) { + exit(1); + } else { + child->endSession(); + alarm(5); // Safety net in case endSession does not complete within 5 seconds + } +} + +void* durationThread(void* pVoid) { + prctl(PR_SET_NAME, (unsigned int)&"gatord-duration", 0, 0, 0); + sem_wait(&startProfile); + if (gSessionData->mSessionIsActive) { + // Time out after duration seconds + // Add a second for host-side filtering + sleep(gSessionData->mDuration + 1); + if (gSessionData->mSessionIsActive) { + logg->logMessage("Duration expired."); + child->endSession(); + } + } + logg->logMessage("Exit duration thread"); + return 0; +} + +void* stopThread(void* pVoid) { + int length; + char type; + OlySocket* socket = child->socket; + + prctl(PR_SET_NAME, (unsigned int)&"gatord-stopper", 0, 0, 0); + while (gSessionData->mSessionIsActive) { + // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected + if (socket->receiveNBytes(&type, sizeof(type)) > 0) { + if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) { + logg->logMessage("INVESTIGATE: Received unknown command type %d", type); + } else { + // verify a length of zero + if (socket->receiveNBytes((char*)&length, sizeof(length)) < 0) { + break; + } + + if (length == 0) { + if (type == COMMAND_APC_STOP) { + logg->logMessage("Stop command received."); + child->endSession(); + } else { + // Ping is used to make sure gator is alive and requires an ACK as the response + logg->logMessage("Ping command received."); + sender->writeData(NULL, 0, RESPONSE_ACK); + } + } else { + logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length); + } + } + } + } + + logg->logMessage("Exit stop thread"); + return 0; +} + +void* senderThread(void* pVoid) { + int length; + char* data; + + sem_post(&senderThreadStarted); + prctl(PR_SET_NAME, (unsigned int)&"gatord-sender", 0, 0, 0); + sem_wait(&haltPipeline); + + do { + data = collectorFifo->read(&length); + sender->writeData(data, length, RESPONSE_APC_DATA); + } while (length > 0); + logg->logMessage("Exit sender thread"); + return 0; +} + +Child::Child(char* path) { + initialization(); + sessionXMLPath = path; + gSessionData->mLocalCapture = true; +} + +Child::Child(OlySocket* sock, int conn) { + initialization(); + socket = sock; + numConnections = conn; +} + +Child::~Child() { +} + +void Child::initialization() { + // Set up different handlers for signals + gSessionData->mSessionIsActive = true; + signal(SIGINT, child_handler); + signal(SIGTERM, child_handler); + signal(SIGABRT, child_handler); + signal(SIGALRM, child_handler); + socket = NULL; + numExceptions = 0; + numConnections = 0; + sessionXMLPath = 0; + + // Initialize semaphores + sem_init(&senderThreadStarted, 0, 0); + sem_init(&startProfile, 0, 0); +} + +void Child::endSession() { + gSessionData->mSessionIsActive = false; + collector->stop(); + sem_post(&haltPipeline); +} + +void Child::run() { + char* collectBuffer; + int bytesCollected = 0; + LocalCapture* localCapture = NULL; + + prctl(PR_SET_NAME, (unsigned int)&"gatord-child", 0, 0, 0); + + // Instantiate the Sender - must be done first, after which error messages can be sent + sender = new Sender(socket); + + if (numConnections > 1) { + logg->logError(__FILE__, __LINE__, "Session already in progress"); + handleException(); + } + + // Populate gSessionData with the configuration + new ConfigurationXML(); + + // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated + collector = new Collector(); + + // Start up and parse session xml + if (socket) { + // Respond to Streamline requests + StreamlineSetup ss(socket); + } else { + xmlString = util->readFromDisk(sessionXMLPath); + if (xmlString == 0) { + logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", sessionXMLPath); + handleException(); + } + gSessionData->parseSessionXML(xmlString); + localCapture = new LocalCapture(); + localCapture->createAPCDirectory(gSessionData->target_path, gSessionData->title); + localCapture->copyImages(gSessionData->images); + localCapture->write(xmlString); + sender->createDataFile(gSessionData->apcDir); + delete xmlString; + } + + // Write configuration into the driver + collector->enablePerfCounters(); + + // Create user-space buffers + int fifoBufferSize = collector->getBufferSize(); + int numCollectorBuffers = (gSessionData->mTotalBufferSize * 1024 * 1024 + fifoBufferSize - 1) / fifoBufferSize; + numCollectorBuffers = (numCollectorBuffers < 4) ? 4 : numCollectorBuffers; + logg->logMessage("Created %d %d-byte collector buffers", numCollectorBuffers, fifoBufferSize); + collectorFifo = new Fifo(numCollectorBuffers, fifoBufferSize); + + // Get the initial pointer to the collect buffer + collectBuffer = collectorFifo->start(); + + // Sender thread shall be halted until it is signaled for one shot mode + sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); + + // Create the duration, stop, and sender threads + bool thread_creation_success = true; + if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) + thread_creation_success = false; + else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) + thread_creation_success = false; + else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)) + thread_creation_success = false; + if (!thread_creation_success) { + logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); + handleException(); + } + + // Wait until thread has started + 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) { + // Depth minus 1 because write() has not yet been called + if ((bytesCollected == -1) || (collectorFifo->numWriteToReadBuffersFilled() == collectorFifo->depth() - 1)) { + logg->logMessage("One shot"); + endSession(); + } + } + collectBuffer = collectorFifo->write(bytesCollected); + } while (bytesCollected > 0); + logg->logMessage("Exit collect data loop"); + + // Wait for the other threads to exit + pthread_join(senderThreadID, NULL); + + // Shutting down the connection should break the stop thread which is stalling on the socket recv() function + if (socket) { + logg->logMessage("Waiting on stop thread"); + socket->shutdownConnection(); + pthread_join(stopThreadID, NULL); + } + + // Write the captured xml file + if (gSessionData->mLocalCapture) { + CapturedXML capturedXML; + capturedXML.write(gSessionData->apcDir); + } + + logg->logMessage("Profiling ended."); + + delete collectorFifo; + delete sender; + delete collector; + delete localCapture; +} diff --git a/drivers/gator/daemon/Child.h b/drivers/gator/daemon/Child.h new file mode 100644 index 00000000000..eee5ac70f9e --- /dev/null +++ b/drivers/gator/daemon/Child.h @@ -0,0 +1,35 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __CHILD_H__ +#define __CHILD_H__ + +#include +#include "Fifo.h" +#include "OlySocket.h" + +class Child { +public: + Child(char* sessionXMLPath); + Child(OlySocket* sock, int numConnections); + ~Child(); + void run(); + OlySocket *socket; + void endSession(); + int numExceptions; +private: + char* xmlString; + char* sessionXMLPath; + int numConnections; + time_t timeStart; + pthread_t durationThreadID, stopThreadID, senderThreadID; + + void initialization(); +}; + +#endif //__CHILD_H__ diff --git a/drivers/gator/daemon/Collector.cpp b/drivers/gator/daemon/Collector.cpp new file mode 100644 index 00000000000..c6f837628f1 --- /dev/null +++ b/drivers/gator/daemon/Collector.cpp @@ -0,0 +1,266 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include +#include +#include +#include "Collector.h" +#include "SessionData.h" +#include "Logging.h" + +extern void handleException(); + +// Driver initialization independent of session settings +Collector::Collector() { + char text[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all events// + + bufferFD = 0; + + checkVersion(); + + int enable = -1; + if (readIntDriver("enable", &enable) != 0 || enable != 0) { + logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); + handleException(); + } + + readIntDriver("cpu_cores", &gSessionData->mCores); + if (gSessionData->mCores == 0) { + gSessionData->mCores = 1; + } + + bufferSize = 512 * 1024; + if (writeReadDriver("buffer_size", &bufferSize) || bufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver buffer size"); + handleException(); + } + + getCoreName(); + + // Read unchanging keys from driver which are created at insmod'ing of gator.ko + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + snprintf(text, sizeof(text), "events/%s/key", gSessionData->mPerfCounterType[i]); + readIntDriver(text, &gSessionData->mPerfCounterKey[i]); + } +} + +Collector::~Collector() { + // Write zero for safety, as a zero should have already been written + writeDriver("enable", "0"); + + // Calls event_buffer_release in the driver + if (bufferFD) { + close(bufferFD); + } +} + +void Collector::enablePerfCounters() { + char base[sizeof(gSessionData->mPerfCounterType[0]) + 10]; // sufficiently large to hold all events/ + char text[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all events// + + for (int i=0; imPerfCounterEnabled[i]) { + continue; + } + snprintf(base, sizeof(base), "events/%s", gSessionData->mPerfCounterType[i]); + snprintf(text, sizeof(text), "%s/event", base); + writeDriver(text, gSessionData->mPerfCounterEvent[i]); + if (gSessionData->mPerfCounterEBSCapable[i]) { + snprintf(text, sizeof(text), "%s/count", base); + if (access(resolvePath(text), F_OK) == 0) { + if (writeReadDriver(text, &gSessionData->mPerfCounterCount[i]) && gSessionData->mPerfCounterCount[i] > 0) { + logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s with a count of %d\n", gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterCount[i]); + handleException(); + } + } else if (gSessionData->mPerfCounterCount[i] > 0) { + logg->logError(__FILE__, __LINE__, "Event Based Sampling is only supported with kernel versions 3.0.0 and higher with CONFIG_PERF_EVENTS=y, and CONFIG_HW_PERF_EVENTS=y\n"); + handleException(); + } + } + snprintf(text, sizeof(text), "%s/enabled", base); + if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) { + gSessionData->mPerfCounterEnabled[i] = 0; + } + } +} + +void Collector::checkVersion() { + int driver_version = 0; + + if (readIntDriver("version", &driver_version) == -1) { + logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); + handleException(); + } + + // Verify the driver version matches the daemon version + if (driver_version != PROTOCOL_VERSION) { + if ((driver_version > PROTOCOL_DEV) || (PROTOCOL_VERSION > PROTOCOL_DEV)) { + // One of the mismatched versions is development version + logg->logError(__FILE__, __LINE__, + "DEVELOPMENT BUILD MISMATCH: gator driver version \"%d\" is not in sync with gator daemon version \"%d\".\n" + ">> The following must be synchronized from engineering repository:\n" + ">> * gator driver\n" + ">> * gator daemon\n" + ">> * Streamline", driver_version, PROTOCOL_VERSION); + handleException(); + } else { + // Release version mismatch + logg->logError(__FILE__, __LINE__, + "gator driver version \"%d\" is different than gator daemon version \"%d\".\n" + ">> Please upgrade the driver and daemon to the latest versions.", driver_version, PROTOCOL_VERSION); + handleException(); + } + } +} + +void Collector::start() { + // Set the maximum backtrace depth + if (writeReadDriver("backtrace_depth", &gSessionData->mBacktraceDepth)) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); + handleException(); + } + + // open the buffer which calls userspace_buffer_open() in the driver + bufferFD = open(resolvePath("buffer"), O_RDONLY); + if (bufferFD < 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + // set the tick rate of the profiling timer + if (writeReadDriver("tick", &gSessionData->mSampleRate) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); + handleException(); + } + + // notify the kernel of the streaming mode, currently used for network stats + int streaming = (int)!gSessionData->mOneShot; + if (writeReadDriver("streaming", &streaming) != 0) { + logg->logError(__FILE__, __LINE__, "Unable to set streaming"); + handleException(); + } + + logg->logMessage("Start the driver"); + + // This command makes the driver start profiling by calling gator_op_start() in the driver + if (writeDriver("enable", "1") != 0) { + logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); + handleException(); + } + + lseek(bufferFD, 0, SEEK_SET); +} + +// These commands should cause the read() function in collect() to return +void Collector::stop() { + // This will stop the driver from profiling + if (writeDriver("enable", "0") != 0) { + logg->logMessage("Stopping kernel failed"); + } +} + +int Collector::collect(char* buffer) { + // Calls event_buffer_read in the driver + int bytesRead = read(bufferFD, buffer, bufferSize); + + // 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(bufferFD, buffer, bufferSize); + } + + logg->logMessage("Driver read of %d bytes", bytesRead); + + return bytesRead; +} + +void Collector::getCoreName() { + char temp[256]; // arbitrarily large amount + strcpy(gSessionData->mCoreName, "unknown"); + + FILE* f = fopen("/proc/cpuinfo", "r"); + if (f == NULL) { + logg->logMessage("Error opening /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + return; + } + + while (fgets(temp, sizeof(temp), f)) { + if (strlen(temp) > 0) + temp[strlen(temp) - 1] = 0; // Replace the line feed with a null + + if (strstr(temp, "Hardware") != 0) { + char* position = strchr(temp, ':'); + if (position == NULL || (unsigned int)(position - temp) + 2 >= strlen(temp)) { + logg->logMessage("Unknown format of /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + return; + } + strncpy(gSessionData->mCoreName, (char *)((int)position + 2), sizeof(gSessionData->mCoreName)); + gSessionData->mCoreName[sizeof(gSessionData->mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string + fclose(f); + return; + } + } + + logg->logMessage("Could not determine core name from /proc/cpuinfo\n" + "The core name in the captured xml file will be 'unknown'."); + fclose(f); +} + +char* Collector::resolvePath(const char* file) { + static char fullpath[100]; // Sufficiently large to hold any path within /dev/gator + snprintf(fullpath, sizeof(fullpath), "/dev/gator/%s", file); + return fullpath; +} + +int Collector::readIntDriver(const char* path, int* value) { + char* fullpath = resolvePath(path); + FILE* file = fopen(fullpath, "r"); + if (file == NULL) { + return -1; + } + if (fscanf(file, "%u", value) != 1) { + fclose(file); + logg->logMessage("Invalid value in file %s", fullpath); + return -1; + } + fclose(file); + return 0; +} + +int Collector::writeDriver(const char* path, int value) { + char data[40]; // Sufficiently large to hold any integer + snprintf(data, sizeof(data), "%d", value); + return writeDriver(path, data); +} + +int Collector::writeDriver(const char* path, const char* data) { + char* fullpath = resolvePath(path); + int fd = open(fullpath, O_WRONLY); + if (fd < 0) { + return -1; + } + if (write(fd, data, strlen(data)) < 0) { + close(fd); + logg->logMessage("Opened but could not write to %s", fullpath); + return -1; + } + close(fd); + return 0; +} + +int Collector::writeReadDriver(const char* path, int* value) { + if (writeDriver(path, *value) || readIntDriver(path, value)) { + return -1; + } + return 0; +} diff --git a/drivers/gator/daemon/Collector.h b/drivers/gator/daemon/Collector.h new file mode 100644 index 00000000000..1cfe84e1b97 --- /dev/null +++ b/drivers/gator/daemon/Collector.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 + +class Collector { +public: + Collector(); + ~Collector(); + void start(); + void stop(); + int collect(char* buffer); + void enablePerfCounters(); + int getBufferSize() {return bufferSize;} +private: + int bufferSize; + int bufferFD; + char * buffer; + + void checkVersion(); + void getCoreName(); + + char* resolvePath(const char* file); + int readIntDriver(const char* path, int* value); + int writeDriver(const char* path, int value); + int writeDriver(const char* path, const char* data); + int writeReadDriver(const char* path, int* value); +}; + +#endif //__COLLECTOR_H__ diff --git a/drivers/gator/daemon/ConfigurationXML.cpp b/drivers/gator/daemon/ConfigurationXML.cpp new file mode 100644 index 00000000000..e1bec0fd4fa --- /dev/null +++ b/drivers/gator/daemon/ConfigurationXML.cpp @@ -0,0 +1,156 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include "ConfigurationXML.h" +#include "Logging.h" +#include "OlyUtility.h" +#include "SessionData.h" + +extern void handleException(); + +static const char* ATTR_COUNTER = "counter"; +static const char* ATTR_VERSION = "version"; +static const char* ATTR_TITLE = "title"; +static const char* ATTR_NAME = "name"; +static const char* ATTR_EVENT = "event"; +static const char* ATTR_COLOR = "color"; +static const char* ATTR_COUNT = "count"; +static const char* ATTR_OPERATION = "operation"; +static const char* ATTR_PER_CPU = "per_cpu"; +static const char* ATTR_DESCRIPTION = "description"; +static const char* ATTR_EBS = "event_based_sampling"; + +ConfigurationXML::ConfigurationXML() { +#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len + index = 0; + char* path = (char *)malloc(PATH_MAX); + + if (gSessionData->configurationXMLPath) { + strncpy(path, gSessionData->configurationXMLPath, PATH_MAX); + } else { + if (util->getApplicationFullPath(path, PATH_MAX) != 0) { + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } + mConfigurationXML = util->readFromDisk(path); + + if (mConfigurationXML == NULL) { + logg->logMessage("Unable to locate configuration.xml, using default in binary"); + // null-terminate configuration_xml + mConfigurationXML = (char*)malloc(configuration_xml_len + 1); + memcpy(mConfigurationXML, (const void*)configuration_xml, configuration_xml_len); + mConfigurationXML[configuration_xml_len] = 0; + } + + // disable all counters prior to parsing the configuration xml + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + gSessionData->mPerfCounterEnabled[i] = 0; + } + + int ret = parse(mConfigurationXML); + if (ret == 1) { + // remove configuration.xml on disk to use the default + if (remove(path) != 0) { + logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); + handleException(); + } + } else if (ret < 0 || isValid() == false) { + logg->logError(__FILE__, __LINE__, "Parsing of the configuration.xml file failed. Please verify configuration.xml on the target filesystem is valid or delete it to use the default."); + handleException(); + } + + free(path); +} + +ConfigurationXML::~ConfigurationXML() { + if (mConfigurationXML) { + free((void*)mConfigurationXML); + } +} + +int ConfigurationXML::parse(const char* configurationXML) { + int ret = 0; + XMLReader reader(configurationXML); + char * tag = reader.nextTag(); + while(tag != 0 && ret == 0) { + if (strcmp(tag, "configurations") == 0) { + ret = configurationsTag(&reader); + } else if (strcmp(tag, "configuration") == 0) { + ret = configurationTag(&reader); + } + tag = reader.nextTag(); + } + + return ret; +} + +bool ConfigurationXML::isValid(void) { + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + if (gSessionData->mPerfCounterEnabled[i]) { + if (strcmp(gSessionData->mPerfCounterType[i], "") == 0 || + strcmp(gSessionData->mPerfCounterTitle[i], "") == 0 || + strcmp(gSessionData->mPerfCounterName[i], "") == 0) { + logg->logMessage("Invalid required attribute\n counter=\"%s\"\n title=\"%s\"\n name=\"%s\"\n event=%d\n", gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterEvent[i]); + return false; // failure + } + + // iterate through the remaining enabled performance counters + for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { + if (gSessionData->mPerfCounterEnabled[j]) { + // check if the type or device are the same + if (strcmp(gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterType[j]) == 0) { + logg->logMessage("Duplicate performance counter type: %s", gSessionData->mPerfCounterType[i]); + return false; // failure + } + } + } + } + } + + return true; // success +} + +#define CONFIGURATION_VERSION 1 +int ConfigurationXML::configurationsTag(XMLReader *in) { + int version = in->getAttributeAsInteger(ATTR_VERSION, 0); + if (version != CONFIGURATION_VERSION) { + logg->logMessage("Incompatible configuration.xml version (%d) detected. The version needs to be %d.", version, CONFIGURATION_VERSION); + return 1; // version issue + } + return 0; +} + +int ConfigurationXML::configurationTag(XMLReader* in) { + // handle all other performance counters + if (index >= MAX_PERFORMANCE_COUNTERS) { + logg->logMessage("Invalid performance counter index: %d", index); + return -1; // failure + } + + // read attributes + in->getAttribute(ATTR_COUNTER, gSessionData->mPerfCounterType[index], sizeof(gSessionData->mPerfCounterType[index]), ""); + in->getAttribute(ATTR_TITLE, gSessionData->mPerfCounterTitle[index], sizeof(gSessionData->mPerfCounterTitle[index]), ""); + in->getAttribute(ATTR_NAME, gSessionData->mPerfCounterName[index], sizeof(gSessionData->mPerfCounterName[index]), ""); + in->getAttribute(ATTR_DESCRIPTION, gSessionData->mPerfCounterDescription[index], sizeof(gSessionData->mPerfCounterDescription[index]), ""); + gSessionData->mPerfCounterEvent[index] = in->getAttributeAsInteger(ATTR_EVENT, 0); + gSessionData->mPerfCounterCount[index] = in->getAttributeAsInteger(ATTR_COUNT, 0); + gSessionData->mPerfCounterColor[index] = in->getAttributeAsInteger(ATTR_COLOR, 0); + gSessionData->mPerfCounterPerCPU[index] = in->getAttributeAsBoolean(ATTR_PER_CPU, false); + gSessionData->mPerfCounterEBSCapable[index] = in->getAttributeAsBoolean(ATTR_EBS, false); + in->getAttribute(ATTR_OPERATION, gSessionData->mPerfCounterOperation[index], sizeof(gSessionData->mPerfCounterOperation[index]), ""); + gSessionData->mPerfCounterEnabled[index] = true; + + // update counter index + index++; + + return 0; // success +} diff --git a/drivers/gator/daemon/ConfigurationXML.h b/drivers/gator/daemon/ConfigurationXML.h new file mode 100644 index 00000000000..047c4b07705 --- /dev/null +++ b/drivers/gator/daemon/ConfigurationXML.h @@ -0,0 +1,29 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 COUNTERS_H +#define COUNTERS_H + +#include "XMLReader.h" + +class ConfigurationXML { +public: + ConfigurationXML(); + ~ConfigurationXML(); + const char* getConfigurationXML() {return mConfigurationXML;} +private: + char* mConfigurationXML; + + int parse(const char* xmlFile); + bool isValid(void); + int configurationsTag(XMLReader *in); + int configurationTag(XMLReader* in); + int index; +}; + +#endif // COUNTERS_H diff --git a/drivers/gator/daemon/Fifo.cpp b/drivers/gator/daemon/Fifo.cpp new file mode 100644 index 00000000000..1456183b0f0 --- /dev/null +++ b/drivers/gator/daemon/Fifo.cpp @@ -0,0 +1,103 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include "Fifo.h" +#include "Logging.h" + +extern void handleException(); + +Fifo::Fifo(int numBuffers, int bufferSize) { + int which; + + if (numBuffers > FIFO_BUFFER_LIMIT) { + logg->logError(__FILE__, __LINE__, "Number of fifo buffers exceeds maximum"); + handleException(); + } + mNumBuffers = numBuffers; + mBufferSize = bufferSize; + mWriteCurrent = 0; + mReadCurrent = mNumBuffers - 1; // (n-1) pipelined + + for (which=0; whichlogError(__FILE__, __LINE__, "sem_init(%d) failed", which); + handleException(); + } + // page-align allocate buffers + mBuffer[which] = (char*)valloc(bufferSize); + if (mBuffer[which] == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize); + handleException(); + } + // touch each page to fault it in + for (int i=0; i + +// Number of buffers allowed with large buffer mode +#define FIFO_BUFFER_LIMIT 64 + +class Fifo { +public: + Fifo(int numBuffers, int bufferSize); + ~Fifo(); + int depth(void); + int numReadToWriteBuffersFilled(); + int numWriteToReadBuffersFilled(); + int numReadToWriteBuffersEmpty() {return depth() - numReadToWriteBuffersFilled();} + int numWriteToReadBuffersEmpty() {return depth() - numWriteToReadBuffersFilled();} + char* start(); + char* write(int length); + char* read(int* length); + +private: + int mNumBuffers; + int mBufferSize; + int mWriteCurrent; + int mReadCurrent; + sem_t mReadToWriteSem[FIFO_BUFFER_LIMIT]; + sem_t mWriteToReadSem[FIFO_BUFFER_LIMIT]; + char* mBuffer[FIFO_BUFFER_LIMIT]; + int mLength[FIFO_BUFFER_LIMIT]; +}; + +#endif //__FIFO_H__ diff --git a/drivers/gator/daemon/LocalCapture.cpp b/drivers/gator/daemon/LocalCapture.cpp new file mode 100644 index 00000000000..4a2d6d755e6 --- /dev/null +++ b/drivers/gator/daemon/LocalCapture.cpp @@ -0,0 +1,208 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include +#include +#include "LocalCapture.h" +#include "SessionData.h" +#include "Logging.h" +#include "OlyUtility.h" + +extern void handleException(); + +LocalCapture::LocalCapture() {} + +LocalCapture::~LocalCapture() {} + +void LocalCapture::createAPCDirectory(char* target_path, char* name) { + gSessionData->apcDir = createUniqueDirectory(target_path, ".apc", name); + if ((removeDirAndAllContents(gSessionData->apcDir) != 0 || mkdir(gSessionData->apcDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { + logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->apcDir); + handleException(); + } +} + +void LocalCapture::write(char* string) { + char* file = (char*)malloc(PATH_MAX); + + // Set full path + snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->apcDir); + + // Write the file + if (util->writeToDisk(file, string) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); + handleException(); + } + + free(file); +} + +char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* ending, char* title) { + int i; + char* output; + char* path = (char*)malloc(PATH_MAX); + + // Ensure the path is an absolute path, i.e. starts with a slash + if (initialPath == 0 || strlen(initialPath) == 0) { + if (getcwd(path, PATH_MAX) == 0) + logg->logMessage("Unable to retrive the current working directory"); + strncat(path, "/@F_@N", PATH_MAX - strlen(path) - 1); + } else if (initialPath[0] != '/') { + if (getcwd(path, PATH_MAX) == 0) + logg->logMessage("Unable to retrive the current working directory"); + strncat(path, "/", PATH_MAX - strlen(path) - 1); + strncat(path, initialPath, PATH_MAX - strlen(path) - 1); + } else { + strncpy(path, initialPath, PATH_MAX); + path[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string + } + + // Convert to uppercase + replaceAll(path, "@f", "@F", PATH_MAX); + replaceAll(path, "@n", "@N", PATH_MAX); + + // Replace @F with the session xml title + replaceAll(path, "@F", title, PATH_MAX); + + // Add ending if it is not already there + if (strcmp(&path[strlen(path) - strlen(ending)], ending) != 0) { + strncat(path, ending, PATH_MAX - strlen(path) - 1); + } + + // Replace @N with a unique integer + if (strstr(path, "@N")) { + char* tempPath = (char*)malloc(PATH_MAX); + for (i = 1; i < 1000; i++) { + char number[4]; + snprintf(number, sizeof(number), "%03d", i); + strcpy(tempPath, path); + replaceAll(tempPath, "@N", number, PATH_MAX); + struct stat mFileInfo; + if (stat(tempPath, &mFileInfo) != 0) { + // if the direcotry does not exist, break + break; + } + } + + if (i == 1000) { + logg->logError(__FILE__, __LINE__, "Unable to create .apc directory, please delete older directories."); + handleException(); + } + + output = strdup(tempPath); + free(tempPath); + } else { + output = strdup(path); + } + + free(path); + return output; +} + +//Replaces all occurrences of in with provided enough is available +void LocalCapture::replaceAll(char* target, const char* find, const char* replace, unsigned int size) { + char* nextOccurrence; + unsigned int count = 0; + + // Duplicate the original string + char* original = strdup(target); + char* ptr = original; + + // Determine number of s + ptr = strstr(ptr, find); + while (ptr) { + count++; + ptr += strlen(find); + ptr = strstr(ptr, find); + } + + // Is there enough space available + if (strlen(target) + (strlen(replace) - strlen(find)) * count > size - 1) { + free(original); + return; + } + + // Reset + ptr = original; + + nextOccurrence = strstr(ptr, find); + while (nextOccurrence) { + // Move pointers to location of replace + int length = nextOccurrence - ptr; + target += length; + ptr += length; + + // Replace with + memcpy(target, replace, strlen(replace)); + + // Increment over / + target += strlen(replace); + ptr += strlen(find); + + // Copy remainder of ptr + strcpy(target, ptr); + + // Get next occurrence + nextOccurrence = strstr(ptr, find); + } + + free(original); +} + +int LocalCapture::removeDirAndAllContents(char *path) { + int error = 0; + struct stat mFileInfo; + // Does the path exist? + if (stat(path, &mFileInfo) == 0) { + // Is it a directory? + if (mFileInfo.st_mode & S_IFDIR) { + DIR * dir = opendir(path); + dirent* entry = readdir(dir); + while (entry) { + if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { + char* newpath = (char*)malloc(strlen(path) + strlen(entry->d_name) + 2); + sprintf(newpath, "%s/%s", path, entry->d_name); + error = removeDirAndAllContents(newpath); + free(newpath); + if (error) break; + } + entry = readdir(dir); + } + closedir(dir); + if (error == 0) { + error = rmdir(path); + } + } else { + error = remove(path); + } + } + return error; +} + +void LocalCapture::copyImages(ImageLinkList* ptr) { + char* dstfilename = (char*)malloc(PATH_MAX); + + while (ptr) { + strncpy(dstfilename, gSessionData->apcDir, PATH_MAX); + dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string + if (gSessionData->apcDir[strlen(gSessionData->apcDir) - 1] != '/') + strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1); + strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1); + if (util->copyFile(ptr->path, dstfilename)) + logg->logMessage("copied file %s to %s", ptr->path, dstfilename); + else + logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename); + + ptr = ptr->next; + } + free(dstfilename); +} diff --git a/drivers/gator/daemon/LocalCapture.h b/drivers/gator/daemon/LocalCapture.h new file mode 100644 index 00000000000..4f5f8186e09 --- /dev/null +++ b/drivers/gator/daemon/LocalCapture.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __LOCAL_CAPTURE_H__ +#define __LOCAL_CAPTURE_H__ + +#include "SessionXML.h" + +class LocalCapture { +public: + LocalCapture(); + ~LocalCapture(); + void write(char *string); + void copyImages(ImageLinkList* ptr); + void createAPCDirectory(char* target_path, char* name); +private: + char* createUniqueDirectory(const char* path, const char* ending, char* title); + void replaceAll(char* target, const char* find, const char* replace, unsigned int size); + int removeDirAndAllContents(char *path); +}; + +#endif //__LOCAL_CAPTURE_H__ diff --git a/drivers/gator/daemon/Logging.cpp b/drivers/gator/daemon/Logging.cpp new file mode 100644 index 00000000000..3e6f8a388a4 --- /dev/null +++ b/drivers/gator/daemon/Logging.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include "OlyUtility.h" + +#ifdef WIN32 +#define MUTEX_INIT() mLoggingMutex = CreateMutex(NULL, false, NULL); +#define MUTEX_LOCK() WaitForSingleObject(mLoggingMutex, 0xFFFFFFFF); +#define MUTEX_UNLOCK() ReleaseMutex(mLoggingMutex); +#define snprintf _snprintf +#else +#include +#define MUTEX_INIT() pthread_mutex_init(&mLoggingMutex, NULL) +#define MUTEX_LOCK() pthread_mutex_lock(&mLoggingMutex) +#define MUTEX_UNLOCK() pthread_mutex_unlock(&mLoggingMutex) +#endif + +#include "Logging.h" + +// Global thread-safe logging +Logging* logg = NULL; + +Logging::Logging(bool debug) { + mDebug = debug; + MUTEX_INIT(); + + strcpy(mErrBuf, "Unknown Error"); + strcpy(mLogBuf, "Unknown Message"); +} + +Logging::~Logging() { +} + +void Logging::logError(const char* file, int line, const char* fmt, ...) { + va_list args; + + MUTEX_LOCK(); + if (mDebug) { + snprintf(mErrBuf, sizeof(mErrBuf), "ERROR[%s:%d]: ", file, line); + } else { + mErrBuf[0] = 0; + } + + va_start(args, fmt); + vsnprintf(mErrBuf + strlen(mErrBuf), sizeof(mErrBuf) - 2 - strlen(mErrBuf), fmt, args); // subtract 2 for \n and \0 + va_end(args); + + if (strlen(mErrBuf) > 0) { + strcat(mErrBuf, "\n"); + } + MUTEX_UNLOCK(); +} + +void Logging::logMessage(const char* fmt, ...) { + if (mDebug) { + va_list args; + + MUTEX_LOCK(); + strcpy(mLogBuf, "INFO: "); + + va_start(args, fmt); + vsnprintf(mLogBuf + strlen(mLogBuf), sizeof(mLogBuf) - 2 - strlen(mLogBuf), fmt, args); // subtract 2 for \n and \0 + va_end(args); + strcat(mLogBuf, "\n"); + + fprintf(stdout, "%s", mLogBuf); + fflush(stdout); + MUTEX_UNLOCK(); + } +} diff --git a/drivers/gator/daemon/Logging.h b/drivers/gator/daemon/Logging.h new file mode 100644 index 00000000000..424796658f7 --- /dev/null +++ b/drivers/gator/daemon/Logging.h @@ -0,0 +1,45 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __LOGGING_H__ +#define __LOGGING_H__ + +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#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" + +class Logging { +public: + Logging(bool debug); + ~Logging(); + void logError(const char* file, int line, const char* fmt, ...); + void logMessage(const char* fmt, ...); + char* getLastError() {return mErrBuf;} + char* getLastMessage() {return mLogBuf;} + +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; + +#endif //__LOGGING_H__ diff --git a/drivers/gator/daemon/Makefile b/drivers/gator/daemon/Makefile new file mode 100644 index 00000000000..4aaf219d784 --- /dev/null +++ b/drivers/gator/daemon/Makefile @@ -0,0 +1,40 @@ +# +# Makefile for ARM Streamline - Gator Daemon +# + +# Uncomment and define CROSS_COMPILE if it is not already defined +# CROSS_COMPILE=/path/to/cross-compiler/arm-none-linux-gnueabi- +ARCH=arm + +CPP=$(CROSS_COMPILE)g++ + +# -g produces debugging information +# -O3 maximum optimization +# -O0 no optimization, used for debugging +# -Wall enables most warnings +# -Werror treats warnings as errors +# -std=c++0x is the planned new c++ standard +# -std=c++98 is the 1998 c++ standard +# -march=armv5t is required to set the minimum architecture +# -mthumb-interwork is required for interworking to ARM or Thumb stdlibc +CFLAGS=-O3 -Wall -Werror -march=armv5t -mthumb-interwork +TARGET=gatord +CPP_SRC = $(wildcard *.cpp) +TGT_OBJS = $(CPP_SRC:%.cpp=%.o) + +all: $(TARGET) + +%.o: %.cpp *.h + $(CPP) -c $(CFLAGS) -o $@ $< + +$(TARGET): convert $(TGT_OBJS) + $(CPP) -s -o $@ $(TGT_OBJS) -lc -lrt -lpthread + rm events_xml.h configuration_xml.h + +convert: + cat events_header.xml events-*\.xml events_footer.xml > events.xml + xxd -i events.xml > events_xml.h + xxd -i configuration.xml > configuration_xml.h + +clean: + rm -f *.o $(TARGET) events.xml events_xml.h configuration_xml.h diff --git a/drivers/gator/daemon/OlySocket.cpp b/drivers/gator/daemon/OlySocket.cpp new file mode 100644 index 00000000000..8a9ca970b94 --- /dev/null +++ b/drivers/gator/daemon/OlySocket.cpp @@ -0,0 +1,257 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#ifdef WIN32 +#include +#else +#include +#include +#include +#include +#endif +#include "OlySocket.h" +#include "Logging.h" + +#ifdef WIN32 +#define CLOSE_SOCKET(x) closesocket(x) +#define SHUTDOWN_RX_TX SD_BOTH +#define snprintf _snprintf +#else +#define CLOSE_SOCKET(x) close(x) +#define SHUTDOWN_RX_TX SHUT_RDWR +#endif + +extern void handleException(); + +OlySocket::OlySocket(int port, bool multiple) { +#ifdef WIN32 + WSADATA wsaData; + if (WSAStartup(0x0202, &wsaData) != 0) { + logg->logError(__FILE__, __LINE__, "Windows socket initialization failed"); + handleException(); + } +#endif + + if (multiple) { + createServerSocket(port); + } else { + createSingleServerConnection(port); + } +} + +OlySocket::OlySocket(int port, char* host) { + fdServer = 0; + createClientSocket(host, port); +} + +OlySocket::~OlySocket() { + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + } +} + +void OlySocket::shutdownConnection() { + // Shutdown is primarily used to unblock other threads that are blocking on send/receive functions + shutdown(mSocketID, SHUTDOWN_RX_TX); +} + +void OlySocket::closeSocket() { + // Used for closing an accepted socket but keeping the server socket active + if (mSocketID > 0) { + CLOSE_SOCKET(mSocketID); + mSocketID = -1; + } +} + +void OlySocket::closeServerSocket() { + if (CLOSE_SOCKET(fdServer) != 0) { + logg->logError(__FILE__, __LINE__, "Failed to close server socket."); + handleException(); + } + fdServer = 0; +} + +void OlySocket::createClientSocket(char* hostname, int portno) { +#ifdef WIN32 + // TODO: Implement for Windows +#else + char buf[32]; + struct addrinfo hints, *res, *res0; + + snprintf(buf, sizeof(buf), "%d", portno); + mSocketID = -1; + memset((void*)&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(hostname, buf, &hints, &res0)) { + logg->logError(__FILE__, __LINE__, "Client socket failed to get address info for %s", hostname); + handleException(); + } + for (res=res0; res!=NULL; res = res->ai_next) { + if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) + continue; + mSocketID = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (mSocketID < 0) { + continue; + } + if (connect(mSocketID, res->ai_addr, res->ai_addrlen) < 0) { + close(mSocketID); + mSocketID = -1; + } + if (mSocketID > 0) break; + } + freeaddrinfo(res0); + if (mSocketID <= 0) { + logg->logError(__FILE__, __LINE__, "Could not connect to client socket. Ensure ARM Streamline is running."); + handleException(); + } +#endif +} + +void OlySocket::createSingleServerConnection(int port) { + createServerSocket(port); + + mSocketID = acceptConnection(); + closeServerSocket(); +} + +void OlySocket::createServerSocket(int port) { + // Create socket + fdServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fdServer < 0) { + logg->logError(__FILE__, __LINE__, "Error creating server socket"); + handleException(); + } + + // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits + int on = 1; + if (setsockopt(fdServer, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) != 0) { + logg->logError(__FILE__, __LINE__, "Setting server socket options failed"); + handleException(); + } + + // Create sockaddr_in structure, ensuring non-populated fields are zero + struct sockaddr_in sockaddr; + memset((void*)&sockaddr, 0, sizeof(struct sockaddr_in)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(port); + sockaddr.sin_addr.s_addr = INADDR_ANY; + + // Bind the socket to an address + if (bind(fdServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?"); + handleException(); + } + + // Listen for connections on this socket + if (listen(fdServer, 1) < 0) { + logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); + handleException(); + } +} + +// 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() { + if (fdServer <= 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(fdServer, NULL, NULL); + if (mSocketID < 0) { + logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); + handleException(); + } + return mSocketID; +} + +void OlySocket::send(char* buffer, int size) { + if (size <= 0 || buffer == NULL) { + return; + } + + while (size > 0) { + int n = ::send(mSocketID, buffer, size, 0); + if (n < 0) { + logg->logError(__FILE__, __LINE__, "Socket send error"); + handleException(); + } + size -= n; + buffer += n; + } +} + +// Returns the number of bytes received +int OlySocket::receive(char* buffer, int size) { + if (size <= 0 || buffer == NULL) { + return 0; + } + + int bytes = recv(mSocketID, buffer, size, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + return bytes; +} + +// Receive exactly size bytes of data. Note, this function will block until all bytes are received +int OlySocket::receiveNBytes(char* buffer, int size) { + int bytes = 0; + while (size > 0 && buffer != NULL) { + bytes = recv(mSocketID, buffer, size, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + buffer += bytes; + size -= bytes; + } + return bytes; +} + +// Receive data until a carriage return, line feed, or null is encountered, or the buffer fills +int OlySocket::receiveString(char* buffer, int size) { + int bytes_received = 0; + bool found = false; + + if (buffer == 0) + return 0; + + while (!found && bytes_received < size) { + // Receive a single character + int bytes = recv(mSocketID, &buffer[bytes_received], 1, 0); + if (bytes < 0) { + logg->logError(__FILE__, __LINE__, "Socket receive error"); + handleException(); + } else if (bytes == 0) { + logg->logMessage("Socket disconnected"); + return -1; + } + + // Replace carriage returns and line feeds with zero + if (buffer[bytes_received] == '\n' || buffer[bytes_received] == '\r' || buffer[bytes_received] == '\0') { + buffer[bytes_received] = '\0'; + found = true; + } + + bytes_received++; + } + + return bytes_received; +} diff --git a/drivers/gator/daemon/OlySocket.h b/drivers/gator/daemon/OlySocket.h new file mode 100644 index 00000000000..2fe6e59516f --- /dev/null +++ b/drivers/gator/daemon/OlySocket.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __OLY_SOCKET_H__ +#define __OLY_SOCKET_H__ + +#include + +class OlySocket { +public: + OlySocket(int port, bool multipleConnections = false); + OlySocket(int port, char* hostname); + ~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));} + int receive(char* buffer, int size); + int receiveNBytes(char* buffer, int size); + int receiveString(char* buffer, int size); + char* getLastError() {return strError;} + int getSocketID() {return mSocketID;} +private: + char* strError; + int mSocketID, fdServer; + void createClientSocket(char* hostname, int port); + void createSingleServerConnection(int port); + void createServerSocket(int port); +}; + +#endif //__OLY_SOCKET_H__ diff --git a/drivers/gator/daemon/OlyUtility.cpp b/drivers/gator/daemon/OlyUtility.cpp new file mode 100644 index 00000000000..1b15d33d0e5 --- /dev/null +++ b/drivers/gator/daemon/OlyUtility.cpp @@ -0,0 +1,167 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include + +#ifndef WIN32 +#include +#else +#include +#endif + +#include "OlyUtility.h" + +OlyUtility* util = NULL; + +void OlyUtility::stringToLower(char* string) { + if (string == NULL) + return; + + while (*string) { + *string = tolower(*string); + string++; + } +} + +// Modifies fullpath with the path part including the trailing path separator +int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { + memset(fullpath, 0, sizeOfPath); +#ifdef WIN32 + int length = GetModuleFileName(NULL, fullpath, sizeOfPath); +#else + int length = readlink("/proc/self/exe", fullpath, sizeOfPath); +#endif + + if (length == sizeOfPath) + return -1; + + fullpath[length] = 0; + fullpath = getPathPart(fullpath); + + return 0; +} + +char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) { + // Open the file + FILE* pFile = fopen(file, "rb"); + if (pFile==NULL) return NULL; + + // Obtain file size + fseek(pFile , 0 , SEEK_END); + unsigned int lSize = ftell(pFile); + rewind(pFile); + + // Allocate memory to contain the whole file + char* buffer = (char*)malloc(lSize + (int)appendNull); + if (buffer == NULL) return NULL; + + // Copy the file into the buffer + if (fread(buffer, 1, lSize, pFile) != lSize) return NULL; + + // Terminate + fclose(pFile); + + if (appendNull) + buffer[lSize] = 0; + + if (size) + *size = lSize; + + return buffer; +} + +int OlyUtility::writeToDisk(const char* path, const char* data) { + // Open the file + FILE* pFile = fopen(path, "wb"); + if (pFile == NULL) return -1; + + // Write the data to disk + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) return -1; + + // Terminate + fclose(pFile); + return 0; +} + +int OlyUtility::appendToDisk(const char* path, const char* data) { + // Open the file + FILE* pFile = fopen(path, "a"); + if (pFile == NULL) return -1; + + // Write the data to disk + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) return -1; + + // Terminate + fclose(pFile); + return 0; +} + +/** + * Copies the srcFile into dstFile in 1kB chunks. + * The dstFile will be overwritten if it exists. + * 0 is returned on an error; otherwise 1. + */ +#define TRANSFER_SIZE 1024 +int OlyUtility::copyFile(const char * srcFile, const char * dstFile) { + char* buffer = (char*)malloc(TRANSFER_SIZE); + FILE * f_src = fopen(srcFile,"rb"); + if (!f_src) { + return 0; + } + FILE * f_dst = fopen(dstFile,"wb"); + if (!f_dst) { + fclose(f_src); + return 0; + } + while (!feof(f_src)) { + int num_bytes_read = fread(buffer, 1, TRANSFER_SIZE, f_src); + if (num_bytes_read < TRANSFER_SIZE && !feof(f_src)) { + fclose(f_src); + fclose(f_dst); + return 0; + } + int num_bytes_written = fwrite(buffer, 1, num_bytes_read, f_dst); + if (num_bytes_written != num_bytes_read) { + fclose(f_src); + fclose(f_dst); + return 0; + } + } + fclose(f_src); + fclose(f_dst); + free(buffer); + return 1; +} + +const char* OlyUtility::getFilePart(const char* path) { + const char* last_sep = strrchr(path, PATH_SEPARATOR); + + // in case path is not a full path + if (last_sep == NULL) { + return path; + } + + return (const char*)((int)last_sep + 1); +} + +// getPathPart may modify the contents of path +// returns the path including the trailing path separator +char* OlyUtility::getPathPart(char* path) { + char* last_sep = strrchr(path, PATH_SEPARATOR); + + // in case path is not a full path + if (last_sep == NULL) { + return 0; + } + *(char*)((int)last_sep + 1) = 0; + + return (path); +} diff --git a/drivers/gator/daemon/OlyUtility.h b/drivers/gator/daemon/OlyUtility.h new file mode 100644 index 00000000000..0fa021dc364 --- /dev/null +++ b/drivers/gator/daemon/OlyUtility.h @@ -0,0 +1,35 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 OLY_UTILITY_H +#define OLY_UTILITY_H + +#ifdef WIN32 +#define PATH_SEPARATOR '\\' +#else +#define PATH_SEPARATOR '/' +#endif + +class OlyUtility { +public: + OlyUtility() {}; + ~OlyUtility() {}; + void stringToLower(char* string); + int getApplicationFullPath(char* path, int sizeOfPath); + char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true); + int writeToDisk(const char* path, const char* file); + int appendToDisk(const char* path, const char* file); + int copyFile(const char * srcFile, const char * dstFile); + const char* getFilePart(const char* path); + char* getPathPart(char* path); +private: +}; + +extern OlyUtility* util; + +#endif // OLY_UTILITY_H diff --git a/drivers/gator/daemon/RequestXML.cpp b/drivers/gator/daemon/RequestXML.cpp new file mode 100644 index 00000000000..e8f24d2d25d --- /dev/null +++ b/drivers/gator/daemon/RequestXML.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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 +#include +#include +#include "RequestXML.h" +#include "Logging.h" + +extern void handleException(); + +static const char* TAG_REQUEST = "request"; + +static const char* ATTR_PROTOCOL = "protocol"; +static const char* ATTR_EVENTS = "events"; +static const char* ATTR_CONFIGURATION = "configuration"; +static const char* ATTR_COUNTERS = "counters"; +static const char* ATTR_SESSION = "session"; +static const char* ATTR_CAPTURED = "captured"; +static const char* ATTR_DEFAULTS = "defaults"; + +RequestXML::RequestXML(const char * str) { + parameters.protocol = false; + parameters.events = false; + parameters.configuration = false; + parameters.counters = false; + parameters.session = false; + parameters.captured = false; + parameters.defaults = false; + + XMLReader reader(str); + char * tag = reader.nextTag(); + while(tag != 0) { + if (strcmp(tag, TAG_REQUEST) == 0) { + requestTag(&reader); + return; + } + tag = reader.nextTag(); + } + + logg->logError(__FILE__, __LINE__, "No request tag found in the request.xml file"); + handleException(); +} + +RequestXML::~RequestXML() { +} + +void RequestXML::requestTag(XMLReader* in) { + parameters.protocol = in->getAttributeAsBoolean(ATTR_PROTOCOL, false); + parameters.events = in->getAttributeAsBoolean(ATTR_EVENTS, false); + parameters.configuration = in->getAttributeAsBoolean(ATTR_CONFIGURATION, false); + parameters.counters = in->getAttributeAsBoolean(ATTR_COUNTERS, false); + parameters.session = in->getAttributeAsBoolean(ATTR_SESSION, false); + parameters.captured = in->getAttributeAsBoolean(ATTR_CAPTURED, false); + parameters.defaults = in->getAttributeAsBoolean(ATTR_DEFAULTS, false); +} diff --git a/drivers/gator/daemon/RequestXML.h b/drivers/gator/daemon/RequestXML.h new file mode 100644 index 00000000000..bebd04809ae --- /dev/null +++ b/drivers/gator/daemon/RequestXML.h @@ -0,0 +1,33 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 REQUEST_XML_H +#define REQUEST_XML_H + +#include "XMLReader.h" + +struct ConfigParameters { + bool protocol; + bool events; + bool configuration; + bool counters; + bool session; + bool captured; + bool defaults; +}; + +class RequestXML { +public: + RequestXML(const char * str); + ~RequestXML(); + ConfigParameters parameters; +private: + void requestTag(XMLReader* in); +}; + +#endif // REQUEST_XML_H diff --git a/drivers/gator/daemon/Sender.cpp b/drivers/gator/daemon/Sender.cpp new file mode 100644 index 00000000000..9792c3677a3 --- /dev/null +++ b/drivers/gator/daemon/Sender.cpp @@ -0,0 +1,106 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include +#include +#include +#include "Sender.h" +#include "Logging.h" +#include "SessionData.h" + +extern void handleException(); + +Sender::Sender(OlySocket* socket) { + dataFile = NULL; + dataSocket = NULL; + + // Set up the socket connection + if (socket) { + char streamline[64] = {0}; + dataSocket = socket; + + // Receive magic sequence - can wait forever + // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+ + while (strcmp("STREAMLINE", streamline) != 0) { + if (dataSocket->receiveString(streamline, sizeof(streamline)) == -1) { + logg->logError(__FILE__, __LINE__, "Socket disconnected"); + handleException(); + } + } + + // Send magic sequence - must be done first, afterwhich error messages can be sent + char magic[] = {'G', 'A', 'T', 'O', 'R', '\n'}; + dataSocket->send(magic, sizeof(magic)); + + gSessionData->mWaitingOnCommand = true; + logg->logMessage("Completed magic sequence"); + } + + pthread_mutex_init(&sendMutex, NULL); +} + +Sender::~Sender() { + delete dataSocket; + dataSocket = NULL; + if (dataFile) { + fclose(dataFile); + } +} + +void Sender::createDataFile(char* apcDir) { + if (apcDir == NULL) + return; + + dataFileName = (char*)malloc(strlen(apcDir) + 12); + sprintf(dataFileName, "%s/0000000000", apcDir); + dataFile = fopen(dataFileName, "wb"); + if (!dataFile) { + logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", dataFileName); + handleException(); + } +} + +void Sender::writeData(const char* data, int length, int type) { + if (length < 0 || (data == NULL && length > 0)) { + return; + } + + // Multiple threads call writeData() + pthread_mutex_lock(&sendMutex); + + // Send data over the socket connection + if (dataSocket) { + // Start alarm + alarm(8); + + // Send data over the socket, sending the type and size first + logg->logMessage("Sending data with length %d", length); + dataSocket->send((char*)&type, 1); + dataSocket->send((char*)&length, sizeof(length)); + dataSocket->send((char*)data, length); + + // Stop alarm + alarm(0); + } + + // Write data to disk as long as it is not meta data + if (dataFile && type == RESPONSE_APC_DATA) { + logg->logMessage("Writing data with length %d", length); + // Send data to the data file, storing the size first + if ((fwrite((char*)&length, 1, sizeof(length), dataFile) != sizeof(length)) || (fwrite(data, 1, length, dataFile) != (unsigned int)length)) { + logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", dataFileName); + handleException(); + } + } + + pthread_mutex_unlock(&sendMutex); +} diff --git a/drivers/gator/daemon/Sender.h b/drivers/gator/daemon/Sender.h new file mode 100644 index 00000000000..3444b852b01 --- /dev/null +++ b/drivers/gator/daemon/Sender.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __SENDER_H__ +#define __SENDER_H__ + +#include +#include +#include "OlySocket.h" + +enum { + RESPONSE_END = 0, // unused + RESPONSE_XML = 1, + RESPONSE_APC_DATA = 3, + RESPONSE_ACK = 4, + RESPONSE_NAK = 5, + RESPONSE_ERROR = 0xFF +}; + +class Sender { +public: + Sender(OlySocket* socket); + ~Sender(); + void writeData(const char* data, int length, int type); + void createDataFile(char* apcDir); +private: + OlySocket* dataSocket; + FILE* dataFile; + char* dataFileName; + pthread_mutex_t sendMutex; +}; + +#endif //__SENDER_H__ diff --git a/drivers/gator/daemon/SessionData.cpp b/drivers/gator/daemon/SessionData.cpp new file mode 100644 index 00000000000..4adcfcd6c20 --- /dev/null +++ b/drivers/gator/daemon/SessionData.cpp @@ -0,0 +1,103 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include "SessionData.h" +#include "SessionXML.h" +#include "Logging.h" +extern void handleException(); + +SessionData* gSessionData = NULL; + +SessionData::SessionData() { + initialize(); +} + +SessionData::~SessionData() { +} + +void SessionData::initialize() { + mWaitingOnCommand = false; + mSessionIsActive = false; + mLocalCapture = false; + mOneShot = false; + strcpy(mCoreName, "unknown"); + configurationXMLPath = NULL; + apcDir = NULL; + mSampleRate = 0; + mDuration = 0; + mBytes = 0; + mBacktraceDepth = 0; + mTotalBufferSize = 0; + mCores = 1; + + initializeCounters(); +} + +void SessionData::initializeCounters() { + // PMU Counters + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + mPerfCounterType[i][0] = 0; + mPerfCounterTitle[i][0] = 0; + mPerfCounterName[i][0] = 0; + mPerfCounterDescription[i][0] = 0; + mPerfCounterEnabled[i] = 0; + mPerfCounterEvent[i] = 0; + mPerfCounterColor[i] = 0; + mPerfCounterKey[i] = 0; + mPerfCounterCount[i] = 0; + mPerfCounterOperation[i][0] = 0; + mPerfCounterPerCPU[i] = false; + mPerfCounterEBSCapable[i] = false; + } +} + +void SessionData::parseSessionXML(char* xmlString) { + SessionXML session(xmlString); + session.parse(); + + // Parameter error checking + if (session.parameters.output_path == 0 && session.parameters.target_path == 0) { + logg->logError(__FILE__, __LINE__, "No capture path (target or host) was provided."); + handleException(); + } else if (gSessionData->mLocalCapture && session.parameters.target_path == 0) { + logg->logError(__FILE__, __LINE__, "Missing target_path tag in session xml required for a local capture."); + handleException(); + } + + // Set session data values + if (strcmp(session.parameters.sample_rate, "high") == 0) { + gSessionData->mSampleRate = 10000; + } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { + gSessionData->mSampleRate = 1000; + } else { // "low" + gSessionData->mSampleRate = 100; + } + gSessionData->mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; + gSessionData->mDuration = session.parameters.duration; + + // Determine buffer size (in MB) based on buffer mode + gSessionData->mOneShot = true; + if (strcmp(session.parameters.buffer_mode, "streaming") == 0) { + gSessionData->mOneShot = false; + gSessionData->mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "small") == 0) { + gSessionData->mTotalBufferSize = 1; + } else if (strcmp(session.parameters.buffer_mode, "normal") == 0) { + gSessionData->mTotalBufferSize = 4; + } else if (strcmp(session.parameters.buffer_mode, "large") == 0) { + gSessionData->mTotalBufferSize = 16; + } else { + logg->logError(__FILE__, __LINE__, "Invalid value for buffer mode in session xml."); + handleException(); + } + + gSessionData->images = session.parameters.images; + gSessionData->target_path = session.parameters.target_path; + gSessionData->title = session.parameters.title; +} diff --git a/drivers/gator/daemon/SessionData.h b/drivers/gator/daemon/SessionData.h new file mode 100644 index 00000000000..cc188f8e694 --- /dev/null +++ b/drivers/gator/daemon/SessionData.h @@ -0,0 +1,68 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 SESSION_DATA_H +#define SESSION_DATA_H + +#define MAX_PERFORMANCE_COUNTERS 50 +#define MAX_STRING_LEN 80 +#define MAX_DESCRIPTION_LEN 400 + +#define PROTOCOL_VERSION 8 +#define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions + +struct ImageLinkList { + char *path; + struct ImageLinkList *next; +}; + +class SessionData { +public: + SessionData(); + ~SessionData(); + void initialize(); + void initializeCounters(); + void parseSessionXML(char* xmlString); + + char mCoreName[MAX_STRING_LEN]; + struct ImageLinkList *images; + char* configurationXMLPath; + char* target_path; + char* apcDir; + char* title; + + bool mWaitingOnCommand; + bool mSessionIsActive; + bool mLocalCapture; + bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled + + int mBacktraceDepth; + int mTotalBufferSize; // approximate number of MB to use for the entire collection buffer, the actual amount is a multiple based on a buffer size retrieved from the driver + int mSampleRate; + int mDuration; + int mCores; + int mBytes; + + // PMU Counters + char mPerfCounterType[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterTitle[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterName[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterDescription[MAX_PERFORMANCE_COUNTERS][MAX_DESCRIPTION_LEN]; + int mPerfCounterEnabled[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterEvent[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterColor[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterCount[MAX_PERFORMANCE_COUNTERS]; + int mPerfCounterKey[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterPerCPU[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterEBSCapable[MAX_PERFORMANCE_COUNTERS]; + char mPerfCounterOperation[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; +}; + +extern SessionData* gSessionData; + +#endif // SESSION_DATA_H diff --git a/drivers/gator/daemon/SessionXML.cpp b/drivers/gator/daemon/SessionXML.cpp new file mode 100644 index 00000000000..4604d7cf193 --- /dev/null +++ b/drivers/gator/daemon/SessionXML.cpp @@ -0,0 +1,119 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include "SessionXML.h" +#include "Logging.h" + +extern void handleException(); + +static const char* TAG_SESSION = "session"; +static const char* TAG_IMAGE = "image"; + +static const char* ATTR_VERSION = "version"; +static const char* ATTR_TITLE = "title"; +static const char* ATTR_UUID = "uuid"; +static const char* ATTR_CALL_STACK_UNWINDING = "call_stack_unwinding"; +static const char* ATTR_BUFFER_MODE = "buffer_mode"; +static const char* ATTR_SAMPLE_RATE = "sample_rate"; +static const char* ATTR_TARGET_PATH = "target_path"; +static const char* ATTR_OUTPUT_PATH = "output_path"; +static const char* ATTR_DURATION = "duration"; +static const char* ATTR_PATH = "path"; + +SessionXML::SessionXML(const char * str) { + parameters.title = 0; + parameters.uuid[0] = 0; + parameters.target_path = 0; + parameters.output_path = 0; + parameters.buffer_mode[0] = 0; + parameters.sample_rate[0] = 0; + parameters.duration = 0; + parameters.call_stack_unwinding = false; + parameters.images = NULL; + mPath = 0; + mSessionXML = (char*)str; + logg->logMessage(mSessionXML); +} + +SessionXML::~SessionXML() { + if (mPath != 0) { + free(mSessionXML); + } +} + +void SessionXML::parse() { + XMLReader reader(mSessionXML); + char * tag = reader.nextTag(); + while(tag != 0) { + if (strcmp(tag, TAG_SESSION) == 0) { + sessionTag(&reader); + return; + } + tag = reader.nextTag(); + } + + logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file"); + handleException(); +} + +void SessionXML::sessionTag(XMLReader* in) { + char* tempBuffer = (char*)malloc(PATH_MAX); + int version = in->getAttributeAsInteger(ATTR_VERSION, 0); + if (version != 1) { + logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version); + handleException(); + } + + in->getAttribute(ATTR_TITLE, tempBuffer, PATH_MAX, "unnamed"); + parameters.title = strdup(tempBuffer); // freed when the child process exits + if (parameters.title == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.title (%d bytes)", strlen(tempBuffer)); + handleException(); + } + in->getAttribute(ATTR_UUID, parameters.uuid, sizeof(parameters.uuid), ""); + parameters.duration = in->getAttributeAsInteger(ATTR_DURATION, 0); + parameters.call_stack_unwinding = in->getAttributeAsBoolean(ATTR_CALL_STACK_UNWINDING, true); + in->getAttribute(ATTR_BUFFER_MODE, parameters.buffer_mode, sizeof(parameters.buffer_mode), "normal"); + in->getAttribute(ATTR_SAMPLE_RATE, parameters.sample_rate, sizeof(parameters.sample_rate), ""); + in->getAttribute(ATTR_TARGET_PATH, tempBuffer, PATH_MAX, ""); + parameters.target_path = strdup(tempBuffer); // freed when the child process exits + if (parameters.target_path == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.target_path (%d bytes)", strlen(tempBuffer)); + handleException(); + } + in->getAttribute(ATTR_OUTPUT_PATH, tempBuffer, PATH_MAX, ""); + parameters.output_path = strdup(tempBuffer); // freed when the child process exits + if (parameters.output_path == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.output_path (%d bytes)", strlen(tempBuffer)); + handleException(); + } + + free(tempBuffer); + + char * tag = in->nextTag(); + while(tag != 0) { + if (strcmp(tag, TAG_IMAGE) == 0) { + sessionImage(in); + } + tag = in->nextTag(); + } +} + +void SessionXML::sessionImage(XMLReader* in) { + int length = in->getAttributeLength(ATTR_PATH); + struct ImageLinkList *image; + + image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList)); + image->path = (char *)malloc(length + 1); + in->getAttribute(ATTR_PATH, image->path, length + 1, ""); + image->next = parameters.images; + parameters.images = image; +} diff --git a/drivers/gator/daemon/SessionXML.h b/drivers/gator/daemon/SessionXML.h new file mode 100644 index 00000000000..4649685bc9e --- /dev/null +++ b/drivers/gator/daemon/SessionXML.h @@ -0,0 +1,40 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 SESSION_XML_H +#define SESSION_XML_H + +#include "XMLReader.h" +#include "SessionData.h" + +struct ConfigParameters { + char* title; // status title + char uuid[64]; // universal unique identifier + char* target_path; // target path of where to write to disk + char* output_path; // host path of where to write to disk + char buffer_mode[64]; // buffer mode, "streaming", "low", "normal", "high" defines oneshot and buffer size + char sample_rate[64]; // capture mode, "high", "normal", or "low" + int duration; // length of profile in seconds + bool call_stack_unwinding; // whether stack unwinding is performed + struct ImageLinkList *images; // linked list of image strings +}; + +class SessionXML { +public: + SessionXML(const char * str); + ~SessionXML(); + void parse(); + ConfigParameters parameters; +private: + char* mSessionXML; + char* mPath; + void sessionTag(XMLReader* in); + void sessionImage(XMLReader* in); +}; + +#endif // SESSION_XML_H diff --git a/drivers/gator/daemon/StreamlineSetup.cpp b/drivers/gator/daemon/StreamlineSetup.cpp new file mode 100644 index 00000000000..5662ee8c76f --- /dev/null +++ b/drivers/gator/daemon/StreamlineSetup.cpp @@ -0,0 +1,321 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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 +#include +#include +#include +#include +#include +#include +#include +#include "XMLOut.h" +#include "Sender.h" +#include "Logging.h" +#include "XMLReader.h" +#include "RequestXML.h" +#include "OlyUtility.h" +#include "SessionData.h" +#include "CapturedXML.h" +#include "StreamlineSetup.h" +#include "ConfigurationXML.h" + +extern void handleException(); + +static const char* TAG_SESSION = "session"; +static const char* TAG_CONFIGURATIONS = "configurations"; + +StreamlineSetup::StreamlineSetup(OlySocket* s) { + bool ready = false; + char *data = NULL; + int type; + + socket = s; + mSessionXML = NULL; + + // Receive commands from Streamline (master) + while (!ready) { + // receive command over socket + gSessionData->mWaitingOnCommand = true; + data = readCommand(&type); + + // parse and handle data + switch (type) { + case COMMAND_REQUEST_XML: + handleRequest(data); + break; + case COMMAND_DELIVER_XML: + handleDeliver(data); + break; + case COMMAND_APC_START: + logg->logMessage("Received apc start request"); + ready = true; + break; + case COMMAND_APC_STOP: + logg->logMessage("Received apc stop request before apc start request"); + exit(0); + break; + case COMMAND_DISCONNECT: + logg->logMessage("Received disconnect command"); + exit(0); + break; + case COMMAND_PING: + logg->logMessage("Received ping command"); + sendData(NULL, 0, RESPONSE_ACK); + break; + default: + logg->logError(__FILE__, __LINE__, "Target error: Unknown command type, %d", type); + handleException(); + } + + delete(data); + } +} + +StreamlineSetup::~StreamlineSetup() { + if (mSessionXML) + free(mSessionXML); +} + +char* StreamlineSetup::readCommand(int* command) { + char type; + char* data; + int response, length; + + // receive type + response = socket->receiveNBytes(&type, sizeof(type)); + + // After receiving a single byte, we are no longer waiting on a command + gSessionData->mWaitingOnCommand = false; + + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // receive length + response = socket->receiveNBytes((char*)&length, sizeof(length)); + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // add artificial limit + if ((length < 0) || length > 1024 * 1024) { + logg->logError(__FILE__, __LINE__, "Target error: Invalid length received, %d", length); + handleException(); + } + + // allocate memory to contain the xml file, size of zero returns a zero size object + data = (char*)calloc(length + 1, 1); + if (data == NULL) { + logg->logError(__FILE__, __LINE__, "Unable to allocate memory for xml"); + handleException(); + } + + // receive data + response = socket->receiveNBytes(data, length); + if (response < 0) { + logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); + handleException(); + } + + // null terminate the data for string parsing + if (length > 0) { + data[length] = 0; + } + + *command = type; + return data; +} + +void StreamlineSetup::handleRequest(char* xml) { + RequestXML request(xml); + + if (request.parameters.protocol) { + sendProtocol(); + logg->logMessage("Sent protocol xml response"); + } else if (request.parameters.events) { + sendEvents(); + logg->logMessage("Sent events xml response"); + } else if (request.parameters.configuration) { + sendConfiguration(); + logg->logMessage("Sent configuration xml response"); + } else if (request.parameters.counters) { + sendCounters(); + logg->logMessage("Sent counters xml response"); + } else if (request.parameters.session) { + sendData(mSessionXML, strlen(mSessionXML), RESPONSE_XML); + logg->logMessage("Sent session xml response"); + } else if (request.parameters.captured) { + CapturedXML capturedXML; + const char* capturedText = capturedXML.getXML(); + sendData(capturedText, strlen(capturedText), RESPONSE_XML); + logg->logMessage("Sent captured xml response"); + } else if (request.parameters.defaults) { + sendDefaults(); + logg->logMessage("Sent default configuration xml response"); + } else { + char error[] = "Unknown request"; + sendData(error, strlen(error), RESPONSE_NAK); + logg->logMessage("Received unknown request:\n%s", xml); + } +} + +typedef enum {UNKNOWN, SESSION_XML, CONFIGURATION_XML} delivery_type_t; +void StreamlineSetup::handleDeliver(char* xml) { + delivery_type_t type = UNKNOWN; + + // Determine xml type + XMLReader reader(xml); + char * tag = reader.nextTag(); + while(tag != 0) { + if (strcmp(tag, TAG_SESSION) == 0) { + type = SESSION_XML; + break; + } else if (strcmp(tag, TAG_CONFIGURATIONS) == 0) { + type = CONFIGURATION_XML; + break; + } + tag = reader.nextTag(); + } + + switch (type) { + case UNKNOWN: + logg->logMessage("Received unknown delivery type: %d", type); + sendData(NULL, 0, RESPONSE_NAK); + break; + case SESSION_XML: + // Parse the session xml + gSessionData->parseSessionXML(xml); + + // Save xml + mSessionXML = strdup(xml); + if (mSessionXML == NULL) { + logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1); + handleException(); + } + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received session xml"); + break; + case CONFIGURATION_XML: + writeConfiguration(xml); + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received configuration xml"); + break; + } +} + +void StreamlineSetup::sendData(const char* data, int length, int type) { + socket->send((char*)&type, 1); + socket->send((char*)&length, sizeof(length)); + socket->send((char*)data, length); +} + +void StreamlineSetup::sendProtocol() { + XMLOut out; + out.xmlHeader(); + + out.startElement("protocol"); + out.attributeInt("version", PROTOCOL_VERSION); + out.endElement("protocol"); + + sendString(out.getXmlString(), RESPONSE_XML); +} + +void StreamlineSetup::sendEvents() { +#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len + char* path = (char*)malloc(PATH_MAX);; + char* buffer; + unsigned int size = 0; + + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + buffer = util->readFromDisk(path, &size); + if (buffer == NULL) { + logg->logMessage("Unable to locate events.xml, using default"); + buffer = (char*)events_xml; + size = events_xml_len; + } + + sendData(buffer, size, RESPONSE_XML); + if (buffer != (char*)events_xml) { + free(buffer); + } + free(path); +} + +void StreamlineSetup::sendConfiguration() { + ConfigurationXML xml; + + const char* string = xml.getConfigurationXML(); + sendData(string, strlen(string), RESPONSE_XML); +} + +void StreamlineSetup::sendDefaults() { +#include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len + // Send the config built into the binary + char* xml = (char*)configuration_xml; + unsigned int size = configuration_xml_len; + + // Artificial size restriction + if (size > 1024*1024) { + logg->logError(__FILE__, __LINE__, "Corrupt default configuration file"); + handleException(); + } + + sendData(xml, size, RESPONSE_XML); +} + +#include +void StreamlineSetup::sendCounters() { + XMLOut out; + struct dirent *ent; + + // 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(); + } + + out.xmlHeader(); + out.startElement("counters"); + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + out.startElement("counter"); + out.attributeString("name", ent->d_name); + out.endElement("counter"); + } + out.endElement("counters"); + closedir (dir); + + sendString(out.getXmlString(), RESPONSE_XML); +} + +void StreamlineSetup::writeConfiguration(char* xml) { + char* path = (char*)malloc(PATH_MAX); + + if (gSessionData->configurationXMLPath) { + strncpy(path, gSessionData->configurationXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); + } + + if (util->writeToDisk(path, xml) < 0) { + logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify write permissions to this path.", path); + handleException(); + } + + // Re-populate gSessionData with the configuration, as it has now changed + new ConfigurationXML(); + free(path); +} diff --git a/drivers/gator/daemon/StreamlineSetup.h b/drivers/gator/daemon/StreamlineSetup.h new file mode 100644 index 00000000000..10327e00bc7 --- /dev/null +++ b/drivers/gator/daemon/StreamlineSetup.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __STREAMLINE_SETUP_H__ +#define __STREAMLINE_SETUP_H__ + +#include "OlySocket.h" + +// Commands from Streamline +enum { + COMMAND_REQUEST_XML = 0, + COMMAND_DELIVER_XML = 1, + COMMAND_APC_START = 2, + COMMAND_APC_STOP = 3, + COMMAND_DISCONNECT = 4, + COMMAND_PING = 5 +}; + +class StreamlineSetup { +public: + StreamlineSetup(OlySocket *socket); + ~StreamlineSetup(); +private: + int numConnections; + OlySocket* socket; + char* mSessionXML; + + char* readCommand(int*); + void handleRequest(char* xml); + void handleDeliver(char* xml); + void sendData(const char* data, int length, int type); + void sendString(const char* string, int type) {sendData(string, strlen(string), type);} + void sendProtocol(); + void sendEvents(); + void sendConfiguration(); + void sendDefaults(); + void sendCounters(); + void writeConfiguration(char* xml); +}; + +#endif //__STREAMLINE_SETUP_H__ diff --git a/drivers/gator/daemon/XMLOut.cpp b/drivers/gator/daemon/XMLOut.cpp new file mode 100644 index 00000000000..c194fc0042d --- /dev/null +++ b/drivers/gator/daemon/XMLOut.cpp @@ -0,0 +1,175 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include "XMLOut.h" + +XMLOut::XMLOut() { + indent = 0; + incomplete = false; + xml_string[0] = 0; +} + +XMLOut::~XMLOut() { +} + +void XMLOut::writeTabs() { + for (int i = 0; i < indent; i++) { + writeData(" "); + } +} + +void XMLOut::encodeAttributeData(const char* data) { + if (data) { + while (*data) { + char ch = *data++; + + if (ch == '<') { + writeData("<"); + } else if (ch == '>') { + writeData(">"); + } else if (ch == '&') { + writeData("&"); + } else if (ch == '"') { + writeData("""); + } else if (ch == '\'') { + writeData("'"); + } else if (ch >= ' ' && ch <= '~') { + writeData("%c",ch); + } else { + writeData("&#%u;",(unsigned int)ch); + } + } + } +} + +void XMLOut::writeData(const char *format, ...) { + va_list ap; + + va_start(ap, format); + vsnprintf(temp_buffer, sizeof(temp_buffer), format, ap); + va_end(ap); + + strncat(xml_string, temp_buffer, sizeof(xml_string) - strlen(xml_string) - 1); +} + +const XMLOut & XMLOut::xmlHeader(void) { + writeData("\n"); + incomplete = false; + return *this; +} + +const XMLOut & XMLOut::comment(const char* text, const bool newline) { + if (incomplete) { + writeData(">\n"); + } + writeTabs(); + writeData("", text); + if (newline) { + writeData("\n"); + } + incomplete = false; + return *this; +} + +const XMLOut & XMLOut::startElement(const char* tag) { + if (incomplete) { + writeData(">\n"); + } + writeTabs(); + writeData("<%s", tag); + incomplete = true; + indent++; + return *this; +} + +const XMLOut & XMLOut::startElement(const char* tag, int index) { + if (incomplete) { + writeData(">\n"); + } + writeTabs(); + writeData("", index); + writeData("<%s", tag); + incomplete = true; + indent++; + return *this; +} + +const XMLOut & XMLOut::endElement(const char* tag) { + indent--; + if (indent < 0) { + indent = 0; + } + if (incomplete) { + writeData("/>\n"); + incomplete = false; + } else { + writeTabs(); + writeData("\n", tag); + } + return *this; +} + +const XMLOut & XMLOut::attributeString(const char* name, const char* value) { + writeData(" %s=\"", name); + encodeAttributeData(value); + writeData("\""); + return *this; +} + +const XMLOut & XMLOut::attributeInt(const char* name, int value) { + writeData(" %s=\"%d\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeUInt(const char* name, unsigned int value) { + writeData(" %s=\"%u\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeLong(const char* name, long value) { + writeData(" %s=\"%ld\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeULong(const char* name, unsigned long value) { + writeData(" %s=\"%lu\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeLongLong(const char* name, long long value) { + writeData(" %s=\"%lld\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeULongLong(const char* name, unsigned long long value) { + writeData(" %s=\"%llu\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeDouble(const char* name, double value) { + writeData(" %s=\"%f\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeBool(const char* name, bool value) { + writeData(" %s=\"%s\"", name, value ? "yes" : "no"); + return *this; +} + +const XMLOut & XMLOut::attributeHex4(const char* name, int value) { + writeData(" %s=\"0x%04x\"", name, value); + return *this; +} + +const XMLOut & XMLOut::attributeHex8(const char* name, int value) { + writeData(" %s=\"0x%08x\"", name, value); + return *this; +} diff --git a/drivers/gator/daemon/XMLOut.h b/drivers/gator/daemon/XMLOut.h new file mode 100644 index 00000000000..3af253dbe83 --- /dev/null +++ b/drivers/gator/daemon/XMLOut.h @@ -0,0 +1,45 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 __XMLOUT_H +#define __XMLOUT_H + +class XMLOut { + int indent; + bool incomplete; + char temp_buffer[4096]; // arbitrarilly large buffer to hold variable arguments + char xml_string[64*1024]; // arbitrarilly large buffer to hold an xml file output by the daemon + + void writeTabs(); + void encodeAttributeData(const char* data); + void writeData(const char *format, ...); + +public: + XMLOut(); + ~XMLOut(); + char* getXmlString() {return xml_string;} + void clearXmlString() {xml_string[0]=0;} + const XMLOut & xmlHeader(void); + const XMLOut & comment(const char* text, const bool newline); + const XMLOut & startElement(const char* tag); + const XMLOut & startElement(const char* tag, int index); + const XMLOut & endElement(const char* tag); + const XMLOut & attributeString(const char* name, const char* value); + const XMLOut & attributeInt(const char* name, int value); + const XMLOut & attributeUInt(const char* name, unsigned int value); + const XMLOut & attributeLong(const char* name, long value); + const XMLOut & attributeULong(const char* name, unsigned long value); + const XMLOut & attributeLongLong(const char* name, long long value); + const XMLOut & attributeULongLong(const char* name, unsigned long long value); + const XMLOut & attributeDouble(const char* name, double value); + const XMLOut & attributeBool(const char* name, bool value); + const XMLOut & attributeHex4(const char* name, int value); + const XMLOut & attributeHex8(const char* name, int value); +}; + +#endif // __XMLOUT_H diff --git a/drivers/gator/daemon/XMLReader.cpp b/drivers/gator/daemon/XMLReader.cpp new file mode 100644 index 00000000000..77c76e29d73 --- /dev/null +++ b/drivers/gator/daemon/XMLReader.cpp @@ -0,0 +1,164 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include "XMLReader.h" +extern void handleException(); + +XMLReader::XMLReader(const char* xmlstring) { + mPtr = (char*)xmlstring; + mNoMore = false; + mFirstTime = true; +} + +XMLReader::~XMLReader() { +} + +char* XMLReader::nextTag() { + static char tag[128]; // arbitrarily set max tag size to 127 characters + nul + + // Check if past the end of the root tag + if (mNoMore) return NULL; + + // Find start character + mPtr = strchr(mPtr, '<'); + + if (mPtr == NULL) return mPtr; + + // Skip tag if it begins with '); + if (end == NULL) + return 0; + + // Check if tag has no attributes + char* tagend = strchr(mPtr, '>'); + if (tagend < end) end = tagend; + + // Check the tag name length + unsigned int length = (int)end - (int)mPtr; + if (length > sizeof(tag) - 1) { + // tag name too long, skip it + return nextTag(); + } + + // Return the tag name + strncpy(tag, mPtr, length); + tag[length] = 0; + + // Mark the root tag + if (mFirstTime) { + mEndXML[0] = '/'; + strcpy(&mEndXML[1], tag); + mFirstTime = false; + } else if (strcmp(tag, mEndXML) == 0) { + // End of root tag found + mNoMore = true; + } + + return tag; +} + +void XMLReader::getAttribute(const char* name, char* value, int maxSize, const char* defValue) { + char searchString[128]; + + // Set up default + strncpy(value, defValue, maxSize - 1); + value[maxSize - 1] = 0; + + // Determine search string by ending the name with =" + if (strlen(name) > sizeof(searchString) - 3) return; + strcpy(searchString, name); + strcat(searchString, "=\""); + + // Find the beginning of the attribute's search string + char* begin = strstr(mPtr, searchString); + if (begin == NULL) return; + + // Find the beginning of the attribute's value + begin += strlen(searchString); + + // Find the end of the current tag to make sure the attribute exists within the tag + char* endtag = strchr(mPtr, '>'); + if (endtag < begin) return; + + // Find the end of the attribute's value + char* end = strchr(begin, '"'); + if (end == NULL) return; + + // Determine length + int length = (int)end - (int)begin; + if (length > maxSize - 1) return; + + strncpy(value, begin, length); + value[length] = 0; +} + +int XMLReader::getAttributeAsInteger(const char* name, int defValue) { + char value[32]; + getAttribute(name, value, sizeof(value), ""); + if (value[0] == 0) return defValue; + if (value[0] == '0' && value[1] == 'x') { + return (int) strtoul(&value[2], (char**)NULL, 16); + } + return strtol(value, NULL, 10); +} + +bool XMLReader::getAttributeAsBoolean(const char* name, bool defValue) { + char value[32]; + getAttribute(name, value, sizeof(value), ""); + if (value[0] == 0) return defValue; + + // Convert to lowercase + int i = 0; + while (value[i]) { + value[i] = tolower(value[i]); + i++; + } + + if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) return true; + else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) return false; + else return defValue; +} + +int XMLReader::getAttributeLength(const char* name) { + char searchString[128]; // arbitrarily large amount + + // Determine search string by ending the name with =" + if (strlen(name) > sizeof(searchString) - 3) return 0; + strcpy(searchString, name); + strcat(searchString, "=\""); + + // Find the beginning of the attribute's search string + char* begin = strstr(mPtr, searchString); + if (begin == NULL) return 0; + + // Find the beginning of the attribute's value + begin += strlen(searchString); + + // Find the end of the current tag to make sure the attribute exists within the tag + char* endtag = strchr(mPtr, '>'); + if (endtag < begin) return 0; + + // Find the end of the attribute's value + char* end = strchr(begin, '"'); + if (end == NULL) return 0; + + // Determine length + return (int)end - (int)begin; +} diff --git a/drivers/gator/daemon/XMLReader.h b/drivers/gator/daemon/XMLReader.h new file mode 100644 index 00000000000..ac0098add0a --- /dev/null +++ b/drivers/gator/daemon/XMLReader.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 _XMLREADER_H +#define _XMLREADER_H + +class XMLReader { +public: + XMLReader(const char* xmlstring); + ~XMLReader(); + char* nextTag(); + void getAttribute(const char* name, char* value, int maxSize, const char* defValue); + int getAttributeAsInteger(const char* name, int defValue); + bool getAttributeAsBoolean(const char* name, bool defValue); + int getAttributeLength(const char* name); +private: + char* mPtr; + bool mFirstTime, mNoMore; + char mEndXML[128]; +}; + +#endif // _XMLREADER_H diff --git a/drivers/gator/daemon/configuration.xml b/drivers/gator/daemon/configuration.xml new file mode 100644 index 00000000000..0f910651950 --- /dev/null +++ b/drivers/gator/daemon/configuration.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gator/daemon/events-ARM11.xml b/drivers/gator/daemon/events-ARM11.xml new file mode 100644 index 00000000000..5742271c29b --- /dev/null +++ b/drivers/gator/daemon/events-ARM11.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gator/daemon/events-ARM11MPCore.xml b/drivers/gator/daemon/events-ARM11MPCore.xml new file mode 100644 index 00000000000..6da9ecb49dd --- /dev/null +++ b/drivers/gator/daemon/events-ARM11MPCore.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-Cortex-A15.xml b/drivers/gator/daemon/events-Cortex-A15.xml new file mode 100644 index 00000000000..86a3d17e9d8 --- /dev/null +++ b/drivers/gator/daemon/events-Cortex-A15.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-Cortex-A5.xml b/drivers/gator/daemon/events-Cortex-A5.xml new file mode 100644 index 00000000000..3683947cbe5 --- /dev/null +++ b/drivers/gator/daemon/events-Cortex-A5.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gator/daemon/events-Cortex-A7.xml b/drivers/gator/daemon/events-Cortex-A7.xml new file mode 100644 index 00000000000..db9f180ca10 --- /dev/null +++ b/drivers/gator/daemon/events-Cortex-A7.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-Cortex-A8.xml b/drivers/gator/daemon/events-Cortex-A8.xml new file mode 100644 index 00000000000..f3396b71144 --- /dev/null +++ b/drivers/gator/daemon/events-Cortex-A8.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-Cortex-A9.xml b/drivers/gator/daemon/events-Cortex-A9.xml new file mode 100644 index 00000000000..1edb54a9359 --- /dev/null +++ b/drivers/gator/daemon/events-Cortex-A9.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-Krait-architected.xml b/drivers/gator/daemon/events-Krait-architected.xml new file mode 100644 index 00000000000..46d6a616b05 --- /dev/null +++ b/drivers/gator/daemon/events-Krait-architected.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-L2C-310.xml b/drivers/gator/daemon/events-L2C-310.xml new file mode 100644 index 00000000000..b1cd8dff5b5 --- /dev/null +++ b/drivers/gator/daemon/events-L2C-310.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gator/daemon/events-Linux.xml b/drivers/gator/daemon/events-Linux.xml new file mode 100644 index 00000000000..338e1605506 --- /dev/null +++ b/drivers/gator/daemon/events-Linux.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/drivers/gator/daemon/events-Mali-400.xml b/drivers/gator/daemon/events-Mali-400.xml new file mode 100644 index 00000000000..f64fec8aaac --- /dev/null +++ b/drivers/gator/daemon/events-Mali-400.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/gator/daemon/events-Scorpion.xml b/drivers/gator/daemon/events-Scorpion.xml new file mode 100644 index 00000000000..8ad196a2dbf --- /dev/null +++ b/drivers/gator/daemon/events-Scorpion.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events-ScorpionMP.xml b/drivers/gator/daemon/events-ScorpionMP.xml new file mode 100644 index 00000000000..bd13b8a9b3a --- /dev/null +++ b/drivers/gator/daemon/events-ScorpionMP.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/gator/daemon/events_footer.xml b/drivers/gator/daemon/events_footer.xml new file mode 100644 index 00000000000..cd2b44665ba --- /dev/null +++ b/drivers/gator/daemon/events_footer.xml @@ -0,0 +1 @@ + diff --git a/drivers/gator/daemon/events_header.xml b/drivers/gator/daemon/events_header.xml new file mode 100644 index 00000000000..38ec4c03246 --- /dev/null +++ b/drivers/gator/daemon/events_header.xml @@ -0,0 +1,2 @@ + + diff --git a/drivers/gator/daemon/main.cpp b/drivers/gator/daemon/main.cpp new file mode 100644 index 00000000000..51237cbcf75 --- /dev/null +++ b/drivers/gator/daemon/main.cpp @@ -0,0 +1,306 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Child.h" +#include "SessionData.h" +#include "OlySocket.h" +#include "Logging.h" +#include "OlyUtility.h" + +#define DEBUG false + +extern Child* child; +extern void handleException(); +int shutdownFilesystem(); +static pthread_mutex_t numSessions_mutex; +static int numSessions = 0; +static OlySocket* socket = NULL; +static bool driverRunningAtStart = false; +static bool driverMountedAtStart = false; + +struct cmdline_t { + int port; + char* sessionXML; +}; + +void cleanUp() { + if (shutdownFilesystem() == -1) { + logg->logMessage("Error shutting down gator filesystem"); + } + delete socket; + delete util; + delete logg; +} + +// CTRL C Signal Handler +void handler(int signum) { + logg->logMessage("Received signal %d, gator daemon exiting", signum); + + // Case 1: both child and parent receive the signal + if (numSessions > 0) { + // Arbitrary sleep of 1 second to give time for the child to exit; + // if something bad happens, continue the shutdown process regardless + sleep(1); + } + + // Case 2: only the parent received the signal + if (numSessions > 0) { + // Kill child threads - the first signal exits gracefully + logg->logMessage("Killing process group as %d child was running when signal was received", numSessions); + kill(0, SIGINT); + + // Give time for the child to exit + sleep(1); + + if (numSessions > 0) { + // The second signal force kills the child + logg->logMessage("Force kill the child"); + kill(0, SIGINT); + // Again, sleep for 1 second + sleep(1); + + if (numSessions > 0) { + // Something bad has really happened; the child is not exiting and therefore may hold the /dev/gator resource open + printf("Unable to kill the gatord child process, thus gator.ko may still be loaded.\n"); + } + } + } + + cleanUp(); + exit(0); +} + +// Child exit Signal Handler +void child_exit(int signum) { + int status; + int pid = wait(&status); + if (pid != -1) { + pthread_mutex_lock(&numSessions_mutex); + numSessions--; + pthread_mutex_unlock(&numSessions_mutex); + logg->logMessage("Child process %d exited with status %d", pid, status); + } +} + +// retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted +int mountGatorFS() { + // If already mounted, + if (access("/dev/gator/buffer", F_OK) == 0) + return 0; + + // else, mount the filesystem + mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) + return -1; + else + return 1; +} + +int setupFilesystem() { + int retval; + + // Verify root permissions + uid_t euid = geteuid(); + if (euid) { + logg->logError(__FILE__, __LINE__, "gatord must be launched with root privileges"); + handleException(); + } + + retval = mountGatorFS(); + if (retval == 1) { + logg->logMessage("Driver already running at startup"); + driverRunningAtStart = true; + } else if (retval == 0) { + logg->logMessage("Driver already mounted at startup"); + driverRunningAtStart = driverMountedAtStart = true; + } else { + char command[256]; // arbitrarily large amount + + // Is the driver co-located in the same directory? + if (util->getApplicationFullPath(command, sizeof(command)) != 0) { // allow some buffer space + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strcat(command, "gator.ko"); + if (access(command, 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"); + handleException(); + } + + // Load driver + strcpy(command, "insmod "); + util->getApplicationFullPath(&command[7], sizeof(command) - 64); // allow some buffer space + strcat(command, "gator.ko >/dev/null 2>&1"); + + if (system(command) != 0) { + logg->logMessage("Unable to load gator.ko driver with command: %s", command); + logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details"); + handleException(); + } + + if (mountGatorFS() == -1) { + logg->logError(__FILE__, __LINE__, "Unable to mount the gator filesystem needed for profiling."); + handleException(); + } + } + + return 0; +} + +int shutdownFilesystem() { + if (driverMountedAtStart == false) + umount("/dev/gator"); + if (driverRunningAtStart == false) + if (system("rmmod gator >/dev/null 2>&1") != 0) + return -1; + + return 0; // success +} + +struct cmdline_t parseCommandLine(int argc, char** argv) { + struct cmdline_t cmdline; + cmdline.port = 8080; + cmdline.sessionXML = NULL; + int c; + + while ((c = getopt (argc, argv, "hvp:s:c:")) != -1) { + switch(c) { + case 'p': + cmdline.port = strtol(optarg, NULL, 10); + break; + case 's': + cmdline.sessionXML = optarg; + break; + case 'c': + gSessionData->configurationXMLPath = optarg; + break; + case 'h': + case '?': + logg->logError(__FILE__, __LINE__, + "Streamline gatord version %d. All parameters are optional:\n" + "-p port_number\tport upon which the server listens; default is 8080\n" + "-s session_xml\tpath and filename of a session xml used for local capture\n" + "-c config_xml\tpath and filename of the configuration.xml to use\n" + "-v\t\tversion information\n" + "-h\t\tthis help page\n", PROTOCOL_VERSION); + handleException(); + break; + case 'v': + logg->logError(__FILE__, __LINE__, "Streamline gatord version %d", PROTOCOL_VERSION); + handleException(); + break; + } + } + + // Error checking + if (cmdline.port != 8080 && cmdline.sessionXML != NULL) { + logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both"); + handleException(); + } + + if (optind < argc) { + logg->logError(__FILE__, __LINE__, "Unknown argument: %s. Use '-h' for help.", argv[optind]); + handleException(); + } + + return cmdline; +} + +// Gator data flow: collector -> collector fifo -> sender +int main(int argc, char** argv, char *envp[]) { + gSessionData = new SessionData(); // Global data class + logg = new Logging(DEBUG); // Set up global thread-safe logging + util = new OlyUtility(); // Set up global utility class + + prctl(PR_SET_NAME, (unsigned int)&"gatord-main", 0, 0, 0); + pthread_mutex_init(&numSessions_mutex, NULL); + + signal(SIGINT, handler); + signal(SIGTERM, handler); + signal(SIGABRT, handler); + + // Set to high priority + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) + logg->logMessage("setpriority() failed"); + + // Initialize session data + gSessionData->initialize(); + + // Parse the command line parameters + struct cmdline_t cmdline = parseCommandLine(argc, argv); + + // Call before setting up the SIGCHLD handler, as system() spawns child processes + setupFilesystem(); + + // Handle child exit codes + signal(SIGCHLD, child_exit); + + // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal + // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler + signal(SIGPIPE, SIG_IGN); + + // If the command line argument is a session xml file, no need to open a socket + if (cmdline.sessionXML) { + child = new Child(cmdline.sessionXML); + child->run(); + delete child; + } else { + socket = new OlySocket(cmdline.port, true); + // Forever loop, can be exited via a signal or exception + while (1) { + logg->logMessage("Waiting on connection..."); + socket->acceptConnection(); + + int pid = fork(); + if (pid < 0) { + // Error + logg->logError(__FILE__, __LINE__, "Fork process failed. Please power cycle the target device if this error persists."); + } else if (pid == 0) { + // Child + socket->closeServerSocket(); + child = new Child(socket, numSessions + 1); + child->run(); + delete child; + exit(0); + } else { + // Parent + socket->closeSocket(); + + pthread_mutex_lock(&numSessions_mutex); + numSessions++; + pthread_mutex_unlock(&numSessions_mutex); + + // Maximum number of connections is 2 + int wait = 0; + while (numSessions > 1) { + // Throttle until one of the children exits before continuing to accept another socket connection + logg->logMessage("%d sessions active!", numSessions); + if (wait++ >= 10) { // Wait no more than 10 seconds + // Kill last created child + kill(pid, SIGALRM); + break; + } + sleep(1); + } + } + } + } + + cleanUp(); + return 0; +} -- cgit v1.2.3 From 1acaa28b2098f7e2aa8eca492c569e894e1a4b79 Mon Sep 17 00:00:00 2001 From: Wade Cherry Date: Thu, 23 Feb 2012 09:56:29 +0000 Subject: gator/daemon: Fix counters initialisation In certain circumstances counter were not enabled correctly. Fixed. Signed-off-by: Wade Cherry Signed-off-by: Pawel Moll Signed-off-by: Jon Medhurst --- drivers/gator/daemon/Child.cpp | 2 +- drivers/gator/daemon/Collector.cpp | 27 +++++++++++++++++++++------ drivers/gator/daemon/Collector.h | 1 + 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/gator/daemon/Child.cpp b/drivers/gator/daemon/Child.cpp index fe5a1f061f9..ddf105b8d31 100644 --- a/drivers/gator/daemon/Child.cpp +++ b/drivers/gator/daemon/Child.cpp @@ -226,7 +226,7 @@ void Child::run() { } // Write configuration into the driver - collector->enablePerfCounters(); + collector->setupPerfCounters(); // Create user-space buffers int fifoBufferSize = collector->getBufferSize(); diff --git a/drivers/gator/daemon/Collector.cpp b/drivers/gator/daemon/Collector.cpp index c6f837628f1..7a41198e5cb 100644 --- a/drivers/gator/daemon/Collector.cpp +++ b/drivers/gator/daemon/Collector.cpp @@ -46,10 +46,14 @@ Collector::Collector() { getCoreName(); + enablePerfCounters(); + // Read unchanging keys from driver which are created at insmod'ing of gator.ko for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { - snprintf(text, sizeof(text), "events/%s/key", gSessionData->mPerfCounterType[i]); - readIntDriver(text, &gSessionData->mPerfCounterKey[i]); + if (gSessionData->mPerfCounterEnabled[i]) { + snprintf(text, sizeof(text), "events/%s/key", gSessionData->mPerfCounterType[i]); + readIntDriver(text, &gSessionData->mPerfCounterKey[i]); + } } } @@ -64,6 +68,21 @@ Collector::~Collector() { } void Collector::enablePerfCounters() { + char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events//enabled + for (int i=0; imPerfCounterEnabled[i]) { + continue; + } + snprintf(text, sizeof(text), "events/%s/enabled", gSessionData->mPerfCounterType[i]); + if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) { + // Disable those events that don't exist on this hardware platform even though they exist in configuration.xml + gSessionData->mPerfCounterEnabled[i] = 0; + continue; + } + } +} + +void Collector::setupPerfCounters() { char base[sizeof(gSessionData->mPerfCounterType[0]) + 10]; // sufficiently large to hold all events/ char text[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all events// @@ -86,10 +105,6 @@ void Collector::enablePerfCounters() { handleException(); } } - snprintf(text, sizeof(text), "%s/enabled", base); - if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) { - gSessionData->mPerfCounterEnabled[i] = 0; - } } } diff --git a/drivers/gator/daemon/Collector.h b/drivers/gator/daemon/Collector.h index 1cfe84e1b97..6c80725ad94 100644 --- a/drivers/gator/daemon/Collector.h +++ b/drivers/gator/daemon/Collector.h @@ -19,6 +19,7 @@ public: void stop(); int collect(char* buffer); void enablePerfCounters(); + void setupPerfCounters(); int getBufferSize() {return bufferSize;} private: int bufferSize; -- cgit v1.2.3 From 18f88ad877a9d3c8bc21ceb0b565e6a32a7a07cc Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 16 Mar 2012 17:36:42 +0000 Subject: gator/driver: Version 5.9 Signed-off-by: Jon Medhurst --- drivers/gator/driver/Makefile | 56 ++ drivers/gator/driver/gator.h | 99 +++ drivers/gator/driver/gator_annotate.c | 181 +++++ drivers/gator/driver/gator_annotate_kernel.c | 90 +++ drivers/gator/driver/gator_backtrace.c | 116 +++ drivers/gator/driver/gator_cookies.c | 403 ++++++++++ drivers/gator/driver/gator_ebs.c | 192 +++++ drivers/gator/driver/gator_events.sh | 19 + drivers/gator/driver/gator_events_armv6.c | 244 ++++++ drivers/gator/driver/gator_events_armv7.c | 320 ++++++++ drivers/gator/driver/gator_events_block.c | 171 ++++ drivers/gator/driver/gator_events_irq.c | 195 +++++ drivers/gator/driver/gator_events_l2c-310.c | 179 +++++ drivers/gator/driver/gator_events_mali.c | 611 ++++++++++++++ drivers/gator/driver/gator_events_meminfo.c | 196 +++++ drivers/gator/driver/gator_events_mmaped.c | 198 +++++ drivers/gator/driver/gator_events_net.c | 144 ++++ drivers/gator/driver/gator_events_perf_pmu.c | 296 +++++++ drivers/gator/driver/gator_events_power.c | 178 +++++ drivers/gator/driver/gator_events_sched.c | 114 +++ drivers/gator/driver/gator_events_scorpion.c | 678 ++++++++++++++++ drivers/gator/driver/gator_fs.c | 284 +++++++ drivers/gator/driver/gator_hrtimer_gator.c | 76 ++ drivers/gator/driver/gator_hrtimer_perf.c | 113 +++ drivers/gator/driver/gator_main.c | 1109 ++++++++++++++++++++++++++ drivers/gator/driver/gator_pack.c | 262 ++++++ drivers/gator/driver/gator_trace_gpu.c | 250 ++++++ drivers/gator/driver/gator_trace_gpu.h | 79 ++ drivers/gator/driver/gator_trace_sched.c | 269 +++++++ 29 files changed, 7122 insertions(+) create mode 100644 drivers/gator/driver/Makefile create mode 100644 drivers/gator/driver/gator.h create mode 100644 drivers/gator/driver/gator_annotate.c create mode 100644 drivers/gator/driver/gator_annotate_kernel.c create mode 100644 drivers/gator/driver/gator_backtrace.c create mode 100644 drivers/gator/driver/gator_cookies.c create mode 100644 drivers/gator/driver/gator_ebs.c create mode 100644 drivers/gator/driver/gator_events.sh create mode 100644 drivers/gator/driver/gator_events_armv6.c create mode 100644 drivers/gator/driver/gator_events_armv7.c create mode 100644 drivers/gator/driver/gator_events_block.c create mode 100644 drivers/gator/driver/gator_events_irq.c create mode 100644 drivers/gator/driver/gator_events_l2c-310.c create mode 100644 drivers/gator/driver/gator_events_mali.c create mode 100644 drivers/gator/driver/gator_events_meminfo.c create mode 100644 drivers/gator/driver/gator_events_mmaped.c create mode 100644 drivers/gator/driver/gator_events_net.c create mode 100644 drivers/gator/driver/gator_events_perf_pmu.c create mode 100644 drivers/gator/driver/gator_events_power.c create mode 100644 drivers/gator/driver/gator_events_sched.c create mode 100644 drivers/gator/driver/gator_events_scorpion.c create mode 100644 drivers/gator/driver/gator_fs.c create mode 100644 drivers/gator/driver/gator_hrtimer_gator.c create mode 100644 drivers/gator/driver/gator_hrtimer_perf.c create mode 100644 drivers/gator/driver/gator_main.c create mode 100644 drivers/gator/driver/gator_pack.c create mode 100644 drivers/gator/driver/gator_trace_gpu.c create mode 100644 drivers/gator/driver/gator_trace_gpu.h create mode 100644 drivers/gator/driver/gator_trace_sched.c diff --git a/drivers/gator/driver/Makefile b/drivers/gator/driver/Makefile new file mode 100644 index 00000000000..e521b9915dc --- /dev/null +++ b/drivers/gator/driver/Makefile @@ -0,0 +1,56 @@ +ifneq ($(KERNELRELEASE),) + +# Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c +# EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING + +obj-m := gator.o + +gator-y := gator_main.o \ + gator_events_irq.o \ + gator_events_sched.o \ + gator_events_net.o \ + gator_events_block.o \ + gator_events_meminfo.o \ + gator_events_power.o \ + gator_events_perf_pmu.o + +gator-y += gator_events_mmaped.o + +ifneq ($(GATOR_WITH_MALI_SUPPORT),) +ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) +gator-y += gator_events_mali_t6xx.o +else +gator-y += gator_events_mali.o +endif +EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) +endif + +gator-$(CONFIG_ARM) += gator_events_armv6.o \ + gator_events_armv7.o \ + gator_events_l2c-310.o \ + gator_events_scorpion.o + +$(obj)/gator_main.o: gator_events.h + +clean-files := gator_events.h + + chk_events.h = : + quiet_chk_events.h = echo ' CHK $@' +silent_chk_events.h = : +gator_events.h: FORCE + @$($(quiet)chk_events.h) + $(Q)cd $(obj) ; $(CONFIG_SHELL) $(obj)/gator_events.sh $@ + +else + +all: + @echo + @echo "usage:" + @echo " make -C M=\`pwd\` ARCH=arm CROSS_COMPILE=<...> modules" + @echo + $(error) + +clean: + rm -f *.o .*.cmd gator_events.h modules.order Module.symvers gator.ko gator.mod.c + +endif diff --git a/drivers/gator/driver/gator.h b/drivers/gator/driver/gator.h new file mode 100644 index 00000000000..a7a323c3fae --- /dev/null +++ b/drivers/gator/driver/gator.h @@ -0,0 +1,99 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 GATOR_H_ +#define GATOR_H_ + +#include +#include +#include +#include + +#define GATOR_PERF_SUPPORT LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) +#define GATOR_PERF_PMU_SUPPORT GATOR_PERF_SUPPORT && defined(CONFIG_PERF_EVENTS) && defined(CONFIG_HW_PERF_EVENTS) +#define GATOR_NO_PERF_SUPPORT (!(GATOR_PERF_SUPPORT)) +#define GATOR_CPU_FREQ_SUPPORT (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) && defined(CONFIG_CPU_FREQ) + +// cpu ids +#define ARM1136 0xb36 +#define ARM1156 0xb56 +#define ARM1176 0xb76 +#define ARM11MPCORE 0xb02 +#define CORTEX_A5 0xc05 +#define CORTEX_A7 0xc07 +#define CORTEX_A8 0xc08 +#define CORTEX_A9 0xc09 +#define CORTEX_A15 0xc0f +#define SCORPION 0x00f +#define SCORPIONMP 0x02d +#define KRAITSIM 0x049 +#define KRAIT 0x04d + +/****************************************************************************** + * 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); + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val); + +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 + ******************************************************************************/ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +# error Kernels prior to 2.6.32 not supported +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name) +#else +# define GATOR_DEFINE_PROBE(probe_name, proto) \ + static void probe_##probe_name(void *data, PARAMS(proto)) +# define GATOR_REGISTER_TRACE(probe_name) \ + register_trace_##probe_name(probe_##probe_name, NULL) +# define GATOR_UNREGISTER_TRACE(probe_name) \ + unregister_trace_##probe_name(probe_##probe_name, NULL) +#endif + +/****************************************************************************** + * Events + ******************************************************************************/ +struct gator_interface { + int (*create_files)(struct super_block *sb, struct dentry *root); + int (*start)(void); + void (*stop)(void); + int (*online)(int** buffer); + int (*offline)(int** buffer); + void (*online_dispatch)(int cpu); // called in process context but may not be running on core 'cpu' + void (*offline_dispatch)(int cpu); // called in process context but may not be running on core 'cpu' + int (*read)(int **buffer); + int (*read64)(long long **buffer); + struct list_head list; +}; + +#define gator_events_init(initfn) \ + static inline int __gator_events_init_test(void) \ + { return initfn(); } + +int gator_events_install(struct gator_interface *interface); +int gator_events_get_key(void); +extern u32 gator_cpuid(void); + +#endif // GATOR_H_ diff --git a/drivers/gator/driver/gator_annotate.c b/drivers/gator/driver/gator_annotate.c new file mode 100644 index 00000000000..36a921cdd25 --- /dev/null +++ b/drivers/gator/driver/gator_annotate.c @@ -0,0 +1,181 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include +#include +#include +#include +#include + +#define ANNOTATE_SIZE (16*1024) +static DEFINE_SPINLOCK(annotate_lock); +static char *annotateBuf; +static char *annotateBuf0; +static char *annotateBuf1; +static int annotatePos; +static int annotateSel; +static bool collect_annotations = false; + +static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + char tempBuffer[512]; + int remaining, size; + uint32_t tid; + + if (*offset) + return -EINVAL; + + // determine size to capture + size = count < sizeof(tempBuffer) ? count : sizeof(tempBuffer); + + // note: copy may be for naught if remaining is zero, but better to do the copy outside of the spinlock + if (file == NULL) { + // copy from kernel + memcpy(tempBuffer, buf, size); + + // set the thread id to the kernel thread, not the current thread + tid = -1; + } else { + // copy from user space + if (copy_from_user(tempBuffer, buf, size) != 0) + return -EINVAL; + tid = current->pid; + } + + // synchronize shared variables annotateBuf and annotatePos + spin_lock(&annotate_lock); + if (collect_annotations && annotateBuf) { + remaining = ANNOTATE_SIZE - annotatePos - 256; // pad for headers and release + size = size < remaining ? size : remaining; + if (size > 0) { + uint64_t time = gator_get_time(); + uint32_t cpuid = smp_processor_id(); + int pos = annotatePos; + pos += gator_write_packed_int(&annotateBuf[pos], tid); + pos += gator_write_packed_int64(&annotateBuf[pos], time); + pos += gator_write_packed_int(&annotateBuf[pos], cpuid); + pos += gator_write_packed_int(&annotateBuf[pos], size); + memcpy(&annotateBuf[pos], tempBuffer, size); + annotatePos = pos + size; + } + } + spin_unlock(&annotate_lock); + + if (size <= 0) { + wake_up(&gator_buffer_wait); + return 0; + } + + // return the number of bytes written + return size; +} + +#include "gator_annotate_kernel.c" + +static int annotate_release(struct inode *inode, struct file *file) +{ + int remaining = ANNOTATE_SIZE - annotatePos; + if (remaining < 16) { + return -EFAULT; + } + + spin_lock(&annotate_lock); + if (annotateBuf) { + uint32_t tid = current->pid; + int pos = annotatePos; + pos += gator_write_packed_int(&annotateBuf[pos], tid); + pos += gator_write_packed_int64(&annotateBuf[pos], 0); // time + pos += gator_write_packed_int(&annotateBuf[pos], 0); // cpuid + pos += gator_write_packed_int(&annotateBuf[pos], 0); // size + annotatePos = pos; + } + spin_unlock(&annotate_lock); + + return 0; +} + +static const struct file_operations annotate_fops = { + .write = annotate_write, + .release = annotate_release +}; + +static int gator_annotate_create_files(struct super_block *sb, struct dentry *root) +{ + annotateBuf = NULL; + return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666); +} + +static int gator_annotate_init(void) +{ + annotateBuf0 = kmalloc(ANNOTATE_SIZE, GFP_KERNEL); + annotateBuf1 = kmalloc(ANNOTATE_SIZE, GFP_KERNEL); + if (!annotateBuf0 || !annotateBuf1) + return -1; + return 0; +} + +static int gator_annotate_start(void) +{ + annotateSel = 0; + annotatePos = 1; + annotateBuf = annotateBuf0; + annotateBuf[0] = FRAME_ANNOTATE; + collect_annotations = true; + return 0; +} + +static void gator_annotate_stop(void) +{ + collect_annotations = false; +} + +static void gator_annotate_shutdown(void) +{ + spin_lock(&annotate_lock); + annotateBuf = NULL; + spin_unlock(&annotate_lock); +} + +static void gator_annotate_exit(void) +{ + spin_lock(&annotate_lock); + kfree(annotateBuf0); + kfree(annotateBuf1); + annotateBuf = annotateBuf0 = annotateBuf1 = NULL; + spin_unlock(&annotate_lock); +} + +static int gator_annotate_ready(void) +{ + return annotatePos > 1 && annotateBuf; +} + +static int gator_annotate_read(char **buffer) +{ + int len; + + if (!gator_annotate_ready()) + return 0; + + annotateSel = !annotateSel; + + if (buffer) + *buffer = annotateBuf; + + spin_lock(&annotate_lock); + len = annotatePos; + annotateBuf = annotateSel ? annotateBuf1 : annotateBuf0; + annotateBuf[0] = FRAME_ANNOTATE; + annotatePos = 1; + spin_unlock(&annotate_lock); + + return len; +} diff --git a/drivers/gator/driver/gator_annotate_kernel.c b/drivers/gator/driver/gator_annotate_kernel.c new file mode 100644 index 00000000000..ffab08795b7 --- /dev/null +++ b/drivers/gator/driver/gator_annotate_kernel.c @@ -0,0 +1,90 @@ +/** + * Copyright (C) ARM Limited 2012. 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 kannotate_write(char* ptr, unsigned int size) +{ + int retval; + int pos = 0; + loff_t offset = 0; + while (pos < size) { + retval = annotate_write(NULL, &ptr[pos], size - pos, &offset); + if (retval < 0) { + printk(KERN_WARNING "gator: kannotate_write failed with return value %d\n", retval); + return; + } + pos += retval; + } +} + +// String annotation +void gator_annotate(char* string) +{ + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate); + +// String annotation with color +void gator_annotate_color(int color, char* string) +{ + kannotate_write((char*)&color, sizeof(color)); + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate_color); + +// Terminate an annotation +void gator_annotate_end(void) +{ + char nul = 0; + kannotate_write(&nul, sizeof(nul)); +} +EXPORT_SYMBOL(gator_annotate_end); + +// Image annotation with optional string +void gator_annotate_visual(char* data, unsigned int length, char* string) +{ + long long visual_annotation = 0x011c | (strlen(string) << 16) | ((long long)length << 32); + kannotate_write((char*)&visual_annotation, 8); + kannotate_write(string, strlen(string)); + kannotate_write(data, length); +} +EXPORT_SYMBOL(gator_annotate_visual); + +// Marker annotation +void gator_annotate_marker(void) +{ + int marker_annotation = 0x00021c; + kannotate_write((char*)&marker_annotation, 3); +} +EXPORT_SYMBOL(gator_annotate_marker); + +// Marker annotation with a string +void gator_annotate_marker_str(char* string) +{ + int marker_annotation = 0x021c; + kannotate_write((char*)&marker_annotation, 2); + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate_marker_str); + +// Marker annotation with a color +void gator_annotate_marker_color(int color) +{ + long long marker_annotation = (0x021c | ((long long)color << 16)) & 0x0000ffffffffffffLL; + kannotate_write((char*)&marker_annotation, 7); +} +EXPORT_SYMBOL(gator_annotate_marker_color); + +// Marker annotationw ith a string and color +void gator_annotate_marker_color_str(int color, char* string) +{ + long long marker_annotation = 0x021c | ((long long)color << 16); + kannotate_write((char*)&marker_annotation, 6); + kannotate_write(string, strlen(string) + 1); +} +EXPORT_SYMBOL(gator_annotate_marker_color_str); diff --git a/drivers/gator/driver/gator_backtrace.c b/drivers/gator/driver/gator_backtrace.c new file mode 100644 index 00000000000..26503ef0a55 --- /dev/null +++ b/drivers/gator/driver/gator_backtrace.c @@ -0,0 +1,116 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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. + * + */ + +/* + * EABI backtrace stores {fp,lr} on the stack. + */ +struct frame_tail_eabi { + unsigned long fp; // points to prev_lr + unsigned long lr; +}; + +static void arm_backtrace_eabi(int cpu, int buftype, struct pt_regs * const regs, unsigned int depth) +{ +#if defined(__arm__) + struct frame_tail_eabi *tail; + struct frame_tail_eabi *next; + struct frame_tail_eabi *ptrtail; + struct frame_tail_eabi buftail; + unsigned long fp = regs->ARM_fp; + unsigned long sp = regs->ARM_sp; + unsigned long lr = regs->ARM_lr; + int is_user_mode = user_mode(regs); + + if (!is_user_mode) { + return; + } + + /* entry preamble may not have executed */ + gator_add_trace(cpu, buftype, lr); + + /* check tail is valid */ + if (fp == 0 || fp < sp) { + return; + } + + tail = (struct frame_tail_eabi *)(fp - 4); + + while (depth-- && tail && !((unsigned long) tail & 3)) { + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(struct frame_tail_eabi))) + return; + if (__copy_from_user_inatomic(&buftail, tail, sizeof(struct frame_tail_eabi))) + return; + ptrtail = &buftail; + + lr = ptrtail[0].lr; + gator_add_trace(cpu, buftype, lr); + + /* frame pointers should progress back up the stack, towards higher addresses */ + next = (struct frame_tail_eabi *)(lr - 4); + if (tail >= next || lr == 0) { + fp = ptrtail[0].fp; + next = (struct frame_tail_eabi *)(fp - 4); + /* check tail is valid */ + if (tail >= next || fp == 0) { + return; + } + } + + tail = next; + } +#endif +} + +#if defined(__arm__) +static DEFINE_PER_CPU(int, backtrace_buffer); +static int report_trace(struct stackframe *frame, void *d) +{ + struct module *mod; + unsigned int *depth = d, addr = frame->pc, cookie = NO_COOKIE, cpu = smp_processor_id(); + + if (*depth) { + mod = __module_address(addr); + if (mod) { + cookie = get_cookie(cpu, per_cpu(backtrace_buffer, cpu), current, NULL, mod, true); + addr = addr - (unsigned long)mod->module_core; + } + gator_buffer_write_packed_int(cpu, per_cpu(backtrace_buffer, cpu), addr & ~1); + gator_buffer_write_packed_int(cpu, per_cpu(backtrace_buffer, cpu), cookie); + (*depth)--; + } + + return *depth == 0; +} +#endif + +// Uncomment the following line to enable kernel stack unwinding within gator, note it can also be defined from the Makefile +// #define GATOR_KERNEL_STACK_UNWINDING +static void kernel_backtrace(int cpu, int buftype, struct pt_regs * const regs) +{ +#if defined(__arm__) +#ifdef GATOR_KERNEL_STACK_UNWINDING + int depth = gator_backtrace_depth; +#else + int depth = 1; +#endif + struct stackframe frame; + if (depth == 0) + depth = 1; + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + per_cpu(backtrace_buffer, cpu) = buftype; + walk_stackframe(&frame, report_trace, &depth); +#else + gator_buffer_write_packed_int(cpu, buftype, PC_REG & ~1); + gator_buffer_write_packed_int(cpu, buftype, NO_COOKIE); +#endif +} diff --git a/drivers/gator/driver/gator_cookies.c b/drivers/gator/driver/gator_cookies.c new file mode 100644 index 00000000000..1beb34f5170 --- /dev/null +++ b/drivers/gator/driver/gator_cookies.c @@ -0,0 +1,403 @@ +/** + * Copyright (C) ARM Limited 2010-2012. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define COOKIEMAP_ENTRIES 1024 /* must be power of 2 */ +#define TRANSLATE_SIZE 256 +#define MAX_COLLISIONS 2 + +static uint32_t *gator_crc32_table; +static uint32_t translate_buffer_mask; + +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(unsigned int *, translate_buffer); + +static inline uint32_t get_cookie(int cpu, int buftype, struct task_struct *task, struct vm_area_struct *vma, struct module *mod, bool in_interrupt); +static void wq_cookie_handler(struct work_struct *unused); +DECLARE_WORK(cookie_work, wq_cookie_handler); + +static uint32_t cookiemap_code(uint64_t value64) { + uint32_t value = (uint32_t)((value64 >> 32) + value64); + uint32_t cookiecode = (value >> 24) & 0xff; + cookiecode = cookiecode * 31 + ((value >> 16) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 8) & 0xff); + cookiecode = cookiecode * 31 + ((value >> 0) & 0xff); + cookiecode &= (COOKIEMAP_ENTRIES-1); + return cookiecode * MAX_COLLISIONS; +} + +static uint32_t gator_chksum_crc32(char *data) +{ + register unsigned long crc; + unsigned char *block = data; + int i, length = strlen(data); + + crc = 0xFFFFFFFF; + for (i = 0; i < length; i++) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ gator_crc32_table[(crc ^ *block++) & 0xFF]; + } + + return (crc ^ 0xFFFFFFFF); +} + +/* + * Exists + * Pre: [0][1][v][3]..[n-1] + * Post: [v][0][1][3]..[n-1] + */ +static uint32_t cookiemap_exists(uint64_t key) { + unsigned long x, flags, retval = 0; + int cpu = smp_processor_id(); + uint32_t cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + + // Can be called from interrupt handler or from work queue + local_irq_save(flags); + for (x = 0; x < MAX_COLLISIONS; x++) { + if (keys[x] == key) { + uint32_t value = values[x]; + for (; x > 0; x--) { + keys[x] = keys[x-1]; + values[x] = values[x-1]; + } + keys[0] = key; + values[0] = value; + retval = value; + break; + } + } + local_irq_restore(flags); + + return retval; +} + +/* + * Add + * Pre: [0][1][2][3]..[n-1] + * Post: [v][0][1][2]..[n-2] + */ +static void cookiemap_add(uint64_t key, uint32_t value) { + int cpu = smp_processor_id(); + int cookiecode = cookiemap_code(key); + uint64_t *keys = &(per_cpu(cookie_keys, cpu)[cookiecode]); + uint32_t *values = &(per_cpu(cookie_values, cpu)[cookiecode]); + int x; + + for (x = MAX_COLLISIONS-1; x > 0; x--) { + keys[x] = keys[x-1]; + values[x] = values[x-1]; + } + keys[0] = key; + values[0] = value; +} + +static void translate_buffer_write_int(int cpu, unsigned int x) +{ + per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_write, cpu)++] = x; + per_cpu(translate_buffer_write, cpu) &= translate_buffer_mask; +} + +static unsigned int translate_buffer_read_int(int cpu) +{ + unsigned int value = per_cpu(translate_buffer, cpu)[per_cpu(translate_buffer_read, cpu)++]; + per_cpu(translate_buffer_read, cpu) &= translate_buffer_mask; + return value; +} + +static void wq_cookie_handler(struct work_struct *unused) +{ + struct task_struct *task; + struct vm_area_struct *vma; + int cpu = smp_processor_id(); + unsigned int cookie, 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_int(cpu); + vma = (struct vm_area_struct *)translate_buffer_read_int(cpu); + cookie = get_cookie(cpu, TIMER_BUF, task, vma, NULL, false); + } + } + + mutex_unlock(&start_mutex); +} + +// Retrieve full name from proc/pid/cmdline for java processes on Android +static int translate_app_process(char** text, int cpu, struct task_struct * task, struct vm_area_struct *vma, bool in_interrupt) +{ + void *maddr; + unsigned int len; + unsigned long addr; + struct mm_struct *mm; + struct page *page = NULL; + struct vm_area_struct *page_vma; + int bytes, offset, retval = 0, ptr; + char * buf = per_cpu(translate_text, cpu); + + // Push work into a work queue if in atomic context as the kernel functions below might sleep + // Rely on the in_interrupt variable rather than in_irq() or in_interrupt() kernel functions, as the value of these functions seems + // inconsistent during a context switch between android/linux versions + if (in_interrupt) { + // Check if already in buffer + ptr = per_cpu(translate_buffer_read, cpu); + while (ptr != per_cpu(translate_buffer_write, cpu)) { + if (per_cpu(translate_buffer, cpu)[ptr] == (int)task) + goto out; + ptr = (ptr + 2) & translate_buffer_mask; + } + + translate_buffer_write_int(cpu, (unsigned int)task); + translate_buffer_write_int(cpu, (unsigned int)vma); + schedule_work(&cookie_work); + goto out; + } + + mm = get_task_mm(task); + if (!mm) + goto out; + if (!mm->arg_end) + goto outmm; + addr = mm->arg_start; + len = mm->arg_end - mm->arg_start; + + if (len > TRANSLATE_SIZE) + len = TRANSLATE_SIZE; + + down_read(&mm->mmap_sem); + while (len) { + if (get_user_pages(task, mm, addr, 1, 0, 1, &page, &page_vma) <= 0) + goto outsem; + + maddr = kmap(page); + offset = addr & (PAGE_SIZE-1); + bytes = len; + if (bytes > PAGE_SIZE - offset) + bytes = PAGE_SIZE - offset; + + copy_from_user_page(page_vma, page, addr, buf, maddr + offset, bytes); + + kunmap(page); // release page allocated by get_user_pages() + page_cache_release(page); + + len -= bytes; + buf += bytes; + addr += bytes; + + *text = per_cpu(translate_text, cpu); + retval = 1; + } + + // On app_process startup, /proc/pid/cmdline is initially "zygote" then "" but changes after an initial startup period + if (strcmp(*text, "zygote") == 0 || strcmp(*text, "") == 0) + retval = 0; + +outsem: + up_read(&mm->mmap_sem); +outmm: + mmput(mm); +out: + return retval; +} + +static inline uint32_t get_cookie(int cpu, int buftype, struct task_struct *task, struct vm_area_struct *vma, struct module *mod, bool in_interrupt) +{ + unsigned long flags, cookie; + struct path *path; + uint64_t key; + char *text; + + if (mod) { + text = mod->name; + } else { + if (!vma || !vma->vm_file) { + return INVALID_COOKIE; + } + path = &vma->vm_file->f_path; + if (!path || !path->dentry) { + return INVALID_COOKIE; + } + + text = (char*)path->dentry->d_name.name; + } + + key = gator_chksum_crc32(text); + key = (key << 32) | (uint32_t)task->tgid; + + cookie = cookiemap_exists(key); + if (cookie) { + return cookie; + } + + if (strcmp(text, "app_process") == 0 && !mod) { + if (!translate_app_process(&text, cpu, task, vma, in_interrupt)) + return INVALID_COOKIE; + } + + // Can be called from interrupt handler or from work queue or from scheduler trace + local_irq_save(flags); + + cookie = INVALID_COOKIE; + if (buffer_check_space(cpu, buftype, strlen(text) + 2 * MAXSIZE_PACK32)) { + cookie = per_cpu(cookie_next_key, cpu) += nr_cpu_ids; + cookiemap_add(key, cookie); + + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COOKIE); + gator_buffer_write_packed_int(cpu, buftype, cookie); + gator_buffer_write_string(cpu, buftype, text); + } + + local_irq_restore(flags); + + return cookie; +} + +static int get_exec_cookie(int cpu, int buftype, struct task_struct *task) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + + if (!mm) + return cookie; + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if (!vma->vm_file) + continue; + if (!(vma->vm_flags & VM_EXECUTABLE)) + continue; + cookie = get_cookie(cpu, buftype, task, vma, NULL, true); + break; + } + + return cookie; +} + +static unsigned long get_address_cookie(int cpu, int buftype, struct task_struct *task, unsigned long addr, off_t *offset) +{ + unsigned long cookie = NO_COOKIE; + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma; + + if (!mm) + return cookie; + + for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { + if (addr < vma->vm_start || addr >= vma->vm_end) + continue; + + if (vma->vm_file) { + cookie = get_cookie(cpu, buftype, task, vma, NULL, true); + *offset = (vma->vm_pgoff << PAGE_SHIFT) + addr - vma->vm_start; + } else { + /* must be an anonymous map */ + *offset = addr; + } + + break; + } + + if (!vma) + cookie = INVALID_COOKIE; + + return cookie; +} + +static int cookies_initialize(void) +{ + uint32_t crc, poly; + int i, j, cpu, size, err = 0; + + int translate_buffer_size = 512; // must be a power of 2 + translate_buffer_mask = translate_buffer_size / sizeof(per_cpu(translate_buffer, 0)[0]) - 1; + + for_each_present_cpu(cpu) { + per_cpu(cookie_next_key, cpu) = nr_cpu_ids + cpu; + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(cookie_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); + if (!per_cpu(cookie_keys, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + memset(per_cpu(cookie_keys, cpu), 0, size); + + size = COOKIEMAP_ENTRIES * MAX_COLLISIONS * sizeof(uint32_t); + per_cpu(cookie_values, cpu) = (uint32_t*)kmalloc(size, GFP_KERNEL); + if (!per_cpu(cookie_values, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + memset(per_cpu(cookie_values, cpu), 0, size); + + per_cpu(translate_buffer, cpu) = (unsigned int *)kmalloc(translate_buffer_size, GFP_KERNEL); + if (!per_cpu(translate_buffer, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + + per_cpu(translate_buffer_write, cpu) = 0; + per_cpu(translate_buffer_read, cpu) = 0; + + per_cpu(translate_text, cpu) = (char *)kmalloc(TRANSLATE_SIZE, GFP_KERNEL); + if (!per_cpu(translate_text, cpu)) { + err = -ENOMEM; + goto cookie_setup_error; + } + } + + // build CRC32 table + poly = 0x04c11db7; + gator_crc32_table = (uint32_t*)kmalloc(256 * sizeof(uint32_t), GFP_KERNEL); + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) { + crc = (crc >> 1) ^ poly; + } else { + crc >>= 1; + } + } + gator_crc32_table[i] = crc; + } + +cookie_setup_error: + return err; +} + +static void cookies_release(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + kfree(per_cpu(cookie_keys, cpu)); + per_cpu(cookie_keys, cpu) = NULL; + + kfree(per_cpu(cookie_values, cpu)); + per_cpu(cookie_values, cpu) = NULL; + + kfree(per_cpu(translate_buffer, cpu)); + per_cpu(translate_buffer, cpu) = NULL; + per_cpu(translate_buffer_read, cpu) = 0; + per_cpu(translate_buffer_write, cpu) = 0; + + kfree(per_cpu(translate_text, cpu)); + per_cpu(translate_text, cpu) = NULL; + } + + kfree(gator_crc32_table); + gator_crc32_table = NULL; +} diff --git a/drivers/gator/driver/gator_ebs.c b/drivers/gator/driver/gator_ebs.c new file mode 100644 index 00000000000..8c2997c57da --- /dev/null +++ b/drivers/gator/driver/gator_ebs.c @@ -0,0 +1,192 @@ +/** + * Copyright (C) ARM Limited 2012. 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. + * + */ + +#if defined(__arm__) && (GATOR_PERF_PMU_SUPPORT) +#include +#include +#include + +#include + +extern int pmnc_counters; +extern int ccnt; +extern unsigned long pmnc_enabled[]; +extern unsigned long pmnc_event[]; +extern unsigned long pmnc_count[]; +extern unsigned long pmnc_key[]; + +static DEFINE_PER_CPU(struct perf_event *, pevent); +static DEFINE_PER_CPU(struct perf_event_attr *, pevent_attr); +static DEFINE_PER_CPU(int, key); +static DEFINE_PER_CPU(unsigned int, prev_value); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ + unsigned int value, delta, cpu = smp_processor_id(), buftype = EVENT_BUF; + + if (event != per_cpu(pevent, cpu)) + return; + + if (buffer_check_space(cpu, buftype, 5 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + value = local64_read(&event->count); + delta = value - per_cpu(prev_value, cpu); + per_cpu(prev_value, cpu) = value; + + // Counters header + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type + gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time + + // Output counter + gator_buffer_write_packed_int(cpu, buftype, 2); // length + gator_buffer_write_packed_int(cpu, buftype, per_cpu(key, cpu)); // key + gator_buffer_write_packed_int(cpu, buftype, delta); // delta + + // End Counters, length of zero + gator_buffer_write_packed_int(cpu, buftype, 0); + } + + // Output backtrace + if (buffer_check_space(cpu, buftype, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) + gator_add_sample(cpu, buftype, regs); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, buftype); +} + +static void gator_event_sampling_online(void) +{ + int cpu = smp_processor_id(), buftype = EVENT_BUF; + + // read the counter and toss the invalid data, return zero instead + struct perf_event * ev = per_cpu(pevent, cpu); + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + ev->pmu->read(ev); + per_cpu(prev_value, cpu) = local64_read(&ev->count); + + // Counters header + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type + gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time + + // Output counter + gator_buffer_write_packed_int(cpu, buftype, 2); // length + gator_buffer_write_packed_int(cpu, buftype, per_cpu(key, cpu)); // key + gator_buffer_write_packed_int(cpu, buftype, 0); // delta - zero for initialization + + // End Counters, length of zero + gator_buffer_write_packed_int(cpu, buftype, 0); + } +} + +static void gator_event_sampling_online_dispatch(int cpu) +{ + struct perf_event * ev; + + if (!event_based_sampling) + return; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler); +#else + ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler, 0); +#endif + + if (IS_ERR(ev)) { + pr_err("gator: unable to start event-based-sampling"); + return; + } + + if (ev->state != PERF_EVENT_STATE_ACTIVE) { + pr_err("gator: unable to start event-based-sampling"); + perf_event_release_kernel(ev); + return; + } + + ev->pmu->read(ev); + per_cpu(prev_value, cpu) = local64_read(&ev->count); +} + +static void gator_event_sampling_offline_dispatch(int cpu) +{ + if (per_cpu(pevent, cpu)) { + perf_event_release_kernel(per_cpu(pevent, cpu)); + per_cpu(pevent, cpu) = NULL; + } +} + +static int gator_event_sampling_start(void) +{ + int cnt, event = 0, count = 0, ebs_key = 0, cpu; + + for_each_present_cpu(cpu) { + per_cpu(pevent, cpu) = NULL; + per_cpu(pevent_attr, cpu) = NULL; + } + + event_based_sampling = false; + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_count[cnt] > 0) { + event_based_sampling = true; + event = pmnc_event[cnt]; + count = pmnc_count[cnt]; + ebs_key = pmnc_key[cnt]; + break; + } + } + + if (!event_based_sampling) + return 0; + + for_each_present_cpu(cpu) { + u32 size = sizeof(struct perf_event_attr); + per_cpu(pevent_attr, cpu) = kmalloc(size, GFP_KERNEL); + if (!per_cpu(pevent_attr, cpu)) + return -1; + + memset(per_cpu(pevent_attr, cpu), 0, size); + per_cpu(pevent_attr, cpu)->type = PERF_TYPE_RAW; + per_cpu(pevent_attr, cpu)->size = size; + per_cpu(pevent_attr, cpu)->config = event; + per_cpu(pevent_attr, cpu)->sample_period = count; + per_cpu(pevent_attr, cpu)->pinned = 1; + + // handle special case for ccnt + if (cnt == ccnt) { + per_cpu(pevent_attr, cpu)->type = PERF_TYPE_HARDWARE; + per_cpu(pevent_attr, cpu)->config = PERF_COUNT_HW_CPU_CYCLES; + } + + per_cpu(key, cpu) = ebs_key; + } + + return 0; +} + +static void gator_event_sampling_stop(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + if (per_cpu(pevent_attr, cpu)) { + kfree(per_cpu(pevent_attr, cpu)); + per_cpu(pevent_attr, cpu) = NULL; + } + } +} + +#else +static void gator_event_sampling_online(void) {} +static void gator_event_sampling_online_dispatch(int cpu) {} +static void gator_event_sampling_offline_dispatch(int cpu) {} +static int gator_event_sampling_start(void) {return 0;} +static void gator_event_sampling_stop(void) {} +#endif diff --git a/drivers/gator/driver/gator_events.sh b/drivers/gator/driver/gator_events.sh new file mode 100644 index 00000000000..5467dd6d17d --- /dev/null +++ b/drivers/gator/driver/gator_events.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +EVENTS=`grep gator_events_init *.c | sed 's/.\+gator_events_init(\(.\+\)).\+/\1/'` + +( + echo /\* This file is auto generated \*/ + echo + for EVENT in $EVENTS; do + echo __weak int $EVENT\(void\)\; + done + echo + echo static int \(*gator_events_list[]\)\(void\) = { + for EVENT in $EVENTS; do + echo \ $EVENT, + done + echo }\; +) > $1.tmp + +cmp -s $1 $1.tmp && rm $1.tmp || mv $1.tmp $1 diff --git a/drivers/gator/driver/gator_events_armv6.c b/drivers/gator/driver/gator_events_armv6.c new file mode 100644 index 00000000000..ef518987906 --- /dev/null +++ b/drivers/gator/driver/gator_events_armv6.c @@ -0,0 +1,244 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +static const char *pmnc_name; + +/* + * Per-CPU PMCR + */ +#define PMCR_E (1 << 0) /* Enable */ +#define PMCR_P (1 << 1) /* Count reset */ +#define PMCR_C (1 << 2) /* Cycle counter reset */ +#define PMCR_OFL_PMN0 (1 << 8) /* Count reg 0 overflow */ +#define PMCR_OFL_PMN1 (1 << 9) /* Count reg 1 overflow */ +#define PMCR_OFL_CCNT (1 << 10) /* Cycle counter overflow */ + +#define PMN0 0 +#define PMN1 1 +#define CCNT 2 +#define CNTMAX (CCNT+1) + +static int pmnc_counters = 0; +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +static inline void armv6_pmnc_write(u32 val) +{ + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0x0ffff77f; + asm volatile("mcr p15, 0, %0, c15, c12, 0" : : "r" (val)); +} + +static inline u32 armv6_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c12, 0" : "=r" (val)); + return val; +} + +static void armv6_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + switch (cnt) { + case CCNT: + asm volatile("mcr p15, 0, %0, c15, c12, 1" : : "r" (val)); + break; + case PMN0: + asm volatile("mcr p15, 0, %0, c15, c12, 2" : : "r" (val)); + break; + case PMN1: + asm volatile("mcr p15, 0, %0, c15, c12, 3" : : "r" (val)); + break; + } +} + +int gator_events_armv6_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + pmnc_counters = 3; + + for (i = PMN0; i <= CCNT; i++) { + char buf[40]; + if (i == CCNT) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i != CCNT) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_armv6_online(int** buffer) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + u32 pmnc; + + if (armv6_pmnc_read() & PMCR_E) { + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + } + + /* initialize PMNC, reset overflow, D bit, C bit and P bit. */ + armv6_pmnc_write(PMCR_OFL_PMN0 | PMCR_OFL_PMN1 | PMCR_OFL_CCNT | + PMCR_C | PMCR_P); + + /* configure control register */ + for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { + unsigned long event; + + per_cpu(perfPrev, cpu)[cnt] = 0; + + if (!pmnc_enabled[cnt]) + continue; + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters) + if (cnt == PMN0) { + pmnc |= event << 20; + } else if (cnt == PMN1) { + pmnc |= event << 12; + } + + // Reset counter + armv6_pmnc_reset_counter(cnt); + } + armv6_pmnc_write(pmnc | PMCR_E); + + // return zero values, no need to read as the counters were just reset + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_armv6_offline(int** buffer) +{ + unsigned int cnt; + + armv6_pmnc_write(armv6_pmnc_read() & ~PMCR_E); + for (cnt = PMN0; cnt <= CCNT; cnt++) { + armv6_pmnc_reset_counter(cnt); + } + + return 0; +} + +static void gator_events_armv6_stop(void) +{ + unsigned int cnt; + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv6_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + if (pmnc_enabled[cnt]) { + u32 value = 0; + switch (cnt) { + case CCNT: + asm volatile("mrc p15, 0, %0, c15, c12, 1" : "=r" (value)); + break; + case PMN0: + asm volatile("mrc p15, 0, %0, c15, c12, 2" : "=r" (value)); + break; + case PMN1: + asm volatile("mrc p15, 0, %0, c15, c12, 3" : "=r" (value)); + break; + } + armv6_pmnc_reset_counter(cnt); + if (value != per_cpu(perfPrev, cpu)[cnt]) { + per_cpu(perfPrev, cpu)[cnt] = value; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_armv6_interface = { + .create_files = gator_events_armv6_create_files, + .stop = gator_events_armv6_stop, + .online = gator_events_armv6_online, + .offline = gator_events_armv6_offline, + .read = gator_events_armv6_read, +}; + +int gator_events_armv6_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case ARM1136: + case ARM1156: + case ARM1176: + pmnc_name = "ARM11"; + break; + case ARM11MPCORE: + pmnc_name = "ARM11MPCore"; + break; + default: + return -1; + } + + for (cnt = PMN0; cnt <= CCNT; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_armv6_interface); +} + +gator_events_init(gator_events_armv6_init); + +#else +int gator_events_armv6_init(void) +{ + return -1; +} +#endif diff --git a/drivers/gator/driver/gator_events_armv7.c b/drivers/gator/driver/gator_events_armv7.c new file mode 100644 index 00000000000..cdf450f1975 --- /dev/null +++ b/drivers/gator/driver/gator_events_armv7.c @@ -0,0 +1,320 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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. + */ + +/* Disabling interrupts + * Many of the functions below disable interrupts via local_irq_save(). This disabling of interrupts is done to prevent any race conditions + * between multiple entities (e.g. hrtimer interrupts and event based interrupts) calling the same functions. As accessing the pmu involves + * several steps (disable, select, read, enable), these steps must be performed atomically. Normal synchronization routines cannot be used + * as these functions are being called from interrupt context. + */ + +#include "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +// Per-CPU PMNC: config reg +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +// ccnt reg +#define CCNT_REG (1 << 31) + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (6+1) + +static const char *pmnc_name; +static int pmnc_counters; + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +inline void armv7_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +inline u32 armv7_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +inline u32 armv7_ccnt_read(u32 reset_value) +{ + unsigned long flags; + u32 newval = -reset_value; + u32 den = CCNT_REG; + u32 val; + + local_irq_save(flags); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); // read + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (newval));// new value + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable + local_irq_restore(flags); + + return val; +} + +inline u32 armv7_cntn_read(unsigned int cnt, u32 reset_value) +{ + unsigned long flags; + u32 newval = -reset_value; + u32 sel = (cnt - CNT0); + u32 den = 1 << sel; + u32 oldval; + + local_irq_save(flags); + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (den)); // disable + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (sel)); // select + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (oldval)); // read + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (newval));// new value + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (den)); // enable + local_irq_restore(flags); + + return oldval; +} + +static inline void armv7_pmnc_disable_interrupt(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : (1 << 31); + asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (val)); +} + +inline u32 armv7_pmnc_reset_interrupt(void) +{ + // Get and reset overflow status flags + u32 flags; + asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (flags)); + flags &= 0x8000003f; + asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (flags)); + return flags; +} + +static inline u32 armv7_pmnc_enable_counter(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + return cnt; +} + +static inline u32 armv7_pmnc_disable_counter(unsigned int cnt) +{ + u32 val = cnt ? (1 << (cnt - CNT0)) : CCNT_REG; + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + return cnt; +} + +static inline int armv7_pmnc_select_counter(unsigned int cnt) +{ + u32 val = (cnt - CNT0); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + return cnt; +} + +static inline void armv7_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (armv7_pmnc_select_counter(cnt) == cnt) { + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } +} + +static int gator_events_armv7_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "ARM_%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "ARM_%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_armv7_online(int** buffer) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + + if (armv7_pmnc_read() & PMNC_E) { + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + } + + // Initialize & Reset PMNC: C bit and P bit + armv7_pmnc_write(PMNC_P | PMNC_C); + + // Reset overflow flags + armv7_pmnc_reset_interrupt(); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + per_cpu(perfPrev, cpu)[cnt] = 0; + + if (!pmnc_enabled[cnt]) + continue; + + // Disable counter + armv7_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters), we don't need to set the event if it's a cycle count + if (cnt != CCNT) + armv7_pmnc_write_evtsel(cnt, event); + + armv7_pmnc_disable_interrupt(cnt); + + // Reset counter + cnt ? armv7_cntn_read(cnt, 0) : armv7_ccnt_read(0); + + // Enable counter + armv7_pmnc_enable_counter(cnt); + } + + // enable + armv7_pmnc_write(armv7_pmnc_read() | PMNC_E); + + // return zero values, no need to read as the counters were just reset + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_armv7_offline(int** buffer) +{ + // disbale all counters, including PMCCNTR; overflow IRQs will not be signaled + armv7_pmnc_write(armv7_pmnc_read() & ~PMNC_E); + + return 0; +} + +static void gator_events_armv7_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_armv7_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = armv7_ccnt_read(0); + } else { + value = armv7_cntn_read(cnt, 0); + } + if (value != per_cpu(perfPrev, cpu)[cnt]) { + per_cpu(perfPrev, cpu)[cnt] = value; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_armv7_interface = { + .create_files = gator_events_armv7_create_files, + .stop = gator_events_armv7_stop, + .online = gator_events_armv7_online, + .offline = gator_events_armv7_offline, + .read = gator_events_armv7_read, +}; + +int gator_events_armv7_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case CORTEX_A5: + pmnc_name = "Cortex-A5"; + pmnc_counters = 2; + break; + case CORTEX_A7: + pmnc_name = "Cortex-A7"; + pmnc_counters = 4; + break; + case CORTEX_A8: + pmnc_name = "Cortex-A8"; + pmnc_counters = 4; + break; + case CORTEX_A9: + pmnc_name = "Cortex-A9"; + pmnc_counters = 6; + break; + case CORTEX_A15: + pmnc_name = "Cortex-A15"; + pmnc_counters = 6; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_armv7_interface); +} + +gator_events_init(gator_events_armv7_init); + +#else +int gator_events_armv7_init(void) +{ + return -1; +} +#endif diff --git a/drivers/gator/driver/gator_events_block.c b/drivers/gator/driver/gator_events_block.c new file mode 100644 index 00000000000..f1bbbc83bfb --- /dev/null +++ b/drivers/gator/driver/gator_events_block.c @@ -0,0 +1,171 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" +#include + +#define BLOCK_RQ_WR 0 +#define BLOCK_RQ_RD 1 + +#define BLOCK_TOTAL (BLOCK_RQ_RD+1) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +#define EVENTWRITE REQ_RW +#else +#define EVENTWRITE REQ_WRITE +#endif + +static ulong block_rq_wr_enabled; +static ulong block_rq_rd_enabled; +static ulong block_rq_wr_key; +static ulong block_rq_rd_key; +static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt); +static DEFINE_PER_CPU(int[BLOCK_TOTAL * 2], blockGet); +static DEFINE_PER_CPU(bool, new_data_avail); + +GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) +{ + unsigned long flags; + int write, size; + int cpu = smp_processor_id(); + + if (!rq) + return; + + write = rq->cmd_flags & EVENTWRITE; + size = rq->resid_len; + + if (!size) + return; + + // disable interrupts to synchronize with gator_events_block_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + if (write) + per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] += size; + else + per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] += size; + local_irq_restore(flags); + + per_cpu(new_data_avail, cpu) = true; +} + +static int gator_events_block_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* block_complete_wr */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_wr"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_wr_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_wr_key); + + /* block_complete_rd */ + dir = gatorfs_mkdir(sb, root, "Linux_block_rq_rd"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &block_rq_rd_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &block_rq_rd_key); + + return 0; +} + +static int gator_events_block_start(void) +{ + int cpu; + + for_each_present_cpu(cpu) + per_cpu(new_data_avail, cpu) = true; + + // register tracepoints + if (block_rq_wr_enabled || block_rq_rd_enabled) + if (GATOR_REGISTER_TRACE(block_rq_complete)) + goto fail_block_rq_exit; + pr_debug("gator: registered block event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_block_rq_exit: + pr_err("gator: block event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_block_stop(void) +{ + if (block_rq_wr_enabled || block_rq_rd_enabled) + GATOR_UNREGISTER_TRACE(block_rq_complete); + pr_debug("gator: unregistered block event tracepoints\n"); + + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; +} + +static int gator_events_block_read(int **buffer) +{ + unsigned long flags; + int len, value, cpu, data = 0; + cpu = smp_processor_id(); + + if (per_cpu(new_data_avail, cpu) == false) + return 0; + + per_cpu(new_data_avail, cpu) = false; + + len = 0; + if (block_rq_wr_enabled) { + local_irq_save(flags); + value = per_cpu(blockCnt, cpu)[BLOCK_RQ_WR]; + per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] = 0; + local_irq_restore(flags); + per_cpu(blockGet, cpu)[len++] = block_rq_wr_key; + per_cpu(blockGet, cpu)[len++] = value; + data += value; + } + if (block_rq_rd_enabled) { + local_irq_save(flags); + value = per_cpu(blockCnt, cpu)[BLOCK_RQ_RD]; + per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] = 0; + local_irq_restore(flags); + per_cpu(blockGet, cpu)[len++] = block_rq_rd_key; + per_cpu(blockGet, cpu)[len++] = value; + data += value; + } + + if (data != 0) + per_cpu(new_data_avail, cpu) = true; + + if (buffer) + *buffer = per_cpu(blockGet, cpu); + + return len; +} + +static struct gator_interface gator_events_block_interface = { + .create_files = gator_events_block_create_files, + .start = gator_events_block_start, + .stop = gator_events_block_stop, + .read = gator_events_block_read, +}; + +int gator_events_block_init(void) +{ + block_rq_wr_enabled = 0; + block_rq_rd_enabled = 0; + + block_rq_wr_key = gator_events_get_key(); + block_rq_rd_key = gator_events_get_key(); + + return gator_events_install(&gator_events_block_interface); +} +gator_events_init(gator_events_block_init); diff --git a/drivers/gator/driver/gator_events_irq.c b/drivers/gator/driver/gator_events_irq.c new file mode 100644 index 00000000000..59461b9799e --- /dev/null +++ b/drivers/gator/driver/gator_events_irq.c @@ -0,0 +1,195 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" +#include + +#define HARDIRQ 0 +#define SOFTIRQ 1 +#define TOTALIRQ (SOFTIRQ+1) + +static ulong hardirq_enabled; +static ulong softirq_enabled; +static ulong hardirq_key; +static ulong softirq_key; +static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt); +static DEFINE_PER_CPU(int[TOTALIRQ], irqPrev); +static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet); + +GATOR_DEFINE_PROBE(irq_handler_exit, TP_PROTO(int irq, + struct irqaction *action, int ret)) +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_irq_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(irqCnt, smp_processor_id())[HARDIRQ]++; + local_irq_restore(flags); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(struct softirq_action *h, struct softirq_action *vec)) +#else +GATOR_DEFINE_PROBE(softirq_exit, TP_PROTO(unsigned int vec_nr)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_irq_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(irqCnt, smp_processor_id())[SOFTIRQ]++; + local_irq_restore(flags); +} + +static int gator_events_irq_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_irq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &hardirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &hardirq_key); + + /* soft irq */ + dir = gatorfs_mkdir(sb, root, "Linux_irq_softirq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &softirq_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &softirq_key); + + return 0; +} + +static int gator_events_irq_online(int** buffer) +{ + int len = 0, cpu = smp_processor_id(); + unsigned long flags; // not necessary as we are in interrupt context anyway, but doesn't hurt + + // synchronization with the irq_exit functions is not necessary as the values are being reset + if (hardirq_enabled) { + local_irq_save(flags); + per_cpu(irqCnt, cpu)[HARDIRQ] = 0; + local_irq_restore(flags); + per_cpu(irqPrev, cpu)[HARDIRQ] = 0; + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (softirq_enabled) { + local_irq_save(flags); + per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; + local_irq_restore(flags); + per_cpu(irqPrev, cpu)[SOFTIRQ] = 0; + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = 0; + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +static int gator_events_irq_start(void) +{ + // register tracepoints + if (hardirq_enabled) + if (GATOR_REGISTER_TRACE(irq_handler_exit)) + goto fail_hardirq_exit; + if (softirq_enabled) + if (GATOR_REGISTER_TRACE(softirq_exit)) + goto fail_softirq_exit; + pr_debug("gator: registered irq tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_softirq_exit: + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); +fail_hardirq_exit: + pr_err("gator: irq tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_irq_stop(void) +{ + if (hardirq_enabled) + GATOR_UNREGISTER_TRACE(irq_handler_exit); + if (softirq_enabled) + GATOR_UNREGISTER_TRACE(softirq_exit); + pr_debug("gator: unregistered irq tracepoints\n"); + + hardirq_enabled = 0; + softirq_enabled = 0; +} + +static int gator_events_irq_read(int **buffer) +{ + unsigned long flags; // not necessary as we are in interrupt context anyway, but doesn't hurt + int len, value; + int cpu = smp_processor_id(); + + len = 0; + if (hardirq_enabled) { + local_irq_save(flags); + value = per_cpu(irqCnt, cpu)[HARDIRQ]; + per_cpu(irqCnt, cpu)[HARDIRQ] = 0; + local_irq_restore(flags); + if (value != per_cpu(irqPrev, cpu)[HARDIRQ]) { + per_cpu(irqPrev, cpu)[HARDIRQ] = value; + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + } + + if (softirq_enabled) { + local_irq_save(flags); + value = per_cpu(irqCnt, cpu)[SOFTIRQ]; + per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; + local_irq_restore(flags); + if (value != per_cpu(irqPrev, cpu)[SOFTIRQ]) { + per_cpu(irqPrev, cpu)[SOFTIRQ] = value; + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = value; + } + } + + if (buffer) + *buffer = per_cpu(irqGet, cpu); + + return len; +} + +static struct gator_interface gator_events_irq_interface = { + .create_files = gator_events_irq_create_files, + .online = gator_events_irq_online, + .start = gator_events_irq_start, + .stop = gator_events_irq_stop, + .read = gator_events_irq_read, +}; + +int gator_events_irq_init(void) +{ + hardirq_key = gator_events_get_key(); + softirq_key = gator_events_get_key(); + + hardirq_enabled = 0; + softirq_enabled = 0; + + return gator_events_install(&gator_events_irq_interface); +} +gator_events_init(gator_events_irq_init); diff --git a/drivers/gator/driver/gator_events_l2c-310.c b/drivers/gator/driver/gator_events_l2c-310.c new file mode 100644 index 00000000000..bd1c48a64e2 --- /dev/null +++ b/drivers/gator/driver/gator_events_l2c-310.c @@ -0,0 +1,179 @@ +/** + * l2c310 (L2 Cache Controller) event counters for gator + * + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include + +#include "gator.h" + +#define L2C310_COUNTERS_NUM 2 + +static struct { + unsigned long enabled; + unsigned long event; + unsigned long key; +} l2c310_counters[L2C310_COUNTERS_NUM]; + +static int l2c310_buffer[L2C310_COUNTERS_NUM * 2]; + +static void __iomem *l2c310_base; + + + +static void gator_events_l2c310_reset_counters(void) +{ + u32 val = readl(l2c310_base + L2X0_EVENT_CNT_CTRL); + + val |= ((1 << L2C310_COUNTERS_NUM) - 1) << 1; + + writel(val, l2c310_base + L2X0_EVENT_CNT_CTRL); +} + + +static int gator_events_l2c310_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "L2C-310_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &l2c310_counters[i].enabled); + gatorfs_create_ulong(sb, dir, "event", + &l2c310_counters[i].event); + gatorfs_create_ro_ulong(sb, dir, "key", + &l2c310_counters[i].key); + } + + return 0; +} + +static int gator_events_l2c310_start(void) +{ + static const unsigned long l2x0_event_cntx_cfg[L2C310_COUNTERS_NUM] = { + L2X0_EVENT_CNT0_CFG, + L2X0_EVENT_CNT1_CFG, + }; + int i; + + /* Counter event sources */ + for (i = 0; i < L2C310_COUNTERS_NUM; i++) + writel((l2c310_counters[i].event & 0xf) << 2, + l2c310_base + l2x0_event_cntx_cfg[i]); + + gator_events_l2c310_reset_counters(); + + /* Event counter enable */ + writel(1, l2c310_base + L2X0_EVENT_CNT_CTRL); + + return 0; +} + +static void gator_events_l2c310_stop(void) +{ + /* Event counter disable */ + writel(0, l2c310_base + L2X0_EVENT_CNT_CTRL); +} + +static int gator_events_l2c310_read(int **buffer) +{ + static const unsigned long l2x0_event_cntx_val[L2C310_COUNTERS_NUM] = { + L2X0_EVENT_CNT0_VAL, + L2X0_EVENT_CNT1_VAL, + }; + int i; + int len = 0; + + if (smp_processor_id()) + return 0; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + if (l2c310_counters[i].enabled) { + l2c310_buffer[len++] = l2c310_counters[i].key; + l2c310_buffer[len++] = readl(l2c310_base + + l2x0_event_cntx_val[i]); + } + } + + /* l2c310 counters are saturating, not wrapping in case of overflow */ + gator_events_l2c310_reset_counters(); + + if (buffer) + *buffer = l2c310_buffer; + + return len; +} + +static struct gator_interface gator_events_l2c310_interface = { + .create_files = gator_events_l2c310_create_files, + .start = gator_events_l2c310_start, + .stop = gator_events_l2c310_stop, + .read = gator_events_l2c310_read, +}; + +static void __maybe_unused gator_events_l2c310_probe(unsigned long phys) +{ + if (l2c310_base) + return; + + l2c310_base = ioremap(phys, SZ_4K); + if (l2c310_base) { + u32 cache_id = readl(l2c310_base + L2X0_CACHE_ID); + + if ((cache_id & 0xff0003c0) != 0x410000c0) { + iounmap(l2c310_base); + l2c310_base = NULL; + } + } +} + +int gator_events_l2c310_init(void) +{ + int i; + + if (gator_cpuid() != CORTEX_A5 && gator_cpuid() != CORTEX_A9) + return -1; + +#if defined(CONFIG_ARCH_EXYNOS4) || defined(CONFIG_ARCH_S5PV310) + gator_events_l2c310_probe(0x10502000); +#endif +#if defined(CONFIG_ARCH_OMAP4) + gator_events_l2c310_probe(0x48242000); +#endif +#if defined(CONFIG_ARCH_TEGRA) + gator_events_l2c310_probe(0x50043000); +#endif +#if defined(CONFIG_ARCH_U8500) + gator_events_l2c310_probe(0xa0412000); +#endif +#if defined(CONFIG_ARCH_VEXPRESS) + // A9x4 core tile (HBI-0191) + gator_events_l2c310_probe(0x1e00a000); + // New memory map tiles + gator_events_l2c310_probe(0x2c0f0000); +#endif + if (!l2c310_base) + return -1; + + for (i = 0; i < L2C310_COUNTERS_NUM; i++) { + l2c310_counters[i].enabled = 0; + l2c310_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_l2c310_interface); +} +gator_events_init(gator_events_l2c310_init); diff --git a/drivers/gator/driver/gator_events_mali.c b/drivers/gator/driver/gator_events_mali.c new file mode 100644 index 00000000000..31e8f0d9030 --- /dev/null +++ b/drivers/gator/driver/gator_events_mali.c @@ -0,0 +1,611 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" + +#include +#include +#include + +#include "linux/mali_linux_trace.h" + +#define ACTIVITY_START 1 +#define ACTIVITY_STOP 2 + +#ifndef MALI_SUPPORT +#error MALI_SUPPORT not defined! +#endif + +#define MALI_200 0x0a07 +#define MALI_300 0x0b06 //This is not actually true; Mali-300 is also 0x0b07 +#define MALI_400 0x0b07 +#define MALI_T6xx 0x0056 + +static const char *mali_name; + +enum counters { + /* Timeline activity */ + ACTIVITY_VP = 0, + ACTIVITY_FP0, + ACTIVITY_FP1, + ACTIVITY_FP2, + ACTIVITY_FP3, + + /* L2 cache counters */ + COUNTER_L2_C0, + COUNTER_L2_C1, + + /* Vertex processor counters */ + COUNTER_VP_C0, + COUNTER_VP_C1, + + /* Fragment processor counters */ + COUNTER_FP0_C0, + COUNTER_FP0_C1, + COUNTER_FP1_C0, + COUNTER_FP1_C1, + COUNTER_FP2_C0, + COUNTER_FP2_C1, + COUNTER_FP3_C0, + COUNTER_FP3_C1, + + /* EGL Software Counters */ + COUNTER_EGL_BLIT_TIME, + + /* GLES Software Counters */ + COUNTER_GLES_DRAW_ELEMENTS_CALLS, + COUNTER_GLES_DRAW_ELEMENTS_NUM_INDICES, + COUNTER_GLES_DRAW_ELEMENTS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_ARRAYS_CALLS, + COUNTER_GLES_DRAW_ARRAYS_NUM_TRANSFORMED, + COUNTER_GLES_DRAW_POINTS, + COUNTER_GLES_DRAW_LINES, + COUNTER_GLES_DRAW_LINE_LOOP, + COUNTER_GLES_DRAW_LINE_STRIP, + COUNTER_GLES_DRAW_TRIANGLES, + COUNTER_GLES_DRAW_TRIANGLE_STRIP, + COUNTER_GLES_DRAW_TRIANGLE_FAN, + COUNTER_GLES_NON_VBO_DATA_COPY_TIME, + COUNTER_GLES_UNIFORM_BYTES_COPIED_TO_MALI, + COUNTER_GLES_UPLOAD_TEXTURE_TIME, + COUNTER_GLES_UPLOAD_VBO_TIME, + COUNTER_GLES_NUM_FLUSHES, + COUNTER_GLES_NUM_VSHADERS_GENERATED, + COUNTER_GLES_NUM_FSHADERS_GENERATED, + COUNTER_GLES_VSHADER_GEN_TIME, + COUNTER_GLES_FSHADER_GEN_TIME, + COUNTER_GLES_INPUT_TRIANGLES, + COUNTER_GLES_VXCACHE_HIT, + COUNTER_GLES_VXCACHE_MISS, + COUNTER_GLES_VXCACHE_COLLISION, + COUNTER_GLES_CULLED_TRIANGLES, + COUNTER_GLES_CULLED_LINES, + COUNTER_GLES_BACKFACE_TRIANGLES, + COUNTER_GLES_GBCLIP_TRIANGLES, + COUNTER_GLES_GBCLIP_LINES, + COUNTER_GLES_TRIANGLES_DRAWN, + COUNTER_GLES_DRAWCALL_TIME, + COUNTER_GLES_TRIANGLES_COUNT, + COUNTER_GLES_INDEPENDENT_TRIANGLES_COUNT, + COUNTER_GLES_STRIP_TRIANGLES_COUNT, + COUNTER_GLES_FAN_TRIANGLES_COUNT, + COUNTER_GLES_LINES_COUNT, + COUNTER_GLES_INDEPENDENT_LINES_COUNT, + COUNTER_GLES_STRIP_LINES_COUNT, + COUNTER_GLES_LOOP_LINES_COUNT, + + COUNTER_FILMSTRIP, + + NUMBER_OF_EVENTS +}; + +#define FIRST_ACTIVITY_EVENT ACTIVITY_VP +#define LAST_ACTIVITY_EVENT ACTIVITY_FP3 + +#define FIRST_HW_COUNTER COUNTER_L2_C0 +#define LAST_HW_COUNTER COUNTER_FP3_C1 + +#define FIRST_SW_COUNTER COUNTER_EGL_BLIT_TIME +#define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT + +#define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP +#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP + +/* gatorfs variables for counter enable state, + * the event the counter should count and the + * 'key' (a unique id set by gatord and returned + * by gator.ko) + */ +static unsigned long counter_enabled[NUMBER_OF_EVENTS]; +static unsigned long counter_event[NUMBER_OF_EVENTS]; +static unsigned long counter_key[NUMBER_OF_EVENTS]; + +/* The data we have recorded */ +static u32 counter_data[NUMBER_OF_EVENTS]; +/* The address to sample (or 0 if samples are sent to us) */ +static u32* counter_address[NUMBER_OF_EVENTS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; +static unsigned long counter_prev[NUMBER_OF_EVENTS]; + +/* Note whether tracepoints have been registered */ +static int trace_registered; + +/** + * Calculate the difference and handle the overflow. + */ +static u32 get_difference(u32 start, u32 end) +{ + if (start - end >= 0) + { + return start - end; + } + + // Mali counters are unsigned 32 bit values that wrap. + return (4294967295u - end) + start; +} + +/** + * Returns non-zero if the given counter ID is an activity counter. + */ +static inline int is_activity_counter(unsigned int event_id) +{ + return (event_id >= FIRST_ACTIVITY_EVENT && + event_id <= LAST_ACTIVITY_EVENT); +} + +/** + * Returns non-zero if the given counter ID is a hardware counter. + */ +static inline int is_hw_counter(unsigned int event_id) +{ + return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER); +} + +/** + * Returns non-zero if the given counter ID is a software counter. + */ +static inline int is_sw_counter(unsigned int event_id) +{ + return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER); +} + +/* + * The Mali DDK uses s64 types to contain software counter values, but gator + * can only use a maximum of 32 bits. This function scales a software counter + * to an appopriate range. + */ +static u32 scale_sw_counter_value(unsigned int event_id, signed long long value) +{ + u32 scaled_value; + + switch (event_id) { + case COUNTER_GLES_UPLOAD_TEXTURE_TIME: + case COUNTER_GLES_UPLOAD_VBO_TIME: + scaled_value = (u32)div_s64(value, 1000000); + break; + default: + scaled_value = (u32)value; + break; + } + + return scaled_value; +} + +/* Probe for continuously sampled counter */ +#if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING +GATOR_DEFINE_PROBE(mali_sample_address, TP_PROTO(unsigned int event_id, u32* addr)) +{ + /* Turning on too many pr_debug statements in frequently called functions + * can cause stability and/or performance problems + */ + //pr_debug("gator: mali_sample_address %d %d\n", event_id, addr); + if (event_id >= ACTIVITY_VP && event_id <= COUNTER_FP3_C1) { + counter_address[event_id] = addr; + } +} +#endif + +/* Probe for hardware counter events */ +GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int value)) +{ + /* Turning on too many pr_debug statements in frequently called functions + * can cause stability and/or performance problems + */ + //pr_debug("gator: mali_hw_counter %d %d\n", event_id, value); + if (is_hw_counter(event_id)) { + counter_data[event_id] = value; + } +} + +GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value)) +{ + if (is_sw_counter(event_id)) { + counter_data[event_id] = scale_sw_counter_value(event_id, value); + } +} + +//TODO need to work out how many fp units we have +u32 gator_mali_get_n_fp(void) { + return 4; +} + +//TODO need to work out what kind of Mali we are looking at +u32 gator_mali_get_id(void) { + return MALI_SUPPORT; +} + +int gator_events_mali_create_files(struct super_block *sb, struct dentry *root) { + struct dentry *dir; + int event; + int n_fp = gator_mali_get_n_fp(); + + /* + * Create the filesystem entries for vertex processor, fragement processor + * and L2 cache timeline and hardware counters. Software counters get + * special handling after this block. + */ + for (event = FIRST_ACTIVITY_EVENT; event <= LAST_HW_COUNTER; event++) + { + char buf[40]; + + /* + * We can skip this event if it's for a non-existent fragment + * processor. + */ + if (((event - ACTIVITY_FP0 >= n_fp) && (event < COUNTER_L2_C0)) || + (((event - COUNTER_FP0_C0)/2 >= n_fp))) + { + continue; + } + + /* Otherwise, set up the filesystem entry for this event. */ + switch (event) { + case ACTIVITY_VP: + snprintf(buf, sizeof buf, "ARM_%s_VP_active", mali_name); + break; + case ACTIVITY_FP0: + case ACTIVITY_FP1: + case ACTIVITY_FP2: + case ACTIVITY_FP3: + snprintf(buf, sizeof buf, "ARM_%s_FP%d_active", + mali_name, event - ACTIVITY_FP0); + break; + case COUNTER_L2_C0: + case COUNTER_L2_C1: + snprintf(buf, sizeof buf, "ARM_%s_L2_cnt%d", + mali_name, event - COUNTER_L2_C0); + break; + case COUNTER_VP_C0: + case COUNTER_VP_C1: + snprintf(buf, sizeof buf, "ARM_%s_VP_cnt%d", + mali_name, event - COUNTER_VP_C0); + break; + case COUNTER_FP0_C0: + case COUNTER_FP0_C1: + case COUNTER_FP1_C0: + case COUNTER_FP1_C1: + case COUNTER_FP2_C0: + case COUNTER_FP2_C1: + case COUNTER_FP3_C0: + case COUNTER_FP3_C1: + snprintf(buf, sizeof buf, "ARM_%s_FP%d_cnt%d", mali_name, + (event - COUNTER_FP0_C0) / 2, (event - COUNTER_FP0_C0) % 2); + break; + default: + printk("gator: trying to create file for non-existent counter (%d)\n", event); + continue; + } + + dir = gatorfs_mkdir(sb, root, buf); + + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + + /* Only create an event node for counters that can change what they count */ + if (event >= COUNTER_L2_C0) { + gatorfs_create_ulong(sb, dir, "event", &counter_event[event]); + } + + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + } + + /* Now set up the software counter entries */ + for (event = FIRST_SW_COUNTER; event <= LAST_SW_COUNTER; event++) + { + char buf[40]; + + snprintf(buf, sizeof(buf), "ARM_%s_SW_%d", mali_name, event); + + dir = gatorfs_mkdir(sb, root, buf); + + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + } + + /* Now set up the special counter entries */ + for (event = FIRST_SPECIAL_COUNTER; event <= LAST_SPECIAL_COUNTER; event++) + { + char buf[40]; + + snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip", mali_name); + + dir = gatorfs_mkdir(sb, root, buf); + + if (!dir) { + return -1; + } + + gatorfs_create_ulong(sb, dir, "event", &counter_event[event]); + gatorfs_create_ulong(sb, dir, "enabled", &counter_enabled[event]); + gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); + } + + + return 0; +} + +//TODO +void _mali_profiling_set_event(unsigned int, unsigned int); +void _mali_osk_fb_control_set(unsigned int, unsigned int); + +void _mali_profiling_get_counters(unsigned int*, unsigned int*, unsigned int*, unsigned int*); +void (*_mali_profiling_get_counters_function_pointer)(unsigned int*, unsigned int*, unsigned int*, unsigned int*); + +static void mali_counter_initialize(void) +{ + /* If a Mali driver is present and exporting the appropriate symbol + * then we can request the HW counters (of which there are only 2) + * be configured to count the desired events + */ + void (*set_hw_event)(unsigned int, unsigned int); + void (*set_fb_event)(unsigned int, unsigned int); + + set_hw_event = symbol_get(_mali_profiling_set_event); + + if (set_hw_event) { + int i; + + pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n",set_hw_event); + + for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { + if (counter_enabled[i]) { + set_hw_event(i, counter_event[i]); + } else { + set_hw_event(i, 0xFFFFFFFF); + } + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali online _mali_profiling_set_event symbol not found\n"); + } + + set_fb_event = symbol_get(_mali_osk_fb_control_set); + + if (set_fb_event) { + pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", set_fb_event); + + set_fb_event(0,(counter_enabled[COUNTER_FILMSTRIP]?1:0)); + + symbol_put(_mali_osk_fb_control_set); + } else { + printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); + } + + _mali_profiling_get_counters_function_pointer = symbol_get(_mali_profiling_get_counters); + if (_mali_profiling_get_counters_function_pointer){ + pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", _mali_profiling_get_counters_function_pointer); + counter_prev[COUNTER_L2_C0] = 0; + counter_prev[COUNTER_L2_C1] = 0; + } + else{ + pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); + } + +} + +static void mali_counter_deinitialize(void) +{ + void (*set_hw_event)(unsigned int, unsigned int); + void (*set_fb_event)(unsigned int, unsigned int); + + set_hw_event = symbol_get(_mali_profiling_set_event); + + if (set_hw_event) { + int i; + + pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n",set_hw_event); + for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { + set_hw_event(i, 0xFFFFFFFF); + } + + symbol_put(_mali_profiling_set_event); + } else { + printk("gator: mali offline _mali_profiling_set_event symbol not found\n"); + } + + set_fb_event = symbol_get(_mali_osk_fb_control_set); + + if (set_fb_event) { + pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", set_fb_event); + + set_fb_event(0,0); + + symbol_put(_mali_osk_fb_control_set); + } else { + printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n"); + } + + if (_mali_profiling_get_counters_function_pointer){ + symbol_put(_mali_profiling_get_counters); + } + +} + +static int gator_events_mali_start(void) { + // register tracepoints + if (GATOR_REGISTER_TRACE(mali_hw_counter)) { + printk("gator: mali_hw_counter tracepoint failed to activate\n"); + return -1; + } + + if (GATOR_REGISTER_TRACE(mali_sw_counter)) { + printk("gator: mali_sw_counter tracepoint failed to activate\n"); + return -1; + } + + trace_registered = 1; + + mali_counter_initialize(); + return 0; +} + +static void gator_events_mali_stop(void) { + unsigned int cnt; + + pr_debug("gator: mali stop\n"); + + if (trace_registered) { + GATOR_UNREGISTER_TRACE(mali_hw_counter); + GATOR_UNREGISTER_TRACE(mali_sw_counter); + + pr_debug("gator: mali timeline tracepoint deactivated\n"); + + trace_registered = 0; + } + + for (cnt = FIRST_ACTIVITY_EVENT; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_address[cnt] = NULL; + } + + mali_counter_deinitialize(); +} + +static int gator_events_mali_read(int **buffer) { + int cnt, len = 0; + + if (smp_processor_id()) return 0; + + // Read the L2 C0 and C1 here. + if (counter_enabled[COUNTER_L2_C0] || counter_enabled[COUNTER_L2_C1] ) { + u32 src0 = 0; + u32 val0 = 0; + u32 src1 = 0; + u32 val1 = 0; + + // Poke the driver to get the counter values + if (_mali_profiling_get_counters_function_pointer){ + _mali_profiling_get_counters_function_pointer(&src0, &val0, &src1, &val1); + } + + if (counter_enabled[COUNTER_L2_C0]) + { + // Calculate and save src0's counter val0 + counter_dump[len++] = counter_key[COUNTER_L2_C0]; + counter_dump[len++] = get_difference(val0, counter_prev[COUNTER_L2_C0]); + } + + if (counter_enabled[COUNTER_L2_C1]) + { + // Calculate and save src1's counter val1 + counter_dump[len++] = counter_key[COUNTER_L2_C1]; + counter_dump[len++] = get_difference(val1, counter_prev[COUNTER_L2_C1]); + } + + // Save the previous values for the counters. + counter_prev[COUNTER_L2_C0] = val0; + counter_prev[COUNTER_L2_C1] = val1; + } + + // Process other (non-timeline) counters. + for (cnt = COUNTER_VP_C0; cnt <= LAST_SW_COUNTER; cnt++) { + if (counter_enabled[cnt]) { + u32 value = 0; + + // Determine the current value of the counter. + if( counter_address[cnt] != NULL && 0 ) { // Never true! + value = *counter_address[cnt]; + } else if (counter_data[cnt]!=0) { + value = counter_data[cnt]; + counter_data[cnt] = 0; + } + + // Send the counter value only if it differs from last time. + if (value != counter_prev[cnt]) { + counter_prev[cnt] = value; + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = value; + } + } + } + + if (buffer) { + *buffer = (int*) counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_interface = { + .create_files = gator_events_mali_create_files, + .start = gator_events_mali_start, + .stop = gator_events_mali_stop, + .read = gator_events_mali_read, +}; + +int gator_events_mali_init(void) +{ + unsigned int cnt; + u32 id = gator_mali_get_id(); + + switch (id) { + case MALI_T6xx: + mali_name = "Mali-T6xx"; + break; + case MALI_400: + mali_name = "Mali-400"; + break; + case MALI_300: + mali_name = "Mali-300"; + break; + case MALI_200: + mali_name = "Mali-200"; + break; + default: + printk("Unknown Mali ID (%d)\n", id); + return -1; + } + + pr_debug("gator: mali init\n"); + + for (cnt = FIRST_ACTIVITY_EVENT; cnt < NUMBER_OF_EVENTS; cnt++) { + counter_enabled[cnt] = 0; + counter_event[cnt] = 0; + counter_key[cnt] = gator_events_get_key(); + counter_address[cnt] = NULL; + counter_data[cnt] = 0; + } + + trace_registered = 0; + + return gator_events_install(&gator_events_mali_interface); +} +gator_events_init(gator_events_mali_init); diff --git a/drivers/gator/driver/gator_events_meminfo.c b/drivers/gator/driver/gator_events_meminfo.c new file mode 100644 index 00000000000..8af9cfcb6d5 --- /dev/null +++ b/drivers/gator/driver/gator_events_meminfo.c @@ -0,0 +1,196 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" +#include +#include + +#define MEMINFO_MEMFREE 0 +#define MEMINFO_MEMUSED 1 +#define MEMINFO_BUFFERRAM 2 +#define MEMINFO_TOTAL 3 + +static ulong meminfo_global_enabled; +static ulong meminfo_enabled[MEMINFO_TOTAL]; +static ulong meminfo_key[MEMINFO_TOTAL]; +static unsigned long long meminfo_buffer[MEMINFO_TOTAL * 2]; +static int meminfo_length = 0; +static unsigned int mem_event = 0; +static bool new_data_avail; + +static void wq_sched_handler(struct work_struct *wsptr); + +DECLARE_WORK(work, wq_sched_handler); + +GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) { + mem_event++; +} + +GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) { + mem_event++; +} + +GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) { + mem_event++; +} + +static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < MEMINFO_TOTAL; i++) { + switch (i) { + case MEMINFO_MEMFREE: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree"); + break; + case MEMINFO_MEMUSED: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused"); + break; + case MEMINFO_BUFFERRAM: + dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram"); + break; + default: + return -1; + } + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]); + } + + return 0; +} + +static int gator_events_meminfo_start(void) +{ + int i; + + new_data_avail = true; + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + meminfo_global_enabled = 1; + } + } + + if (meminfo_global_enabled == 0) + return 0; + + if (GATOR_REGISTER_TRACE(mm_page_free_direct)) + goto mm_page_free_direct_exit; + if (GATOR_REGISTER_TRACE(mm_pagevec_free)) + goto mm_pagevec_free_exit; + if (GATOR_REGISTER_TRACE(mm_page_alloc)) + goto mm_page_alloc_exit; + + return 0; + +mm_page_alloc_exit: + GATOR_UNREGISTER_TRACE(mm_pagevec_free); +mm_pagevec_free_exit: + GATOR_UNREGISTER_TRACE(mm_page_free_direct); +mm_page_free_direct_exit: + return -1; +} + +static void gator_events_meminfo_stop(void) +{ + int i; + + if (meminfo_global_enabled) { + GATOR_UNREGISTER_TRACE(mm_page_free_direct); + GATOR_UNREGISTER_TRACE(mm_pagevec_free); + GATOR_UNREGISTER_TRACE(mm_page_alloc); + } + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + } +} + +// Must be run in a work queue as the kernel function si_meminfo() can sleep +static void wq_sched_handler(struct work_struct *wsptr) +{ + struct sysinfo info; + int i, len; + unsigned long long value; + + meminfo_length = len = 0; + + si_meminfo(&info); + for (i = 0; i < MEMINFO_TOTAL; i++) { + if (meminfo_enabled[i]) { + switch (i) { + case MEMINFO_MEMFREE: + value = info.freeram * PAGE_SIZE; + break; + case MEMINFO_MEMUSED: + value = (info.totalram - info.freeram) * PAGE_SIZE; + break; + case MEMINFO_BUFFERRAM: + value = info.bufferram * PAGE_SIZE; + break; + default: + value = 0; + break; + } + meminfo_buffer[len++] = (unsigned long long)meminfo_key[i]; + meminfo_buffer[len++] = value; + } + } + + meminfo_length = len; + new_data_avail = true; +} + +static int gator_events_meminfo_read(long long **buffer) +{ + static unsigned int last_mem_event = 0; + + if (smp_processor_id() || !meminfo_global_enabled) + return 0; + + if (last_mem_event != mem_event) { + last_mem_event = mem_event; + schedule_work(&work); + } + + if (!new_data_avail) + return 0; + + new_data_avail = false; + + if (buffer) + *buffer = meminfo_buffer; + + return meminfo_length; +} + +static struct gator_interface gator_events_meminfo_interface = { + .create_files = gator_events_meminfo_create_files, + .start = gator_events_meminfo_start, + .stop = gator_events_meminfo_stop, + .read64 = gator_events_meminfo_read, +}; + +int gator_events_meminfo_init(void) +{ + int i; + + meminfo_global_enabled = 0; + for (i = 0; i < MEMINFO_TOTAL; i++) { + meminfo_enabled[i] = 0; + meminfo_key[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_meminfo_interface); +} +gator_events_init(gator_events_meminfo_init); diff --git a/drivers/gator/driver/gator_events_mmaped.c b/drivers/gator/driver/gator_events_mmaped.c new file mode 100644 index 00000000000..acd5f85ebb7 --- /dev/null +++ b/drivers/gator/driver/gator_events_mmaped.c @@ -0,0 +1,198 @@ +/* + * Example events provider + * + * Copyright (C) ARM Limited 2010-2012. 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. + * + * Similar entries to those below must be present in the events.xml file. + * To add them to the events.xml, create an events-mmap.xml with the + * following contents and rebuild gatord: + * + * + * + * + * + * + * + * + * + * + */ + +#include +#include +#include + +#include "gator.h" + +#define MMAPED_COUNTERS_NUM 3 + +static struct { + unsigned long enabled; + unsigned long event; + unsigned long key; +} mmaped_counters[MMAPED_COUNTERS_NUM]; + +static int mmaped_buffer[MMAPED_COUNTERS_NUM * 2]; + +#ifdef TODO +static void __iomem *mmaped_base; +#endif + +/* Adds mmaped_cntX directories and enabled, event, and key files to /dev/gator/driver/events */ +static int gator_events_mmaped_create_files(struct super_block *sb, + struct dentry *root) +{ + int i; + + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + char buf[16]; + struct dentry *dir; + + snprintf(buf, sizeof(buf), "mmaped_cnt%d", i); + dir = gatorfs_mkdir(sb, root, buf); + if (WARN_ON(!dir)) + return -1; + gatorfs_create_ulong(sb, dir, "enabled", + &mmaped_counters[i].enabled); + gatorfs_create_ulong(sb, dir, "event", + &mmaped_counters[i].event); + gatorfs_create_ro_ulong(sb, dir, "key", + &mmaped_counters[i].key); + } + + return 0; +} + +static int gator_events_mmaped_start(void) +{ +#ifdef TODO + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) + writel(mmaped_counters[i].event, + mmaped_base + COUNTERS_CONFIG_OFFSET[i]); + + writel(ENABLED, COUNTERS_CONTROL_OFFSET); +#endif + + return 0; +} + +static void gator_events_mmaped_stop(void) +{ +#ifdef TODO + writel(DISABLED, COUNTERS_CONTROL_OFFSET); +#endif +} + +#ifndef TODO +/* This function "simulates" counters, generating values of fancy + * functions like sine or triangle... */ +static int mmaped_simulate(int counter) +{ + int result = 0; + + switch (counter) { + case 0: /* sort-of-sine */ + { + static int t; + int x; + + if (t % 1024 < 512) + x = 512 - (t % 512); + else + x = t % 512; + + result = 32 * x / 512; + result = result * result; + + if (t < 1024) + result = 1922 - result; + + t = (t + 1) % 2048; + } + break; + case 1: /* triangle */ + { + static int v, d = 1; + + v += d; + if (v % 2000 == 0) + d = -d; + + result = v; + } + break; + case 2: /* PWM signal */ + { + static int t, dc; + + t = (t + 1) % 2000; + if (t % 100) + dc = (dc + 200) % 2000; + + result = t < dc ? 0 : 2000; + } + break; + } + + return result; +} +#endif + +static int gator_events_mmaped_read(int **buffer) +{ + int i; + int len = 0; + + /* System wide counters - read from one core only */ + if (smp_processor_id()) + return 0; + + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + if (mmaped_counters[i].enabled) { + mmaped_buffer[len++] = mmaped_counters[i].key; +#ifdef TODO + mmaped_buffer[len++] = readl(mmaped_base + + COUNTERS_VALUE_OFFSET[i]); +#else + mmaped_buffer[len++] = mmaped_simulate( + mmaped_counters[i].event); +#endif + } + } + + if (buffer) + *buffer = mmaped_buffer; + + return len; +} + +static struct gator_interface gator_events_mmaped_interface = { + .create_files = gator_events_mmaped_create_files, + .start = gator_events_mmaped_start, + .stop = gator_events_mmaped_stop, + .read = gator_events_mmaped_read, +}; + +/* Must not be static! */ +int __init gator_events_mmaped_init(void) +{ + int i; + +#ifdef TODO + mmaped_base = ioremap(COUNTERS_PHYS_ADDR, SZ_4K); + if (!mmaped_base) + return -ENOMEM; +#endif + + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { + mmaped_counters[i].enabled = 0; + mmaped_counters[i].key = gator_events_get_key(); + } + + return gator_events_install(&gator_events_mmaped_interface); +} +gator_events_init(gator_events_mmaped_init); diff --git a/drivers/gator/driver/gator_events_net.c b/drivers/gator/driver/gator_events_net.c new file mode 100644 index 00000000000..ef1623b9a25 --- /dev/null +++ b/drivers/gator/driver/gator_events_net.c @@ -0,0 +1,144 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" +#include + +#define NETRX 0 +#define NETTX 1 +#define TOTALNET 2 + +static ulong netrx_enabled; +static ulong nettx_enabled; +static ulong netrx_key; +static ulong nettx_key; +static int rx_total, tx_total; +static ulong netPrev[TOTALNET]; +static int netGet[TOTALNET * 2]; + +static void get_network_stats(struct work_struct *wsptr) { + int rx = 0, tx = 0; + struct net_device *dev; + + for_each_netdev(&init_net, dev) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + const struct net_device_stats *stats = dev_get_stats(dev); +#else + struct rtnl_link_stats64 temp; + const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); +#endif + rx += stats->rx_bytes; + tx += stats->tx_bytes; + } + rx_total = rx; + tx_total = tx; +} +DECLARE_WORK(wq_get_stats, get_network_stats); + +static void calculate_delta(int *rx, int *tx) +{ + int rx_calc, tx_calc; + + rx_calc = (int)(rx_total - netPrev[NETRX]); + if (rx_calc < 0) + rx_calc = 0; + netPrev[NETRX] += rx_calc; + + tx_calc = (int)(tx_total - netPrev[NETTX]); + if (tx_calc < 0) + tx_calc = 0; + netPrev[NETTX] += tx_calc; + + *rx = rx_calc; + *tx = tx_calc; +} + +static int gator_events_net_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + dir = gatorfs_mkdir(sb, root, "Linux_net_rx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &netrx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &netrx_key); + + dir = gatorfs_mkdir(sb, root, "Linux_net_tx"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &nettx_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &nettx_key); + + return 0; +} + +static int gator_events_net_start(void) +{ + get_network_stats(NULL); + netPrev[NETRX] = rx_total; + netPrev[NETTX] = tx_total; + return 0; +} + +static void gator_events_net_stop(void) +{ + netrx_enabled = 0; + nettx_enabled = 0; +} + +static int gator_events_net_read(int **buffer) +{ + int len, rx_delta, tx_delta; + static int last_rx_delta = 0, last_tx_delta = 0; + + if (smp_processor_id() != 0) + return 0; + + schedule_work(&wq_get_stats); + calculate_delta(&rx_delta, &tx_delta); + + len = 0; + if (netrx_enabled && last_rx_delta != rx_delta) { + last_rx_delta = rx_delta; + netGet[len++] = netrx_key; + netGet[len++] = rx_delta; + } + + if (nettx_enabled && last_tx_delta != tx_delta) { + last_tx_delta = tx_delta; + netGet[len++] = nettx_key; + netGet[len++] = tx_delta; + } + + if (buffer) + *buffer = netGet; + + return len; +} + +static struct gator_interface gator_events_net_interface = { + .create_files = gator_events_net_create_files, + .start = gator_events_net_start, + .stop = gator_events_net_stop, + .read = gator_events_net_read, +}; + +int gator_events_net_init(void) +{ + netrx_key = gator_events_get_key(); + nettx_key = gator_events_get_key(); + + netrx_enabled = 0; + nettx_enabled = 0; + + return gator_events_install(&gator_events_net_interface); +} +gator_events_init(gator_events_net_init); diff --git a/drivers/gator/driver/gator_events_perf_pmu.c b/drivers/gator/driver/gator_events_perf_pmu.c new file mode 100644 index 00000000000..322ebc430ec --- /dev/null +++ b/drivers/gator/driver/gator_events_perf_pmu.c @@ -0,0 +1,296 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include +#include "gator.h" + +// gator_events_armvX.c is used for Linux 2.6.x +#if GATOR_PERF_PMU_SUPPORT + +static const char *pmnc_name; +int pmnc_counters; +int ccnt = 0; + +#define CNTMAX (6+1) + +unsigned long pmnc_enabled[CNTMAX]; +unsigned long pmnc_event[CNTMAX]; +unsigned long pmnc_count[CNTMAX]; +unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfCurr); +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX], perfPrevDelta); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); +static DEFINE_PER_CPU(struct perf_event *[CNTMAX], pevent); +static DEFINE_PER_CPU(struct perf_event_attr *[CNTMAX], pevent_attr); + +static void gator_events_perf_pmu_stop(void); + +static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ulong(sb, dir, "count", &pmnc_count[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) +static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) +#else +static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) +#endif +{ +// Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll +} + +static int gator_events_perf_pmu_online(int** buffer) +{ + int cnt, len = 0, cpu = smp_processor_id(); + + // read the counters and toss the invalid data, return zero instead + for (cnt = 0; cnt < pmnc_counters; cnt++) { + struct perf_event * ev = per_cpu(pevent, cpu)[cnt]; + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + ev->pmu->read(ev); + per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); + per_cpu(perfPrevDelta, cpu)[cnt] = 0; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static void gator_events_perf_pmu_online_dispatch(int cpu) +{ + int cnt; + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (per_cpu(pevent, cpu)[cnt] != NULL || per_cpu(pevent_attr, cpu)[cnt] == 0) + continue; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) + per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler); +#else + per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler, 0); +#endif + if (IS_ERR(per_cpu(pevent, cpu)[cnt])) { + pr_debug("gator: unable to online a counter on cpu %d\n", cpu); + per_cpu(pevent, cpu)[cnt] = NULL; + continue; + } + + if (per_cpu(pevent, cpu)[cnt]->state != PERF_EVENT_STATE_ACTIVE) { + pr_debug("gator: inactive counter on cpu %d\n", cpu); + perf_event_release_kernel(per_cpu(pevent, cpu)[cnt]); + per_cpu(pevent, cpu)[cnt] = NULL; + continue; + } + } +} + +static void gator_events_perf_pmu_offline_dispatch(int cpu) +{ + int cnt; + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (per_cpu(pevent, cpu)[cnt] != NULL) { + perf_event_release_kernel(per_cpu(pevent, cpu)[cnt]); + per_cpu(pevent, cpu)[cnt] = NULL; + } + } +} + +static int gator_events_perf_pmu_start(void) +{ + int cnt, cpu; + u32 size = sizeof(struct perf_event_attr); + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < pmnc_counters; cnt++) { + per_cpu(pevent, cpu)[cnt] = NULL; + if (!pmnc_enabled[cnt] || pmnc_count[cnt] > 0) // Skip disabled counters and EBS counters + continue; + + per_cpu(perfPrev, cpu)[cnt] = 0; + per_cpu(perfCurr, cpu)[cnt] = 0; + per_cpu(perfPrevDelta, cpu)[cnt] = 0; + per_cpu(pevent_attr, cpu)[cnt] = kmalloc(size, GFP_KERNEL); + if (!per_cpu(pevent_attr, cpu)[cnt]) { + gator_events_perf_pmu_stop(); + return -1; + } + + memset(per_cpu(pevent_attr, cpu)[cnt], 0, size); + per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_RAW; + per_cpu(pevent_attr, cpu)[cnt]->size = size; + per_cpu(pevent_attr, cpu)[cnt]->config = pmnc_event[cnt]; + per_cpu(pevent_attr, cpu)[cnt]->sample_period = 0; + per_cpu(pevent_attr, cpu)[cnt]->pinned = 1; + + // handle special case for ccnt + if (cnt == ccnt) { + per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_HARDWARE; + per_cpu(pevent_attr, cpu)[cnt]->config = PERF_COUNT_HW_CPU_CYCLES; + } + } + } + + return 0; +} + +static void gator_events_perf_pmu_stop(void) +{ + unsigned int cnt, cpu; + + for_each_present_cpu(cpu) { + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (per_cpu(pevent_attr, cpu)[cnt]) { + kfree(per_cpu(pevent_attr, cpu)[cnt]); + per_cpu(pevent_attr, cpu)[cnt] = NULL; + } + } + } + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_count[cnt] = 0; + } +} + +static int gator_events_perf_pmu_read(int **buffer) +{ + int cnt, delta, len = 0; + int cpu = smp_processor_id(); + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + struct perf_event * ev = per_cpu(pevent, cpu)[cnt]; + if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { + ev->pmu->read(ev); + per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); + delta = per_cpu(perfCurr, cpu)[cnt] - per_cpu(perfPrev, cpu)[cnt]; + if (delta != per_cpu(perfPrevDelta, cpu)[cnt]) { + per_cpu(perfPrevDelta, cpu)[cnt] = delta; + per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt]; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + if (delta < 0) + delta *= -1; + per_cpu(perfCnt, cpu)[len++] = delta; + } + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_perf_pmu_interface = { + .create_files = gator_events_perf_pmu_create_files, + .start = gator_events_perf_pmu_start, + .stop = gator_events_perf_pmu_stop, + .online = gator_events_perf_pmu_online, + .online_dispatch = gator_events_perf_pmu_online_dispatch, + .offline_dispatch = gator_events_perf_pmu_offline_dispatch, + .read = gator_events_perf_pmu_read, +}; + +int gator_events_perf_pmu_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case ARM1136: + case ARM1156: + case ARM1176: + pmnc_name = "ARM_ARM11"; + pmnc_counters = 3; + ccnt = 2; + break; + case ARM11MPCORE: + pmnc_name = "ARM_ARM11MPCore"; + pmnc_counters = 3; + break; + case CORTEX_A5: + pmnc_name = "ARM_Cortex-A5"; + pmnc_counters = 2; + break; + case CORTEX_A7: + pmnc_name = "ARM_Cortex-A7"; + pmnc_counters = 4; + break; + case CORTEX_A8: + pmnc_name = "ARM_Cortex-A8"; + pmnc_counters = 4; + break; + case CORTEX_A9: + pmnc_name = "ARM_Cortex-A9"; + pmnc_counters = 6; + break; + case CORTEX_A15: + pmnc_name = "ARM_Cortex-A15"; + pmnc_counters = 6; + break; + case SCORPION: + pmnc_name = "Scorpion"; + pmnc_counters = 4; + break; + case SCORPIONMP: + pmnc_name = "ScorpionMP"; + pmnc_counters = 4; + break; + case KRAITSIM: + case KRAIT: + pmnc_name = "Krait"; + pmnc_counters = 4; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = 0; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_count[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_perf_pmu_interface); +} + +gator_events_init(gator_events_perf_pmu_init); +#endif diff --git a/drivers/gator/driver/gator_events_power.c b/drivers/gator/driver/gator_events_power.c new file mode 100644 index 00000000000..a0ae684827d --- /dev/null +++ b/drivers/gator/driver/gator_events_power.c @@ -0,0 +1,178 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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 "gator.h" +#include +#include +#include + +// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38 +// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86 +// CPU Idle is currently disabled in the .xml +#if GATOR_CPU_FREQ_SUPPORT +enum { + POWER_CPU_FREQ, + POWER_CPU_IDLE, + POWER_TOTAL +}; + +static ulong power_cpu_enabled[POWER_TOTAL]; +static ulong power_cpu_key[POWER_TOTAL]; +static DEFINE_PER_CPU(ulong[POWER_TOTAL], power); +static DEFINE_PER_CPU(ulong[POWER_TOTAL], prev); +static DEFINE_PER_CPU(int *, powerGet); + +GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu)) +{ + per_cpu(power, cpu)[POWER_CPU_FREQ] = frequency * 1000; +} + +GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) +{ + per_cpu(power, cpu)[POWER_CPU_IDLE] = state; +} + +static int gator_events_power_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + // cpu_frequency + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]); + + // cpu_idle + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]); + + return 0; +} + +static int gator_events_power_populate(int cpu, int** buffer) +{ + int i, len = 0; + + for (i = 0; i < POWER_TOTAL; i++) { + if (power_cpu_enabled[i]) { + if (per_cpu(power, cpu)[i] != per_cpu(prev, cpu)[i]) { + per_cpu(prev, cpu)[i] = per_cpu(power, cpu)[i]; + per_cpu(powerGet, cpu)[len++] = power_cpu_key[i]; + per_cpu(powerGet, cpu)[len++] = per_cpu(power, cpu)[i]; + } + } + } + + if (buffer) + *buffer = per_cpu(powerGet, cpu); + + return len; +} + +static int gator_events_power_online(int** buffer) +{ + int i, cpu = smp_processor_id(); + for (i = 0; i < POWER_TOTAL; i++) + per_cpu(prev, cpu)[i] = -1; + per_cpu(power, cpu)[POWER_CPU_FREQ] = cpufreq_quick_get(cpu) * 1000; + return gator_events_power_populate(cpu, buffer); +} + +static int gator_events_power_offline(int** buffer) +{ + int cpu = smp_processor_id(); + // Set frequency to zero on an offline + per_cpu(power, cpu)[POWER_CPU_FREQ] = 0; + return gator_events_power_populate(cpu, buffer); +} + +static int gator_events_power_start(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + per_cpu(powerGet, cpu) = kmalloc(POWER_TOTAL * 2, GFP_KERNEL); + if (!per_cpu(powerGet, cpu)) + return -1; + } + + // register tracepoints + if (power_cpu_enabled[POWER_CPU_FREQ]) + if (GATOR_REGISTER_TRACE(cpu_frequency)) + goto fail_cpu_frequency_exit; + if (power_cpu_enabled[POWER_CPU_IDLE]) + if (GATOR_REGISTER_TRACE(cpu_idle)) + goto fail_cpu_idle_exit; + pr_debug("gator: registered power event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_cpu_idle_exit: + GATOR_UNREGISTER_TRACE(cpu_frequency); +fail_cpu_frequency_exit: + pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_power_stop(void) +{ + int i, cpu; + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); + if (power_cpu_enabled[POWER_CPU_IDLE]) + GATOR_UNREGISTER_TRACE(cpu_idle); + pr_debug("gator: unregistered power event tracepoints\n"); + + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + } + + for_each_present_cpu(cpu) { + kfree(per_cpu(powerGet, cpu)); + } +} + +static int gator_events_power_read(int **buffer) +{ + return gator_events_power_populate(smp_processor_id(), buffer); +} + +static struct gator_interface gator_events_power_interface = { + .create_files = gator_events_power_create_files, + .online = gator_events_power_online, + .offline = gator_events_power_offline, + .start = gator_events_power_start, + .stop = gator_events_power_stop, + .read = gator_events_power_read, +}; +#endif + +int gator_events_power_init(void) +{ +#if (GATOR_CPU_FREQ_SUPPORT) + int i; + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + power_cpu_key[i] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_power_interface); +#else + return -1; +#endif +} +gator_events_init(gator_events_power_init); diff --git a/drivers/gator/driver/gator_events_sched.c b/drivers/gator/driver/gator_events_sched.c new file mode 100644 index 00000000000..9bed3641dbb --- /dev/null +++ b/drivers/gator/driver/gator_events_sched.c @@ -0,0 +1,114 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" +#include + +#define SCHED_SWITCH 0 +#define SCHED_TOTAL (SCHED_SWITCH+1) + +static ulong sched_switch_enabled; +static ulong sched_switch_key; +static DEFINE_PER_CPU(int[SCHED_TOTAL], schedCnt); +static DEFINE_PER_CPU(int[SCHED_TOTAL * 2], schedGet); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + unsigned long flags; + + // disable interrupts to synchronize with gator_events_sched_read() + // spinlocks not needed since percpu buffers are used + local_irq_save(flags); + per_cpu(schedCnt, smp_processor_id())[SCHED_SWITCH]++; + local_irq_restore(flags); +} + +static int gator_events_sched_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + /* switch */ + dir = gatorfs_mkdir(sb, root, "Linux_sched_switch"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &sched_switch_enabled); + gatorfs_create_ro_ulong(sb, dir, "key", &sched_switch_key); + + return 0; +} + +static int gator_events_sched_start(void) +{ + // register tracepoints + if (sched_switch_enabled) + if (GATOR_REGISTER_TRACE(sched_switch)) + goto sched_switch_exit; + pr_debug("gator: registered scheduler event tracepoints\n"); + + return 0; + + // unregister tracepoints on error +sched_switch_exit: + pr_err("gator: scheduler event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_events_sched_stop(void) +{ + if (sched_switch_enabled) + GATOR_UNREGISTER_TRACE(sched_switch); + pr_debug("gator: unregistered scheduler event tracepoints\n"); + + sched_switch_enabled = 0; +} + +static int gator_events_sched_read(int **buffer) +{ + unsigned long flags; + int len, value; + int cpu = smp_processor_id(); + + len = 0; + if (sched_switch_enabled) { + local_irq_save(flags); + value = per_cpu(schedCnt, cpu)[SCHED_SWITCH]; + per_cpu(schedCnt, cpu)[SCHED_SWITCH] = 0; + local_irq_restore(flags); + per_cpu(schedGet, cpu)[len++] = sched_switch_key; + per_cpu(schedGet, cpu)[len++] = value; + } + + if (buffer) + *buffer = per_cpu(schedGet, cpu); + + return len; +} + +static struct gator_interface gator_events_sched_interface = { + .create_files = gator_events_sched_create_files, + .start = gator_events_sched_start, + .stop = gator_events_sched_stop, + .read = gator_events_sched_read, +}; + +int gator_events_sched_init(void) +{ + sched_switch_enabled = 0; + + sched_switch_key = gator_events_get_key(); + + return gator_events_install(&gator_events_sched_interface); +} +gator_events_init(gator_events_sched_init); diff --git a/drivers/gator/driver/gator_events_scorpion.c b/drivers/gator/driver/gator_events_scorpion.c new file mode 100644 index 00000000000..477e7c98948 --- /dev/null +++ b/drivers/gator/driver/gator_events_scorpion.c @@ -0,0 +1,678 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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 "gator.h" + +// gator_events_perf_pmu.c is used if perf is supported +#if GATOR_NO_PERF_SUPPORT + +static const char *pmnc_name; +static int pmnc_counters; + +// Per-CPU PMNC: config reg +#define PMNC_E (1 << 0) /* Enable all counters */ +#define PMNC_P (1 << 1) /* Reset all counters */ +#define PMNC_C (1 << 2) /* Cycle counter reset */ +#define PMNC_D (1 << 3) /* CCNT counts every 64th cpu cycle */ +#define PMNC_X (1 << 4) /* Export to ETM */ +#define PMNC_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ +#define PMNC_MASK 0x3f /* Mask for writable bits */ + +// ccnt reg +#define CCNT_REG (1 << 31) + +#define CCNT 0 +#define CNT0 1 +#define CNTMAX (4+1) + +static unsigned long pmnc_enabled[CNTMAX]; +static unsigned long pmnc_event[CNTMAX]; +static unsigned long pmnc_key[CNTMAX]; + +static DEFINE_PER_CPU(int[CNTMAX], perfPrev); +static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); + +enum scorpion_perf_types { + SCORPION_ICACHE_EXPL_INV = 0x4c, + SCORPION_ICACHE_MISS = 0x4d, + SCORPION_ICACHE_ACCESS = 0x4e, + SCORPION_ICACHE_CACHEREQ_L2 = 0x4f, + SCORPION_ICACHE_NOCACHE_L2 = 0x50, + SCORPION_HIQUP_NOPED = 0x51, + SCORPION_DATA_ABORT = 0x52, + SCORPION_IRQ = 0x53, + SCORPION_FIQ = 0x54, + SCORPION_ALL_EXCPT = 0x55, + SCORPION_UNDEF = 0x56, + SCORPION_SVC = 0x57, + SCORPION_SMC = 0x58, + SCORPION_PREFETCH_ABORT = 0x59, + SCORPION_INDEX_CHECK = 0x5a, + SCORPION_NULL_CHECK = 0x5b, + SCORPION_EXPL_ICIALLU = 0x5c, + SCORPION_IMPL_ICIALLU = 0x5d, + SCORPION_NONICIALLU_BTAC_INV = 0x5e, + SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5f, + SCORPION_SPIPE_ONLY_CYCLES = 0x60, + SCORPION_XPIPE_ONLY_CYCLES = 0x61, + SCORPION_DUAL_CYCLES = 0x62, + SCORPION_DISPATCH_ANY_CYCLES = 0x63, + SCORPION_FIFO_FULLBLK_CMT = 0x64, + SCORPION_FAIL_COND_INST = 0x65, + SCORPION_PASS_COND_INST = 0x66, + SCORPION_ALLOW_VU_CLK = 0x67, + SCORPION_VU_IDLE = 0x68, + SCORPION_ALLOW_L2_CLK = 0x69, + SCORPION_L2_IDLE = 0x6a, + SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b, + SCORPION_DTLB_EXPL_INV = 0x6c, + SCORPION_DTLB_MISS = 0x6d, + SCORPION_DTLB_ACCESS = 0x6e, + SCORPION_ITLB_MISS = 0x6f, + SCORPION_ITLB_IMPL_INV = 0x70, + SCORPION_ITLB_EXPL_INV = 0x71, + SCORPION_UTLB_D_MISS = 0x72, + SCORPION_UTLB_D_ACCESS = 0x73, + SCORPION_UTLB_I_MISS = 0x74, + SCORPION_UTLB_I_ACCESS = 0x75, + SCORPION_UTLB_INV_ASID = 0x76, + SCORPION_UTLB_INV_MVA = 0x77, + SCORPION_UTLB_INV_ALL = 0x78, + SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79, + SCORPION_S2_HOLD = 0x7a, + SCORPION_S2_HOLD_DEV_OP = 0x7b, + SCORPION_S2_HOLD_ORDER = 0x7c, + SCORPION_S2_HOLD_BARRIER = 0x7d, + SCORPION_VIU_DUAL_CYCLE = 0x7e, + SCORPION_VIU_SINGLE_CYCLE = 0x7f, + SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80, + SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81, + SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82, + SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83, + SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84, + SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85, + SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86, + SCORPION_EXCEPTIONS_INV_OPERATION = 0x87, + SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88, + SCORPION_COND_INST_FAIL_VX_PIPE = 0x89, + SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a, + SCORPION_EXCEPTIONS_OVERFLOW = 0x8b, + SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c, + SCORPION_EXCEPTIONS_DENORM = 0x8d, +#ifdef CONFIG_ARCH_MSM_SCORPIONMP + SCORPIONMP_NUM_BARRIERS = 0x8e, + SCORPIONMP_BARRIER_CYCLES = 0x8f, +#else + SCORPION_BANK_AB_HIT = 0x8e, + SCORPION_BANK_AB_ACCESS = 0x8f, + SCORPION_BANK_CD_HIT = 0x90, + SCORPION_BANK_CD_ACCESS = 0x91, + SCORPION_BANK_AB_DSIDE_HIT = 0x92, + SCORPION_BANK_AB_DSIDE_ACCESS = 0x93, + SCORPION_BANK_CD_DSIDE_HIT = 0x94, + SCORPION_BANK_CD_DSIDE_ACCESS = 0x95, + SCORPION_BANK_AB_ISIDE_HIT = 0x96, + SCORPION_BANK_AB_ISIDE_ACCESS = 0x97, + SCORPION_BANK_CD_ISIDE_HIT = 0x98, + SCORPION_BANK_CD_ISIDE_ACCESS = 0x99, + SCORPION_ISIDE_RD_WAIT = 0x9a, + SCORPION_DSIDE_RD_WAIT = 0x9b, + SCORPION_BANK_BYPASS_WRITE = 0x9c, + SCORPION_BANK_AB_NON_CASTOUT = 0x9d, + SCORPION_BANK_AB_L2_CASTOUT = 0x9e, + SCORPION_BANK_CD_NON_CASTOUT = 0x9f, + SCORPION_BANK_CD_L2_CASTOUT = 0xa0, +#endif + MSM_MAX_EVT +}; + +struct scorp_evt { + u32 evt_type; + u32 val; + u8 grp; + u32 evt_type_act; +}; + +static const struct scorp_evt sc_evt[] = { + {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d}, + {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e}, + {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f}, + {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f}, + {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f}, + {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e}, + {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c}, + {SCORPION_IRQ, 0x80000a00, 0, 0x4d}, + {SCORPION_FIQ, 0x800a0000, 0, 0x4e}, + {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f}, + {SCORPION_UNDEF, 0x8000000b, 0, 0x4c}, + {SCORPION_SVC, 0x80000b00, 0, 0x4d}, + {SCORPION_SMC, 0x800b0000, 0, 0x4e}, + {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f}, + {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c}, + {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d}, + {SCORPION_EXPL_ICIALLU, 0x8000000d, 0, 0x4c}, + {SCORPION_IMPL_ICIALLU, 0x80000d00, 0, 0x4d}, + {SCORPION_NONICIALLU_BTAC_INV, 0x800d0000, 0, 0x4e}, + {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8d000000, 0, 0x4f}, + + {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51}, + {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52}, + {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53}, + {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53}, + {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50}, + {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52}, + {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53}, + {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50}, + {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51}, + {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52}, + {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53}, + + {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54}, + {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55}, + {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56}, + {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57}, + {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55}, + {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56}, + {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57}, + {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54}, + {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55}, + {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56}, + {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57}, + {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55}, + {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56}, + {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57}, + {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55}, + {SCORPION_S2_HOLD, 0x88000000, 2, 0x57}, + {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55}, + {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56}, + {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57}, + + {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c}, + {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d}, + {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c}, + {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d}, + {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e}, + {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c}, + {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c}, + {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d}, + {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e}, + {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c}, + {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d}, + {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e}, + {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f}, + {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c}, + {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d}, + {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f}, + +#ifdef CONFIG_ARCH_MSM_SCORPIONMP + {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59}, + {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a}, +#else + {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58}, + {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59}, + {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a}, + {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b}, + {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58}, + {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59}, + {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a}, + {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b}, + {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58}, + {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59}, + {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a}, + {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b}, + {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58}, + {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a}, + {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58}, + {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58}, + {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59}, + {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a}, + {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b}, +#endif +}; + +static inline void scorpion_pmnc_write(u32 val) +{ + val &= PMNC_MASK; + asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r" (val)); +} + +static inline u32 scorpion_pmnc_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val)); + return val; +} + +static inline u32 scorpion_ccnt_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); + return val; +} + +static inline u32 scorpion_cntn_read(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); + return val; +} + +static inline u32 scorpion_pmnc_enable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u enabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CCNT_REG; + else + val = (1 << (cnt - CNT0)); + + asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (val)); + + return cnt; +} + +static inline u32 scorpion_pmnc_disable_counter(unsigned int cnt) +{ + u32 val; + + if (cnt >= CNTMAX) { + pr_err("gator: CPU%u disabling wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + if (cnt == CCNT) + val = CCNT_REG; + else + val = (1 << (cnt - CNT0)); + + asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (val)); + + return cnt; +} + +static inline int scorpion_pmnc_select_counter(unsigned int cnt) +{ + u32 val; + + if ((cnt == CCNT) || (cnt >= CNTMAX)) { + pr_err("gator: CPU%u selecting wrong PMNC counter %d\n", smp_processor_id(), cnt); + return -1; + } + + val = (cnt - CNT0); + asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + + return cnt; +} + +static u32 scorpion_read_lpm0(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm0(u32 val) +{ + asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm1(void) +{ + u32 val; + asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm1(u32 val) +{ + asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm2(void) +{ + u32 val; + asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm2(u32 val) +{ + asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_l2lpm(void) +{ + u32 val; + asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_l2lpm(u32 val) +{ + asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val)); +} + +static u32 scorpion_read_vlpm(void) +{ + u32 val; + asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_vlpm(u32 val) +{ + asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val)); +} + +struct scorpion_access_funcs { + u32 (*read) (void); + void (*write) (u32); +}; + +struct scorpion_access_funcs scor_func[] = { + {scorpion_read_lpm0, scorpion_write_lpm0}, + {scorpion_read_lpm1, scorpion_write_lpm1}, + {scorpion_read_lpm2, scorpion_write_lpm2}, + {scorpion_read_l2lpm, scorpion_write_l2lpm}, + {scorpion_read_vlpm, scorpion_write_vlpm}, +}; + +u32 venum_orig_val; +u32 fp_orig_val; + +static void scorpion_pre_vlpm(void) +{ + u32 venum_new_val; + u32 fp_new_val; + + /* CPACR Enable CP10 access*/ + asm volatile("mrc p15, 0, %0, c1, c0, 2" : "=r" (venum_orig_val)); + venum_new_val = venum_orig_val | 0x00300000; + asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_new_val)); + /* Enable FPEXC */ + asm volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (fp_orig_val)); + fp_new_val = fp_orig_val | 0x40000000; + asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_new_val)); +} + +static void scorpion_post_vlpm(void) +{ + /* Restore FPEXC*/ + asm volatile("mcr p10, 7, %0, c8, c0, 0" : : "r" (fp_orig_val)); + /* Restore CPACR*/ + asm volatile("mcr p15, 0, %0, c1, c0, 2" : : "r" (venum_orig_val)); +} + +#define COLMN0MASK 0x000000ff +#define COLMN1MASK 0x0000ff00 +#define COLMN2MASK 0x00ff0000 +static u32 scorpion_get_columnmask(u32 setval) +{ + if (setval & COLMN0MASK) + return 0xffffff00; + else if (setval & COLMN1MASK) + return 0xffff00ff; + else if (setval & COLMN2MASK) + return 0xff00ffff; + else + return 0x80ffffff; +} + +static void scorpion_evt_setup(u32 gr, u32 setval) +{ + u32 val; + if (gr == 4) + scorpion_pre_vlpm(); + val = scorpion_get_columnmask(setval) & scor_func[gr].read(); + val = val | setval; + scor_func[gr].write(val); + if (gr == 4) + scorpion_post_vlpm(); +} + +static int get_scorpion_evtinfo(unsigned int evt_type, struct scorp_evt *evtinfo) +{ + u32 idx; + if ((evt_type < 0x4c) || (evt_type >= MSM_MAX_EVT)) + return 0; + idx = evt_type - 0x4c; + if (sc_evt[idx].evt_type == evt_type) { + evtinfo->val = sc_evt[idx].val; + evtinfo->grp = sc_evt[idx].grp; + evtinfo->evt_type_act = sc_evt[idx].evt_type_act; + return 1; + } + return 0; +} + +static inline void scorpion_pmnc_write_evtsel(unsigned int cnt, u32 val) +{ + if (scorpion_pmnc_select_counter(cnt) == cnt) { + if (val < 0x40) { + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val)); + } else { + u32 zero = 0; + struct scorp_evt evtinfo; + // extract evtinfo.grp and evtinfo.tevt_type_act from val + if (get_scorpion_evtinfo(val, &evtinfo) == 0) return; + asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (evtinfo.evt_type_act)); + asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (zero)); + scorpion_evt_setup(evtinfo.grp, val); + } + } +} + +static void scorpion_pmnc_reset_counter(unsigned int cnt) +{ + u32 val = 0; + + if (cnt == CCNT) { + scorpion_pmnc_disable_counter(cnt); + + asm volatile("mcr p15, 0, %0, c9, c13, 0" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + scorpion_pmnc_enable_counter(cnt); + + } else if (cnt >= CNTMAX) { + pr_err("gator: CPU%u resetting wrong PMNC counter %d\n", smp_processor_id(), cnt); + } else { + scorpion_pmnc_disable_counter(cnt); + + if (scorpion_pmnc_select_counter(cnt) == cnt) + asm volatile("mcr p15, 0, %0, c9, c13, 2" : : "r" (val)); + + if (pmnc_enabled[cnt] != 0) + scorpion_pmnc_enable_counter(cnt); + } +} + +static int gator_events_scorpion_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + int i; + + for (i = 0; i < pmnc_counters; i++) { + char buf[40]; + if (i == 0) { + snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name); + } else { + snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i-1); + } + dir = gatorfs_mkdir(sb, root, buf); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]); + gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]); + if (i > 0) { + gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]); + } + } + + return 0; +} + +static int gator_events_scorpion_online(int** buffer) +{ + unsigned int cnt, len = 0, cpu = smp_processor_id(); + + if (scorpion_pmnc_read() & PMNC_E) { + scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); + } + + /* Initialize & Reset PMNC: C bit and P bit */ + scorpion_pmnc_write(PMNC_P | PMNC_C); + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + unsigned long event; + + per_cpu(perfPrev, smp_processor_id())[cnt] = 0; + + if (!pmnc_enabled[cnt]) + continue; + + // disable counter + scorpion_pmnc_disable_counter(cnt); + + event = pmnc_event[cnt] & 255; + + // Set event (if destined for PMNx counters), We don't need to set the event if it's a cycle count + if (cnt != CCNT) + scorpion_pmnc_write_evtsel(cnt, event); + + // reset counter + scorpion_pmnc_reset_counter(cnt); + + // Enable counter, do not enable interrupt for this counter + scorpion_pmnc_enable_counter(cnt); + } + + // enable + scorpion_pmnc_write(scorpion_pmnc_read() | PMNC_E); + + // read the counters and toss the invalid data, return zero instead + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + value = scorpion_cntn_read(); + } else { + value = 0; + } + scorpion_pmnc_reset_counter(cnt); + per_cpu(perfPrev, cpu)[cnt] = 0; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = 0; + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static int gator_events_scorpion_offline(int** buffer) +{ + scorpion_pmnc_write(scorpion_pmnc_read() & ~PMNC_E); + return 0; +} + +static void gator_events_scorpion_stop(void) +{ + unsigned int cnt; + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + } +} + +static int gator_events_scorpion_read(int **buffer) +{ + int cnt, len = 0; + int cpu = smp_processor_id(); + + for (cnt = 0; cnt < pmnc_counters; cnt++) { + if (pmnc_enabled[cnt]) { + int value; + if (cnt == CCNT) { + value = scorpion_ccnt_read(); + } else if (scorpion_pmnc_select_counter(cnt) == cnt) { + value = scorpion_cntn_read(); + } else { + value = 0; + } + scorpion_pmnc_reset_counter(cnt); + if (value != per_cpu(perfPrev, cpu)[cnt]) { + per_cpu(perfPrev, cpu)[cnt] = value; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; + } + } + } + + if (buffer) + *buffer = per_cpu(perfCnt, cpu); + + return len; +} + +static struct gator_interface gator_events_scorpion_interface = { + .create_files = gator_events_scorpion_create_files, + .stop = gator_events_scorpion_stop, + .online = gator_events_scorpion_online, + .offline = gator_events_scorpion_offline, + .read = gator_events_scorpion_read, +}; + +int gator_events_scorpion_init(void) +{ + unsigned int cnt; + + switch (gator_cpuid()) { + case SCORPION: + pmnc_name = "Scorpion"; + pmnc_counters = 4; + break; + case SCORPIONMP: + pmnc_name = "ScorpionMP"; + pmnc_counters = 4; + break; + default: + return -1; + } + + pmnc_counters++; // CNT[n] + CCNT + + for (cnt = CCNT; cnt < CNTMAX; cnt++) { + pmnc_enabled[cnt] = 0; + pmnc_event[cnt] = 0; + pmnc_key[cnt] = gator_events_get_key(); + } + + return gator_events_install(&gator_events_scorpion_interface); +} + +gator_events_init(gator_events_scorpion_init); + +#else +int gator_events_scorpion_init(void) +{ + return -1; +} +#endif diff --git a/drivers/gator/driver/gator_fs.c b/drivers/gator/driver/gator_fs.c new file mode 100644 index 00000000000..8277c3aac3e --- /dev/null +++ b/drivers/gator/driver/gator_fs.c @@ -0,0 +1,284 @@ +/** + * @file gatorfs.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * + * A simple filesystem for configuration and + * access of oprofile. + */ + +#include +#include +#include +#include +#include + +#define gatorfs_MAGIC 0x24051020 +#define TMPBUFSIZE 50 +DEFINE_SPINLOCK(gatorfs_lock); + +static struct inode *gatorfs_get_inode(struct super_block *sb, int mode) +{ + struct inode *inode = new_inode(sb); + + if (inode) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + inode->i_ino = get_next_ino(); +#endif + inode->i_mode = mode; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + +static const struct super_operations s_ops = { + .statfs = simple_statfs, + .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) +{ + char tmpbuf[TMPBUFSIZE]; + size_t maxlen = snprintf(tmpbuf, TMPBUFSIZE, "%lu\n", val); + if (maxlen > TMPBUFSIZE) + maxlen = TMPBUFSIZE; + 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) +{ + char tmpbuf[TMPBUFSIZE]; + unsigned long flags; + + if (!count) + return 0; + + if (count > TMPBUFSIZE - 1) + return -EINVAL; + + memset(tmpbuf, 0x0, TMPBUFSIZE); + + if (copy_from_user(tmpbuf, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gatorfs_lock, flags); + *val = simple_strtoul(tmpbuf, NULL, 0); + spin_unlock_irqrestore(&gatorfs_lock, flags); + return 0; +} + +static ssize_t ulong_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + unsigned long *val = file->private_data; + return gatorfs_ulong_to_user(*val, buf, count, offset); +} + +static ssize_t ulong_write_file(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long *value = file->private_data; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(value, buf, count); + + if (retval) + return retval; + return count; +} + +static int default_open(struct inode *inode, struct file *filp) +{ + if (inode->i_private) + filp->private_data = inode->i_private; + return 0; +} + +static const struct file_operations ulong_fops = { + .read = ulong_read_file, + .write = ulong_write_file, + .open = default_open, +}; + +static const struct file_operations ulong_ro_fops = { + .read = ulong_read_file, + .open = default_open, +}; + +static struct dentry *__gatorfs_create_file(struct super_block *sb, + struct dentry *root, char const *name, const struct file_operations *fops, + int perm) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFREG | perm); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_fop = fops; + d_add(dentry, inode); + return dentry; +} + +int gatorfs_create_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_fops, 0644); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +int gatorfs_create_ro_ulong(struct super_block *sb, struct dentry *root, + char const *name, unsigned long *val) +{ + struct dentry *d = __gatorfs_create_file(sb, root, name, + &ulong_ro_fops, 0444); + if (!d) + return -EFAULT; + + d->d_inode->i_private = val; + return 0; +} + +static ssize_t atomic_read_file(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + atomic_t *val = file->private_data; + return gatorfs_ulong_to_user(atomic_read(val), buf, count, offset); +} + +static const struct file_operations atomic_ro_fops = { + .read = atomic_read_file, + .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) +{ + 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) +{ + if (!__gatorfs_create_file(sb, root, name, fops, perm)) + return -EFAULT; + return 0; +} + +struct dentry *gatorfs_mkdir(struct super_block *sb, + struct dentry *root, char const *name) +{ + struct dentry *dentry; + struct inode *inode; + + dentry = d_alloc_name(root, name); + if (!dentry) + return NULL; + inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!inode) { + dput(dentry); + return NULL; + } + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + d_add(dentry, inode); + return dentry; +} + +static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode; + struct dentry *root_dentry; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = gatorfs_MAGIC; + sb->s_op = &s_ops; + sb->s_time_gran = 1; + + root_inode = gatorfs_get_inode(sb, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + root_inode->i_op = &simple_dir_inode_operations; + root_inode->i_fop = &simple_dir_operations; + root_dentry = d_alloc_root(root_inode); + if (!root_dentry) { + iput(root_inode); + return -ENOMEM; + } + + sb->s_root = root_dentry; + + gator_op_create_files(sb, root_dentry); + + // FIXME: verify kill_litter_super removes our dentries + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) +static int gatorfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, data, gatorfs_fill_super, mnt); +} +#else +static struct dentry *gatorfs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return mount_nodev(fs_type, flags, data, gatorfs_fill_super); +} +#endif + +static struct file_system_type gatorfs_type = { + .owner = THIS_MODULE, + .name = "gatorfs", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) + .get_sb = gatorfs_get_sb, +#else + .mount = gatorfs_mount, +#endif + + .kill_sb = kill_litter_super, +}; + +int __init gatorfs_register(void) +{ + return register_filesystem(&gatorfs_type); +} + +void gatorfs_unregister(void) +{ + unregister_filesystem(&gatorfs_type); +} diff --git a/drivers/gator/driver/gator_hrtimer_gator.c b/drivers/gator/driver/gator_hrtimer_gator.c new file mode 100644 index 00000000000..5896b3c39ae --- /dev/null +++ b/drivers/gator/driver/gator_hrtimer_gator.c @@ -0,0 +1,76 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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_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); +static ktime_t profiling_interval; +static void gator_hrtimer_online(int cpu); +static void gator_hrtimer_offline(int cpu); + +static enum hrtimer_restart gator_hrtimer_notify(struct hrtimer *hrtimer) +{ + hrtimer_forward_now(hrtimer, profiling_interval); + (*callback)(); + return HRTIMER_RESTART; +} + +static void gator_hrtimer_switch_cpus_online(void *unused) +{ + gator_hrtimer_online(smp_processor_id()); +} + +static void gator_hrtimer_online(int cpu) +{ + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + if (cpu != smp_processor_id()) { + smp_call_function_single(cpu, gator_hrtimer_switch_cpus_online, NULL, 1); + return; + } + + hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer->function = gator_hrtimer_notify; + hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED); +} + +static void gator_hrtimer_switch_cpus_offline(void *unused) +{ + gator_hrtimer_offline(smp_processor_id()); +} + +static void gator_hrtimer_offline(int cpu) +{ + struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + if (cpu != smp_processor_id()) { + smp_call_function_single(cpu, gator_hrtimer_switch_cpus_offline, NULL, 1); + return; + } + + hrtimer_cancel(hrtimer); +} + +static int gator_hrtimer_init(int interval, void (*func)(void)) +{ + (callback) = (func); + + // calculate profiling interval + profiling_interval = ns_to_ktime(1000000000UL / interval); + + return 0; +} + +static void gator_hrtimer_shutdown(void) +{ + /* empty */ +} + +#endif diff --git a/drivers/gator/driver/gator_hrtimer_perf.c b/drivers/gator/driver/gator_hrtimer_perf.c new file mode 100644 index 00000000000..7c0333f6445 --- /dev/null +++ b/drivers/gator/driver/gator_hrtimer_perf.c @@ -0,0 +1,113 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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/drivers/gator/driver/gator_main.c b/drivers/gator/driver/gator_main.c new file mode 100644 index 00000000000..6341ef6f051 --- /dev/null +++ b/drivers/gator/driver/gator_main.c @@ -0,0 +1,1109 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 unsigned long gator_protocol_version = 8; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gator.h" +#include "gator_events.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) +#error kernels prior to 2.6.32 are not supported +#endif + +#if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING) +#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined +#endif + +#ifndef CONFIG_PROFILING +#error gator requires the kernel to have CONFIG_PROFILING defined +#endif + +#ifndef CONFIG_HIGH_RES_TIMERS +#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined +#endif + +#if defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) +#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems +#endif + +#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) +#ifndef CONFIG_PERF_EVENTS +#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters +#elif !defined CONFIG_HW_PERF_EVENTS +#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters +#endif +#endif + +#if (!(GATOR_CPU_FREQ_SUPPORT)) +#warning gator requires kernel version 2.6.38 or greater and CONFIG_CPU_FREQ defined in order to enable the CPU Freq timeline chart +#endif + +/****************************************************************************** + * DEFINES + ******************************************************************************/ +#define TIMER_BUFFER_SIZE_DEFAULT (512*1024) +#define EVENT_BUFFER_SIZE_DEFAULT (128*1024) + +#define NO_COOKIE 0UL +#define INVALID_COOKIE ~0UL + +#define FRAME_HRTIMER 1 +#define FRAME_EVENT 2 +#define FRAME_ANNOTATE 3 + +#define MESSAGE_COOKIE 1 +#define MESSAGE_COUNTERS 3 +#define MESSAGE_START_BACKTRACE 5 +#define MESSAGE_END_BACKTRACE 7 +#define MESSAGE_SCHEDULER_TRACE 9 +#define MESSAGE_PID_NAME 11 +#define MESSAGE_GPU_TRACE 13 +#define MESSAGE_OVERFLOW 127 + +#define MAXSIZE_PACK32 5 +#define MAXSIZE_PACK64 9 + +#if defined(__arm__) +#define PC_REG regs->ARM_pc +#else +#define PC_REG regs->ip +#endif + +enum {TIMER_BUF, EVENT_BUF, NUM_GATOR_BUFS}; + +/****************************************************************************** + * Globals + ******************************************************************************/ +static unsigned long gator_cpu_cores; +static unsigned long userspace_buffer_size; +static unsigned long gator_backtrace_depth; + +static unsigned long gator_started; +static unsigned long gator_buffer_opened; +static unsigned long gator_timer_count; +static unsigned long gator_streaming; +static DEFINE_MUTEX(start_mutex); +static DEFINE_MUTEX(gator_buffer_mutex); + +bool event_based_sampling; + +static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); + +static void buffer_check(int cpu, int buftype); + +/****************************************************************************** + * Prototypes + ******************************************************************************/ +static bool buffer_check_space(int cpu, int buftype, int bytes); +static void gator_buffer_write_packed_int(int cpu, int buftype, unsigned int x); +static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long long x); +static void gator_buffer_write_string(int cpu, int buftype, char *x); +static int gator_write_packed_int(char *buffer, unsigned int x); +static int gator_write_packed_int64(char *buffer, unsigned long long x); +static void gator_add_trace(int cpu, int buftype, unsigned int address); +static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs); +static uint64_t gator_get_time(void); + +static uint32_t gator_buffer_size[NUM_GATOR_BUFS]; +static uint32_t gator_buffer_mask[NUM_GATOR_BUFS]; +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read); +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write); +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit); +static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available); +static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); +static DEFINE_PER_CPU(uint64_t, emit_overflow); + +/****************************************************************************** + * Application Includes + ******************************************************************************/ +#include "gator_hrtimer_perf.c" +#include "gator_hrtimer_gator.c" +#include "gator_cookies.c" +#include "gator_trace_sched.c" +#include "gator_trace_gpu.c" +#include "gator_backtrace.c" +#include "gator_annotate.c" +#include "gator_fs.c" +#include "gator_ebs.c" +#include "gator_pack.c" + +/****************************************************************************** + * Misc + ******************************************************************************/ +#if defined(__arm__) +u32 gator_cpuid(void) +{ + u32 val; + asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val)); + return (val >> 4) & 0xfff; +} +#endif + +/****************************************************************************** + * Commit interface + ******************************************************************************/ +static bool buffer_commit_ready(int* cpu, int* buftype) +{ + int cpu_x, x; + for_each_present_cpu(cpu_x) { + for (x = 0; x < NUM_GATOR_BUFS; x++) + if (per_cpu(gator_buffer_commit, cpu_x)[x] != per_cpu(gator_buffer_read, cpu_x)[x]) { + *cpu = cpu_x; + *buftype = x; + return true; + } + } + return false; +} + +/****************************************************************************** + * Buffer management + ******************************************************************************/ +static bool buffer_check_space(int cpu, int buftype, int bytes) +{ + 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; + } + + if (remaining < bytes) { + if (per_cpu(buffer_space_available, cpu)[buftype] == true) { + // overflow packet to be emitted at a later time, as we may be in the middle of writing a message, e.g. counters + per_cpu(emit_overflow, cpu) = gator_get_time(); + pr_err("overflow: remaining = %d\n", gator_buffer_size[buftype] - filled); + } + 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, 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, 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_buffer_header(int cpu, int buftype) +{ + int frame; + + if (buftype == TIMER_BUF) + frame = FRAME_HRTIMER; + else if (buftype == EVENT_BUF) + frame = FRAME_EVENT; + else + frame = -1; + + gator_buffer_write_packed_int(cpu, buftype, frame); + gator_buffer_write_packed_int(cpu, buftype, cpu); +} + +static void gator_commit_buffer(int cpu, int buftype) +{ + per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; + gator_buffer_header(cpu, buftype); + wake_up(&gator_buffer_wait); +} + +static void buffer_check(int cpu, int buftype) +{ + 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); + } +} + +static void gator_add_trace(int cpu, int buftype, unsigned int address) +{ + off_t offset = 0; + unsigned long cookie = get_address_cookie(cpu, buftype, current, address & ~1, &offset); + + if (cookie == NO_COOKIE || cookie == INVALID_COOKIE) { + offset = address; + } + + gator_buffer_write_packed_int(cpu, buftype, offset & ~1); + gator_buffer_write_packed_int(cpu, buftype, cookie); +} + +static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs) +{ + int inKernel = regs ? !user_mode(regs) : 1; + unsigned long exec_cookie = inKernel ? NO_COOKIE : get_exec_cookie(cpu, buftype, current); + + if (!regs) + return; + + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_START_BACKTRACE); + gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); + gator_buffer_write_packed_int(cpu, buftype, exec_cookie); + gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->tgid); + gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->pid); + gator_buffer_write_packed_int(cpu, buftype, inKernel); + + if (inKernel) { + kernel_backtrace(cpu, buftype, regs); + } else { + // Cookie+PC + gator_add_trace(cpu, buftype, PC_REG); + + // Backtrace + if (gator_backtrace_depth) + arm_backtrace_eabi(cpu, buftype, regs, gator_backtrace_depth); + } + + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_END_BACKTRACE); +} + +/****************************************************************************** + * hrtimer interrupt processing + ******************************************************************************/ +static LIST_HEAD(gator_events); + +static void gator_timer_interrupt(void) +{ + struct pt_regs * const regs = get_irq_regs(); + int cpu = smp_processor_id(); + int *buffer, len, i, buftype = TIMER_BUF; + long long *buffer64; + struct gator_interface *gi; + + // Output scheduler trace + len = gator_trace_sched_read(&buffer64); + if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_SCHEDULER_TRACE); + gator_buffer_write_packed_int(cpu, buftype, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); + } + } + + // Output GPU trace + len = gator_trace_gpu_read(&buffer64); + if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_GPU_TRACE); + gator_buffer_write_packed_int(cpu, buftype, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); + } + } + + // Output counters + if (buffer_check_space(cpu, buftype, MAXSIZE_PACK32 * 2 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); + gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); + list_for_each_entry(gi, &gator_events, list) { + if (gi->read) { + len = gi->read(&buffer); + if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, buftype, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int(cpu, buftype, buffer[i]); + } + } + } else if (gi->read64) { + len = gi->read64(&buffer64); + if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, buftype, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); + } + } + } + } + gator_buffer_write_packed_int(cpu, buftype, 0); + } + + // Output backtrace + if (!event_based_sampling && buffer_check_space(cpu, buftype, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) + gator_add_sample(cpu, buftype, regs); + + // Overflow message + if (per_cpu(emit_overflow, cpu)) { + gator_buffer_write_packed_int(cpu, buftype, MESSAGE_OVERFLOW); + gator_buffer_write_packed_int64(cpu, buftype, per_cpu(emit_overflow, cpu)); + per_cpu(emit_overflow, cpu) = 0; + } + + // Check and commit; generally, commit is set to occur once per second + buffer_check(cpu, buftype); +} + +DEFINE_PER_CPU(int, hrtimer_is_active); +static int hrtimer_running; + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_offline(void* unused) +{ + int i, len, cpu = smp_processor_id(); + int* buffer; + long long* buffer64; + + if (per_cpu(hrtimer_is_active, cpu)) { + struct gator_interface *gi; + gator_hrtimer_offline(cpu); + per_cpu(hrtimer_is_active, cpu) = 0; + + // Output scheduler trace + len = gator_trace_sched_offline(&buffer64); + if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_SCHEDULER_TRACE); + gator_buffer_write_packed_int(cpu, TIMER_BUF, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int64(cpu, TIMER_BUF, buffer64[i]); + } + } + + // Output GPU trace + len = gator_trace_gpu_offline(&buffer64); + if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_GPU_TRACE); + gator_buffer_write_packed_int(cpu, TIMER_BUF, len); + for (i = 0; i < len; i++) { + gator_buffer_write_packed_int64(cpu, TIMER_BUF, buffer64[i]); + } + } + + // offline any events and output counters + gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_COUNTERS); + gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); + list_for_each_entry(gi, &gator_events, list) { + if (gi->offline) { + len = gi->offline(&buffer); + if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, TIMER_BUF, len); + for (i = 0; i < len; i++) + gator_buffer_write_packed_int(cpu, TIMER_BUF, buffer[i]); + } + } + } + gator_buffer_write_packed_int(cpu, TIMER_BUF, 0); + + gator_commit_buffer(cpu, TIMER_BUF); + } + + if (event_based_sampling) { + gator_commit_buffer(cpu, EVENT_BUF); + } +} + +// This function runs in interrupt context and may be running on a core other than core 'cpu' +static void gator_timer_offline_dispatch(int cpu) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) + if (gi->offline_dispatch) + gi->offline_dispatch(cpu); + + gator_event_sampling_offline_dispatch(cpu); +} + +static void gator_timer_stop(void) +{ + int cpu; + + if (hrtimer_running) { + on_each_cpu(gator_timer_offline, NULL, 1); + for_each_online_cpu(cpu) { + gator_timer_offline_dispatch(cpu); + } + + hrtimer_running = 0; + gator_hrtimer_shutdown(); + } +} + +// This function runs in interrupt context and on the appropriate core +static void gator_timer_online(void* unused) +{ + int i, len, cpu = smp_processor_id(); + int* buffer; + + if (!per_cpu(hrtimer_is_active, cpu)) { + struct gator_interface *gi; + + // online any events and output counters + gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_COUNTERS); + gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); + list_for_each_entry(gi, &gator_events, list) { + if (gi->online) { + len = gi->online(&buffer); + if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, TIMER_BUF, len); + for (i = 0; i < len; i++) + gator_buffer_write_packed_int(cpu, TIMER_BUF, buffer[i]); + } + } + } + gator_buffer_write_packed_int(cpu, TIMER_BUF, 0); + + gator_event_sampling_online(); + + gator_hrtimer_online(cpu); + per_cpu(hrtimer_is_active, cpu) = 1; + } +} + +// This function runs in interrupt context and may be running on a core other than core 'cpu' +static void gator_timer_online_dispatch(int cpu) +{ + struct gator_interface *gi; + + list_for_each_entry(gi, &gator_events, list) + if (gi->online_dispatch) + gi->online_dispatch(cpu); + + gator_event_sampling_online_dispatch(cpu); +} + +int gator_timer_start(unsigned long setup) +{ + int cpu; + + if (!setup) { + pr_err("gator: cannot start due to a system tick value of zero\n"); + return -1; + } else if (hrtimer_running) { + pr_notice("gator: high res timer already running\n"); + return 0; + } + + hrtimer_running = 1; + + if (gator_hrtimer_init(setup, gator_timer_interrupt) == -1) + return -1; + + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(cpu); + } + on_each_cpu(gator_timer_online, NULL, 1); + + return 0; +} + +static uint64_t gator_get_time(void) +{ + struct timespec ts; + uint64_t timestamp; + + getnstimeofday(&ts); + timestamp = timespec_to_ns(&ts); + + return timestamp; +} + +/****************************************************************************** + * cpu hotplug and pm notifiers + ******************************************************************************/ +static int __cpuinit gator_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +{ + long cpu = (long)hcpu; + + switch (action) { + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + smp_call_function_single(cpu, gator_timer_offline, NULL, 1); + gator_timer_offline_dispatch(cpu); + break; + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + gator_timer_online_dispatch(cpu); + smp_call_function_single(cpu, gator_timer_online, NULL, 1); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block __refdata gator_cpu_notifier = { + .notifier_call = gator_cpu_notify, +}; + +// n.b. calling "on_each_cpu" only runs on those that are online +// Registered linux events are not disabled, so their counters will continue to collect +static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) +{ + int cpu; + + switch (event) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + unregister_hotcpu_notifier(&gator_cpu_notifier); + unregister_scheduler_tracepoints(); + on_each_cpu(gator_timer_offline, NULL, 1); + for_each_online_cpu(cpu) { + gator_timer_offline_dispatch(cpu); + } + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + for_each_online_cpu(cpu) { + gator_timer_online_dispatch(cpu); + } + on_each_cpu(gator_timer_online, NULL, 1); + register_scheduler_tracepoints(); + register_hotcpu_notifier(&gator_cpu_notifier); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block gator_pm_notifier = { + .notifier_call = gator_pm_notify, +}; + +static int gator_notifier_start(void) +{ + int retval; + retval = register_hotcpu_notifier(&gator_cpu_notifier); + if (retval == 0) + retval = register_pm_notifier(&gator_pm_notifier); + return retval; +} + +static void gator_notifier_stop(void) +{ + unregister_pm_notifier(&gator_pm_notifier); + unregister_hotcpu_notifier(&gator_cpu_notifier); +} + +/****************************************************************************** + * Main + ******************************************************************************/ +int gator_events_install(struct gator_interface *interface) +{ + list_add_tail(&interface->list, &gator_events); + + return 0; +} + +int gator_events_get_key(void) +{ + static int key; + + return key++; +} + +static int gator_init(void) +{ + int i; + + if (gator_annotate_init()) + return -1; + + // events sources (gator_events.h, generated by gator_events.sh) + for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) + if (gator_events_list[i]) + gator_events_list[i](); + + return 0; +} + +static int gator_start(void) +{ + struct gator_interface *gi; + + // start all events + list_for_each_entry(gi, &gator_events, list) { + if (gi->start && gi->start() != 0) { + struct list_head *ptr = gi->list.prev; + + while (ptr != &gator_events) { + gi = list_entry(ptr, struct gator_interface, list); + + if (gi->stop) + gi->stop(); + + ptr = ptr->prev; + } + goto events_failure; + } + } + + // cookies shall be initialized before trace_sched_start() and gator_timer_start() + if (cookies_initialize()) + goto cookies_failure; + if (gator_annotate_start()) + goto annotate_failure; + if (gator_trace_sched_start()) + goto sched_failure; + if (gator_trace_gpu_start()) + goto gpu_failure; + if (gator_event_sampling_start()) + goto event_sampling_failure; + if (gator_timer_start(gator_timer_count)) + goto timer_failure; + if (gator_notifier_start()) + goto notifier_failure; + + return 0; + +notifier_failure: + gator_timer_stop(); +timer_failure: + gator_event_sampling_stop(); +event_sampling_failure: + gator_trace_gpu_stop(); +gpu_failure: + gator_trace_sched_stop(); +sched_failure: + gator_annotate_stop(); +annotate_failure: + cookies_release(); +cookies_failure: + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); +events_failure: + + return -1; +} + +static void gator_stop(void) +{ + struct gator_interface *gi; + + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); + + gator_annotate_stop(); + gator_trace_sched_stop(); + gator_trace_gpu_stop(); + gator_event_sampling_stop(); + + // stop all interrupt callback reads before tearing down other interfaces + gator_notifier_stop(); // should be called before gator_timer_stop to avoid re-enabling the hrtimer after it has been offlined + gator_timer_stop(); +} + +static void gator_exit(void) +{ + gator_annotate_exit(); +} + +/****************************************************************************** + * Filesystem + ******************************************************************************/ +/* fopen("buffer") */ +static int gator_op_setup(void) +{ + int err = 0; + int cpu, i; + + mutex_lock(&start_mutex); + + gator_buffer_size[TIMER_BUF] = userspace_buffer_size; + gator_buffer_mask[TIMER_BUF] = userspace_buffer_size - 1; + + // must be a power of 2 + if (gator_buffer_size[TIMER_BUF] & (gator_buffer_size[TIMER_BUF] - 1)) { + err = -ENOEXEC; + goto setup_error; + } + + gator_buffer_size[EVENT_BUF] = EVENT_BUFFER_SIZE_DEFAULT; + gator_buffer_mask[EVENT_BUF] = gator_buffer_size[EVENT_BUF] - 1; + + // Initialize percpu per buffer variables + for (i = 0; i < NUM_GATOR_BUFS; i++) { + for_each_present_cpu(cpu) { + per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]); + if (!per_cpu(gator_buffer, cpu)[i]) { + err = -ENOMEM; + goto setup_error; + } + + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + per_cpu(emit_overflow, cpu) = 0; + gator_buffer_header(cpu, i); + } + } + +setup_error: + mutex_unlock(&start_mutex); + return err; +} + +/* Actually start profiling (echo 1>/dev/gator/driver/enable) */ +static int gator_op_start(void) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started || gator_start()) + err = -EINVAL; + else + gator_started = 1; + + mutex_unlock(&start_mutex); + + return err; +} + +/* echo 0>/dev/gator/driver/enable */ +static void gator_op_stop(void) +{ + mutex_lock(&start_mutex); + + if (gator_started) { + gator_stop(); + + mutex_lock(&gator_buffer_mutex); + + gator_started = 0; + cookies_release(); + wake_up(&gator_buffer_wait); + + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static void gator_shutdown(void) +{ + int cpu, i; + + mutex_lock(&start_mutex); + + gator_annotate_shutdown(); + + for_each_present_cpu(cpu) { + mutex_lock(&gator_buffer_mutex); + for (i = 0; i < NUM_GATOR_BUFS; i++) { + vfree(per_cpu(gator_buffer, cpu)[i]); + per_cpu(gator_buffer, cpu)[i] = NULL; + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + per_cpu(emit_overflow, cpu) = 0; + } + mutex_unlock(&gator_buffer_mutex); + } + + mutex_unlock(&start_mutex); +} + +static int gator_set_backtrace(unsigned long val) +{ + int err = 0; + + mutex_lock(&start_mutex); + + if (gator_started) + err = -EBUSY; + else + gator_backtrace_depth = val; + + mutex_unlock(&start_mutex); + + return err; +} + +static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_started, buf, count, offset); +} + +static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + if (val) + retval = gator_op_start(); + else + gator_op_stop(); + + if (retval) + return retval; + return count; +} + +static const struct file_operations enable_fops = { + .read = enable_read, + .write = enable_write, +}; + +static int userspace_buffer_open(struct inode *inode, struct file *file) +{ + int err = -EPERM; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (test_and_set_bit_lock(0, &gator_buffer_opened)) + return -EBUSY; + + if ((err = gator_op_setup())) + goto fail; + + /* NB: the actual start happens from userspace + * echo 1 >/dev/gator/driver/enable + */ + + return 0; + +fail: + __clear_bit_unlock(0, &gator_buffer_opened); + return err; +} + +static int userspace_buffer_release(struct inode *inode, struct file *file) +{ + gator_op_stop(); + gator_shutdown(); + __clear_bit_unlock(0, &gator_buffer_opened); + return 0; +} + +static ssize_t userspace_buffer_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + int retval = -EINVAL; + int commit = 0, length1, length2, read; + char *buffer1; + char *buffer2 = NULL; + int cpu, buftype; + + /* do not handle partial reads */ + if (count != userspace_buffer_size || *offset) + return -EINVAL; + + // sleep until the condition is true or a signal is received + // the condition is checked each time gator_buffer_wait is woken up + buftype = cpu = -1; + wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || gator_annotate_ready() || !gator_started); + + if (signal_pending(current)) + return -EINTR; + + length2 = 0; + retval = -EFAULT; + + mutex_lock(&gator_buffer_mutex); + + if (buftype != -1 && cpu != -1) { + read = per_cpu(gator_buffer_read, cpu)[buftype]; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + + /* May happen if the buffer is freed during pending reads. */ + if (!per_cpu(gator_buffer, cpu)[buftype]) { + retval = -EFAULT; + goto out; + } + + /* determine the size of two halves */ + length1 = commit - read; + buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]); + buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]); + if (length1 < 0) { + length1 = gator_buffer_size[buftype] - read; + length2 = commit; + } + } else if (gator_annotate_ready()) { + length1 = gator_annotate_read(&buffer1); + if (!length1) + goto out; + } else { + retval = 0; + goto out; + } + + /* start, middle or end */ + if (length1 > 0) { + if (copy_to_user(&buf[0], buffer1, length1)) { + goto out; + } + } + + /* possible wrap around */ + if (length2 > 0) { + if (copy_to_user(&buf[length1], buffer2, length2)) { + goto out; + } + } + + if (buftype != -1 && cpu != -1) + per_cpu(gator_buffer_read, cpu)[buftype] = commit; + + retval = length1 + length2; + + /* kick just in case we've lost an SMP event */ + wake_up(&gator_buffer_wait); + +out: + mutex_unlock(&gator_buffer_mutex); + return retval; +} + +const struct file_operations gator_event_buffer_fops = { + .open = userspace_buffer_open, + .release = userspace_buffer_release, + .read = userspace_buffer_read, +}; + +static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) +{ + return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, + offset); +} + +static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +{ + unsigned long val; + int retval; + + if (*offset) + return -EINVAL; + + retval = gatorfs_ulong_from_user(&val, buf, count); + if (retval) + return retval; + + retval = gator_set_backtrace(val); + + if (retval) + return retval; + return count; +} + +static const struct file_operations depth_fops = { + .read = depth_read, + .write = depth_write +}; + +void gator_op_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + struct gator_interface *gi; + int cpu; + + /* reinitialize default values */ + gator_cpu_cores = 0; + for_each_present_cpu(cpu) { + gator_cpu_cores++; + } + userspace_buffer_size = TIMER_BUFFER_SIZE_DEFAULT; + gator_streaming = 1; + + gatorfs_create_file(sb, root, "enable", &enable_fops); + gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops); + gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops); + gatorfs_create_ulong(sb, root, "cpu_cores", &gator_cpu_cores); + gatorfs_create_ulong(sb, root, "buffer_size", &userspace_buffer_size); + gatorfs_create_ulong(sb, root, "tick", &gator_timer_count); + gatorfs_create_ulong(sb, root, "streaming", &gator_streaming); + gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version); + + // Annotate interface + gator_annotate_create_files(sb, root); + + // Linux Events + dir = gatorfs_mkdir(sb, root, "events"); + list_for_each_entry(gi, &gator_events, list) + if (gi->create_files) + gi->create_files(sb, dir); +} + +/****************************************************************************** + * Module + ******************************************************************************/ +static int __init gator_module_init(void) +{ + if (gatorfs_register()) { + return -1; + } + + if (gator_init()) { + gatorfs_unregister(); + return -1; + } + + return 0; +} + +static void __exit gator_module_exit(void) +{ + tracepoint_synchronize_unregister(); + gatorfs_unregister(); + gator_exit(); +} + +module_init(gator_module_init); +module_exit(gator_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ARM Ltd"); +MODULE_DESCRIPTION("Gator system profiler"); diff --git a/drivers/gator/driver/gator_pack.c b/drivers/gator/driver/gator_pack.c new file mode 100644 index 00000000000..985e960ad59 --- /dev/null +++ b/drivers/gator/driver/gator_pack.c @@ -0,0 +1,262 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 gator_buffer_write_packed_int(int cpu, int buftype, unsigned int x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int write0 = (write + 0) & mask; + int write1 = (write + 1) & mask; + + if ((x & 0xffffff80) == 0) { + buffer[write0] = x & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write1; + } else if ((x & 0xffffc000) == 0) { + int write2 = (write + 2) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write2; + } else if ((x & 0xffe00000) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write3; + } else if ((x & 0xf0000000) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write4; + } else { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) & 0x0f; + per_cpu(gator_buffer_write, cpu)[buftype] = write5; + } +} + +static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long long x) +{ + uint32_t write = per_cpu(gator_buffer_write, cpu)[buftype]; + uint32_t mask = gator_buffer_mask[buftype]; + char *buffer = per_cpu(gator_buffer, cpu)[buftype]; + int write0 = (write + 0) & mask; + int write1 = (write + 1) & mask; + + if ((x & 0xffffffffffffff80LL) == 0) { + buffer[write0] = x & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write1; + } else if ((x & 0xffffffffffffc000LL) == 0) { + int write2 = (write + 2) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write2; + } else if ((x & 0xffffffffffe00000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write3; + } else if ((x & 0xfffffffff0000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write4; + } else if ((x & 0xfffffff800000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write5; + } else if ((x & 0xfffffc0000000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write6; + } else if ((x & 0xfffe000000000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + int write7 = (write + 7) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) | 0x80; + buffer[write6] = (x>>42) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write7; + } else if ((x & 0xff00000000000000LL) == 0) { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + int write7 = (write + 7) & mask; + int write8 = (write + 8) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) | 0x80; + buffer[write6] = (x>>42) | 0x80; + buffer[write7] = (x>>49) & 0x7f; + per_cpu(gator_buffer_write, cpu)[buftype] = write8; + } else { + int write2 = (write + 2) & mask; + int write3 = (write + 3) & mask; + int write4 = (write + 4) & mask; + int write5 = (write + 5) & mask; + int write6 = (write + 6) & mask; + int write7 = (write + 7) & mask; + int write8 = (write + 8) & mask; + int write9 = (write + 9) & mask; + buffer[write0] = x | 0x80; + buffer[write1] = (x>>7) | 0x80; + buffer[write2] = (x>>14) | 0x80; + buffer[write3] = (x>>21) | 0x80; + buffer[write4] = (x>>28) | 0x80; + buffer[write5] = (x>>35) | 0x80; + buffer[write6] = (x>>42) | 0x80; + buffer[write7] = (x>>49) | 0x80; + buffer[write8] = (x>>56) & 0xff; + per_cpu(gator_buffer_write, cpu)[buftype] = write9; + } +} + +static int gator_write_packed_int(char *buffer, unsigned int x) +{ + if ((x & 0xffffff80) == 0) { + buffer[0] = x & 0x7f; + return 1; + } else if ((x & 0xffffc000) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) & 0x7f; + return 2; + } else if ((x & 0xffe00000) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) & 0x7f; + return 3; + } else if ((x & 0xf0000000) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) & 0x7f; + return 4; + } else { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) | 0x80; + buffer[4] = (x>>28) & 0x0f; + return 5; + } +} + +static int gator_write_packed_int64(char *buffer, unsigned long long x) +{ + if ((x & 0xffffffffffffff80LL) == 0) { + buffer[0] = x & 0x7f; + return 1; + } else if ((x & 0xffffffffffffc000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) & 0x7f; + return 2; + } else if ((x & 0xffffffffffe00000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) & 0x7f; + return 3; + } else if ((x & 0xfffffffff0000000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) & 0x7f; + return 4; + } else if ((x & 0xfffffff800000000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) | 0x80; + buffer[4] = (x>>28) & 0x7f; + return 5; + } else if ((x & 0xfffffc0000000000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) | 0x80; + buffer[4] = (x>>28) | 0x80; + buffer[5] = (x>>35) & 0x7f; + return 6; + } else if ((x & 0xfffe000000000000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) | 0x80; + buffer[4] = (x>>28) | 0x80; + buffer[5] = (x>>35) | 0x80; + buffer[6] = (x>>42) & 0x7f; + return 7; + } else if ((x & 0xff00000000000000LL) == 0) { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) | 0x80; + buffer[4] = (x>>28) | 0x80; + buffer[5] = (x>>35) | 0x80; + buffer[6] = (x>>42) | 0x80; + buffer[7] = (x>>49) & 0x7f; + return 8; + } else { + buffer[0] = x | 0x80; + buffer[1] = (x>>7) | 0x80; + buffer[2] = (x>>14) | 0x80; + buffer[3] = (x>>21) | 0x80; + buffer[4] = (x>>28) | 0x80; + buffer[5] = (x>>35) | 0x80; + buffer[6] = (x>>42) | 0x80; + buffer[7] = (x>>49) | 0x80; + buffer[8] = (x>>56) & 0xff; + return 9; + } +} diff --git a/drivers/gator/driver/gator_trace_gpu.c b/drivers/gator/driver/gator_trace_gpu.c new file mode 100644 index 00000000000..bc63995c847 --- /dev/null +++ b/drivers/gator/driver/gator_trace_gpu.c @@ -0,0 +1,250 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 "gator.h" + +#include +#include +#include +#include + +#ifdef MALI_SUPPORT +#include "linux/mali_linux_trace.h" +#endif +#include "gator_trace_gpu.h" + +#define ACTIVITY_START 1 +#define ACTIVITY_STOP 2 + +/* Note whether tracepoints have been registered */ +static int mali_trace_registered; +static int gpu_trace_registered; + +#define GPU_OVERFLOW -1 +#define GPU_START 1 +#define GPU_STOP 2 + +#define GPU_UNIT_VP 1 +#define GPU_UNIT_FP 2 + +#define TRACESIZE (8*1024) + +static DEFINE_PER_CPU(uint64_t *[2], theGpuTraceBuf); +static DEFINE_PER_CPU(int, theGpuTraceSel); +static DEFINE_PER_CPU(int, theGpuTracePos); +static DEFINE_PER_CPU(int, theGpuTraceErr); + +int gator_trace_gpu_read(long long **buffer); + +static void probe_gpu_write(int type, int unit, int core, struct task_struct* task) +{ + int tracePos; + unsigned long flags; + uint64_t *traceBuf, time; + int pid, tgid; + int cpu = smp_processor_id(); + + if (!per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]) + return; + + if (task) { + tgid = (int)task->tgid; + pid = (int)task->pid; + } else { + tgid = pid = 0; + } + + // disable interrupts to synchronize with gator_trace_gpu_read(); spinlocks not needed since percpu buffers are used + local_irq_save(flags); + + time = gator_get_time(); + tracePos = per_cpu(theGpuTracePos, cpu); + traceBuf = per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]; + + if (tracePos < (TRACESIZE - 100)) { + // capture + traceBuf[tracePos++] = type; + traceBuf[tracePos++] = time; + traceBuf[tracePos++] = unit; + traceBuf[tracePos++] = core; + traceBuf[tracePos++] = tgid; + traceBuf[tracePos++] = pid; + } else if (!per_cpu(theGpuTraceErr, cpu)) { + per_cpu(theGpuTraceErr, cpu) = 1; + traceBuf[tracePos++] = GPU_OVERFLOW; + traceBuf[tracePos++] = time; + traceBuf[tracePos++] = 0; + traceBuf[tracePos++] = 0; + traceBuf[tracePos++] = 0; + traceBuf[tracePos++] = 0; + pr_debug("gator: gpu trace overflow\n"); + } + per_cpu(theGpuTracePos, cpu) = tracePos; + local_irq_restore(flags); +} + +#ifdef MALI_SUPPORT + +enum components { + COMPONENT_VP0 = 1, + COMPONENT_FP0 = 5, + COMPONENT_FP1, + COMPONENT_FP2, + COMPONENT_FP3, + COMPONENT_FP4, + COMPONENT_FP5, + COMPONENT_FP6, + COMPONENT_FP7, +}; + +GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)) +{ + unsigned int component, state; + + // do as much work as possible before disabling interrupts + component = (event_id >> 16) & 0xF; + state = (event_id >> 24) & 0xF; + + if ((component == COMPONENT_VP0) || (component >= COMPONENT_FP0 && component <= COMPONENT_FP7)) { + if (state == ACTIVITY_START || state == ACTIVITY_STOP) { + unsigned int type = (state == ACTIVITY_START) ? GPU_START : GPU_STOP; + unsigned int unit = (component < COMPONENT_FP0) ? GPU_UNIT_VP : GPU_UNIT_FP; + unsigned int core = (component < COMPONENT_FP0) ? component - COMPONENT_VP0 : component - COMPONENT_FP0; + struct task_struct* task = (state == ACTIVITY_START) ? (struct task_struct*)d2 : NULL; + + probe_gpu_write(type, unit, core, task); + } + } +} +#endif + +GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p)) +{ + probe_gpu_write(GPU_START, gpu_unit, gpu_core, p); +} + +GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) +{ + probe_gpu_write(GPU_STOP, gpu_unit, gpu_core, NULL); +} + +int gator_trace_gpu_start(void) +{ + int cpu; + + /* + * Returns 0 for installation failed + * Absence of gpu trace points is not an error + */ + + gpu_trace_registered = mali_trace_registered = 0; + +#ifdef MALI_SUPPORT + if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { + mali_trace_registered = 1; + } +#endif + + if (!mali_trace_registered) { + if (GATOR_REGISTER_TRACE(gpu_activity_start)) { + return 0; + } + if (GATOR_REGISTER_TRACE(gpu_activity_stop)) { + GATOR_UNREGISTER_TRACE(gpu_activity_start); + return 0; + } + gpu_trace_registered = 1; + } + + if (!gpu_trace_registered && !mali_trace_registered) { + return 0; + } + + for_each_present_cpu(cpu) { + per_cpu(theGpuTraceSel, cpu) = 0; + per_cpu(theGpuTracePos, cpu) = 0; + per_cpu(theGpuTraceErr, cpu) = 0; + per_cpu(theGpuTraceBuf, cpu)[0] = kmalloc(TRACESIZE * sizeof(uint64_t), GFP_KERNEL); + per_cpu(theGpuTraceBuf, cpu)[1] = kmalloc(TRACESIZE * sizeof(uint64_t), GFP_KERNEL); + if (!per_cpu(theGpuTraceBuf, cpu)[0] || !per_cpu(theGpuTraceBuf, cpu)[1]) { +#ifdef MALI_SUPPORT + if (mali_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_timeline_event); + } +#endif + if (gpu_trace_registered) { + GATOR_UNREGISTER_TRACE(gpu_activity_stop); + GATOR_UNREGISTER_TRACE(gpu_activity_start); + } + + gpu_trace_registered = mali_trace_registered = 0; + + return -1; + } + } + + return 0; +} + +int gator_trace_gpu_offline(long long **buffer) +{ + return gator_trace_gpu_read(buffer); +} + +void gator_trace_gpu_stop(void) +{ + int cpu; + + if (gpu_trace_registered || mali_trace_registered) { + for_each_present_cpu(cpu) { + kfree(per_cpu(theGpuTraceBuf, cpu)[0]); + kfree(per_cpu(theGpuTraceBuf, cpu)[1]); + per_cpu(theGpuTraceBuf, cpu)[0] = NULL; + per_cpu(theGpuTraceBuf, cpu)[1] = NULL; + } + +#ifdef MALI_SUPPORT + if (mali_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_timeline_event); + } +#endif + if (gpu_trace_registered) { + GATOR_UNREGISTER_TRACE(gpu_activity_stop); + GATOR_UNREGISTER_TRACE(gpu_activity_start); + } + + gpu_trace_registered = mali_trace_registered = 0; + } +} + +int gator_trace_gpu_read(long long **buffer) +{ + int cpu = smp_processor_id(); + unsigned long flags; + uint64_t *traceBuf; + int tracePos; + + if (!per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]) + return 0; + + local_irq_save(flags); + + traceBuf = per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]; + tracePos = per_cpu(theGpuTracePos, cpu); + + per_cpu(theGpuTraceSel, cpu) = !per_cpu(theGpuTraceSel, cpu); + per_cpu(theGpuTracePos, cpu) = 0; + per_cpu(theGpuTraceErr, cpu) = 0; + + local_irq_restore(flags); + + if (buffer) + *buffer = traceBuf; + + return tracePos; +} diff --git a/drivers/gator/driver/gator_trace_gpu.h b/drivers/gator/driver/gator_trace_gpu.h new file mode 100644 index 00000000000..894289b4316 --- /dev/null +++ b/drivers/gator/driver/gator_trace_gpu.h @@ -0,0 +1,79 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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. + */ + +#undef TRACE_GPU +#define TRACE_GPU gpu + +#if !defined(_TRACE_GPU_H) +#define _TRACE_GPU_H + +#include + +/* + * UNIT - the GPU processor type + * 1 = Vertex Processor + * 2 = Fragment Processor + * + * CORE - the GPU processor core number + * this is not the CPU core number + */ + +/* + * Tracepoint for calling GPU unit start activity on core + */ +TRACE_EVENT(gpu_activity_start, + + TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p), + + TP_ARGS(gpu_unit, gpu_core, p), + + TP_STRUCT__entry( + __field( int, gpu_unit ) + __field( int, gpu_core ) + __array( char, comm, TASK_COMM_LEN ) + __field( pid_t, pid ) + ), + + TP_fast_assign( + __entry->gpu_unit = gpu_unit; + __entry->gpu_core = gpu_core; + memcpy(__entry->comm, p->comm, TASK_COMM_LEN); + __entry->pid = p->pid; + ), + + TP_printk("unit=%d core=%d comm=%s pid=%d", + __entry->gpu_unit, __entry->gpu_core, __entry->comm, __entry->pid) +); + +/* + * Tracepoint for calling GPU unit stop activity on core + */ +TRACE_EVENT(gpu_activity_stop, + + TP_PROTO(int gpu_unit, int gpu_core), + + TP_ARGS(gpu_unit, gpu_core), + + TP_STRUCT__entry( + __field( int, gpu_unit ) + __field( int, gpu_core ) + ), + + TP_fast_assign( + __entry->gpu_unit = gpu_unit; + __entry->gpu_core = gpu_core; + ), + + TP_printk("unit=%d core=%d", + __entry->gpu_unit, __entry->gpu_core) +); + +#endif /* _TRACE_GPU_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/gator/driver/gator_trace_sched.c b/drivers/gator/driver/gator_trace_sched.c new file mode 100644 index 00000000000..dafacb75416 --- /dev/null +++ b/drivers/gator/driver/gator_trace_sched.c @@ -0,0 +1,269 @@ +/** + * Copyright (C) ARM Limited 2010-2012. 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 +#include "gator.h" + +#define SCHED_OVERFLOW -1 +#define SCHED_SWITCH 1 +#define SCHED_PROCESS_FREE 2 + +#define SCHEDSIZE (8*1024) +#define TASK_MAP_ENTRIES 1024 /* must be power of 2 */ +#define TASK_MAX_COLLISIONS 2 + +static DEFINE_PER_CPU(uint64_t *[2], theSchedBuf); +static DEFINE_PER_CPU(int, theSchedSel); +static DEFINE_PER_CPU(int, theSchedPos); +static DEFINE_PER_CPU(int, theSchedErr); +static DEFINE_PER_CPU(uint64_t *, taskname_keys); + +enum { + STATE_CONTENTION = 0, + STATE_WAIT_ON_IO, + STATE_WAIT_ON_OTHER +}; + +int gator_trace_sched_read(long long **buffer); + +void emit_pid_name(struct task_struct* task) +{ + bool found = false; + unsigned long flags; + char taskcomm[TASK_COMM_LEN + 3]; + int x, cpu = smp_processor_id(); + uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]); + uint64_t value; + + value = gator_chksum_crc32(task->comm); + value = (value << 32) | (uint32_t)task->pid; + + // determine if the thread name was emitted already + for (x = 0; x < TASK_MAX_COLLISIONS; x++) { + if (keys[x] == value) { + found = true; + break; + } + } + + if (!found && buffer_check_space(cpu, TIMER_BUF, TASK_COMM_LEN + 2 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + // shift values, new value always in front + uint64_t oldv, newv = value; + for (x = 0; x < TASK_MAX_COLLISIONS; x++) { + oldv = keys[x]; + keys[x] = newv; + newv = oldv; + } + + // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions + if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) + // append ellipses if task->comm has length of TASK_COMM_LEN - 1 + strcat(taskcomm, "..."); + + // disable interrupts to synchronize with hrtimer populating timer buf + local_irq_save(flags); + gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_PID_NAME); + gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, TIMER_BUF, task->pid); + gator_buffer_write_string(cpu, TIMER_BUF, taskcomm); + local_irq_restore(flags); + } +} + +static void probe_sched_write(int type, struct task_struct* task, struct task_struct* old_task) +{ + int schedPos, cookie = 0, state = 0; + unsigned long flags; + uint64_t *schedBuf, time; + int cpu = smp_processor_id(); + int pid = task->pid; + int tgid = task->tgid; + + if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) + return; + + if (type == SCHED_SWITCH) { + // do as much work as possible before disabling interrupts + cookie = get_exec_cookie(cpu, TIMER_BUF, task); + emit_pid_name(task); + if (old_task->state == 0) + state = STATE_CONTENTION; + else if (old_task->in_iowait) + state = STATE_WAIT_ON_IO; + else + state = STATE_WAIT_ON_OTHER; + } + + // disable interrupts to synchronize with gator_trace_sched_read(); spinlocks not needed since percpu buffers are used + local_irq_save(flags); + + time = gator_get_time(); + schedPos = per_cpu(theSchedPos, cpu); + schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; + + if (schedPos < (SCHEDSIZE - 100)) { + // capture + schedBuf[schedPos++] = type; + schedBuf[schedPos++] = time; + schedBuf[schedPos++] = pid; + schedBuf[schedPos++] = tgid; + schedBuf[schedPos++] = cookie; + schedBuf[schedPos++] = state; + } else if (!per_cpu(theSchedErr, cpu)) { + per_cpu(theSchedErr, cpu) = 1; + schedBuf[schedPos++] = SCHED_OVERFLOW; + schedBuf[schedPos++] = time; + schedBuf[schedPos++] = 0; + schedBuf[schedPos++] = 0; + schedBuf[schedPos++] = 0; + schedBuf[schedPos++] = 0; + pr_debug("gator: tracepoint overflow\n"); + } + per_cpu(theSchedPos, cpu) = schedPos; + local_irq_restore(flags); +} + +// special case used during a suspend of the system +static void trace_sched_insert_idle(void) +{ + unsigned long flags; + uint64_t *schedBuf; + int schedPos, cpu = smp_processor_id(); + + if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) + return; + + local_irq_save(flags); + + schedPos = per_cpu(theSchedPos, cpu); + schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; + + if (schedPos < (SCHEDSIZE - (6 * 8))) { + // capture + schedBuf[schedPos++] = SCHED_SWITCH; + schedBuf[schedPos++] = gator_get_time(); + schedBuf[schedPos++] = 0; // idle pid is zero + schedBuf[schedPos++] = 0; // idle tid is zero + schedBuf[schedPos++] = 0; // idle cookie is zero + schedBuf[schedPos++] = STATE_WAIT_ON_OTHER; + } + + per_cpu(theSchedPos, cpu) = schedPos; + local_irq_restore(flags); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct rq *rq, struct task_struct *prev, struct task_struct *next)) +#else +GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_struct *next)) +#endif +{ + probe_sched_write(SCHED_SWITCH, next, prev); +} + +GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p)) +{ + probe_sched_write(SCHED_PROCESS_FREE, p, 0); +} + +static int register_scheduler_tracepoints(void) { + // register tracepoints + if (GATOR_REGISTER_TRACE(sched_switch)) + goto fail_sched_switch; + if (GATOR_REGISTER_TRACE(sched_process_free)) + goto fail_sched_process_free; + pr_debug("gator: registered tracepoints\n"); + + return 0; + + // unregister tracepoints on error +fail_sched_process_free: + GATOR_UNREGISTER_TRACE(sched_switch); +fail_sched_switch: + pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +int gator_trace_sched_start(void) +{ + int cpu, size; + + for_each_present_cpu(cpu) { + per_cpu(theSchedSel, cpu) = 0; + per_cpu(theSchedPos, cpu) = 0; + per_cpu(theSchedErr, cpu) = 0; + per_cpu(theSchedBuf, cpu)[0] = kmalloc(SCHEDSIZE * sizeof(uint64_t), GFP_KERNEL); + per_cpu(theSchedBuf, cpu)[1] = kmalloc(SCHEDSIZE * sizeof(uint64_t), GFP_KERNEL); + if (!per_cpu(theSchedBuf, cpu)) + return -1; + + size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t); + per_cpu(taskname_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); + if (!per_cpu(taskname_keys, cpu)) + return -1; + memset(per_cpu(taskname_keys, cpu), 0, size); + } + + return register_scheduler_tracepoints(); +} + +int gator_trace_sched_offline(long long **buffer) +{ + trace_sched_insert_idle(); + return gator_trace_sched_read(buffer); +} + +static void unregister_scheduler_tracepoints(void) +{ + GATOR_UNREGISTER_TRACE(sched_switch); + GATOR_UNREGISTER_TRACE(sched_process_free); + pr_debug("gator: unregistered tracepoints\n"); +} + +void gator_trace_sched_stop(void) +{ + int cpu; + unregister_scheduler_tracepoints(); + + for_each_present_cpu(cpu) { + kfree(per_cpu(theSchedBuf, cpu)[0]); + kfree(per_cpu(theSchedBuf, cpu)[1]); + per_cpu(theSchedBuf, cpu)[0] = NULL; + per_cpu(theSchedBuf, cpu)[1] = NULL; + kfree(per_cpu(taskname_keys, cpu)); + } +} + +int gator_trace_sched_read(long long **buffer) +{ + int cpu = smp_processor_id(); + unsigned long flags; + uint64_t *schedBuf; + int schedPos; + + if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) + return 0; + + local_irq_save(flags); + + schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; + schedPos = per_cpu(theSchedPos, cpu); + + per_cpu(theSchedSel, cpu) = !per_cpu(theSchedSel, cpu); + per_cpu(theSchedPos, cpu) = 0; + per_cpu(theSchedErr, cpu) = 0; + + local_irq_restore(flags); + + if (buffer) + *buffer = schedBuf; + + return schedPos; +} -- cgit v1.2.3 From 7cad876d059cd1b86990b799850f9596ad68c8fb Mon Sep 17 00:00:00 2001 From: Wade Cherry Date: Mon, 5 Mar 2012 09:22:16 +0000 Subject: gator/driver: Fix build failure on Linux 3.3 Some meminfo tracepoint APIs were renamed in commit b413d48a (mm-tracepoint: rename page-free events) Signed-off-by: Wade Cherry Signed-off-by: Jon Medhurst --- drivers/gator/driver/gator_events_meminfo.c | 37 +++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/gator/driver/gator_events_meminfo.c b/drivers/gator/driver/gator_events_meminfo.c index 8af9cfcb6d5..c763634ef53 100644 --- a/drivers/gator/driver/gator_events_meminfo.c +++ b/drivers/gator/driver/gator_events_meminfo.c @@ -28,11 +28,19 @@ static void wq_sched_handler(struct work_struct *wsptr); DECLARE_WORK(work, wq_sched_handler); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) { +#else +GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order)) { +#endif mem_event++; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) { +#else +GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold)) { +#endif mem_event++; } @@ -83,20 +91,36 @@ static int gator_events_meminfo_start(void) if (meminfo_global_enabled == 0) return 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) if (GATOR_REGISTER_TRACE(mm_page_free_direct)) - goto mm_page_free_direct_exit; +#else + if (GATOR_REGISTER_TRACE(mm_page_free)) +#endif + goto mm_page_free_exit; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) if (GATOR_REGISTER_TRACE(mm_pagevec_free)) - goto mm_pagevec_free_exit; +#else + if (GATOR_REGISTER_TRACE(mm_page_free_batched)) +#endif + goto mm_page_free_batched_exit; if (GATOR_REGISTER_TRACE(mm_page_alloc)) goto mm_page_alloc_exit; return 0; mm_page_alloc_exit: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_UNREGISTER_TRACE(mm_pagevec_free); -mm_pagevec_free_exit: +#else + GATOR_UNREGISTER_TRACE(mm_page_free_batched); +#endif +mm_page_free_batched_exit: +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_UNREGISTER_TRACE(mm_page_free_direct); -mm_page_free_direct_exit: +#else + GATOR_UNREGISTER_TRACE(mm_page_free); +#endif +mm_page_free_exit: return -1; } @@ -105,8 +129,13 @@ static void gator_events_meminfo_stop(void) int i; if (meminfo_global_enabled) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_UNREGISTER_TRACE(mm_page_free_direct); GATOR_UNREGISTER_TRACE(mm_pagevec_free); +#else + GATOR_UNREGISTER_TRACE(mm_page_free); + GATOR_UNREGISTER_TRACE(mm_page_free_batched); +#endif GATOR_UNREGISTER_TRACE(mm_page_alloc); } -- cgit v1.2.3 From 3f6e6ef41f2946737532eaf5fcc9b03320649a49 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 16 Mar 2012 17:48:46 +0000 Subject: gator/driver: Makefile fix for output path of generated file gator_events.h Signed-off-by: Jon Medhurst --- drivers/gator/driver/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gator/driver/Makefile b/drivers/gator/driver/Makefile index e521b9915dc..5af89c4d1bb 100644 --- a/drivers/gator/driver/Makefile +++ b/drivers/gator/driver/Makefile @@ -39,7 +39,7 @@ clean-files := gator_events.h silent_chk_events.h = : gator_events.h: FORCE @$($(quiet)chk_events.h) - $(Q)cd $(obj) ; $(CONFIG_SHELL) $(obj)/gator_events.sh $@ + $(Q)cd $(srctree)/$(src) ; $(CONFIG_SHELL) gator_events.sh $(objtree)/$(obj)/$@ else -- cgit v1.2.3 From 587f10a737e32807821b8bd438853f0c215cf0e5 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 16 Mar 2012 17:49:42 +0000 Subject: gator/driver: Makefile fixes for building in-tree Signed-off-by: Jon Medhurst --- drivers/gator/driver/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gator/driver/Makefile b/drivers/gator/driver/Makefile index 5af89c4d1bb..d6dfeebf102 100644 --- a/drivers/gator/driver/Makefile +++ b/drivers/gator/driver/Makefile @@ -3,7 +3,7 @@ ifneq ($(KERNELRELEASE),) # Uncomment the following line to enable kernel stack unwinding within gator, or update gator_backtrace.c # EXTRA_CFLAGS += -DGATOR_KERNEL_STACK_UNWINDING -obj-m := gator.o +obj-$(CONFIG_GATOR) := gator.o gator-y := gator_main.o \ gator_events_irq.o \ -- cgit v1.2.3 From 648ec275cc53a16f7ae503314bfd4fd9bd525dd0 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 16 Mar 2012 17:52:01 +0000 Subject: gator/driver: Add config for building the module in-tree Signed-off-by: Jon Medhurst --- drivers/Kconfig | 2 ++ drivers/Makefile | 2 ++ drivers/gator/driver/Kconfig | 7 +++++++ 3 files changed, 11 insertions(+) create mode 100644 drivers/gator/driver/Kconfig diff --git a/drivers/Kconfig b/drivers/Kconfig index 5afe5d1f199..ccad64bcc54 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -136,4 +136,6 @@ source "drivers/virt/Kconfig" source "drivers/devfreq/Kconfig" +source "drivers/gator/driver/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index c07be024b96..683aea47baa 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -132,3 +132,5 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_PM_DEVFREQ) += devfreq/ + +obj-$(CONFIG_GATOR) += gator/driver/ diff --git a/drivers/gator/driver/Kconfig b/drivers/gator/driver/Kconfig new file mode 100644 index 00000000000..3b685ad9963 --- /dev/null +++ b/drivers/gator/driver/Kconfig @@ -0,0 +1,7 @@ +config GATOR + tristate "Gator module for ARM's Streamline Performance Analyzer" + default m + depends on GENERIC_TRACER || TRACING + depends on PROFILING + depends on HIGH_RES_TIMERS + depends on LOCAL_TIMERS || !(ARM && SMP) -- cgit v1.2.3