diff options
Diffstat (limited to 'tools/gator/daemon/Child.cpp')
-rw-r--r-- | tools/gator/daemon/Child.cpp | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/tools/gator/daemon/Child.cpp b/tools/gator/daemon/Child.cpp new file mode 100644 index 000000000000..6b5bbb3bf6af --- /dev/null +++ b/tools/gator/daemon/Child.cpp @@ -0,0 +1,392 @@ +/** + * Copyright (C) ARM Limited 2010-2014. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "Child.h" + +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> +#include <sys/prctl.h> + +#include "CapturedXML.h" +#include "Command.h" +#include "ConfigurationXML.h" +#include "Driver.h" +#include "DriverSource.h" +#include "ExternalSource.h" +#include "FtraceSource.h" +#include "LocalCapture.h" +#include "Logging.h" +#include "OlySocket.h" +#include "OlyUtility.h" +#include "PerfSource.h" +#include "Sender.h" +#include "SessionData.h" +#include "StreamlineSetup.h" +#include "UserSpaceSource.h" + +static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads +static Source *primarySource = NULL; +static Source *externalSource = NULL; +static Source *userSpaceSource = NULL; +static Source *ftraceSource = NULL; +static Sender* sender = NULL; // Shared by Child.cpp and spawned threads +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); + } + + // Ensure all data is flushed + child->socket->shutdownConnection(); + + // 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 +static 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 || !primarySource) { + exit(1); + } else { + child->endSession(); + alarm(5); // Safety net in case endSession does not complete within 5 seconds + } +} + +static void *durationThread(void *) { + prctl(PR_SET_NAME, (unsigned long)&"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; +} + +static void *stopThread(void *) { + OlySocket* socket = child->socket; + + prctl(PR_SET_NAME, (unsigned long)&"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 + unsigned char header[5]; + const int result = socket->receiveNBytes((char*)&header, sizeof(header)); + const char type = header[0]; + const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24); + if (result == -1) { + child->endSession(); + } else if (result > 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 (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; +} + +static void *senderThread(void *) { + char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; + + sem_post(&senderThreadStarted); + prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0); + sem_wait(&haltPipeline); + + while (!primarySource->isDone() || + !externalSource->isDone() || + (userSpaceSource != NULL && !userSpaceSource->isDone()) || + (ftraceSource != NULL && !ftraceSource->isDone())) { + sem_wait(&senderSem); + + primarySource->write(sender); + externalSource->write(sender); + if (userSpaceSource != NULL) { + userSpaceSource->write(sender); + } + if (ftraceSource != NULL) { + ftraceSource->write(sender); + } + } + + // write end-of-capture sequence + if (!gSessionData->mLocalCapture) { + sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA); + } + + logg->logMessage("Exit sender thread"); + return 0; +} + +Child::Child() { + initialization(); + gSessionData->mLocalCapture = true; +} + +Child::Child(OlySocket* sock, int conn) { + initialization(); + socket = sock; + mNumConnections = 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; + mNumConnections = 0; + + // Initialize semaphores + sem_init(&senderThreadStarted, 0, 0); + sem_init(&startProfile, 0, 0); + sem_init(&senderSem, 0, 0); +} + +void Child::endSession() { + gSessionData->mSessionIsActive = false; + primarySource->interrupt(); + externalSource->interrupt(); + if (userSpaceSource != NULL) { + userSpaceSource->interrupt(); + } + if (ftraceSource != NULL) { + ftraceSource->interrupt(); + } + sem_post(&haltPipeline); +} + +void Child::run() { + LocalCapture* localCapture = NULL; + pthread_t durationThreadID, stopThreadID, senderThreadID; + + prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0); + + // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually + mxmlSetWrapMargin(0); + + // Instantiate the Sender - must be done first, after which error messages can be sent + sender = new Sender(socket); + + if (mNumConnections > 1) { + logg->logError(__FILE__, __LINE__, "Session already in progress"); + handleException(); + } + + // Populate gSessionData with the configuration + { ConfigurationXML configuration; } + + // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated + if (!gSessionData->perf.isSetup()) { + primarySource = new DriverSource(&senderSem, &startProfile); + } else { + primarySource = new PerfSource(&senderSem, &startProfile); + } + + // Initialize all drivers + for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { + driver->resetCounters(); + } + + // Set up counters using the associated driver's setup function + for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { + Counter & counter = gSessionData->mCounters[i]; + if (counter.isEnabled()) { + counter.getDriver()->setupCounter(counter); + } + } + + // Start up and parse session xml + if (socket) { + // Respond to Streamline requests + StreamlineSetup ss(socket); + } else { + char* xmlString; + xmlString = util->readFromDisk(gSessionData->mSessionXMLPath); + if (xmlString == 0) { + logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath); + handleException(); + } + gSessionData->parseSessionXML(xmlString); + localCapture = new LocalCapture(); + localCapture->createAPCDirectory(gSessionData->mTargetPath); + localCapture->copyImages(gSessionData->mImages); + localCapture->write(xmlString); + sender->createDataFile(gSessionData->mAPCDir); + free(xmlString); + } + + if (gSessionData->kmod.isMaliCapture() && (gSessionData->mSampleRate == 0)) { + logg->logError(__FILE__, __LINE__, "Mali counters are not supported with Sample Rate: None."); + handleException(); + } + + // Must be after session XML is parsed + if (!primarySource->prepare()) { + if (gSessionData->perf.isSetup()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare gator driver for capture"); + } else { + logg->logError(__FILE__, __LINE__, "Unable to communicate with the perf API, please ensure that CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER are enabled. Please refer to README_Streamline.txt for more information."); + } + handleException(); + } + + // Sender thread shall be halted until it is signaled for one shot mode + sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2); + + // Must be initialized before senderThread is started as senderThread checks externalSource + externalSource = new ExternalSource(&senderSem); + if (!externalSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare external source for capture"); + handleException(); + } + externalSource->start(); + + // 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; + } + + bool startUSSource = false; + for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) { + if (gSessionData->usDrivers[i]->countersEnabled()) { + startUSSource = true; + } + } + if (startUSSource) { + userSpaceSource = new UserSpaceSource(&senderSem); + if (!userSpaceSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare userspace source for capture"); + handleException(); + } + userSpaceSource->start(); + } + + if (gSessionData->ftraceDriver.countersEnabled()) { + ftraceSource = new FtraceSource(&senderSem); + if (!ftraceSource->prepare()) { + logg->logError(__FILE__, __LINE__, "Unable to prepare userspace source for capture"); + handleException(); + } + ftraceSource->start(); + } + + if (gSessionData->mAllowCommands && (gSessionData->mCaptureCommand != NULL)) { + pthread_t thread; + if (pthread_create(&thread, NULL, commandThread, 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 + primarySource->run(); + + if (ftraceSource != NULL) { + ftraceSource->join(); + } + if (userSpaceSource != NULL) { + userSpaceSource->join(); + } + externalSource->join(); + + // 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->mAPCDir); + } + + logg->logMessage("Profiling ended."); + + delete ftraceSource; + delete userSpaceSource; + delete externalSource; + delete primarySource; + delete sender; + delete localCapture; +} |