xref: /libuv/test/runner-unix.c (revision ca544ed6)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "runner-unix.h"
23 #include "runner.h"
24 
25 #include <limits.h>
26 #include <stdint.h> /* uintptr_t */
27 
28 #include <errno.h>
29 #include <unistd.h> /* usleep */
30 #include <string.h> /* strdup */
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34 #include <signal.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #include <assert.h>
38 
39 #include <sys/select.h>
40 #include <sys/time.h>
41 #include <pthread.h>
42 
43 #ifdef __APPLE__
44 #include <TargetConditionals.h>
45 #endif
46 
47 extern char** environ;
48 
closefd(int fd)49 static void closefd(int fd) {
50   if (close(fd) == 0 || errno == EINTR || errno == EINPROGRESS)
51     return;
52 
53   perror("close");
54   abort();
55 }
56 
57 
notify_parent_process(void)58 void notify_parent_process(void) {
59   char* arg;
60   int fd;
61 
62   arg = getenv("UV_TEST_RUNNER_FD");
63   if (arg == NULL)
64     return;
65 
66   fd = atoi(arg);
67   assert(fd > STDERR_FILENO);
68   unsetenv("UV_TEST_RUNNER_FD");
69   closefd(fd);
70 }
71 
72 
73 /* Do platform-specific initialization. */
platform_init(int argc,char ** argv)74 void platform_init(int argc, char **argv) {
75   /* Disable stdio output buffering. */
76   setvbuf(stdout, NULL, _IONBF, 0);
77   setvbuf(stderr, NULL, _IONBF, 0);
78   signal(SIGPIPE, SIG_IGN);
79   snprintf(executable_path, sizeof(executable_path), "%s", argv[0]);
80 }
81 
82 
83 /* Invoke "argv[0] test-name [test-part]". Store process info in *p. Make sure
84  * that all stdio output of the processes is buffered up. */
process_start(char * name,char * part,process_info_t * p,int is_helper)85 int process_start(char* name, char* part, process_info_t* p, int is_helper) {
86   FILE* stdout_file;
87   int stdout_fd;
88   const char* arg;
89   char* args[16];
90   int pipefd[2];
91   char fdstr[8];
92   ssize_t rc;
93   int n;
94   pid_t pid;
95 
96   arg = getenv("UV_USE_VALGRIND");
97   n = 0;
98 
99   /* Disable valgrind for helpers, it complains about helpers leaking memory.
100    * They're killed after the test and as such never get a chance to clean up.
101    */
102   if (is_helper == 0 && arg != NULL && atoi(arg) != 0) {
103     args[n++] = "valgrind";
104     args[n++] = "--quiet";
105     args[n++] = "--leak-check=full";
106     args[n++] = "--show-reachable=yes";
107     args[n++] = "--error-exitcode=125";
108   }
109 
110   args[n++] = executable_path;
111   args[n++] = name;
112   args[n++] = part;
113   args[n++] = NULL;
114 
115   stdout_file = tmpfile();
116   stdout_fd = fileno(stdout_file);
117   if (!stdout_file) {
118     perror("tmpfile");
119     return -1;
120   }
121 
122   if (is_helper) {
123     if (pipe(pipefd)) {
124       perror("pipe");
125       return -1;
126     }
127 
128     snprintf(fdstr, sizeof(fdstr), "%d", pipefd[1]);
129     if (setenv("UV_TEST_RUNNER_FD", fdstr, /* overwrite */ 1)) {
130       perror("setenv");
131       return -1;
132     }
133   }
134 
135   p->terminated = 0;
136   p->status = 0;
137 
138 #if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)
139   pid = -1;
140 #else
141   pid = fork();
142 #endif
143 
144   if (pid < 0) {
145     perror("fork");
146     return -1;
147   }
148 
149   if (pid == 0) {
150     /* child */
151     if (is_helper)
152       closefd(pipefd[0]);
153     dup2(stdout_fd, STDOUT_FILENO);
154     dup2(stdout_fd, STDERR_FILENO);
155 #if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH))
156     execve(args[0], args, environ);
157 #endif
158     perror("execve()");
159     _exit(127);
160   }
161 
162   /* parent */
163   p->pid = pid;
164   p->name = strdup(name);
165   p->stdout_file = stdout_file;
166 
167   if (!is_helper)
168     return 0;
169 
170   closefd(pipefd[1]);
171   unsetenv("UV_TEST_RUNNER_FD");
172 
173   do
174     rc = read(pipefd[0], &n, 1);
175   while (rc == -1 && errno == EINTR);
176 
177   closefd(pipefd[0]);
178 
179   if (rc == -1) {
180     perror("read");
181     return -1;
182   }
183 
184   if (rc > 0) {
185     fprintf(stderr, "EOF expected but got data.\n");
186     return -1;
187   }
188 
189   return 0;
190 }
191 
192 
193 typedef struct {
194   int pipe[2];
195   process_info_t* vec;
196   int n;
197 } dowait_args;
198 
199 
200 /* This function is run inside a pthread. We do this so that we can possibly
201  * timeout.
202  */
dowait(void * data)203 static void* dowait(void* data) {
204   dowait_args* args = data;
205 
206   int i, r;
207   process_info_t* p;
208 
209   for (i = 0; i < args->n; i++) {
210     p = &args->vec[i];
211     if (p->terminated) continue;
212     r = waitpid(p->pid, &p->status, 0);
213     if (r < 0) {
214       perror("waitpid");
215       return NULL;
216     }
217     p->terminated = 1;
218   }
219 
220   if (args->pipe[1] >= 0) {
221     /* Write a character to the main thread to notify it about this. */
222     ssize_t r;
223 
224     do
225       r = write(args->pipe[1], "", 1);
226     while (r == -1 && errno == EINTR);
227   }
228 
229   return NULL;
230 }
231 
232 
233 /* Wait for all `n` processes in `vec` to terminate. Time out after `timeout`
234  * msec, or never if timeout == -1. Return 0 if all processes are terminated,
235  * -1 on error, -2 on timeout. */
process_wait(process_info_t * vec,int n,int timeout)236 int process_wait(process_info_t* vec, int n, int timeout) {
237   int i;
238   int r;
239   int retval;
240   process_info_t* p;
241   dowait_args args;
242   pthread_t tid;
243   pthread_attr_t attr;
244   unsigned int elapsed_ms;
245   struct timeval timebase;
246   struct timeval tv;
247   fd_set fds;
248 
249   args.vec = vec;
250   args.n = n;
251   args.pipe[0] = -1;
252   args.pipe[1] = -1;
253 
254   /* The simple case is where there is no timeout */
255   if (timeout == -1) {
256     dowait(&args);
257     return 0;
258   }
259 
260   /* Hard case. Do the wait with a timeout.
261    *
262    * Assumption: we are the only ones making this call right now. Otherwise
263    * we'd need to lock vec.
264    */
265 
266   r = pipe((int*)&(args.pipe));
267   if (r) {
268     perror("pipe()");
269     return -1;
270   }
271 
272   if (pthread_attr_init(&attr))
273     abort();
274 
275 #if defined(__MVS__)
276   if (pthread_attr_setstacksize(&attr, 1024 * 1024))
277 #else
278   if (pthread_attr_setstacksize(&attr, 256 * 1024))
279 #endif
280     abort();
281 
282   r = pthread_create(&tid, &attr, dowait, &args);
283 
284   if (pthread_attr_destroy(&attr))
285     abort();
286 
287   if (r) {
288     perror("pthread_create()");
289     retval = -1;
290     goto terminate;
291   }
292 
293   if (gettimeofday(&timebase, NULL))
294     abort();
295 
296   tv = timebase;
297   for (;;) {
298     /* Check that gettimeofday() doesn't jump back in time. */
299     assert(tv.tv_sec > timebase.tv_sec ||
300            (tv.tv_sec == timebase.tv_sec && tv.tv_usec >= timebase.tv_usec));
301 
302     elapsed_ms =
303         (tv.tv_sec - timebase.tv_sec) * 1000 +
304         (tv.tv_usec / 1000) -
305         (timebase.tv_usec / 1000);
306 
307     r = 0;  /* Timeout. */
308     if (elapsed_ms >= (unsigned) timeout)
309       break;
310 
311     tv.tv_sec = (timeout - elapsed_ms) / 1000;
312     tv.tv_usec = (timeout - elapsed_ms) % 1000 * 1000;
313 
314     FD_ZERO(&fds);
315     FD_SET(args.pipe[0], &fds);
316 
317     r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv);
318     if (!(r == -1 && errno == EINTR))
319       break;
320 
321     if (gettimeofday(&tv, NULL))
322       abort();
323   }
324 
325   if (r == -1) {
326     perror("select()");
327     retval = -1;
328 
329   } else if (r) {
330     /* The thread completed successfully. */
331     retval = 0;
332 
333   } else {
334     /* Timeout. Kill all the children. */
335     for (i = 0; i < n; i++) {
336       p = &vec[i];
337       kill(p->pid, SIGTERM);
338     }
339     retval = -2;
340   }
341 
342   if (pthread_join(tid, NULL))
343     abort();
344 
345 terminate:
346   closefd(args.pipe[0]);
347   closefd(args.pipe[1]);
348   return retval;
349 }
350 
351 
352 /* Returns the number of bytes in the stdio output buffer for process `p`. */
process_output_size(process_info_t * p)353 long int process_output_size(process_info_t *p) {
354   /* Size of the p->stdout_file */
355   struct stat buf;
356 
357   memset(&buf, 0, sizeof(buf));
358   int r = fstat(fileno(p->stdout_file), &buf);
359   if (r < 0) {
360     return -1;
361   }
362 
363   return (long)buf.st_size;
364 }
365 
366 
367 /* Copy the contents of the stdio output buffer to `fd`. */
process_copy_output(process_info_t * p,FILE * stream)368 int process_copy_output(process_info_t* p, FILE* stream) {
369   char buf[1024];
370   int r;
371 
372   r = fseek(p->stdout_file, 0, SEEK_SET);
373   if (r < 0) {
374     perror("fseek");
375     return -1;
376   }
377 
378   /* TODO: what if the line is longer than buf */
379   while ((r = fread(buf, 1, sizeof(buf), p->stdout_file)) != 0)
380     print_lines(buf, r, stream);
381 
382   if (ferror(p->stdout_file)) {
383     perror("read");
384     return -1;
385   }
386 
387   return 0;
388 }
389 
390 
391 /* Copy the last line of the stdio output buffer to `buffer` */
process_read_last_line(process_info_t * p,char * buffer,size_t buffer_len)392 int process_read_last_line(process_info_t *p,
393                            char* buffer,
394                            size_t buffer_len) {
395   char* ptr;
396 
397   int r = fseek(p->stdout_file, 0, SEEK_SET);
398   if (r < 0) {
399     perror("fseek");
400     return -1;
401   }
402 
403   buffer[0] = '\0';
404 
405   while (fgets(buffer, buffer_len, p->stdout_file) != NULL) {
406     for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++)
407       ;
408     *ptr = '\0';
409   }
410 
411   if (ferror(p->stdout_file)) {
412     perror("read");
413     buffer[0] = '\0';
414     return -1;
415   }
416   return 0;
417 }
418 
419 
420 /* Return the name that was specified when `p` was started by process_start */
process_get_name(process_info_t * p)421 char* process_get_name(process_info_t *p) {
422   return p->name;
423 }
424 
425 
426 /* Terminate process `p`. */
process_terminate(process_info_t * p)427 int process_terminate(process_info_t *p) {
428   return kill(p->pid, SIGTERM);
429 }
430 
431 
432 /* Return the exit code of process p. On error, return -1. */
process_reap(process_info_t * p)433 int process_reap(process_info_t *p) {
434   if (WIFEXITED(p->status)) {
435     return WEXITSTATUS(p->status);
436   } else  {
437     return p->status; /* ? */
438   }
439 }
440 
441 
442 /* Clean up after terminating process `p` (e.g. free the output buffer etc.). */
process_cleanup(process_info_t * p)443 void process_cleanup(process_info_t *p) {
444   fclose(p->stdout_file);
445   free(p->name);
446 }
447 
448 
449 /* Move the console cursor one line up and back to the first column. */
rewind_cursor(void)450 void rewind_cursor(void) {
451 #if defined(__MVS__)
452   fprintf(stderr, "\047[2K\r");
453 #else
454   fprintf(stderr, "\033[2K\r");
455 #endif
456 }
457