bcc868a553c612defc2980058f77130d73f8678d
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 int)&"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 int)&"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 if (socket->receiveNBytes(&type, sizeof(type)) > 0) {
106 if ((type != COMMAND_APC_STOP) && (type != COMMAND_PING)) {
107 logg->logMessage("INVESTIGATE: Received unknown command type %d", type);
108 } else {
109 // verify a length of zero
110 if (socket->receiveNBytes((char*)&length, sizeof(length)) < 0) {
111 break;
112 }
114 if (length == 0) {
115 if (type == COMMAND_APC_STOP) {
116 logg->logMessage("Stop command received.");
117 child->endSession();
118 } else {
119 // Ping is used to make sure gator is alive and requires an ACK as the response
120 logg->logMessage("Ping command received.");
121 sender->writeData(NULL, 0, RESPONSE_ACK);
122 }
123 } else {
124 logg->logMessage("INVESTIGATE: Received stop command but with length = %d", length);
125 }
126 }
127 }
128 }
130 logg->logMessage("Exit stop thread");
131 return 0;
132 }
134 void* senderThread(void* pVoid) {
135 int length;
136 char* data;
137 char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0};
139 sem_post(&senderThreadStarted);
140 prctl(PR_SET_NAME, (unsigned int)&"gatord-sender", 0, 0, 0);
141 sem_wait(&haltPipeline);
143 do {
144 data = collectorFifo->read(&length);
145 sender->writeData(data, length, RESPONSE_APC_DATA);
146 } while (length > 0);
148 // write end-of-capture sequence
149 if (!gSessionData->mLocalCapture) {
150 sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA);
151 }
153 logg->logMessage("Exit sender thread");
154 return 0;
155 }
157 Child::Child() {
158 initialization();
159 gSessionData->mLocalCapture = true;
160 }
162 Child::Child(OlySocket* sock, int conn) {
163 initialization();
164 socket = sock;
165 mNumConnections = conn;
166 }
168 Child::~Child() {
169 }
171 void Child::initialization() {
172 // Set up different handlers for signals
173 gSessionData->mSessionIsActive = true;
174 signal(SIGINT, child_handler);
175 signal(SIGTERM, child_handler);
176 signal(SIGABRT, child_handler);
177 signal(SIGALRM, child_handler);
178 socket = NULL;
179 numExceptions = 0;
180 mNumConnections = 0;
182 // Initialize semaphores
183 sem_init(&senderThreadStarted, 0, 0);
184 sem_init(&startProfile, 0, 0);
185 }
187 void Child::endSession() {
188 gSessionData->mSessionIsActive = false;
189 collector->stop();
190 sem_post(&haltPipeline);
191 }
193 void Child::run() {
194 char* collectBuffer;
195 int bytesCollected = 0;
196 LocalCapture* localCapture = NULL;
197 pthread_t durationThreadID, stopThreadID, senderThreadID;
199 prctl(PR_SET_NAME, (unsigned int)&"gatord-child", 0, 0, 0);
201 // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually
202 mxmlSetWrapMargin(0);
204 // Instantiate the Sender - must be done first, after which error messages can be sent
205 sender = new Sender(socket);
207 if (mNumConnections > 1) {
208 logg->logError(__FILE__, __LINE__, "Session already in progress");
209 handleException();
210 }
212 // Populate gSessionData with the configuration
213 new ConfigurationXML();
215 // Set up the driver; must be done after gSessionData->mPerfCounterType[] is populated
216 collector = new Collector();
218 // Start up and parse session xml
219 if (socket) {
220 // Respond to Streamline requests
221 StreamlineSetup ss(socket);
222 } else {
223 char* xmlString;
224 xmlString = util->readFromDisk(gSessionData->mSessionXMLPath);
225 if (xmlString == 0) {
226 logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath);
227 handleException();
228 }
229 gSessionData->parseSessionXML(xmlString);
230 localCapture = new LocalCapture();
231 localCapture->createAPCDirectory(gSessionData->mTargetPath, gSessionData->mTitle);
232 localCapture->copyImages(gSessionData->mImages);
233 localCapture->write(xmlString);
234 sender->createDataFile(gSessionData->mAPCDir);
235 free(xmlString);
236 }
238 // Write configuration into the driver
239 collector->setupPerfCounters();
241 // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length
242 logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize());
243 collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize* 1024 * 1024);
245 // Get the initial pointer to the collect buffer
246 collectBuffer = collectorFifo->start();
248 // Sender thread shall be halted until it is signaled for one shot mode
249 sem_init(&haltPipeline, 0, gSessionData->mOneShot ? 0 : 2);
251 // Create the duration, stop, and sender threads
252 bool thread_creation_success = true;
253 if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) {
254 thread_creation_success = false;
255 } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) {
256 thread_creation_success = false;
257 } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){
258 thread_creation_success = false;
259 }
261 if (!thread_creation_success) {
262 logg->logError(__FILE__, __LINE__, "Failed to create gator threads");
263 handleException();
264 }
266 // Wait until thread has started
267 sem_wait(&senderThreadStarted);
269 // Start profiling
270 logg->logMessage("********** Profiling started **********");
271 collector->start();
272 sem_post(&startProfile);
274 // Collect Data
275 do {
276 // This command will stall until data is received from the driver
277 bytesCollected = collector->collect(collectBuffer);
279 // In one shot mode, stop collection once all the buffers are filled
280 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
281 if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) {
282 logg->logMessage("One shot");
283 endSession();
284 }
285 }
286 collectBuffer = collectorFifo->write(bytesCollected);
287 } while (bytesCollected > 0);
288 logg->logMessage("Exit collect data loop");
290 // Wait for the other threads to exit
291 pthread_join(senderThreadID, NULL);
293 // Shutting down the connection should break the stop thread which is stalling on the socket recv() function
294 if (socket) {
295 logg->logMessage("Waiting on stop thread");
296 socket->shutdownConnection();
297 pthread_join(stopThreadID, NULL);
298 }
300 // Write the captured xml file
301 if (gSessionData->mLocalCapture) {
302 CapturedXML capturedXML;
303 capturedXML.write(gSessionData->mAPCDir);
304 }
306 logg->logMessage("Profiling ended.");
308 delete collectorFifo;
309 delete sender;
310 delete collector;
311 delete localCapture;
312 }