blob: a19e9cf86805661255d47e4c387a523f69c62022 [file] [log] [blame]
Jon Medhurstaaf37a32013-06-11 12:10:56 +01001/**
Jon Medhurstb1d07442015-05-08 12:04:18 +01002 * Copyright (C) ARM Limited 2010-2015. All rights reserved.
Jon Medhurstaaf37a32013-06-11 12:10:56 +01003 *
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
Jon Medhurst15ce78d2014-04-10 09:02:02 +01009#include "Child.h"
10
Jon Medhurstaaf37a32013-06-11 12:10:56 +010011#include <stdlib.h>
12#include <string.h>
13#include <signal.h>
14#include <unistd.h>
15#include <sys/prctl.h>
Jon Medhurst15ce78d2014-04-10 09:02:02 +010016
Jon Medhurstaaf37a32013-06-11 12:10:56 +010017#include "CapturedXML.h"
Jon Medhurst96b56152014-10-30 18:01:15 +000018#include "Command.h"
Jon Medhurstaaf37a32013-06-11 12:10:56 +010019#include "ConfigurationXML.h"
20#include "Driver.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010021#include "DriverSource.h"
Jon Medhurst15ce78d2014-04-10 09:02:02 +010022#include "ExternalSource.h"
Jon Medhurst96b56152014-10-30 18:01:15 +000023#include "FtraceSource.h"
24#include "LocalCapture.h"
25#include "Logging.h"
26#include "OlySocket.h"
27#include "OlyUtility.h"
28#include "PerfSource.h"
29#include "Sender.h"
30#include "SessionData.h"
31#include "StreamlineSetup.h"
Jon Medhurste31266f2014-08-04 15:47:44 +010032#include "UserSpaceSource.h"
Jon Medhurstaaf37a32013-06-11 12:10:56 +010033
34static sem_t haltPipeline, senderThreadStarted, startProfile, senderSem; // Shared by Child and spawned threads
Jon Medhurst15ce78d2014-04-10 09:02:02 +010035static Source *primarySource = NULL;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010036static Source *externalSource = NULL;
Jon Medhurste31266f2014-08-04 15:47:44 +010037static Source *userSpaceSource = NULL;
Jon Medhurst96b56152014-10-30 18:01:15 +000038static Source *ftraceSource = NULL;
Jon Medhurstaaf37a32013-06-11 12:10:56 +010039static Sender* sender = NULL; // Shared by Child.cpp and spawned threads
Jon Medhurstaaf37a32013-06-11 12:10:56 +010040Child* child = NULL; // shared by Child.cpp and main.cpp
41
42extern void cleanUp();
43void handleException() {
44 if (child && child->numExceptions++ > 0) {
45 // it is possible one of the below functions itself can cause an exception, thus allow only one exception
46 logg->logMessage("Received multiple exceptions, terminating the child");
47 exit(1);
48 }
49 fprintf(stderr, "%s", logg->getLastError());
50
51 if (child && child->socket) {
52 if (sender) {
53 // send the error, regardless of the command sent by Streamline
54 sender->writeData(logg->getLastError(), strlen(logg->getLastError()), RESPONSE_ERROR);
55
56 // cannot close the socket before Streamline issues the command, so wait for the command before exiting
57 if (gSessionData->mWaitingOnCommand) {
58 char discard;
59 child->socket->receiveNBytes(&discard, 1);
60 }
61
Jon Medhurstd3698592013-10-10 16:48:56 +010062 // Ensure all data is flushed
63 child->socket->shutdownConnection();
64
Jon Medhurstaaf37a32013-06-11 12:10:56 +010065 // this indirectly calls close socket which will ensure the data has been sent
66 delete sender;
67 }
68 }
69
70 if (gSessionData->mLocalCapture)
71 cleanUp();
72
73 exit(1);
74}
75
76// CTRL C Signal Handler for child process
77static void child_handler(int signum) {
78 static bool beenHere = false;
79 if (beenHere == true) {
80 logg->logMessage("Gator is being forced to shut down.");
81 exit(1);
82 }
83 beenHere = true;
84 logg->logMessage("Gator is shutting down.");
Jon Medhurst15ce78d2014-04-10 09:02:02 +010085 if (signum == SIGALRM || !primarySource) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +010086 exit(1);
87 } else {
88 child->endSession();
89 alarm(5); // Safety net in case endSession does not complete within 5 seconds
90 }
91}
92
Jon Medhurst34d97692013-12-19 09:23:06 +000093static void *durationThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +010094 prctl(PR_SET_NAME, (unsigned long)&"gatord-duration", 0, 0, 0);
95 sem_wait(&startProfile);
96 if (gSessionData->mSessionIsActive) {
97 // Time out after duration seconds
98 // Add a second for host-side filtering
99 sleep(gSessionData->mDuration + 1);
100 if (gSessionData->mSessionIsActive) {
101 logg->logMessage("Duration expired.");
102 child->endSession();
103 }
104 }
105 logg->logMessage("Exit duration thread");
106 return 0;
107}
108
Jon Medhurst34d97692013-12-19 09:23:06 +0000109static void *stopThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100110 OlySocket* socket = child->socket;
111
112 prctl(PR_SET_NAME, (unsigned long)&"gatord-stopper", 0, 0, 0);
113 while (gSessionData->mSessionIsActive) {
114 // This thread will stall until the APC_STOP or PING command is received over the socket or the socket is disconnected
115 unsigned char header[5];
116 const int result = socket->receiveNBytes((char*)&header, sizeof(header));
117 const char type = header[0];
118 const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24);
119 if (result == -1) {
120 child->endSession();
121 } else if (result > 0) {
122 if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
123 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
124 } else {
125 // verify a length of zero
126 if (length == 0) {
127 if (type == COMMAND_APC_STOP) {
128 logg->logMessage("Stop command received.");
129 child->endSession();
130 } else {
131 // Ping is used to make sure gator is alive and requires an ACK as the response
132 logg->logMessage("Ping command received.");
133 sender->writeData(NULL, 0, RESPONSE_ACK);
134 }
135 } else {
136 logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
137 }
138 }
139 }
140 }
141
142 logg->logMessage("Exit stop thread");
143 return 0;
144}
145
Jon Medhurst34d97692013-12-19 09:23:06 +0000146static void *senderThread(void *) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100147 char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
148
149 sem_post(&senderThreadStarted);
150 prctl(PR_SET_NAME, (unsigned long)&"gatord-sender", 0, 0, 0);
151 sem_wait(&haltPipeline);
152
Jon Medhurstb1d07442015-05-08 12:04:18 +0100153 while (!externalSource->isDone() ||
Jon Medhurst96b56152014-10-30 18:01:15 +0000154 (userSpaceSource != NULL && !userSpaceSource->isDone()) ||
Jon Medhurstb1d07442015-05-08 12:04:18 +0100155 (ftraceSource != NULL && !ftraceSource->isDone()) ||
156 !primarySource->isDone()) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100157 sem_wait(&senderSem);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100158
Jon Medhurste31266f2014-08-04 15:47:44 +0100159 externalSource->write(sender);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100160 if (userSpaceSource != NULL) {
161 userSpaceSource->write(sender);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100162 }
Jon Medhurst96b56152014-10-30 18:01:15 +0000163 if (ftraceSource != NULL) {
164 ftraceSource->write(sender);
165 }
Jon Medhurstb1d07442015-05-08 12:04:18 +0100166 primarySource->write(sender);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100167 }
168
169 // write end-of-capture sequence
170 if (!gSessionData->mLocalCapture) {
171 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
172 }
173
174 logg->logMessage("Exit sender thread");
175 return 0;
176}
177
178Child::Child() {
179 initialization();
180 gSessionData->mLocalCapture = true;
181}
182
183Child::Child(OlySocket* sock, int conn) {
184 initialization();
185 socket = sock;
186 mNumConnections = conn;
187}
188
189Child::~Child() {
190}
191
192void Child::initialization() {
193 // Set up different handlers for signals
194 gSessionData->mSessionIsActive = true;
195 signal(SIGINT, child_handler);
196 signal(SIGTERM, child_handler);
197 signal(SIGABRT, child_handler);
198 signal(SIGALRM, child_handler);
199 socket = NULL;
200 numExceptions = 0;
201 mNumConnections = 0;
202
203 // Initialize semaphores
204 sem_init(&senderThreadStarted, 0, 0);
205 sem_init(&startProfile, 0, 0);
206 sem_init(&senderSem, 0, 0);
207}
208
209void Child::endSession() {
210 gSessionData->mSessionIsActive = false;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100211 primarySource->interrupt();
Jon Medhurste31266f2014-08-04 15:47:44 +0100212 externalSource->interrupt();
213 if (userSpaceSource != NULL) {
214 userSpaceSource->interrupt();
215 }
Jon Medhurst96b56152014-10-30 18:01:15 +0000216 if (ftraceSource != NULL) {
217 ftraceSource->interrupt();
218 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100219 sem_post(&haltPipeline);
220}
221
222void Child::run() {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100223 LocalCapture* localCapture = NULL;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100224 pthread_t durationThreadID, stopThreadID, senderThreadID;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100225
226 prctl(PR_SET_NAME, (unsigned long)&"gatord-child", 0, 0, 0);
227
228 // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
229 mxmlSetWrapMargin(0);
230
231 // Instantiate the Sender - must be done first, after which error messages can be sent
232 sender = new Sender(socket);
233
234 if (mNumConnections > 1) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100235 logg->logError("Session already in progress");
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100236 handleException();
237 }
238
239 // Populate gSessionData with the configuration
240 { ConfigurationXML configuration; }
241
242 // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100243 if (!gSessionData->perf.isSetup()) {
Jon Medhurste31266f2014-08-04 15:47:44 +0100244 primarySource = new DriverSource(&senderSem, &startProfile);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100245 } else {
Jon Medhurste31266f2014-08-04 15:47:44 +0100246 primarySource = new PerfSource(&senderSem, &startProfile);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100247 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100248
249 // Initialize all drivers
250 for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
251 driver->resetCounters();
252 }
253
254 // Set up counters using the associated driver's setup function
255 for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) {
256 Counter & counter = gSessionData->mCounters[i];
257 if (counter.isEnabled()) {
258 counter.getDriver()->setupCounter(counter);
259 }
260 }
261
262 // Start up and parse session xml
263 if (socket) {
264 // Respond to Streamline requests
265 StreamlineSetup ss(socket);
266 } else {
267 char* xmlString;
268 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
269 if (xmlString == 0) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100270 logg->logError("Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100271 handleException();
272 }
273 gSessionData->parseSessionXML(xmlString);
274 localCapture = new LocalCapture();
275 localCapture->createAPCDirectory(gSessionData->mTargetPath);
276 localCapture->copyImages(gSessionData->mImages);
277 localCapture->write(xmlString);
278 sender->createDataFile(gSessionData->mAPCDir);
279 free(xmlString);
280 }
281
Jon Medhurst96b56152014-10-30 18:01:15 +0000282 if (gSessionData->kmod.isMaliCapture() && (gSessionData->mSampleRate == 0)) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100283 logg->logError("Mali counters are not supported with Sample Rate: None.");
Jon Medhurst96b56152014-10-30 18:01:15 +0000284 handleException();
285 }
286
Jon Medhurstb1d07442015-05-08 12:04:18 +0100287 // Initialize ftrace source before child as it's slow and dependens on nothing else
288 // If initialized later, us gator with ftrace has time sync issues
289 if (gSessionData->ftraceDriver.countersEnabled()) {
290 ftraceSource = new FtraceSource(&senderSem);
291 if (!ftraceSource->prepare()) {
292 logg->logError("Unable to prepare userspace source for capture");
293 handleException();
294 }
295 ftraceSource->start();
296 }
297
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100298 // Must be after session XML is parsed
299 if (!primarySource->prepare()) {
Jon Medhurst96b56152014-10-30 18:01:15 +0000300 if (gSessionData->perf.isSetup()) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100301 logg->logError("Unable to communicate with the perf API, please ensure that CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER are enabled. Please refer to README_Streamline.txt for more information.");
Jon Medhurst96b56152014-10-30 18:01:15 +0000302 } else {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100303 logg->logError("Unable to prepare gator driver for capture");
Jon Medhurst96b56152014-10-30 18:01:15 +0000304 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100305 handleException();
306 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100307
308 // Sender thread shall be halted until it is signaled for one shot mode
309 sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
310
Jon Medhurst96b56152014-10-30 18:01:15 +0000311 // Must be initialized before senderThread is started as senderThread checks externalSource
312 externalSource = new ExternalSource(&senderSem);
313 if (!externalSource->prepare()) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100314 logg->logError("Unable to prepare external source for capture");
Jon Medhurst96b56152014-10-30 18:01:15 +0000315 handleException();
316 }
317 externalSource->start();
318
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100319 // Create the duration, stop, and sender threads
320 bool thread_creation_success = true;
321 if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
322 thread_creation_success = false;
323 } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
324 thread_creation_success = false;
Jon Medhurste31266f2014-08-04 15:47:44 +0100325 } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)) {
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100326 thread_creation_success = false;
327 }
328
Jon Medhurst96b56152014-10-30 18:01:15 +0000329 bool startUSSource = false;
330 for (int i = 0; i < ARRAY_LENGTH(gSessionData->usDrivers); ++i) {
331 if (gSessionData->usDrivers[i]->countersEnabled()) {
332 startUSSource = true;
333 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100334 }
Jon Medhurst96b56152014-10-30 18:01:15 +0000335 if (startUSSource) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100336 userSpaceSource = new UserSpaceSource(&senderSem);
337 if (!userSpaceSource->prepare()) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100338 logg->logError("Unable to prepare userspace source for capture");
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100339 handleException();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100340 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100341 userSpaceSource->start();
342 }
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100343
Jon Medhurst96b56152014-10-30 18:01:15 +0000344 if (gSessionData->mAllowCommands && (gSessionData->mCaptureCommand != NULL)) {
345 pthread_t thread;
346 if (pthread_create(&thread, NULL, commandThread, NULL)) {
347 thread_creation_success = false;
348 }
349 }
350
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100351 if (!thread_creation_success) {
Jon Medhurstb1d07442015-05-08 12:04:18 +0100352 logg->logError("Failed to create gator threads");
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100353 handleException();
354 }
355
356 // Wait until thread has started
357 sem_wait(&senderThreadStarted);
358
359 // Start profiling
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100360 primarySource->run();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100361
Jon Medhurstb1d07442015-05-08 12:04:18 +0100362 // Wait for the other threads to exit
Jon Medhurst96b56152014-10-30 18:01:15 +0000363 if (ftraceSource != NULL) {
364 ftraceSource->join();
365 }
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100366 if (userSpaceSource != NULL) {
367 userSpaceSource->join();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100368 }
Jon Medhurste31266f2014-08-04 15:47:44 +0100369 externalSource->join();
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100370 pthread_join(senderThreadID, NULL);
371
372 // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
373 if (socket) {
374 logg->logMessage("Waiting on stop thread");
375 socket->shutdownConnection();
376 pthread_join(stopThreadID, NULL);
377 }
378
379 // Write the captured xml file
380 if (gSessionData->mLocalCapture) {
381 CapturedXML capturedXML;
382 capturedXML.write(gSessionData->mAPCDir);
383 }
384
385 logg->logMessage("Profiling ended.");
386
Jon Medhurst96b56152014-10-30 18:01:15 +0000387 delete ftraceSource;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100388 delete userSpaceSource;
Jon Medhurste31266f2014-08-04 15:47:44 +0100389 delete externalSource;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100390 delete primarySource;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100391 delete sender;
Jon Medhurstaaf37a32013-06-11 12:10:56 +0100392 delete localCapture;
393}