2 * Copyright (C) ARM Limited 2010-2015. All rights reserved.
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.
9 #include "ExternalSource.h"
12 #include <sys/prctl.h>
17 #include "OlySocket.h"
18 #include "SessionData.h"
22 static const char STREAMLINE_ANNOTATE[] = "\0streamline-annotate";
23 static const char MALI_VIDEO[] = "\0mali-video";
24 static const char MALI_VIDEO_STARTUP[] = "\0mali-video-startup";
25 static const char MALI_VIDEO_V1[] = "MALI_VIDEO 1\n";
26 static const char MALI_GRAPHICS[] = "\0mali_thirdparty_server";
27 static const char MALI_GRAPHICS_STARTUP[] = "\0mali_thirdparty_client";
28 static const char MALI_GRAPHICS_V1[] = "MALI_GRAPHICS 1\n";
30 static bool setNonblock(const int fd) {
33 flags = fcntl(fd, F_GETFL);
35 logg->logMessage("fcntl getfl failed");
39 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
40 logg->logMessage("fcntl setfl failed");
47 ExternalSource::ExternalSource(sem_t *senderSem) : mBuffer(0, FRAME_EXTERNAL, 128*1024, senderSem), mMonitor(), mMveStartupUds(MALI_VIDEO_STARTUP, sizeof(MALI_VIDEO_STARTUP)), mMaliStartupUds(MALI_GRAPHICS_STARTUP, sizeof(MALI_GRAPHICS_STARTUP)), mAnnotate(8083), mAnnotateUds(STREAMLINE_ANNOTATE, sizeof(STREAMLINE_ANNOTATE), true), mInterruptFd(-1), mMaliUds(-1), mMveUds(-1) {
48 sem_init(&mBufferSem, 0, 0);
51 ExternalSource::~ExternalSource() {
54 void ExternalSource::waitFor(const int bytes) {
55 while (mBuffer.bytesAvailable() <= bytes) {
56 if (gSessionData->mOneShot && gSessionData->mSessionIsActive) {
57 logg->logMessage("One shot (external)");
60 sem_wait(&mBufferSem);
64 void ExternalSource::configureConnection(const int fd, const char *const handshake, size_t size) {
65 if (!setNonblock(fd)) {
66 logg->logError("Unable to set nonblock on fh");
70 if (!mMonitor.add(fd)) {
71 logg->logError("Unable to add fh to monitor");
75 // Write the handshake to the circular buffer
76 waitFor(Buffer::MAXSIZE_PACK32 + size - 1);
78 mBuffer.writeBytes(handshake, size - 1);
79 mBuffer.commit(1, true);
82 bool ExternalSource::connectMali() {
83 mMaliUds = OlySocket::connect(MALI_GRAPHICS, sizeof(MALI_GRAPHICS));
88 configureConnection(mMaliUds, MALI_GRAPHICS_V1, sizeof(MALI_GRAPHICS_V1));
93 bool ExternalSource::connectMve() {
94 if (!gSessionData->maliVideo.countersEnabled()) {
98 mMveUds = OlySocket::connect(MALI_VIDEO, sizeof(MALI_VIDEO));
103 if (!gSessionData->maliVideo.start(mMveUds)) {
107 configureConnection(mMveUds, MALI_VIDEO_V1, sizeof(MALI_VIDEO_V1));
112 bool ExternalSource::prepare() {
113 if (!mMonitor.init() ||
114 !setNonblock(mMveStartupUds.getFd()) || !mMonitor.add(mMveStartupUds.getFd()) ||
115 !setNonblock(mMaliStartupUds.getFd()) || !mMonitor.add(mMaliStartupUds.getFd()) ||
116 !setNonblock(mAnnotate.getFd()) || !mMonitor.add(mAnnotate.getFd()) ||
117 !setNonblock(mAnnotateUds.getFd()) || !mMonitor.add(mAnnotateUds.getFd()) ||
128 void ExternalSource::run() {
131 prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0);
133 if (pipe_cloexec(pipefd) != 0) {
134 logg->logError("pipe failed");
137 mInterruptFd = pipefd[1];
139 if (!mMonitor.add(pipefd[0])) {
140 logg->logError("Monitor::add failed");
144 // Notify annotate clients to retry connecting to gatord
146 if (::write(gSessionData->mAnnotateStart, &val, sizeof(val)) != sizeof(val)) {
147 logg->logMessage("Writing to annotate pipe failed");
150 while (gSessionData->mSessionIsActive) {
151 struct epoll_event events[16];
152 // Clear any pending sem posts
153 while (sem_trywait(&mBufferSem) == 0);
154 int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1);
156 logg->logError("Monitor::wait failed");
160 const uint64_t currTime = getTime() - gSessionData->mMonotonicStarted;
162 for (int i = 0; i < ready; ++i) {
163 const int fd = events[i].data.fd;
164 if (fd == mMveStartupUds.getFd()) {
165 // Mali Video Engine says it's alive
166 int client = mMveStartupUds.acceptConnection();
167 // Don't read from this connection, establish a new connection to Mali-V500
170 logg->logError("Unable to configure incoming Mali video connection");
173 } else if (fd == mMaliStartupUds.getFd()) {
174 // Mali Graphics says it's alive
175 int client = mMaliStartupUds.acceptConnection();
176 // Don't read from this connection, establish a new connection to Mali Graphics
178 if (!connectMali()) {
179 logg->logError("Unable to configure incoming Mali graphics connection");
182 } else if (fd == mAnnotate.getFd()) {
183 int client = mAnnotate.acceptConnection();
184 if (!setNonblock(client) || !mMonitor.add(client)) {
185 logg->logError("Unable to set socket options on incoming annotation connection");
188 } else if (fd == mAnnotateUds.getFd()) {
189 int client = mAnnotateUds.acceptConnection();
190 if (!setNonblock(client) || !mMonitor.add(client)) {
191 logg->logError("Unable to set socket options on incoming annotation connection");
194 } else if (fd == pipefd[0]) {
195 // Means interrupt has been called and mSessionIsActive should be reread
197 /* This can result in some starvation if there are multiple
198 * threads which are annotating heavily, but it is not
199 * recommended that threads annotate that much as it can also
200 * starve out the gator data.
202 while (gSessionData->mSessionIsActive) {
203 // Wait until there is enough room for the fd, two headers and two ints
204 waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t));
206 const int contiguous = mBuffer.contiguousSpaceAvailable();
207 const int bytes = read(fd, mBuffer.getWritePos(), contiguous);
209 if (errno == EAGAIN) {
210 // Nothing left to read
211 mBuffer.commit(currTime, true);
214 // Something else failed, close the socket
215 mBuffer.commit(currTime, true);
218 // Here and other commits, always force-flush the buffer as this frame don't work like others
219 mBuffer.commit(currTime, true);
222 } else if (bytes == 0) {
223 // The other side is closed
224 mBuffer.commit(currTime, true);
227 mBuffer.commit(currTime, true);
232 mBuffer.advanceWrite(bytes);
233 mBuffer.commit(currTime, true);
235 // Short reads also mean nothing is left to read
236 if (bytes < contiguous) {
247 gSessionData->maliVideo.stop(mMveUds);
255 void ExternalSource::interrupt() {
256 if (mInterruptFd >= 0) {
258 // Write to the pipe to wake the monitor which will cause mSessionIsActive to be reread
259 if (::write(mInterruptFd, &c, sizeof(c)) != sizeof(c)) {
260 logg->logError("write failed");
266 bool ExternalSource::isDone() {
267 return mBuffer.isDone();
270 void ExternalSource::write(Sender *sender) {
271 // Don't send external data until the summary packet is sent so that monotonic delta is available
272 if (!gSessionData->mSentSummary) {
275 if (!mBuffer.isDone()) {
276 mBuffer.write(sender);
277 sem_post(&mBufferSem);