From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23895 invoked by alias); 19 Aug 2014 13:58:00 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 23885 invoked by uid 89); 19 Aug 2014 13:57:59 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.6 required=5.0 tests=AWL,BAYES_00,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.2 X-HELO: mailout1.w1.samsung.com Received: from mailout1.w1.samsung.com (HELO mailout1.w1.samsung.com) (210.118.77.11) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (DES-CBC3-SHA encrypted) ESMTPS; Tue, 19 Aug 2014 13:57:57 +0000 Received: from eucpsbgm2.samsung.com (unknown [203.254.199.245]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0NAK001N744A1M30@mailout1.w1.samsung.com> for gcc-patches@gcc.gnu.org; Tue, 19 Aug 2014 14:57:46 +0100 (BST) Received: from eusync3.samsung.com ( [203.254.199.213]) by eucpsbgm2.samsung.com (EUCPMTA) with SMTP id 45.79.15956.0E753F35; Tue, 19 Aug 2014 14:57:52 +0100 (BST) Received: from [106.109.130.31] by eusync3.samsung.com (Oracle Communications Messaging Server 7u4-23.01(7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0NAK00GMX44FGQ30@eusync3.samsung.com>; Tue, 19 Aug 2014 14:57:52 +0100 (BST) Message-id: <53F357DF.6010103@partner.samsung.com> Date: Tue, 19 Aug 2014 13:58:00 -0000 From: Maxim Ostapenko User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.1.0 MIME-version: 1.0 To: Jeff Law , GCC Patches Cc: tsaunders@mozilla.com, Yury Gribov , Slava Garbuzov , Maxim Ostapenko Subject: Fwd: [PATCH] Add patch for debugging compiler ICEs. References: <53DFBCDA.1010504@partner.samsung.com> In-reply-to: <53DFBCDA.1010504@partner.samsung.com> X-Forwarded-Message-Id: <53DFBCDA.1010504@partner.samsung.com> Content-type: multipart/mixed; boundary=------------070303050901030608050107 X-IsSubscribed: yes X-SW-Source: 2014-08/txt/msg01866.txt.bz2 This is a multi-part message in MIME format. --------------070303050901030608050107 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1479 Ping. -Maxim -------- Original Message -------- Subject: [PATCH] Add patch for debugging compiler ICEs. Date: Mon, 04 Aug 2014 21:03:22 +0400 From: Maxim Ostapenko To: GCC Patches CC: Jeff Law , Jakub Jelinek , tsaunders@mozilla.com, Yury Gribov , Slava Garbuzov 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. I've updated the patch that I've posted earlier (https://gcc.gnu.org/ml/gcc-patches/2014-07/msg01649.html ) according to recent upstream discussion (https://gcc.gnu.org/ml/gcc-patches/2014-08/msg00020.html). The debugging functionality is disabled by default and can be enabled with adding -freport-bug into compile options. It can be also enabled by default with --with-spec during GCC build. There are several directions in which this can be improved e.g: 1) more user-friendly ways to report bugs (autosubmitting to Bugzilla, etc.) 2) generate repro in case of segfault. but having basic functionality (autogenerating reprocase in temprorary file) already seems quite useful. -Maxim --------------070303050901030608050107 Content-Type: text/x-patch; name="ICE.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="ICE.diff" Content-length: 14867 2014-08-04 Jakub Jelinek Max Ostapenko * common.opt: New option. * doc/invoke.texi: Describe new option. * 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. (do_report_bug): Likewise. (append_text): Likewise. (try_generate_repro): Likewise diff --git a/gcc/common.opt b/gcc/common.opt index 0c4f86b..aa79250 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1120,6 +1120,11 @@ fdump-noaddr Common Report Var(flag_dump_noaddr) Suppress output of addresses in debugging dumps +freport-bug +Common Driver Var(flag_report_bug) +Collect and dump debug information into temporary file if ICE in C/C++ +compiler occured. + fdump-passes Common Var(flag_dump_passes) Init(0) Dump optimization passes 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/doc/invoke.texi b/gcc/doc/invoke.texi index 4f327df..dafb573 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -6271,6 +6271,11 @@ feasible to use diff on debugging dumps for compiler invocations with different compiler binaries and/or different text / bss / data / heap / stack / dso start locations. +@item -freport-bug +@opindex freport-bug +Collect and dump debug information into temporary file if ICE in C/C++ +compiler occured. + @item -fdump-unnumbered @opindex fdump-unnumbered When doing debugging dumps, suppress instruction numbers and address output. diff --git a/gcc/gcc.c b/gcc/gcc.c index 44d0416..f7a56d1 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 +#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 **); @@ -2849,7 +2859,7 @@ execute (void) } } - if (string != commands[i].prog) + if (i && string != commands[i].prog) free (CONST_CAST (char *, string)); } @@ -2902,6 +2912,17 @@ 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 (flag_report_bug + && 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; @@ -2959,6 +2980,9 @@ execute (void) } } + if (commands[0].argv[0] != commands[0].prog) + free (CONST_CAST (char *, commands[0].argv[0])); + return ret_code; } } @@ -6150,6 +6174,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 +do_report_bug (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//", 3); + for (i = 0; i < nargs; i++) + { + write (fd, " ", 1); + write (fd, new_argv[i], strlen (new_argv[i])); + } + write (fd, "\n\n", 2); + 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_files, '\0', sizeof (temp_files)); + new_argv = XALLOCAVEC (const char *, nargs + 4); + memcpy (new_argv, argv, (nargs + 1) * sizeof (const char *)); + new_argv[nargs++] = "-frandom-seed=0"; + new_argv[nargs++] = "-fdump-noaddr"; + 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. */ + do_report_bug (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. */ @@ -6919,43 +7279,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) --------------070303050901030608050107--