2 * Copyright (C) ARM Limited 2010-2014. All rights reserved.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
15 #include <sys/prctl.h>
18 #include "CapturedXML.h"
19 #include "SessionData.h"
20 #include "LocalCapture.h"
22 #include "OlyUtility.h"
23 #include "OlySocket.h"
24 #include "StreamlineSetup.h"
25 #include "ConfigurationXML.h"
27 #include "PerfSource.h"
28 #include "DriverSource.h"
29 #include "UserSpaceSource.h"
30 #include "ExternalSource.h"
32 static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
33 static Source *primarySource = NULL;
34 static Source *userSpaceSource = NULL;
35 static Source *externalSource = NULL;
36 static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
37 Child* child = NULL; // shared by Child.cpp and main.cpp
39 extern void cleanUp();
40 void handleException() {
41 if (child && child->numExceptions++ > 0) {
42 // it is possible one of the below functions itself can cause an exception, thus allow only one exception
43 logg->logMessage("Received multiple exceptions, terminating the child");
46 fprintf(stderr, "%s", logg->getLastError());
48 if (child && child->socket) {
50 // send the error, regardless of the command sent by Streamline
51 sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
53 // cannot close the socket before Streamline issues the command, so wait for the command before exiting
54 if (gSessionData->mWaitingOnCommand) {
56 child->socket->receiveNBytes(&discard, 1);
59 // Ensure all data is flushed
60 child->socket->shutdownConnection();
62 // this indirectly calls close socket which will ensure the data has been sent
67 if (gSessionData->mLocalCapture)
73 // CTRL C Signal Handler for child process
74 static void child_handler(int signum) {
75 static bool beenHere = false;
76 if (beenHere == true) {
77 logg->logMessage("Gator is being forced to shut down.");
81 logg->logMessage("Gator is shutting down.");
82 if (signum == SIGALRM || !primarySource) {
86 alarm(5); // Safety net in case endSession does not complete within 5 seconds
90 static void *durationThread(void *) {
91 prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
92 sem_wait(&startProfile);
93 if (gSessionData->mSessionIsActive) {
94 // Time out after duration seconds
95 // Add a second for host-side filtering
96 sleep(gSessionData->mDuration + 1);
97 if (gSessionData->mSessionIsActive) {
98 logg->logMessage("Duration expired.");
102 logg->logMessage("Exit duration thread");
106 static void *stopThread(void *) {
107 OlySocket* socket = child->socket;
109 prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
110 while (gSessionData->mSessionIsActive) {
111 // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
112 unsigned char header[5];
113 const int result = socket->receiveNBytes((char*)&header, sizeof(header));
114 const char type = header[0];
115 const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
118 } else if (result > 0) {
119 if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
120 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
122 // verify a length of zero
124 if (type == COMMAND_APC_STOP) {
125 logg->logMessage("Stop command received.");
128 // Ping is used to make sure gator is alive and requires an ACK as the response
129 logg->logMessage("Ping command received.");
130 sender->writeData(NULL, 0, RESPONSE_ACK);
133 logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
139 logg->logMessage("Exit stop thread");
143 static void *senderThread(void *) {
144 char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
146 sem_post(&senderThreadStarted);
147 prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
148 sem_wait(&haltPipeline);
150 while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) {
151 sem_wait(&senderSem);
153 primarySource->write(sender);
154 if (userSpaceSource != NULL) {
155 userSpaceSource->write(sender);
157 if (externalSource != NULL) {
158 externalSource->write(sender);
162 // write end-of-capture sequence
163 if (!gSessionData->mLocalCapture) {
164 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
167 logg->logMessage("Exit sender thread");
173 gSessionData->mLocalCapture = true;
176 Child::Child(OlySocket* sock, int conn) {
179 mNumConnections = conn;
185 void Child::initialization() {
186 // Set up different handlers for signals
187 gSessionData->mSessionIsActive = true;
188 signal(SIGINT, child_handler);
189 signal(SIGTERM, child_handler);
190 signal(SIGABRT, child_handler);
191 signal(SIGALRM, child_handler);
196 // Initialize semaphores
197 sem_init(&senderThreadStarted, 0, 0);
198 sem_init(&startProfile, 0, 0);
199 sem_init(&senderSem, 0, 0);
202 void Child::endSession() {
203 gSessionData->mSessionIsActive = false;
204 primarySource->interrupt();
205 sem_post(&haltPipeline);
209 LocalCapture* localCapture = NULL;
210 pthread_t durationThreadID, stopThreadID, senderThreadID;
212 prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
214 // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
215 mxmlSetWrapMargin(0);
217 // Instantiate the Sender - must be done first, after which error messages can be sent
218 sender = new Sender(socket);
220 if (mNumConnections > 1) {
221 logg->logError(__FILE__, __LINE__, "Session already in progress");
225 // Populate gSessionData with the configuration
226 { ConfigurationXML configuration; }
228 // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
229 if (!gSessionData->perf.isSetup()) {
230 primarySource = new DriverSource(&senderSem, &startProfile);
232 primarySource = new PerfSource(&senderSem, &startProfile);
235 // Initialize all drivers
236 for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
237 driver->resetCounters();
240 // Set up counters using the associated driver's setup function
241 for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
242 Counter & counter = gSessionData->mCounters[i];
243 if (counter.isEnabled()) {
244 counter.getDriver()->setupCounter(counter);
248 // Start up and parse session xml
250 // Respond to Streamline requests
251 StreamlineSetup ss(socket);
254 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
255 if (xmlString == 0) {
256 logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
259 gSessionData->parseSessionXML(xmlString);
260 localCapture = new LocalCapture();
261 localCapture->createAPCDirectory(gSessionData->mTargetPath);
262 localCapture->copyImages(gSessionData->mImages);
263 localCapture->write(xmlString);
264 sender->createDataFile(gSessionData->mAPCDir);
268 // Must be after session XML is parsed
269 if (!primarySource->prepare()) {
270 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
274 // Sender thread shall be halted until it is signaled for one shot mode
275 sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
277 // Create the duration, stop, and sender threads
278 bool thread_creation_success = true;
279 if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
280 thread_creation_success = false;
281 } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
282 thread_creation_success = false;
283 } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){
284 thread_creation_success = false;
287 if (gSessionData->hwmon.countersEnabled()) {
288 userSpaceSource = new UserSpaceSource(&senderSem);
289 if (!userSpaceSource->prepare()) {
290 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
293 userSpaceSource->start();
295 if (access("/tmp/gator", F_OK) == 0) {
296 externalSource = new ExternalSource(&senderSem);
297 if (!externalSource->prepare()) {
298 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
301 externalSource->start();
304 if (!thread_creation_success) {
305 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
309 // Wait until thread has started
310 sem_wait(&senderThreadStarted);
313 primarySource->run();
315 if (externalSource != NULL) {
316 externalSource->join();
318 if (userSpaceSource != NULL) {
319 userSpaceSource->join();
322 // Wait for the other threads to exit
323 pthread_join(senderThreadID, NULL);
325 // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
327 logg->logMessage("Waiting on stop thread");
328 socket->shutdownConnection();
329 pthread_join(stopThreadID, NULL);
332 // Write the captured xml file
333 if (gSessionData->mLocalCapture) {
334 CapturedXML capturedXML;
335 capturedXML.write(gSessionData->mAPCDir);
338 logg->logMessage("Profiling ended.");
340 delete externalSource;
341 delete userSpaceSource;
342 delete primarySource;