b97e0db1e3ccaa1867c4106d76305d1056eacf70
[android-sdk/arm-ds5-gator.git] / daemon / Child.cpp
1 /**
2  * Copyright (C) ARM Limited 2010-2012. 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  */
9 #include <stdlib.h>
10 #include <string.h>
11 #include <signal.h>
12 #include <sys/syscall.h>
13 #include <sys/resource.h>
14 #include <unistd.h>
15 #include <sys/prctl.h>
16 #include "Logging.h"
17 #include "CapturedXML.h"
18 #include "SessionData.h"
19 #include "Child.h"
20 #include "LocalCapture.h"
21 #include "Collector.h"
22 #include "Sender.h"
23 #include "OlyUtility.h"
24 #include "StreamlineSetup.h"
25 #include "ConfigurationXML.h"
27 static sem_t haltPipeline, senderThreadStarted, startProfile; // Shared by Child and spawned threads
28 static Fifo* collectorFifo = NULL;   // Shared by Child.cpp and spawned threads
29 static Sender* sender = NULL;        // Shared by Child.cpp and spawned threads
30 Collector* collector = NULL;
31 Child* child = NULL;                 // shared by Child.cpp and main.cpp
33 extern void cleanUp();
34 void handleException() {
35         if (child && child->numExceptions++ > 0) {
36                 // it is possible one of the below functions itself can cause an exception, thus allow only one exception
37                 logg->logMessage("Received multiple exceptions, terminating the child");
38                 exit(1);
39         }
40         fprintf(stderr, "%s", logg->getLastError());
42         if (child && child->socket) {
43                 if (sender) {
44                         // send the error, regardless of the command sent by Streamline
45                         sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
47                         // cannot close the socket before Streamline issues the command, so wait for the command before exiting
48                         if (gSessionData->mWaitingOnCommand) {
49                                 char discard;
50                                 child->socket->receiveNBytes(&discard, 1);
51                         }
53                         // this indirectly calls close socket which will ensure the data has been sent
54                         delete sender;
55                 }
56         }
58         if (gSessionData->mLocalCapture)
59                 cleanUp();
61         exit(1);
62 }
64 // CTRL C Signal Handler for child process
65 void child_handler(int signum) {
66         static bool beenHere = false;
67         if (beenHere == true) {
68                 logg->logMessage("Gator is being forced to shut down.");
69                 exit(1);
70         }
71         beenHere = true;
72         logg->logMessage("Gator is shutting down.");
73         if (signum == SIGALRM || !collector) {
74                 exit(1);
75         } else {
76                 child->endSession();
77                 alarm(5); // Safety net in case endSession does not complete within 5 seconds
78         }
79 }
81 void* durationThread(void* pVoid) {
82         prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
83         sem_wait(&startProfile);
84         if (gSessionData->mSessionIsActive) {
85                 // Time out after duration seconds
86                 // Add a second for host-side filtering
87                 sleep(gSessionData->mDuration + 1);
88                 if (gSessionData->mSessionIsActive) {
89                         logg->logMessage("Duration expired.");
90                         child->endSession();
91                 }
92         }
93         logg->logMessage("Exit duration thread");
94         return 0;
95 }
97 void* stopThread(void* pVoid) {
98         int length;
99         char type;
100         OlySocket* socket = child->socket;
102         prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
103         while (gSessionData->mSessionIsActive) {
104                 // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
105                 const int result = socket->receiveNBytes(&type, sizeof(type));
106                 if (result == -1) {
107                         child->endSession();
108                 } else if (result > 0) {
109                         if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
110                                 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
111                         } else {
112                                 // verify a length of zero
113                                 if (socket->receiveNBytes((char*)&length, sizeof(length)) < 0) {
114                                         break;
115                                 }
117                                 if (length == 0) {
118                                         if (type == COMMAND_APC_STOP) {
119                                                 logg->logMessage("Stop command received.");
120                                                 child->endSession();
121                                         } else {
122                                                 // Ping is used to make sure gator is alive and requires an ACK as the response
123                                                 logg->logMessage("Ping command received.");
124                                                 sender->writeData(NULL, 0, RESPONSE_ACK);
125                                         }
126                                 } else {
127                                         logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
128                                 }
129                         }
130                 }
131         }
133         logg->logMessage("Exit stop thread");
134         return 0;
137 void* senderThread(void* pVoid) {
138         int length;
139         char* data;
140         char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
142         sem_post(&senderThreadStarted);
143         prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
144         sem_wait(&haltPipeline);
146         do {
147                 data = collectorFifo->read(&length);
148                 sender->writeData(data, length, RESPONSE_APC_DATA);
149         } while (length > 0);
151         // write end-of-capture sequence
152         if (!gSessionData->mLocalCapture) {
153                 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
154         }
156         logg->logMessage("Exit sender thread");
157         return 0;
160 Child::Child() {
161         initialization();
162         gSessionData->mLocalCapture = true;
165 Child::Child(OlySocket* sock, int conn) {
166         initialization();
167         socket = sock;
168         mNumConnections = conn;
171 Child::~Child() {
174 void Child::initialization() {
175         // Set up different handlers for signals
176         gSessionData->mSessionIsActive = true;
177         signal(SIGINT, child_handler);
178         signal(SIGTERM, child_handler);
179         signal(SIGABRT, child_handler);
180         signal(SIGALRM, child_handler);
181         socket = NULL;
182         numExceptions = 0;
183         mNumConnections = 0;
185         // Initialize semaphores
186         sem_init(&senderThreadStarted, 0, 0);
187         sem_init(&startProfile, 0, 0);
190 void Child::endSession() {
191         gSessionData->mSessionIsActive = false;
192         collector->stop();
193         sem_post(&haltPipeline);
196 void Child::run() {
197         char* collectBuffer;
198         int bytesCollected = 0;
199         LocalCapture* localCapture = NULL;
200         pthread_t durationThreadID, stopThreadID, senderThreadID;
202         prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
204         // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
205         mxmlSetWrapMargin(0);
207         // Instantiate the Sender - must be done first, after which error messages can be sent
208         sender = new Sender(socket);
210         if (mNumConnections > 1) {
211                 logg->logError(__FILE__, __LINE__, "Session already in progress");
212                 handleException();
213         }
215         // Populate gSessionData with the configuration
216         new ConfigurationXML();
218         // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
219         collector = new Collector();
221         // Start up and parse session xml
222         if (socket) {
223                 // Respond to Streamline requests
224                 StreamlineSetup ss(socket);
225         } else {
226                 char* xmlString;
227                 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
228                 if (xmlString == 0) {
229                         logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
230                         handleException();
231                 }
232                 gSessionData->parseSessionXML(xmlString);
233                 localCapture = new LocalCapture();
234                 localCapture->createAPCDirectory(gSessionData->mTargetPath);
235                 localCapture->copyImages(gSessionData->mImages);
236                 localCapture->write(xmlString);
237                 sender->createDataFile(gSessionData->mAPCDir);
238                 free(xmlString);
239         }
241         // Write configuration into the driver
242         collector->setupPerfCounters();
244         // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
245         logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
246         collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize* 1024 * 1024);
248         // Get the initial pointer to the collect buffer
249         collectBuffer = collectorFifo->start();
251         // Sender thread shall be halted until it is signaled for one shot mode
252         sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
254         // Create the duration, stop, and sender threads
255         bool thread_creation_success = true;
256         if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
257                 thread_creation_success = false;
258         } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
259                 thread_creation_success = false;
260         } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){
261                 thread_creation_success = false;
262         }
264         if (!thread_creation_success) {
265                 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
266                 handleException();
267         }
269         // Wait until thread has started
270         sem_wait(&senderThreadStarted);
272         // Start profiling
273         logg->logMessage("********** Profiling started **********");
274         collector->start();
275         sem_post(&startProfile);
277         // Collect Data
278         do {
279                 // This command will stall until data is received from the driver
280                 bytesCollected = collector->collect(collectBuffer);
282                 // In one shot mode, stop collection once all the buffers are filled
283                 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
284                         if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
285                                 logg->logMessage("One shot");
286                                 endSession();
287                         }
288                 }
289                 collectBuffer = collectorFifo->write(bytesCollected);
290         } while (bytesCollected > 0);
291         logg->logMessage("Exit collect data loop");
293         // Wait for the other threads to exit
294         pthread_join(senderThreadID, NULL);
296         // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
297         if (socket) {
298                 logg->logMessage("Waiting on stop thread");
299                 socket->shutdownConnection();
300                 pthread_join(stopThreadID, NULL);
301         }
303         // Write the captured xml file
304         if (gSessionData->mLocalCapture) {
305                 CapturedXML capturedXML;
306                 capturedXML.write(gSessionData->mAPCDir);
307         }
309         logg->logMessage("Profiling ended.");
311         delete collectorFifo;
312         delete sender;
313         delete collector;
314         delete localCapture;