OSDN Git Service

Merge change 24751 into eclair
[android-x86/dalvik.git] / vm / StdioConverter.c
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /*
17  * Thread that reads from stdout/stderr and converts them to log messages.
18  * (Sort of a hack.)
19  */
20 #include "Dalvik.h"
21
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26
27 #define kFilenoStdout   1
28 #define kFilenoStderr   2
29
30 /*
31  * Hold our replacement stdout/stderr.
32  */
33 typedef struct StdPipes {
34     int stdoutPipe[2];
35     int stderrPipe[2];
36 } StdPipes;
37
38 #define kMaxLine    512
39
40 /*
41  * Hold some data.
42  */
43 typedef struct BufferedData {
44     char    buf[kMaxLine+1];
45     int     count;
46 } BufferedData;
47
48 // fwd
49 static void* stdioConverterThreadStart(void* arg);
50 static bool readAndLog(int fd, BufferedData* data, const char* tag);
51
52
53 /*
54  * Crank up the stdout/stderr converter thread.
55  *
56  * Returns immediately.
57  */
58 bool dvmStdioConverterStartup(void)
59 {
60     StdPipes* pipeStorage;
61
62     gDvm.haltStdioConverter = false;
63
64     dvmInitMutex(&gDvm.stdioConverterLock);
65     pthread_cond_init(&gDvm.stdioConverterCond, NULL);
66
67     pipeStorage = (StdPipes*) malloc(sizeof(StdPipes));
68     if (pipeStorage == NULL)
69         return false;
70
71     if (pipe(pipeStorage->stdoutPipe) != 0) {
72         LOGW("pipe failed: %s\n", strerror(errno));
73         return false;
74     }
75     if (pipe(pipeStorage->stderrPipe) != 0) {
76         LOGW("pipe failed: %s\n", strerror(errno));
77         return false;
78     }
79
80     if (dup2(pipeStorage->stdoutPipe[1], kFilenoStdout) != kFilenoStdout) {
81         LOGW("dup2(1) failed: %s\n", strerror(errno));
82         return false;
83     }
84     close(pipeStorage->stdoutPipe[1]);
85     pipeStorage->stdoutPipe[1] = -1;
86 #ifdef HAVE_ANDROID_OS
87     /* don't redirect stderr on sim -- logs get written there! */
88     /* (don't need this on the sim anyway) */
89     if (dup2(pipeStorage->stderrPipe[1], kFilenoStderr) != kFilenoStderr) {
90         LOGW("dup2(2) failed: %d %s\n", errno, strerror(errno));
91         return false;
92     }
93     close(pipeStorage->stderrPipe[1]);
94     pipeStorage->stderrPipe[1] = -1;
95 #endif
96
97
98     /*
99      * Create the thread.
100      */
101     dvmLockMutex(&gDvm.stdioConverterLock);
102
103     if (!dvmCreateInternalThread(&gDvm.stdioConverterHandle,
104                 "Stdio Converter", stdioConverterThreadStart, pipeStorage))
105     {
106         free(pipeStorage);
107         return false;
108     }
109     /* new thread owns pipeStorage */
110
111     while (!gDvm.stdioConverterReady) {
112         int cc = pthread_cond_wait(&gDvm.stdioConverterCond,
113                     &gDvm.stdioConverterLock);
114         assert(cc == 0);
115     }
116     dvmUnlockMutex(&gDvm.stdioConverterLock);
117
118     return true;
119 }
120
121 /*
122  * Shut down the stdio converter thread if it was started.
123  *
124  * Since we know the thread is just sitting around waiting for something
125  * to arrive on stdout, print something.
126  */
127 void dvmStdioConverterShutdown(void)
128 {
129     gDvm.haltStdioConverter = true;
130     if (gDvm.stdioConverterHandle == 0)    // not started, or still starting
131         return;
132
133     /* print something to wake it up */
134     printf("Shutting down\n");
135     fflush(stdout);
136
137     LOGD("Joining stdio converter...\n");
138     pthread_join(gDvm.stdioConverterHandle, NULL);
139 }
140
141 /*
142  * Select on stdout/stderr pipes, waiting for activity.
143  *
144  * DO NOT use printf from here.
145  */
146 static void* stdioConverterThreadStart(void* arg)
147 {
148 #define MAX(a,b) ((a) > (b) ? (a) : (b))
149     StdPipes* pipeStorage = (StdPipes*) arg;
150     BufferedData* stdoutData;
151     BufferedData* stderrData;
152     int cc;
153
154     /* tell the main thread that we're ready */
155     dvmLockMutex(&gDvm.stdioConverterLock);
156     gDvm.stdioConverterReady = true;
157     cc = pthread_cond_signal(&gDvm.stdioConverterCond);
158     assert(cc == 0);
159     dvmUnlockMutex(&gDvm.stdioConverterLock);
160
161     /* we never do anything that affects the rest of the VM */
162     dvmChangeStatus(NULL, THREAD_VMWAIT);
163
164     /*
165      * Allocate read buffers.
166      */
167     stdoutData = (BufferedData*) malloc(sizeof(*stdoutData));
168     stderrData = (BufferedData*) malloc(sizeof(*stderrData));
169     stdoutData->count = stderrData->count = 0;
170
171     /*
172      * Read until shutdown time.
173      */
174     while (!gDvm.haltStdioConverter) {
175         ssize_t actual;
176         fd_set readfds;
177         int maxFd, fdCount;
178
179         FD_ZERO(&readfds);
180         FD_SET(pipeStorage->stdoutPipe[0], &readfds);
181         FD_SET(pipeStorage->stderrPipe[0], &readfds);
182         maxFd = MAX(pipeStorage->stdoutPipe[0], pipeStorage->stderrPipe[0]);
183
184         fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);
185
186         if (fdCount < 0) {
187             if (errno != EINTR) {
188                 LOGE("select on stdout/stderr failed\n");
189                 break;
190             }
191             LOGD("Got EINTR, ignoring\n");
192         } else if (fdCount == 0) {
193             LOGD("WEIRD: select returned zero\n");
194         } else {
195             bool err = false;
196             if (FD_ISSET(pipeStorage->stdoutPipe[0], &readfds)) {
197                 err |= !readAndLog(pipeStorage->stdoutPipe[0], stdoutData,
198                     "stdout");
199             }
200             if (FD_ISSET(pipeStorage->stderrPipe[0], &readfds)) {
201                 err |= !readAndLog(pipeStorage->stderrPipe[0], stderrData,
202                     "stderr");
203             }
204
205             /* probably EOF; give up */
206             if (err) {
207                 LOGW("stdio converter got read error; shutting it down\n");
208                 break;
209             }
210         }
211     }
212
213     close(pipeStorage->stdoutPipe[0]);
214     close(pipeStorage->stderrPipe[0]);
215
216     free(pipeStorage);
217     free(stdoutData);
218     free(stderrData);
219
220     /* change back for shutdown sequence */
221     dvmChangeStatus(NULL, THREAD_RUNNING);
222     return NULL;
223 #undef MAX
224 }
225
226 /*
227  * Data is pending on "fd".  Read as much as will fit in "data", then
228  * write out any full lines and compact "data".
229  */
230 static bool readAndLog(int fd, BufferedData* data, const char* tag)
231 {
232     ssize_t actual;
233     size_t want;
234
235     assert(data->count < kMaxLine);
236
237     want = kMaxLine - data->count;
238     actual = read(fd, data->buf + data->count, want);
239     if (actual <= 0) {
240         LOGW("read %s: (%d,%d) failed (%d): %s\n",
241             tag, fd, want, (int)actual, strerror(errno));
242         return false;
243     } else {
244         //LOGI("read %s: %d at %d\n", tag, actual, data->count);
245     }
246     data->count += actual;
247
248     /*
249      * Got more data, look for an EOL.  We expect LF or CRLF, but will
250      * try to handle a standalone CR.
251      */
252     char* cp = data->buf;
253     const char* start = data->buf;
254     int i = data->count;
255     for (i = data->count; i > 0; i--, cp++) {
256         if (*cp == '\n' || (*cp == '\r' && i != 0 && *(cp+1) != '\n')) {
257             *cp = '\0';
258             //LOGW("GOT %d at %d '%s'\n", cp - start, start - data->buf, start);
259             LOG(LOG_INFO, tag, "%s", start);
260             start = cp+1;
261         }
262     }
263
264     /*
265      * See if we overflowed.  If so, cut it off.
266      */
267     if (start == data->buf && data->count == kMaxLine) {
268         data->buf[kMaxLine] = '\0';
269         LOG(LOG_INFO, tag, "%s!", start);
270         start = cp + kMaxLine;
271     }
272
273     /*
274      * Update "data" if we consumed some output.  If there's anything left
275      * in the buffer, it's because we didn't see an EOL and need to keep
276      * reading until we see one.
277      */
278     if (start != data->buf) {
279         if (start >= data->buf + data->count) {
280             /* consumed all available */
281             data->count = 0;
282         } else {
283             /* some left over */
284             int remaining = data->count - (start - data->buf);
285             memmove(data->buf, start, remaining);
286             data->count = remaining;
287         }
288     }
289
290     return true;
291 }
292