2014-06-02 Jakub Jelinek <jakub@redhat.com>
Max Ostapenko <m.ostapenko@partner.samsung.com>
* diagnostic.c (diagnostic_action_after_output): Exit with
ICE_EXIT_CODE instead of FATAL_EXIT_CODE.
* gcc.c (execute): Don't free first string early, but at the end
of the function. Call retry_ice if compiler exited with
ICE_EXIT_CODE.
(main): Factor out common code.
(print_configuration): New function.
(try_fork): Likewise.
(redirect_stdout_stderr): Likewise.
(files_equal_p): Likewise.
(check_repro): Likewise.
(run_attempt): Likewise.
(generate_preprocessed_code): Likewise.
(append_text): Likewise.
(try_generate_repro): Likewise.
@@ -492,7 +492,7 @@ diagnostic_action_after_output (diagnostic_context *context,
real_abort ();
diagnostic_finish (context);
fnotice (stderr, "compilation terminated.\n");
- exit (FATAL_EXIT_CODE);
+ exit (ICE_EXIT_CODE);
default:
gcc_unreachable ();
@@ -43,6 +43,13 @@ compilation is specified by a string called a "spec". */
#include "params.h"
#include "vec.h"
#include "filenames.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if !(defined (__MSDOS__) || defined (OS2) || defined (VMS))
+#define RETRY_ICE_SUPPORTED
+#endif
/* By default there is no special suffix for target executables. */
/* FIXME: when autoconf is fixed, remove the host check - dj */
@@ -253,6 +260,9 @@ static void init_gcc_specs (struct obstack *, const char *, const char *,
static const char *convert_filename (const char *, int, int);
#endif
+#ifdef RETRY_ICE_SUPPORTED
+static void try_generate_repro (const char *prog, const char **argv);
+#endif
static const char *getenv_spec_function (int, const char **);
static const char *if_exists_spec_function (int, const char **);
static const char *if_exists_else_spec_function (int, const char **);
@@ -2797,7 +2807,7 @@ execute (void)
}
}
- if (string != commands[i].prog)
+ if (i && string != commands[i].prog)
free (CONST_CAST (char *, string));
}
@@ -2850,6 +2860,16 @@ execute (void)
else if (WIFEXITED (status)
&& WEXITSTATUS (status) >= MIN_FATAL_STATUS)
{
+#ifdef RETRY_ICE_SUPPORTED
+ /* For ICEs in cc1, cc1obj, cc1plus see if it is
+ reproducible or not. */
+ const char *p;
+ if (WEXITSTATUS (status) == ICE_EXIT_CODE
+ && i == 0
+ && (p = strrchr (commands[0].argv[0], DIR_SEPARATOR))
+ && ! strncmp (p + 1, "cc1", 3))
+ try_generate_repro (commands[0].prog, commands[0].argv);
+#endif
if (WEXITSTATUS (status) > greatest_status)
greatest_status = WEXITSTATUS (status);
ret_code = -1;
@@ -2907,6 +2927,9 @@ execute (void)
}
}
+ if (commands[0].argv[0] != commands[0].prog)
+ free (CONST_CAST (char *, commands[0].argv[0]));
+
return ret_code;
}
}
@@ -6098,6 +6121,342 @@ give_switch (int switchnum, int omit_first_word)
switches[switchnum].validated = true;
}
+static void
+print_configuration (void)
+{
+ int n;
+ const char *thrmod;
+
+ fnotice (stderr, "Target: %s\n", spec_machine);
+ fnotice (stderr, "Configured with: %s\n", configuration_arguments);
+
+#ifdef THREAD_MODEL_SPEC
+ /* We could have defined THREAD_MODEL_SPEC to "%*" by default,
+ but there's no point in doing all this processing just to get
+ thread_model back. */
+ obstack_init (&obstack);
+ do_spec_1 (THREAD_MODEL_SPEC, 0, thread_model);
+ obstack_1grow (&obstack, '\0');
+ thrmod = XOBFINISH (&obstack, const char *);
+#else
+ thrmod = thread_model;
+#endif
+
+ fnotice (stderr, "Thread model: %s\n", thrmod);
+
+ /* compiler_version is truncated at the first space when initialized
+ from version string, so truncate version_string at the first space
+ before comparing. */
+ for (n = 0; version_string[n]; n++)
+ if (version_string[n] == ' ')
+ break;
+
+ if (! strncmp (version_string, compiler_version, n)
+ && compiler_version[n] == 0)
+ fnotice (stderr, "gcc version %s %s\n\n", version_string,
+ pkgversion_string);
+ else
+ fnotice (stderr, "gcc driver version %s %sexecuting gcc version %s\n\n",
+ version_string, pkgversion_string, compiler_version);
+}
+
+#ifdef RETRY_ICE_SUPPORTED
+#define RETRY_ICE_ATTEMPTS 3
+
+static int
+try_fork ()
+{
+ int pid, retries, sleep_interval;
+ sleep_interval = 1;
+ pid = -1;
+ for (retries = 0; retries < 4; retries++)
+ {
+ pid = fork ();
+ if (pid >= 0)
+ break;
+ sleep (sleep_interval);
+ sleep_interval *= 2;
+ }
+ return pid;
+}
+
+
+static void
+redirect_stdout_stderr (const char *out_temp, const char *err_temp,
+ int append)
+{
+ int fd;
+ fd = open (out_temp, append ? O_RDWR | O_APPEND : O_RDWR);
+
+ if (fd < 0)
+ exit (-1);
+
+ close (STDOUT_FILENO);
+ dup (fd);
+ close (fd);
+
+ fd = open (err_temp, append ? O_RDWR | O_APPEND : O_RDWR);
+
+ if (fd < 0)
+ exit (-1);
+
+ close (STDERR_FILENO);
+ dup (fd);
+ close (fd);
+}
+
+static int
+files_equal_p (char *file1, char *file2, char *buf, const int bufsize)
+{
+ struct stat st1, st2;
+ size_t n, len;
+ int fd1, fd2;
+
+ fd1 = open (file1, O_RDONLY);
+ fd2 = open (file2, O_RDONLY);
+
+ if (fd1 < 0 || fd2 < 0)
+ goto error;
+
+ if (fstat (fd1, &st1) < 0 || fstat (fd2, &st2) < 0)
+ goto error;
+
+ if (st1.st_size != st2.st_size)
+ goto error;
+
+ for (n = st1.st_size; n; n -= len)
+ {
+ len = n;
+ if ((int) len > bufsize / 2)
+ len = bufsize / 2;
+
+ if (read (fd1, buf, len) != (int) len
+ || read (fd2, buf + bufsize / 2, len) != (int) len)
+ {
+ goto error;
+ }
+
+ if (memcmp (buf, buf + bufsize / 2, len) != 0)
+ goto error;
+ }
+
+ close (fd1);
+ close (fd2);
+
+ return 1;
+
+error:
+ close (fd1);
+ close (fd2);
+ return 0;
+}
+
+static int
+check_repro (char **temp_stdout_files, char **temp_stderr_files)
+{
+ int i;
+ const int ice_bufsize = 8192;
+ char *ice_buf = XNEWVEC (char, ice_bufsize);
+ for (i = 0; i < RETRY_ICE_ATTEMPTS - 2; ++i)
+ {
+ if (!files_equal_p (temp_stdout_files[i], temp_stdout_files[i + 1],
+ ice_buf, ice_bufsize)
+ || !files_equal_p (temp_stderr_files[i], temp_stderr_files[i + 1],
+ ice_buf, ice_bufsize))
+ {
+ fnotice (stderr, "The bug is not reproducible, so it is"
+ " likely a hardware or OS problem.\n");
+ break;
+ }
+ }
+ free (ice_buf);
+ return i == RETRY_ICE_ATTEMPTS - 2;
+}
+
+enum attempt_status {
+ ATTEMPT_STATUS_FAIL_TO_RUN,
+ ATTEMPT_STATUS_SUCCESS,
+ ATTEMPT_STATUS_ICE
+};
+
+static enum attempt_status
+run_attempt (const char *prog, const char **new_argv, const char *out_temp,
+ const char *err_temp, int emit_system_info, int append)
+{
+ int status;
+ int pid = try_fork ();
+ if (pid < 0)
+ return ATTEMPT_STATUS_FAIL_TO_RUN;
+ else if (pid == 0)
+ {
+ redirect_stdout_stderr (out_temp, err_temp, append);
+
+ if (emit_system_info)
+ print_configuration ();
+
+ if (prog == new_argv[0])
+ execvp (prog, CONST_CAST2 (char *const *, const char **, new_argv));
+ else
+ execv (new_argv[0], CONST_CAST2 (char *const *, const char **,
+ new_argv));
+ exit (-1);
+ }
+
+ if (waitpid (pid, &status, 0) < 0)
+ return ATTEMPT_STATUS_FAIL_TO_RUN;
+
+ switch (WEXITSTATUS (status))
+ {
+ case ICE_EXIT_CODE:
+ return ATTEMPT_STATUS_ICE;
+
+ case SUCCESS_EXIT_CODE:
+ return ATTEMPT_STATUS_SUCCESS;
+
+ default:
+ return ATTEMPT_STATUS_FAIL_TO_RUN;
+ }
+}
+
+static void
+generate_preprocessed_code (const char *prog, const char **new_argv,
+ const int nargs, char **out_file, char **err_file)
+{
+ int i, status;
+ int fd = open (*out_file, O_RDWR | O_APPEND);
+ if (fd < 0)
+ return;
+ write (fd, "\n\n//", 4);
+ for (i = 0; i < nargs; i++)
+ {
+ write (fd, " ", 1);
+ write (fd, new_argv[i], strlen (new_argv[i]));
+ }
+ write (fd, "\n", 1);
+ close (fd);
+ new_argv[nargs] = "-E";
+ new_argv[nargs + 1] = NULL;
+
+ status = run_attempt (prog, new_argv, *out_file, *err_file, 0, 1);
+
+ if (status == ATTEMPT_STATUS_SUCCESS)
+ {
+ fnotice (stderr, "Preprocessed source stored into %s file,"
+ " please attach this to your bugreport.\n",
+ *out_file);
+ /* Make sure it is not deleted. */
+ free (*out_file);
+ *out_file = NULL;
+ }
+}
+
+static void
+append_text (char *file, const char *str)
+{
+ int fd = open (file, O_RDWR | O_APPEND);
+ if (fd < 0)
+ return;
+
+ write (fd, str, strlen (str));
+ close (fd);
+}
+
+static void
+try_generate_repro (const char *prog, const char **argv)
+{
+ int i, nargs, out_arg = -1, quiet = 0, attempt;
+ const char **new_argv;
+ char *temp_files[RETRY_ICE_ATTEMPTS * 2];
+ char **temp_stdout_files = &temp_files[0];
+ char **temp_stderr_files = &temp_files[RETRY_ICE_ATTEMPTS];
+
+ if (gcc_input_filename == NULL || ! strcmp (gcc_input_filename, "-"))
+ return;
+
+ for (nargs = 0; argv[nargs] != NULL; ++nargs)
+ /* Only retry compiler ICEs, not preprocessor ones. */
+ if (! strcmp (argv[nargs], "-E"))
+ return;
+ else if (argv[nargs][0] == '-' && argv[nargs][1] == 'o')
+ {
+ if (out_arg == -1)
+ out_arg = nargs;
+ else
+ return;
+ }
+ /* If the compiler is going to output any time information,
+ it might varry between invocations. */
+ else if (! strcmp (argv[nargs], "-quiet"))
+ quiet = 1;
+ else if (! strcmp (argv[nargs], "-ftime-report"))
+ return;
+
+ if (out_arg == -1 || !quiet)
+ return;
+
+ memset (temp_stdout_files, '\0', sizeof (temp_stdout_files));
+ memset (temp_stderr_files, '\0', sizeof (temp_stderr_files));
+ new_argv = XALLOCAVEC (const char *, nargs + 3);
+ memcpy (new_argv, argv, (nargs + 1) * sizeof (const char *));
+ new_argv[nargs++] = "-frandom-seed=0";
+ new_argv[nargs] = NULL;
+ if (new_argv[out_arg][2] == '\0')
+ new_argv[out_arg + 1] = "-";
+ else
+ new_argv[out_arg] = "-o-";
+
+ int status;
+ for (attempt = 0; attempt < RETRY_ICE_ATTEMPTS; ++attempt)
+ {
+ int emit_system_info = 0;
+ int append = 0;
+ temp_stdout_files[attempt] = make_temp_file (".out");
+ temp_stderr_files[attempt] = make_temp_file (".err");
+
+ if (attempt == RETRY_ICE_ATTEMPTS - 1)
+ {
+ append = 1;
+ emit_system_info = 1;
+ }
+
+ if (emit_system_info)
+ append_text (temp_stderr_files[attempt], "/*\n");
+
+ /* Fork a subprocess; wait and retry if it fails. */
+ status = run_attempt (prog, new_argv, temp_stdout_files[attempt],
+ temp_stderr_files[attempt], emit_system_info,
+ append);
+
+ if (emit_system_info)
+ append_text (temp_stderr_files[attempt], "*/\n");
+
+ if (status != ATTEMPT_STATUS_ICE)
+ {
+ fnotice (stderr, "The bug is not reproducible, so it is"
+ " likely a hardware or OS problem.\n");
+ goto out;
+ }
+ }
+
+ if (!check_repro (temp_stdout_files, temp_stderr_files))
+ goto out;
+
+ /* In final attempt we append cc1 options and preprocesssed code to last
+ generated .err file with configuration and backtrace. */
+ generate_preprocessed_code (prog, new_argv, nargs,
+ &temp_stderr_files[RETRY_ICE_ATTEMPTS - 1],
+ &temp_stdout_files[RETRY_ICE_ATTEMPTS - 1]);
+
+out:
+ for (i = 0; i < RETRY_ICE_ATTEMPTS * 2; i++)
+ if (temp_files[i])
+ {
+ unlink (temp_stdout_files[i]);
+ free (temp_stdout_files[i]);
+ }
+}
+#endif
+
/* Search for a file named NAME trying various prefixes including the
user's -B prefix and some standard ones.
Return the absolute file name found. If nothing is found, return NAME. */
@@ -6867,43 +7226,9 @@ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
if (verbose_flag)
{
- int n;
- const char *thrmod;
-
- fnotice (stderr, "Target: %s\n", spec_machine);
- fnotice (stderr, "Configured with: %s\n", configuration_arguments);
-
-#ifdef THREAD_MODEL_SPEC
- /* We could have defined THREAD_MODEL_SPEC to "%*" by default,
- but there's no point in doing all this processing just to get
- thread_model back. */
- obstack_init (&obstack);
- do_spec_1 (THREAD_MODEL_SPEC, 0, thread_model);
- obstack_1grow (&obstack, '\0');
- thrmod = XOBFINISH (&obstack, const char *);
-#else
- thrmod = thread_model;
-#endif
-
- fnotice (stderr, "Thread model: %s\n", thrmod);
-
- /* compiler_version is truncated at the first space when initialized
- from version string, so truncate version_string at the first space
- before comparing. */
- for (n = 0; version_string[n]; n++)
- if (version_string[n] == ' ')
- break;
-
- if (! strncmp (version_string, compiler_version, n)
- && compiler_version[n] == 0)
- fnotice (stderr, "gcc version %s %s\n", version_string,
- pkgversion_string);
- else
- fnotice (stderr, "gcc driver version %s %sexecuting gcc version %s\n",
- version_string, pkgversion_string, compiler_version);
-
+ print_configuration ();
if (n_infiles == 0)
- return (0);
+ return (0);
}
if (n_infiles == added_libraries)