Ping.
-------- Original Message --------
Subject: [PATCH][Ping] Add patch for debugging compiler ICEs
Date: Wed, 11 Jun 2014 18:15:27 +0400
From: Maxim Ostapenko <m.ostape...@partner.samsung.com>
To: GCC Patches <gcc-patches@gcc.gnu.org>
CC: Yury Gribov <y.gri...@samsung.com>, Slava Garbuzov
<v.garbu...@samsung.com>, Jakub Jelinek <ja...@redhat.com>,
tsaund...@mozilla.com, chefm...@gmail.com
Ping.
-------- Original Message --------
Subject: [PATCH] Add patch for debugging compiler ICEs
Date: Mon, 02 Jun 2014 19:21:14 +0400
From: Maxim Ostapenko <m.ostape...@partner.samsung.com>
To: GCC Patches <gcc-patches@gcc.gnu.org>
CC: Yury Gribov <y.gri...@samsung.com>, Slava Garbuzov
<v.garbu...@samsung.com>, Jakub Jelinek <ja...@redhat.com>,
tsaund...@mozilla.com, chefm...@gmail.com
Hi,
A years ago there was a discussion
(https://gcc.gnu.org/ml/gcc-patches/2004-01/msg02437.html) about debugging
compiler ICEs that resulted in a patch from Jakub, which dumps
useful information into temporary file, but for some reasons this patch wasn't
applied to trunk.
This is the resurrected patch with added GCC version information into generated
repro file.
-Maxim
2014-06-02 Jakub Jelinek <ja...@redhat.com>
Max Ostapenko <m.ostape...@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.
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 0cc7593..67b8c5b 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -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 ();
diff --git a/gcc/gcc.c b/gcc/gcc.c
index 9ac18e6..86dce03 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -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)