blob: 1901ecc6a7248a498ce67b559fcf5f3c85abf312 [file] [log] [blame]
Jon Medhurstaaf37a32013-06-11 12:10:56 +01001/**
Jon Medhurst15ce78d2014-04-10 09:02:02 +01002 * Copyright (C) ARM Limited 2010-2014. All rights reserved.
Jon Medhurstaaf37a32013-06-11 12:10:56 +01003 *
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.
7 */
8
Jon Medhurst15ce78d2014-04-10 09:02:02 +01009#include "Child.h"
10
Jon Medhurstaaf37a32013-06-11 12:10:56 +010011#include <stdlib.h>
12#include <string.h>
13#include <signal.h>
14#include <unistd.h>
15#include <sys/prctl.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010016
Jon Medhurstaaf37a32013-06-11 12:10:56 +010017#include "Logging.h"
18#include "CapturedXML.h"
19#include "SessionData.h"
Jon Medhurstaaf37a32013-06-11 12:10:56 +010020#include "LocalCapture.h"
Jon Medhurstaaf37a32013-06-11 12:10:56 +010021#include "Sender.h"
22#include "OlyUtility.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010023#include "OlySocket.h"
Jon Medhurstaaf37a32013-06-11 12:10:56 +010024#include "StreamlineSetup.h"
25#include "ConfigurationXML.h"
26#include "Driver.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010027#include "PerfSource.h"
28#include "DriverSource.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010029#include "ExternalSource.h"
Jon Medhurste31266f2014-08-04 15:47:44 +010030#include "UserSpaceSource.h"
Jon Medhurstaaf37a32013-06-11 12:10:56 +010031
32static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
Jon Medhurst15ce78d2014-04-10 09:02:02 +010033static Source *primarySource = NULL;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010034static Source *externalSource = NULL;
Jon Medhurste31266f2014-08-04 15:47:44 +010035static Source *userSpaceSource = NULL;
Jon Medhurstaaf37a32013-06-11 12:10:56 +010036static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
Jon Medhurstaaf37a32013-06-11 12:10:56 +010037Child* child = NULL; // shared by Child.cpp and main.cpp
38
39extern void cleanUp();
40void 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");
44 exit(1);
45 }
46 fprintf(stderr, "%s", logg->getLastError());
47
48 if (child && child->socket) {
49 if (sender) {
50 // send the error, regardless of the command sent by Streamline
51 sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
52
53 // cannot close the socket before Streamline issues the command, so wait for the command before exiting
54 if (gSessionData->mWaitingOnCommand) {
55 char discard;
56 child->socket->receiveNBytes(&discard, 1);
57 }
58
Jon Medhurstd3698592013-10-10 16:48:56 +010059 // Ensure all data is flushed
60 child->socket->shutdownConnection();
61
Jon Medhurstaaf37a32013-06-11 12:10:56 +010062 // this indirectly calls close socket which will ensure the data has been sent
63 delete sender;
64 }
65 }
66
67 if (gSessionData->mLocalCapture)
68 cleanUp();
69
70 exit(1);
71}
72
73// CTRL C Signal Handler for child process
74static void child_handler(int signum) {
75 static bool beenHere = false;
76 if (beenHere == true) {
77 logg->logMessage("Gator is being forced to shut down.");
78 exit(1);
79 }
80 beenHere = true;
81 logg->logMessage("Gator is shutting down.");
Jon Medhurst15ce78d2014-04-10 09:02:02 +010082 if (signum == SIGALRM || !primarySource) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +010083 exit(1);
84 } else {
85 child->endSession();
86 alarm(5); // Safety net in case endSession does not complete within 5 seconds
87 }
88}
89
Jon Medhurst34d97692013-12-19 09:23:06 +000090static void *durationThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +010091 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.");
99 child->endSession();
100 }
101 }
102 logg->logMessage("Exit duration thread");
103 return 0;
104}
105
Jon Medhurst34d97692013-12-19 09:23:06 +0000106static void *stopThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100107 OlySocket* socket = child->socket;
108
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);
116 if (result == -1) {
117 child->endSession();
118 } else if (result > 0) {
119 if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
120 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
121 } else {
122 // verify a length of zero
123 if (length == 0) {
124 if (type == COMMAND_APC_STOP) {
125 logg->logMessage("Stop command received.");
126 child->endSession();
127 } else {
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);
131 }
132 } else {
133 logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
134 }
135 }
136 }
137 }
138
139 logg->logMessage("Exit stop thread");
140 return 0;
141}
142
Jon Medhurst34d97692013-12-19 09:23:06 +0000143static void *senderThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100144 char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
145
146 sem_post(&senderThreadStarted);
147 prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
148 sem_wait(&haltPipeline);
149
Jon Medhurste31266f2014-08-04 15:47:44 +0100150 while (!primarySource->isDone() ||
151 !externalSource->isDone() ||
152 (userSpaceSource != NULL && !userSpaceSource->isDone())) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100153 sem_wait(&senderSem);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100154
155 primarySource->write(sender);
Jon Medhurste31266f2014-08-04 15:47:44 +0100156 externalSource->write(sender);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100157 if (userSpaceSource != NULL) {
158 userSpaceSource->write(sender);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100159 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100160 }
161
162 // write end-of-capture sequence
163 if (!gSessionData->mLocalCapture) {
164 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
165 }
166
167 logg->logMessage("Exit sender thread");
168 return 0;
169}
170
171Child::Child() {
172 initialization();
173 gSessionData->mLocalCapture = true;
174}
175
176Child::Child(OlySocket* sock, int conn) {
177 initialization();
178 socket = sock;
179 mNumConnections = conn;
180}
181
182Child::~Child() {
183}
184
185void 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);
192 socket = NULL;
193 numExceptions = 0;
194 mNumConnections = 0;
195
196 // Initialize semaphores
197 sem_init(&senderThreadStarted, 0, 0);
198 sem_init(&startProfile, 0, 0);
199 sem_init(&senderSem, 0, 0);
200}
201
202void Child::endSession() {
203 gSessionData->mSessionIsActive = false;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100204 primarySource->interrupt();
Jon Medhurste31266f2014-08-04 15:47:44 +0100205 externalSource->interrupt();
206 if (userSpaceSource != NULL) {
207 userSpaceSource->interrupt();
208 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100209 sem_post(&haltPipeline);
210}
211
212void Child::run() {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100213 LocalCapture* localCapture = NULL;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100214 pthread_t durationThreadID, stopThreadID, senderThreadID;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100215
216 prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
217
218 // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
219 mxmlSetWrapMargin(0);
220
221 // Instantiate the Sender - must be done first, after which error messages can be sent
222 sender = new Sender(socket);
223
224 if (mNumConnections > 1) {
225 logg->logError(__FILE__, __LINE__, "Session already in progress");
226 handleException();
227 }
228
229 // Populate gSessionData with the configuration
230 { ConfigurationXML configuration; }
231
232 // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100233 if (!gSessionData->perf.isSetup()) {
Jon Medhurste31266f2014-08-04 15:47:44 +0100234 primarySource = new DriverSource(&senderSem, &startProfile);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100235 } else {
Jon Medhurste31266f2014-08-04 15:47:44 +0100236 primarySource = new PerfSource(&senderSem, &startProfile);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100237 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100238
239 // Initialize all drivers
240 for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
241 driver->resetCounters();
242 }
243
244 // Set up counters using the associated driver's setup function
245 for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
246 Counter & counter = gSessionData->mCounters[i];
247 if (counter.isEnabled()) {
248 counter.getDriver()->setupCounter(counter);
249 }
250 }
251
252 // Start up and parse session xml
253 if (socket) {
254 // Respond to Streamline requests
255 StreamlineSetup ss(socket);
256 } else {
257 char* xmlString;
258 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
259 if (xmlString == 0) {
260 logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
261 handleException();
262 }
263 gSessionData->parseSessionXML(xmlString);
264 localCapture = new LocalCapture();
265 localCapture->createAPCDirectory(gSessionData->mTargetPath);
266 localCapture->copyImages(gSessionData->mImages);
267 localCapture->write(xmlString);
268 sender->createDataFile(gSessionData->mAPCDir);
269 free(xmlString);
270 }
271
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100272 // Must be after session XML is parsed
273 if (!primarySource->prepare()) {
274 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
275 handleException();
276 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100277
278 // Sender thread shall be halted until it is signaled for one shot mode
279 sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
280
281 // Create the duration, stop, and sender threads
282 bool thread_creation_success = true;
283 if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
284 thread_creation_success = false;
285 } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
286 thread_creation_success = false;
Jon Medhurste31266f2014-08-04 15:47:44 +0100287 } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100288 thread_creation_success = false;
289 }
290
Jon Medhurste31266f2014-08-04 15:47:44 +0100291 externalSource = new ExternalSource(&senderSem);
292 if (!externalSource->prepare()) {
293 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
294 handleException();
295 }
296 externalSource->start();
297
298 if (gSessionData->hwmon.countersEnabled() || gSessionData->fsDriver.countersEnabled()) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100299 userSpaceSource = new UserSpaceSource(&senderSem);
300 if (!userSpaceSource->prepare()) {
301 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
302 handleException();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100303 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100304 userSpaceSource->start();
305 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100306
307 if (!thread_creation_success) {
308 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
309 handleException();
310 }
311
312 // Wait until thread has started
313 sem_wait(&senderThreadStarted);
314
315 // Start profiling
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100316 primarySource->run();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100317
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100318 if (userSpaceSource != NULL) {
319 userSpaceSource->join();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100320 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100321 externalSource->join();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100322
323 // Wait for the other threads to exit
324 pthread_join(senderThreadID, NULL);
325
326 // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
327 if (socket) {
328 logg->logMessage("Waiting on stop thread");
329 socket->shutdownConnection();
330 pthread_join(stopThreadID, NULL);
331 }
332
333 // Write the captured xml file
334 if (gSessionData->mLocalCapture) {
335 CapturedXML capturedXML;
336 capturedXML.write(gSessionData->mAPCDir);
337 }
338
339 logg->logMessage("Profiling ended.");
340
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100341 delete userSpaceSource;
Jon Medhurste31266f2014-08-04 15:47:44 +0100342 delete externalSource;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100343 delete primarySource;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100344 delete sender;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100345 delete localCapture;
346}