gator: Version 5.14
[android-sdk/arm-ds5-gator.git] / daemon / Child.cpp
1 /**
2  * Copyright (C) ARM Limited 2010-2013. 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 <unistd.h>
13 #include <sys/prctl.h>
14 #include "Logging.h"
15 #include "CapturedXML.h"
16 #include "SessionData.h"
17 #include "Child.h"
18 #include "LocalCapture.h"
19 #include "Collector.h"
20 #include "Sender.h"
21 #include "OlyUtility.h"
22 #include "StreamlineSetup.h"
23 #include "ConfigurationXML.h"
24 #include "Driver.h"
25 #include "Fifo.h"
26 #include "Buffer.h"
28 #define NS_PER_S ((uint64_t)1000000000)
29 #define NS_PER_US 1000
31 static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
32 static Fifo* collectorFifo = NULL;   // Shared by Child.cpp and spawned threads
33 static Buffer* buffer = NULL;
34 static Sender* sender = NULL;        // Shared by Child.cpp and spawned threads
35 static Collector* collector = NULL;
36 Child* child = NULL;                 // shared by Child.cpp and main.cpp
38 extern void cleanUp();
39 void handleException() {
40         if (child && child->numExceptions++ > 0) {
41                 // it is possible one of the below functions itself can cause an exception, thus allow only one exception
42                 logg->logMessage("Received multiple exceptions, terminating the child");
43                 exit(1);
44         }
45         fprintf(stderr, "%s", logg->getLastError());
47         if (child && child->socket) {
48                 if (sender) {
49                         // send the error, regardless of the command sent by Streamline
50                         sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
52                         // cannot close the socket before Streamline issues the command, so wait for the command before exiting
53                         if (gSessionData->mWaitingOnCommand) {
54                                 char discard;
55                                 child->socket->receiveNBytes(&discard, 1);
56                         }
58                         // this indirectly calls close socket which will ensure the data has been sent
59                         delete sender;
60                 }
61         }
63         if (gSessionData->mLocalCapture)
64                 cleanUp();
66         exit(1);
67 }
69 // CTRL C Signal Handler for child process
70 static void child_handler(int signum) {
71         static bool beenHere = false;
72         if (beenHere == true) {
73                 logg->logMessage("Gator is being forced to shut down.");
74                 exit(1);
75         }
76         beenHere = true;
77         logg->logMessage("Gator is shutting down.");
78         if (signum == SIGALRM || !collector) {
79                 exit(1);
80         } else {
81                 child->endSession();
82                 alarm(5); // Safety net in case endSession does not complete within 5 seconds
83         }
84 }
86 static void* durationThread(void* pVoid) {
87         prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
88         sem_wait(&startProfile);
89         if (gSessionData->mSessionIsActive) {
90                 // Time out after duration seconds
91                 // Add a second for host-side filtering
92                 sleep(gSessionData->mDuration + 1);
93                 if (gSessionData->mSessionIsActive) {
94                         logg->logMessage("Duration expired.");
95                         child->endSession();
96                 }
97         }
98         logg->logMessage("Exit duration thread");
99         return 0;
102 static void* stopThread(void* pVoid) {
103         int length;
104         char type;
105         OlySocket* socket = child->socket;
107         prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
108         while (gSessionData->mSessionIsActive) {
109                 // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
110                 const int result = socket->receiveNBytes(&type, sizeof(type));
111                 if (result == -1) {
112                         child->endSession();
113                 } else if (result > 0) {
114                         if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
115                                 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
116                         } else {
117                                 // verify a length of zero
118                                 if (socket->receiveNBytes((char*)&length, sizeof(length)) < 0) {
119                                         break;
120                                 }
122                                 if (length == 0) {
123                                         if (type == COMMAND_APC_STOP) {
124                                                 logg->logMessage("Stop command received.");
125                                                 child->endSession();
126                                         } else {
127                                                 // Ping is used to make sure gator is alive and requires an ACK as the response
128                                                 logg->logMessage("Ping command received.");
129                                                 sender->writeData(NULL, 0, RESPONSE_ACK);
130                                         }
131                                 } else {
132                                         logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
133                                 }
134                         }
135                 }
136         }
138         logg->logMessage("Exit stop thread");
139         return 0;
142 void* countersThread(void* pVoid) {
143         prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
145         gSessionData->hwmon.start();
147         int64_t monotonic_started = 0;
148         while (monotonic_started <= 0) {
149                 usleep(10);
151                 if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
152                         logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
153                         handleException();
154                 }
155         }
157         uint64_t next_time = 0;
158         while (gSessionData->mSessionIsActive) {
159                 struct timespec ts;
160 #ifndef CLOCK_MONOTONIC_RAW
161                 // Android doesn't have this defined but it was added in Linux 2.6.28
162 #define CLOCK_MONOTONIC_RAW 4
163 #endif
164                 if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
165                         logg->logError(__FILE__, __LINE__, "Failed to get uptime");
166                         handleException();
167                 }
168                 const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started;
169                 // Sample ten times a second ignoring gSessionData->mSampleRate
170                 next_time += NS_PER_S/10;//gSessionData->mSampleRate;
171                 if (next_time < curr_time) {
172                         logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
173                         next_time = curr_time;
174                 }
176                 if (buffer->eventHeader(curr_time)) {
177                         gSessionData->hwmon.read(buffer);
178                         // Only check after writing all counters so that time and corresponding counters appear in the same frame
179                         buffer->check(curr_time);
180                 }
182                 if (buffer->bytesAvailable() <= 0) {
183                         logg->logMessage("One shot (counters)");
184                         child->endSession();
185                 }
187                 usleep((next_time - curr_time)/NS_PER_US);
188         }
190         buffer->setDone();
192         return NULL;
195 static void* senderThread(void* pVoid) {
196         int length = 1;
197         char* data;
198         char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
200         sem_post(&senderThreadStarted);
201         prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
202         sem_wait(&haltPipeline);
204         while (length > 0 || !buffer->isDone()) {
205                 sem_wait(&senderSem);
206                 data = collectorFifo->read(&length);
207                 if (data != NULL) {
208                         sender->writeData(data, length, RESPONSE_APC_DATA);
209                         collectorFifo->release();
210                 }
211                 if (!buffer->isDone()) {
212                         buffer->write(sender);
213                 }
214         }
216         // write end-of-capture sequence
217         if (!gSessionData->mLocalCapture) {
218                 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
219         }
221         logg->logMessage("Exit sender thread");
222         return 0;
225 Child::Child() {
226         initialization();
227         gSessionData->mLocalCapture = true;
230 Child::Child(OlySocket* sock, int conn) {
231         initialization();
232         socket = sock;
233         mNumConnections = conn;
236 Child::~Child() {
239 void Child::initialization() {
240         // Set up different handlers for signals
241         gSessionData->mSessionIsActive = true;
242         signal(SIGINT, child_handler);
243         signal(SIGTERM, child_handler);
244         signal(SIGABRT, child_handler);
245         signal(SIGALRM, child_handler);
246         socket = NULL;
247         numExceptions = 0;
248         mNumConnections = 0;
250         // Initialize semaphores
251         sem_init(&senderThreadStarted, 0, 0);
252         sem_init(&startProfile, 0, 0);
253         sem_init(&senderSem, 0, 0);
256 void Child::endSession() {
257         gSessionData->mSessionIsActive = false;
258         collector->stop();
259         sem_post(&haltPipeline);
262 void Child::run() {
263         char* collectBuffer;
264         int bytesCollected = 0;
265         LocalCapture* localCapture = NULL;
266         pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID;
268         prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
270         // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
271         mxmlSetWrapMargin(0);
273         // Instantiate the Sender - must be done first, after which error messages can be sent
274         sender = new Sender(socket);
276         if (mNumConnections > 1) {
277                 logg->logError(__FILE__, __LINE__, "Session already in progress");
278                 handleException();
279         }
281         // Populate gSessionData with the configuration
282         { ConfigurationXML configuration; }
284         // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
285         collector = new Collector();
287         // Initialize all drivers
288         for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
289                 driver->resetCounters();
290         }
292         // Set up counters using the associated driver's setup function
293         for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
294                 Counter & counter = gSessionData->mCounters[i];
295                 if (counter.isEnabled()) {
296                         counter.getDriver()->setupCounter(counter);
297                 }
298         }
300         // Start up and parse session xml
301         if (socket) {
302                 // Respond to Streamline requests
303                 StreamlineSetup ss(socket);
304         } else {
305                 char* xmlString;
306                 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
307                 if (xmlString == 0) {
308                         logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
309                         handleException();
310                 }
311                 gSessionData->parseSessionXML(xmlString);
312                 localCapture = new LocalCapture();
313                 localCapture->createAPCDirectory(gSessionData->mTargetPath);
314                 localCapture->copyImages(gSessionData->mImages);
315                 localCapture->write(xmlString);
316                 sender->createDataFile(gSessionData->mAPCDir);
317                 free(xmlString);
318         }
320         // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
321         logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
322         collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
324         // Get the initial pointer to the collect buffer
325         collectBuffer = collectorFifo->start();
327         // Create a new Block Counter Buffer
328         buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
330         // Sender thread shall be halted until it is signaled for one shot mode
331         sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
333         // Create the duration, stop, and sender threads
334         bool thread_creation_success = true;
335         if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
336                 thread_creation_success = false;
337         } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
338                 thread_creation_success = false;
339         } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){
340                 thread_creation_success = false;
341         }
343         if (gSessionData->hwmon.countersEnabled()) {
344                 if (pthread_create(&countersThreadID, NULL, countersThread, this)) {
345                         thread_creation_success = false;
346                 }
347         } else {
348                 // Let senderThread know there is no buffer data to send
349                 buffer->setDone();
350         }
352         if (!thread_creation_success) {
353                 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
354                 handleException();
355         }
357         // Wait until thread has started
358         sem_wait(&senderThreadStarted);
360         // Start profiling
361         logg->logMessage("********** Profiling started **********");
362         collector->start();
363         sem_post(&startProfile);
365         // Collect Data
366         do {
367                 // This command will stall until data is received from the driver
368                 bytesCollected = collector->collect(collectBuffer);
370                 // In one shot mode, stop collection once all the buffers are filled
371                 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
372                         if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
373                                 logg->logMessage("One shot");
374                                 endSession();
375                         }
376                 }
377                 collectBuffer = collectorFifo->write(bytesCollected);
378         } while (bytesCollected > 0);
379         logg->logMessage("Exit collect data loop");
381         if (gSessionData->hwmon.countersEnabled()) {
382                 pthread_join(countersThreadID, NULL);
383         }
385         // Wait for the other threads to exit
386         pthread_join(senderThreadID, NULL);
388         // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
389         if (socket) {
390                 logg->logMessage("Waiting on stop thread");
391                 socket->shutdownConnection();
392                 pthread_join(stopThreadID, NULL);
393         }
395         // Write the captured xml file
396         if (gSessionData->mLocalCapture) {
397                 CapturedXML capturedXML;
398                 capturedXML.write(gSessionData->mAPCDir);
399         }
401         logg->logMessage("Profiling ended.");
403         delete buffer;
404         delete collectorFifo;
405         delete sender;
406         delete collector;
407         delete localCapture;