X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=test-skeleton.c;h=6a7fc429d4e344e5c7a6634ec73dfe0a5d6b0119;hb=0f1753c9c476b500f52755d1cc07c1eb6927e49a;hp=db04b11e63c4e734e6a4c85503489b28dbd0ef14;hpb=e7c036b39ef12abc7ff131982df75e3ec35c0f31;p=platform%2Fupstream%2Fglibc.git diff --git a/test-skeleton.c b/test-skeleton.c index db04b11..6a7fc42 100644 --- a/test-skeleton.c +++ b/test-skeleton.c @@ -1,23 +1,28 @@ /* Skeleton for test programs. - Copyright (C) 1998, 2000 Free Software Foundation, Inc. + Copyright (C) 1998-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. Contributed by Ulrich Drepper , 1998. The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ +#include +#include +#include #include +#include +#include #include #include #include @@ -26,6 +31,8 @@ #include #include #include +#include +#include /* The test function is normally called `do_test' and it is called with argc and argv as the arguments. We nevertheless provide the @@ -34,6 +41,9 @@ # define TEST_FUNCTION do_test (argc, argv) #endif +#ifndef TEST_DATA_LIMIT +# define TEST_DATA_LIMIT (64 << 20) /* Data limit (bytes) to run with. */ +#endif #define OPT_DIRECT 1000 #define OPT_TESTDIR 1001 @@ -49,58 +59,112 @@ static struct option options[] = }; /* PID of the test itself. */ -static int pid; +static pid_t pid; /* Directory to place temporary files in. */ static const char *test_dir; /* List of temporary files. */ -struct name_list +struct temp_name_list { struct qelem q; const char *name; -} *name_list; +} *temp_name_list; /* Add temporary files in list. */ -void +static void +__attribute__ ((unused)) add_temp_file (const char *name) { - struct name_list *newp = (struct name_list *) calloc (sizeof (*newp), 1); + struct temp_name_list *newp + = (struct temp_name_list *) calloc (sizeof (*newp), 1); if (newp != NULL) { newp->name = name; - if (name_list == NULL) - name_list = (struct name_list *) &newp->q; + if (temp_name_list == NULL) + temp_name_list = (struct temp_name_list *) &newp->q; else - insque (newp, name_list); + insque (newp, temp_name_list); } } /* Delete all temporary files. */ -void +static void delete_temp_files (void) { - while (name_list != NULL) + while (temp_name_list != NULL) + { + remove (temp_name_list->name); + temp_name_list = (struct temp_name_list *) temp_name_list->q.q_forw; + } +} + +/* Create a temporary file. */ +static int +__attribute__ ((unused)) +create_temp_file (const char *base, char **filename) +{ + char *fname; + int fd; + + fname = (char *) malloc (strlen (test_dir) + 1 + strlen (base) + + sizeof ("XXXXXX")); + if (fname == NULL) + { + puts ("out of memory"); + return -1; + } + strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX"); + + fd = mkstemp (fname); + if (fd == -1) { - remove (name_list->name); - name_list = (struct name_list *) name_list->q.q_forw; + printf ("cannot open temporary file '%s': %m\n", fname); + free (fname); + return -1; } + + add_temp_file (fname); + if (filename != NULL) + *filename = fname; + + return fd; } /* Timeout handler. We kill the child and exit with an error. */ -void -timeout_handler (int sig __attribute__ ((unused))) +static void +__attribute__ ((noreturn)) +signal_handler (int sig __attribute__ ((unused))) { int killed; + int status; - /* Send signal. */ + assert (pid > 1); + /* Kill the whole process group. */ + kill (-pid, SIGKILL); + /* In case setpgid failed in the child, kill it individually too. */ kill (pid, SIGKILL); /* Wait for it to terminate. */ - killed = waitpid (pid, NULL, WNOHANG); + int i; + for (i = 0; i < 5; ++i) + { + killed = waitpid (pid, &status, WNOHANG|WUNTRACED); + if (killed != 0) + break; + + /* Delay, give the system time to process the kill. If the + nanosleep() call return prematurely, all the better. We + won't restart it since this probably means the child process + finally died. */ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep (&ts, NULL); + } if (killed != 0 && killed != pid) { - perror ("Failed to killed test process"); + printf ("Failed to kill test process: %m\n"); exit (1); } @@ -108,12 +172,60 @@ timeout_handler (int sig __attribute__ ((unused))) CLEANUP_HANDLER; #endif - fputs ("Timed out: killed the child process\n", stderr); + if (sig == SIGINT) + { + signal (sig, SIG_DFL); + raise (sig); + } + + /* If we expected this signal: good! */ +#ifdef EXPECTED_SIGNAL + if (EXPECTED_SIGNAL == SIGALRM) + exit (0); +#endif + + if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL)) + puts ("Timed out: killed the child process"); + else if (WIFSTOPPED (status)) + printf ("Timed out: the child process was %s\n", + strsignal (WSTOPSIG (status))); + else if (WIFSIGNALED (status)) + printf ("Timed out: the child process got signal %s\n", + strsignal (WTERMSIG (status))); + else + printf ("Timed out: killed the child process but it exited %d\n", + WEXITSTATUS (status)); /* Exit with an error. */ exit (1); } +/* Set fortification error handler. Used when tests want to verify that bad + code is caught by the library. */ +static void +__attribute__ ((unused)) +set_fortify_handler (void (*handler) (int sig)) +{ + struct sigaction sa; + + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + sigaction (SIGABRT, &sa, NULL); + + /* Avoid all the buffer overflow messages on stderr. */ + int fd = open (_PATH_DEVNULL, O_WRONLY); + if (fd == -1) + close (STDERR_FILENO); + else + { + dup2 (fd, STDERR_FILENO); + close (fd); + } + setenv ("LIBC_FATAL_STDERR_", "1", 1); +} + /* We provide the entry point here. */ int main (int argc, char *argv[]) @@ -121,12 +233,17 @@ main (int argc, char *argv[]) int direct = 0; /* Directly call the test function? */ int status; int opt; + unsigned int timeoutfactor = 1; + pid_t termpid; + + /* Make uses of freed and uninitialized memory known. */ + mallopt (M_PERTURB, 42); #ifdef STDOUT_UNBUFFERED setbuf (stdout, NULL); #endif - while ((opt = getopt_long (argc, argv, "", options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1) switch (opt) { case '?': @@ -142,6 +259,19 @@ main (int argc, char *argv[]) #endif } + /* If set, read the test TIMEOUTFACTOR value from the environment. + This value is used to scale the default test timeout values. */ + char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR"); + if (envstr_timeoutfactor != NULL) + { + char *envstr_conv = envstr_timeoutfactor; + unsigned long int env_fact; + + env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0); + if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor) + timeoutfactor = MAX (env_fact, 1); + } + /* Set TMPDIR to specified test directory. */ if (test_dir != NULL) { @@ -149,7 +279,7 @@ main (int argc, char *argv[]) if (chdir (test_dir) < 0) { - perror ("chdir"); + printf ("chdir: %m\n"); exit (1); } } @@ -166,6 +296,11 @@ main (int argc, char *argv[]) /* make sure temporary files are deleted. */ atexit (delete_temp_files); + /* Correct for the possible parameters. */ + argv[optind - 1] = argv[0]; + argv += optind - 1; + argc -= optind - 1; + /* Call the initializing function, if one is available. */ #ifdef PREPARE PREPARE (argc, argv); @@ -192,12 +327,34 @@ main (int argc, char *argv[]) setrlimit (RLIMIT_CORE, &core_limit); #endif +#ifdef RLIMIT_DATA + /* Try to avoid eating all memory if a test leaks. */ + struct rlimit data_limit; + if (getrlimit (RLIMIT_DATA, &data_limit) == 0) + { + if (TEST_DATA_LIMIT == RLIM_INFINITY) + data_limit.rlim_cur = data_limit.rlim_max; + else if (data_limit.rlim_cur > (rlim_t) TEST_DATA_LIMIT) + data_limit.rlim_cur = MIN ((rlim_t) TEST_DATA_LIMIT, + data_limit.rlim_max); + if (setrlimit (RLIMIT_DATA, &data_limit) < 0) + printf ("setrlimit: RLIMIT_DATA: %m\n"); + } + else + printf ("getrlimit: RLIMIT_DATA: %m\n"); +#endif + + /* We put the test process in its own pgrp so that if it bogusly + generates any job control signals, they won't hit the whole build. */ + if (setpgid (0, 0) != 0) + printf ("Failed to set the process group ID: %m\n"); + /* Execute the test function and exit with the return value. */ exit (TEST_FUNCTION); } else if (pid < 0) { - perror ("Cannot fork test program"); + printf ("Cannot fork test program: %m\n"); exit (1); } @@ -206,31 +363,66 @@ main (int argc, char *argv[]) /* Default timeout is two seconds. */ # define TIMEOUT 2 #endif - alarm (TIMEOUT); - signal (SIGALRM, timeout_handler); + signal (SIGALRM, signal_handler); + alarm (TIMEOUT * timeoutfactor); + + /* Make sure we clean up if the wrapper gets interrupted. */ + signal (SIGINT, signal_handler); /* Wait for the regular termination. */ - if (waitpid (pid, &status, 0) != pid) + termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)); + if (termpid == -1) + { + printf ("Waiting for test program failed: %m\n"); + exit (1); + } + if (termpid != pid) { - perror ("Oops, wrong test program terminated"); + printf ("Oops, wrong test program terminated: expected %ld, got %ld\n", + (long int) pid, (long int) termpid); exit (1); } -#ifndef EXPECTED_SIGNAL - /* We don't expect any signal. */ -# define EXPECTED_SIGNAL 0 -#endif - if (WTERMSIG (status) != EXPECTED_SIGNAL) + /* Process terminated normaly without timeout etc. */ + if (WIFEXITED (status)) { - if (EXPECTED_SIGNAL != 0) - fprintf (stderr, "Incorrect signal from child: got `%s', need `%s'\n", - strsignal (WTERMSIG (status)), strsignal (EXPECTED_SIGNAL)); - else - fprintf (stderr, "Didn't expect signal from child: got `%s'\n", - strsignal (WTERMSIG (status))); +#ifndef EXPECTED_STATUS +# ifndef EXPECTED_SIGNAL + /* Simply exit with the return value of the test. */ + return WEXITSTATUS (status); +# else + printf ("Expected signal '%s' from child, got none\n", + strsignal (EXPECTED_SIGNAL)); exit (1); +# endif +#else + if (WEXITSTATUS (status) != EXPECTED_STATUS) + { + printf ("Expected status %d, got %d\n", + EXPECTED_STATUS, WEXITSTATUS (status)); + exit (1); + } + + return 0; +#endif } + /* Process was killed by timer or other signal. */ + else + { +#ifndef EXPECTED_SIGNAL + printf ("Didn't expect signal from child: got `%s'\n", + strsignal (WTERMSIG (status))); + exit (1); +#else + if (WTERMSIG (status) != EXPECTED_SIGNAL) + { + printf ("Incorrect signal from child: got `%s', need `%s'\n", + strsignal (WTERMSIG (status)), + strsignal (EXPECTED_SIGNAL)); + exit (1); + } - /* Simply exit with the return value of the test. */ - return WEXITSTATUS (status); + return 0; +#endif + } }