blob: 286c7e7ba3987540a13db1855a3d9aa5a09ce268 [file] [log] [blame]
Jon Medhurstaaf37a32013-06-11 12:10:56 +01001/**
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 */
8
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"
27
28#define NS_PER_S ((uint64_t)1000000000)
29#define NS_PER_US 1000
30
31static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
32static Fifo* collectorFifo = NULL; // Shared by Child.cpp and spawned threads
33static Buffer* buffer = NULL;
34static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
35static Collector* collector = NULL;
36Child* child = NULL; // shared by Child.cpp and main.cpp
37
38extern void cleanUp();
39void 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());
46
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);
51
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 }
57
58 // this indirectly calls close socket which will ensure the data has been sent
59 delete sender;
60 }
61 }
62
63 if (gSessionData->mLocalCapture)
64 cleanUp();
65
66 exit(1);
67}
68
69// CTRL C Signal Handler for child process
70static 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}
85
86static 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;
100}
101
102static void* stopThread(void* pVoid) {
103 OlySocket* socket = child->socket;
104
105 prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
106 while (gSessionData->mSessionIsActive) {
107 // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
108 unsigned char header[5];
109 const int result = socket->receiveNBytes((char*)&header, sizeof(header));
110 const char type = header[0];
111 const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
112 if (result == -1) {
113 child->endSession();
114 } else if (result > 0) {
115 if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
116 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
117 } else {
118 // verify a length of zero
119 if (length == 0) {
120 if (type == COMMAND_APC_STOP) {
121 logg->logMessage("Stop command received.");
122 child->endSession();
123 } else {
124 // Ping is used to make sure gator is alive and requires an ACK as the response
125 logg->logMessage("Ping command received.");
126 sender->writeData(NULL, 0, RESPONSE_ACK);
127 }
128 } else {
129 logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
130 }
131 }
132 }
133 }
134
135 logg->logMessage("Exit stop thread");
136 return 0;
137}
138
139void* countersThread(void* pVoid) {
140 prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
141
142 gSessionData->hwmon.start();
143
144 int64_t monotonic_started = 0;
145 while (monotonic_started <= 0) {
146 usleep(10);
147
148 if (Collector::readInt64Driver("/dev/gator/started", &monotonic_started) == -1) {
149 logg->logError(__FILE__, __LINE__, "Error reading gator driver start time");
150 handleException();
151 }
152 }
153
154 uint64_t next_time = 0;
155 while (gSessionData->mSessionIsActive) {
156 struct timespec ts;
157#ifndef CLOCK_MONOTONIC_RAW
158 // Android doesn't have this defined but it was added in Linux 2.6.28
159#define CLOCK_MONOTONIC_RAW 4
160#endif
161 if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
162 logg->logError(__FILE__, __LINE__, "Failed to get uptime");
163 handleException();
164 }
165 const uint64_t curr_time = (NS_PER_S*ts.tv_sec + ts.tv_nsec) - monotonic_started;
166 // Sample ten times a second ignoring gSessionData->mSampleRate
167 next_time += NS_PER_S/10;//gSessionData->mSampleRate;
168 if (next_time < curr_time) {
169 logg->logMessage("Too slow, curr_time: %lli next_time: %lli", curr_time, next_time);
170 next_time = curr_time;
171 }
172
173 if (buffer->eventHeader(curr_time)) {
174 gSessionData->hwmon.read(buffer);
175 // Only check after writing all counters so that time and corresponding counters appear in the same frame
176 buffer->check(curr_time);
177 }
178
179 if (buffer->bytesAvailable() <= 0) {
180 logg->logMessage("One shot (counters)");
181 child->endSession();
182 }
183
184 usleep((next_time - curr_time)/NS_PER_US);
185 }
186
187 buffer->setDone();
188
189 return NULL;
190}
191
192static void* senderThread(void* pVoid) {
193 int length = 1;
194 char* data;
195 char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
196
197 sem_post(&senderThreadStarted);
198 prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
199 sem_wait(&haltPipeline);
200
201 while (length > 0 || !buffer->isDone()) {
202 sem_wait(&senderSem);
203 data = collectorFifo->read(&length);
204 if (data != NULL) {
205 sender->writeData(data, length, RESPONSE_APC_DATA);
206 collectorFifo->release();
207 }
208 if (!buffer->isDone()) {
209 buffer->write(sender);
210 }
211 }
212
213 // write end-of-capture sequence
214 if (!gSessionData->mLocalCapture) {
215 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
216 }
217
218 logg->logMessage("Exit sender thread");
219 return 0;
220}
221
222Child::Child() {
223 initialization();
224 gSessionData->mLocalCapture = true;
225}
226
227Child::Child(OlySocket* sock, int conn) {
228 initialization();
229 socket = sock;
230 mNumConnections = conn;
231}
232
233Child::~Child() {
234}
235
236void Child::initialization() {
237 // Set up different handlers for signals
238 gSessionData->mSessionIsActive = true;
239 signal(SIGINT, child_handler);
240 signal(SIGTERM, child_handler);
241 signal(SIGABRT, child_handler);
242 signal(SIGALRM, child_handler);
243 socket = NULL;
244 numExceptions = 0;
245 mNumConnections = 0;
246
247 // Initialize semaphores
248 sem_init(&senderThreadStarted, 0, 0);
249 sem_init(&startProfile, 0, 0);
250 sem_init(&senderSem, 0, 0);
251}
252
253void Child::endSession() {
254 gSessionData->mSessionIsActive = false;
255 collector->stop();
256 sem_post(&haltPipeline);
257}
258
259void Child::run() {
260 char* collectBuffer;
261 int bytesCollected = 0;
262 LocalCapture* localCapture = NULL;
263 pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID;
264
265 prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
266
267 // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
268 mxmlSetWrapMargin(0);
269
270 // Instantiate the Sender - must be done first, after which error messages can be sent
271 sender = new Sender(socket);
272
273 if (mNumConnections > 1) {
274 logg->logError(__FILE__, __LINE__, "Session already in progress");
275 handleException();
276 }
277
278 // Populate gSessionData with the configuration
279 { ConfigurationXML configuration; }
280
281 // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
282 collector = new Collector();
283
284 // Initialize all drivers
285 for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
286 driver->resetCounters();
287 }
288
289 // Set up counters using the associated driver's setup function
290 for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
291 Counter & counter = gSessionData->mCounters[i];
292 if (counter.isEnabled()) {
293 counter.getDriver()->setupCounter(counter);
294 }
295 }
296
297 // Start up and parse session xml
298 if (socket) {
299 // Respond to Streamline requests
300 StreamlineSetup ss(socket);
301 } else {
302 char* xmlString;
303 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
304 if (xmlString == 0) {
305 logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
306 handleException();
307 }
308 gSessionData->parseSessionXML(xmlString);
309 localCapture = new LocalCapture();
310 localCapture->createAPCDirectory(gSessionData->mTargetPath);
311 localCapture->copyImages(gSessionData->mImages);
312 localCapture->write(xmlString);
313 sender->createDataFile(gSessionData->mAPCDir);
314 free(xmlString);
315 }
316
317 // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
318 logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
319 collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
320
321 // Get the initial pointer to the collect buffer
322 collectBuffer = collectorFifo->start();
323
324 // Create a new Block Counter Buffer
325 buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
326
327 // Sender thread shall be halted until it is signaled for one shot mode
328 sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
329
330 // Create the duration, stop, and sender threads
331 bool thread_creation_success = true;
332 if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
333 thread_creation_success = false;
334 } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
335 thread_creation_success = false;
336 } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){
337 thread_creation_success = false;
338 }
339
340 if (gSessionData->hwmon.countersEnabled()) {
341 if (pthread_create(&countersThreadID, NULL, countersThread, this)) {
342 thread_creation_success = false;
343 }
344 } else {
345 // Let senderThread know there is no buffer data to send
346 buffer->setDone();
347 }
348
349 if (!thread_creation_success) {
350 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
351 handleException();
352 }
353
354 // Wait until thread has started
355 sem_wait(&senderThreadStarted);
356
357 // Start profiling
358 logg->logMessage("********** Profiling started **********");
359 collector->start();
360 sem_post(&startProfile);
361
362 // Collect Data
363 do {
364 // This command will stall until data is received from the driver
365 bytesCollected = collector->collect(collectBuffer);
366
367 // In one shot mode, stop collection once all the buffers are filled
368 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
369 if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
370 logg->logMessage("One shot");
371 endSession();
372 }
373 }
374 collectBuffer = collectorFifo->write(bytesCollected);
375 } while (bytesCollected > 0);
376 logg->logMessage("Exit collect data loop");
377
378 if (gSessionData->hwmon.countersEnabled()) {
379 pthread_join(countersThreadID, NULL);
380 }
381
382 // Wait for the other threads to exit
383 pthread_join(senderThreadID, NULL);
384
385 // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
386 if (socket) {
387 logg->logMessage("Waiting on stop thread");
388 socket->shutdownConnection();
389 pthread_join(stopThreadID, NULL);
390 }
391
392 // Write the captured xml file
393 if (gSessionData->mLocalCapture) {
394 CapturedXML capturedXML;
395 capturedXML.write(gSessionData->mAPCDir);
396 }
397
398 logg->logMessage("Profiling ended.");
399
400 delete buffer;
401 delete collectorFifo;
402 delete sender;
403 delete collector;
404 delete localCapture;
405}