gator: Version 5.18
[firefly-linux-kernel-4.4.55.git] / tools / gator / daemon / Child.cpp
1 /**
2  * Copyright (C) ARM Limited 2010-2014. All rights reserved.
3  *
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
9 #include "Child.h"
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <signal.h>
14 #include <unistd.h>
15 #include <sys/prctl.h>
16
17 #include "Logging.h"
18 #include "CapturedXML.h"
19 #include "SessionData.h"
20 #include "LocalCapture.h"
21 #include "Sender.h"
22 #include "OlyUtility.h"
23 #include "OlySocket.h"
24 #include "StreamlineSetup.h"
25 #include "ConfigurationXML.h"
26 #include "Driver.h"
27 #include "PerfSource.h"
28 #include "DriverSource.h"
29 #include "UserSpaceSource.h"
30 #include "ExternalSource.h"
31
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
38
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");
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
59                         // Ensure all data is flushed
60                         child->socket->shutdownConnection();
61
62                         // 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
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.");
78                 exit(1);
79         }
80         beenHere = true;
81         logg->logMessage("Gator is shutting down.");
82         if (signum == SIGALRM || !primarySource) {
83                 exit(1);
84         } else {
85                 child->endSession();
86                 alarm(5); // Safety net in case endSession does not complete within 5 seconds
87         }
88 }
89
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.");
99                         child->endSession();
100                 }
101         }
102         logg->logMessage("Exit duration thread");
103         return 0;
104 }
105
106 static void *stopThread(void *) {
107         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
143 static void *senderThread(void *) {
144         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
150         while (!primarySource->isDone() || (userSpaceSource != NULL && !userSpaceSource->isDone()) || (externalSource != NULL && !externalSource->isDone())) {
151                 sem_wait(&senderSem);
152
153                 primarySource->write(sender);
154                 if (userSpaceSource != NULL) {
155                         userSpaceSource->write(sender);
156                 }
157                 if (externalSource != NULL) {
158                         externalSource->write(sender);
159                 }
160         }
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
171 Child::Child() {
172         initialization();
173         gSessionData->mLocalCapture = true;
174 }
175
176 Child::Child(OlySocket* sock, int conn) {
177         initialization();
178         socket = sock;
179         mNumConnections = conn;
180 }
181
182 Child::~Child() {
183 }
184
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);
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
202 void Child::endSession() {
203         gSessionData->mSessionIsActive = false;
204         primarySource->interrupt();
205         sem_post(&haltPipeline);
206 }
207
208 void Child::run() {
209         LocalCapture* localCapture = NULL;
210         pthread_t durationThreadID, stopThreadID, senderThreadID;
211
212         prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
213
214         // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
215         mxmlSetWrapMargin(0);
216
217         // Instantiate the Sender - must be done first, after which error messages can be sent
218         sender = new Sender(socket);
219
220         if (mNumConnections > 1) {
221                 logg->logError(__FILE__, __LINE__, "Session already in progress");
222                 handleException();
223         }
224
225         // Populate gSessionData with the configuration
226         { ConfigurationXML configuration; }
227
228         // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
229         if (!gSessionData->perf.isSetup()) {
230           primarySource = new DriverSource(&senderSem, &startProfile);
231         } else {
232           primarySource = new PerfSource(&senderSem, &startProfile);
233         }
234
235         // Initialize all drivers
236         for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
237                 driver->resetCounters();
238         }
239
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);
245                 }
246         }
247
248         // Start up and parse session xml
249         if (socket) {
250                 // Respond to Streamline requests
251                 StreamlineSetup ss(socket);
252         } else {
253                 char* xmlString;
254                 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
255                 if (xmlString == 0) {
256                         logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
257                         handleException();
258                 }
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);
265                 free(xmlString);
266         }
267
268         // Must be after session XML is parsed
269         if (!primarySource->prepare()) {
270                 logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
271                 handleException();
272         }
273
274         // Sender thread shall be halted until it is signaled for one shot mode
275         sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
276
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;
285         }
286
287         if (gSessionData->hwmon.countersEnabled()) {
288                 userSpaceSource = new UserSpaceSource(&senderSem);
289                 if (!userSpaceSource->prepare()) {
290                         logg->logError(__FILE__, __LINE__, "Unable to prepare for capture");
291                         handleException();
292                 }
293                 userSpaceSource->start();
294         }
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");
299                         handleException();
300                 }
301                 externalSource->start();
302         }
303
304         if (!thread_creation_success) {
305                 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
306                 handleException();
307         }
308
309         // Wait until thread has started
310         sem_wait(&senderThreadStarted);
311
312         // Start profiling
313         primarySource->run();
314
315         if (externalSource != NULL) {
316                 externalSource->join();
317         }
318         if (userSpaceSource != NULL) {
319                 userSpaceSource->join();
320         }
321
322         // Wait for the other threads to exit
323         pthread_join(senderThreadID, NULL);
324
325         // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
326         if (socket) {
327                 logg->logMessage("Waiting on stop thread");
328                 socket->shutdownConnection();
329                 pthread_join(stopThreadID, NULL);
330         }
331
332         // Write the captured xml file
333         if (gSessionData->mLocalCapture) {
334                 CapturedXML capturedXML;
335                 capturedXML.write(gSessionData->mAPCDir);
336         }
337
338         logg->logMessage("Profiling ended.");
339
340         delete externalSource;
341         delete userSpaceSource;
342         delete primarySource;
343         delete sender;
344         delete localCapture;
345 }