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