blob: 9ee2ef8afb9d582225cb0823151758138e9b119c [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
Jon Medhurstd3698592013-10-10 16:48:56 +010058 // Ensure all data is flushed
59 child->socket->shutdownConnection();
60
Jon Medhurstaaf37a32013-06-11 12:10:56 +010061 // this indirectly calls close socket which will ensure the data has been sent
62 delete sender;
63 }
64 }
65
66 if (gSessionData->mLocalCapture)
67 cleanUp();
68
69 exit(1);
70}
71
72// CTRL C Signal Handler for child process
73static void child_handler(int signum) {
74 static bool beenHere = false;
75 if (beenHere == true) {
76 logg->logMessage("Gator is being forced to shut down.");
77 exit(1);
78 }
79 beenHere = true;
80 logg->logMessage("Gator is shutting down.");
81 if (signum == SIGALRM || !collector) {
82 exit(1);
83 } else {
84 child->endSession();
85 alarm(5); // Safety net in case endSession does not complete within 5 seconds
86 }
87}
88
Jon Medhurst34d97692013-12-19 09:23:06 +000089static void *durationThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +010090 prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
91 sem_wait(&startProfile);
92 if (gSessionData->mSessionIsActive) {
93 // Time out after duration seconds
94 // Add a second for host-side filtering
95 sleep(gSessionData->mDuration + 1);
96 if (gSessionData->mSessionIsActive) {
97 logg->logMessage("Duration expired.");
98 child->endSession();
99 }
100 }
101 logg->logMessage("Exit duration thread");
102 return 0;
103}
104
Jon Medhurst34d97692013-12-19 09:23:06 +0000105static void *stopThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100106 OlySocket* socket = child->socket;
107
108 prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
109 while (gSessionData->mSessionIsActive) {
110 // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
111 unsigned char header[5];
112 const int result = socket->receiveNBytes((char*)&header, sizeof(header));
113 const char type = header[0];
114 const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
115 if (result == -1) {
116 child->endSession();
117 } else if (result > 0) {
118 if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
119 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
120 } else {
121 // verify a length of zero
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 }
137
138 logg->logMessage("Exit stop thread");
139 return 0;
140}
141
Jon Medhurst34d97692013-12-19 09:23:06 +0000142static void *countersThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100143 prctl(PR_SET_NAME, (unsigned long)&"gatord-counters", 0, 0, 0);
144
145 gSessionData->hwmon.start();
146
147 int64_t monotonic_started = 0;
148 while (monotonic_started <= 0) {
149 usleep(10);
150
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 }
156
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 }
175
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 }
181
182 if (buffer->bytesAvailable() <= 0) {
183 logg->logMessage("One shot (counters)");
184 child->endSession();
185 }
186
187 usleep((next_time - curr_time)/NS_PER_US);
188 }
189
190 buffer->setDone();
191
192 return NULL;
193}
194
Jon Medhurst34d97692013-12-19 09:23:06 +0000195static void *senderThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100196 int length = 1;
197 char* data;
198 char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
199
200 sem_post(&senderThreadStarted);
201 prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
202 sem_wait(&haltPipeline);
203
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 }
215
216 // write end-of-capture sequence
217 if (!gSessionData->mLocalCapture) {
218 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
219 }
220
221 logg->logMessage("Exit sender thread");
222 return 0;
223}
224
225Child::Child() {
226 initialization();
227 gSessionData->mLocalCapture = true;
228}
229
230Child::Child(OlySocket* sock, int conn) {
231 initialization();
232 socket = sock;
233 mNumConnections = conn;
234}
235
236Child::~Child() {
237}
238
239void 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;
249
250 // Initialize semaphores
251 sem_init(&senderThreadStarted, 0, 0);
252 sem_init(&startProfile, 0, 0);
253 sem_init(&senderSem, 0, 0);
254}
255
256void Child::endSession() {
257 gSessionData->mSessionIsActive = false;
258 collector->stop();
259 sem_post(&haltPipeline);
260}
261
262void Child::run() {
263 char* collectBuffer;
264 int bytesCollected = 0;
265 LocalCapture* localCapture = NULL;
266 pthread_t durationThreadID, stopThreadID, senderThreadID, countersThreadID;
267
268 prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
269
270 // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
271 mxmlSetWrapMargin(0);
272
273 // Instantiate the Sender - must be done first, after which error messages can be sent
274 sender = new Sender(socket);
275
276 if (mNumConnections > 1) {
277 logg->logError(__FILE__, __LINE__, "Session already in progress");
278 handleException();
279 }
280
281 // Populate gSessionData with the configuration
282 { ConfigurationXML configuration; }
283
284 // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
285 collector = new Collector();
286
287 // Initialize all drivers
288 for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
289 driver->resetCounters();
290 }
291
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 }
299
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 }
319
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);
323
324 // Get the initial pointer to the collect buffer
325 collectBuffer = collectorFifo->start();
326
327 // Create a new Block Counter Buffer
328 buffer = new Buffer(0, 5, gSessionData->mTotalBufferSize*1024*1024, &senderSem);
329
330 // Sender thread shall be halted until it is signaled for one shot mode
331 sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
332
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 }
342
Jon Medhurst34d97692013-12-19 09:23:06 +0000343 bool startcountersThread = gSessionData->hwmon.countersEnabled();
344 if (startcountersThread) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100345 if (pthread_create(&countersThreadID, NULL, countersThread, this)) {
346 thread_creation_success = false;
347 }
348 } else {
349 // Let senderThread know there is no buffer data to send
350 buffer->setDone();
351 }
352
353 if (!thread_creation_success) {
354 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
355 handleException();
356 }
357
358 // Wait until thread has started
359 sem_wait(&senderThreadStarted);
360
361 // Start profiling
362 logg->logMessage("********** Profiling started **********");
363 collector->start();
364 sem_post(&startProfile);
365
366 // Collect Data
367 do {
368 // This command will stall until data is received from the driver
369 bytesCollected = collector->collect(collectBuffer);
370
371 // In one shot mode, stop collection once all the buffers are filled
372 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
373 if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
374 logg->logMessage("One shot");
375 endSession();
376 }
377 }
378 collectBuffer = collectorFifo->write(bytesCollected);
379 } while (bytesCollected > 0);
380 logg->logMessage("Exit collect data loop");
381
Jon Medhurst34d97692013-12-19 09:23:06 +0000382 if (startcountersThread) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100383 pthread_join(countersThreadID, NULL);
384 }
385
386 // Wait for the other threads to exit
387 pthread_join(senderThreadID, NULL);
388
389 // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
390 if (socket) {
391 logg->logMessage("Waiting on stop thread");
392 socket->shutdownConnection();
393 pthread_join(stopThreadID, NULL);
394 }
395
396 // Write the captured xml file
397 if (gSessionData->mLocalCapture) {
398 CapturedXML capturedXML;
399 capturedXML.write(gSessionData->mAPCDir);
400 }
401
402 logg->logMessage("Profiling ended.");
403
404 delete buffer;
405 delete collectorFifo;
406 delete sender;
407 delete collector;
408 delete localCapture;
409}