2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * Thread that reads from stdout/stderr and converts them to log messages.
27 #define kFilenoStdout 1
28 #define kFilenoStderr 2
41 static void* stdioConverterThreadStart(void* arg);
42 static bool readAndLog(int fd, BufferedData* data, const char* tag);
46 * Crank up the stdout/stderr converter thread.
48 * Returns immediately.
50 bool dvmStdioConverterStartup()
52 gDvm.haltStdioConverter = false;
54 dvmInitMutex(&gDvm.stdioConverterLock);
55 pthread_cond_init(&gDvm.stdioConverterCond, NULL);
57 if (pipe(gDvm.stdoutPipe) != 0) {
58 ALOGW("pipe failed: %s", strerror(errno));
61 if (pipe(gDvm.stderrPipe) != 0) {
62 ALOGW("pipe failed: %s", strerror(errno));
66 if (dup2(gDvm.stdoutPipe[1], kFilenoStdout) != kFilenoStdout) {
67 ALOGW("dup2(1) failed: %s", strerror(errno));
70 close(gDvm.stdoutPipe[1]);
71 gDvm.stdoutPipe[1] = -1;
72 #ifdef HAVE_ANDROID_OS
73 /* don't redirect stderr on sim -- logs get written there! */
74 /* (don't need this on the sim anyway) */
75 if (dup2(gDvm.stderrPipe[1], kFilenoStderr) != kFilenoStderr) {
76 ALOGW("dup2(2) failed: %d %s", errno, strerror(errno));
79 close(gDvm.stderrPipe[1]);
80 gDvm.stderrPipe[1] = -1;
87 dvmLockMutex(&gDvm.stdioConverterLock);
89 if (!dvmCreateInternalThread(&gDvm.stdioConverterHandle,
91 stdioConverterThreadStart,
96 while (!gDvm.stdioConverterReady) {
97 dvmWaitCond(&gDvm.stdioConverterCond, &gDvm.stdioConverterLock);
99 dvmUnlockMutex(&gDvm.stdioConverterLock);
105 * Shut down the stdio converter thread if it was started.
107 * Since we know the thread is just sitting around waiting for something
108 * to arrive on stdout, print something.
110 void dvmStdioConverterShutdown()
112 gDvm.haltStdioConverter = true;
113 if (gDvm.stdioConverterHandle == 0) // not started, or still starting
116 /* print something to wake it up */
117 printf("Shutting down\n");
120 ALOGD("Joining stdio converter...");
121 pthread_join(gDvm.stdioConverterHandle, NULL);
125 * Select on stdout/stderr pipes, waiting for activity.
127 * DO NOT use printf from here.
129 static void* stdioConverterThreadStart(void* arg)
133 /* tell the main thread that we're ready */
134 dvmLockMutex(&gDvm.stdioConverterLock);
135 gDvm.stdioConverterReady = true;
136 cc = pthread_cond_signal(&gDvm.stdioConverterCond);
138 dvmUnlockMutex(&gDvm.stdioConverterLock);
140 /* we never do anything that affects the rest of the VM */
141 dvmChangeStatus(NULL, THREAD_VMWAIT);
144 * Allocate read buffers.
146 BufferedData* stdoutData = new BufferedData;
147 BufferedData* stderrData = new BufferedData;
148 stdoutData->count = stderrData->count = 0;
151 * Read until shutdown time.
153 while (!gDvm.haltStdioConverter) {
158 FD_SET(gDvm.stdoutPipe[0], &readfds);
159 FD_SET(gDvm.stderrPipe[0], &readfds);
160 maxFd = MAX(gDvm.stdoutPipe[0], gDvm.stderrPipe[0]);
162 fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);
165 if (errno != EINTR) {
166 ALOGE("select on stdout/stderr failed");
169 ALOGD("Got EINTR, ignoring");
170 } else if (fdCount == 0) {
171 ALOGD("WEIRD: select returned zero");
174 if (FD_ISSET(gDvm.stdoutPipe[0], &readfds)) {
175 err |= !readAndLog(gDvm.stdoutPipe[0], stdoutData,
178 if (FD_ISSET(gDvm.stderrPipe[0], &readfds)) {
179 err |= !readAndLog(gDvm.stderrPipe[0], stderrData,
183 /* probably EOF; give up */
185 ALOGW("stdio converter got read error; shutting it down");
191 close(gDvm.stdoutPipe[0]);
192 close(gDvm.stderrPipe[0]);
197 /* change back for shutdown sequence */
198 dvmChangeStatus(NULL, THREAD_RUNNING);
203 * Data is pending on "fd". Read as much as will fit in "data", then
204 * write out any full lines and compact "data".
206 static bool readAndLog(int fd, BufferedData* data, const char* tag)
211 assert(data->count < kMaxLine);
213 want = kMaxLine - data->count;
214 actual = read(fd, data->buf + data->count, want);
216 ALOGW("read %s: (%d,%d) failed (%d): %s",
217 tag, fd, want, (int)actual, strerror(errno));
220 //ALOGI("read %s: %d at %d", tag, actual, data->count);
222 data->count += actual;
225 * Got more data, look for an EOL. We expect LF or CRLF, but will
226 * try to handle a standalone CR.
228 char* cp = data->buf;
229 const char* start = data->buf;
231 for (i = data->count; i > 0; i--, cp++) {
232 if (*cp == '\n' || (*cp == '\r' && i != 0 && *(cp+1) != '\n')) {
234 //ALOGW("GOT %d at %d '%s'", cp - start, start - data->buf, start);
235 ALOG(LOG_INFO, tag, "%s", start);
241 * See if we overflowed. If so, cut it off.
243 if (start == data->buf && data->count == kMaxLine) {
244 data->buf[kMaxLine] = '\0';
245 ALOG(LOG_INFO, tag, "%s!", start);
246 start = cp + kMaxLine;
250 * Update "data" if we consumed some output. If there's anything left
251 * in the buffer, it's because we didn't see an EOL and need to keep
252 * reading until we see one.
254 if (start != data->buf) {
255 if (start >= data->buf + data->count) {
256 /* consumed all available */
260 int remaining = data->count - (start - data->buf);
261 memmove(data->buf, start, remaining);
262 data->count = remaining;