diff mbox

[Ping,v2] Add patch for debugging compiler ICEs.

Message ID 53FEDAC2.9050306@partner.samsung.com
State New
Headers show

Commit Message

max Aug. 28, 2014, 7:31 a.m. UTC
Ping. Add Joseph S. Myers as driver maintainer.

-Maxim
-------- Original Message --------
Subject: 	Fwd: [PATCH] Add patch for debugging compiler ICEs.
Date: 	Tue, 19 Aug 2014 17:57:51 +0400
From: 	Maxim Ostapenko <m.ostapenko@partner.samsung.com>
To: 	Jeff Law <law@redhat.com>, GCC Patches <gcc-patches@gcc.gnu.org>
CC: 	tsaunders@mozilla.com, Yury Gribov <y.gribov@samsung.com>, Slava 
Garbuzov <v.garbuzov@samsung.com>, Maxim Ostapenko <chefmax7@gmail.com>



Ping.

-Maxim
-------- Original Message --------
Subject: 	[PATCH] Add patch for debugging compiler ICEs.
Date: 	Mon, 04 Aug 2014 21:03:22 +0400
From: 	Maxim Ostapenko <m.ostapenko@partner.samsung.com>
To: 	GCC Patches <gcc-patches@gcc.gnu.org>
CC: 	Jeff Law <law@redhat.com>, Jakub Jelinek <jakub@redhat.com>,
tsaunders@mozilla.com, Yury Gribov <y.gribov@samsung.com>, Slava
Garbuzov <v.garbuzov@samsung.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.

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

Comments

Joseph Myers Sept. 9, 2014, 10:51 p.m. UTC | #1
On Thu, 28 Aug 2014, Maxim Ostapenko wrote:

> 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);

Why?  This is the case for fatal_error.  FATAL_EXIT_CODE seems right for 
this, and ICE_EXIT_CODE wrong.

> 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 <unistd.h>
> +#endif

No, unistd.h is already included in system.h, so no other (host-side) 
source files need to include it directly.

> +#if !(defined (__MSDOS__) || defined (OS2) || defined (VMS))
> +#define RETRY_ICE_SUPPORTED
> +#endif

I don't think it's a good idea to have this sort of host conditional in 
random source files if it can be avoided.  And feature tests (e.g. 
HAVE_FORK) are better than tests for particular systems.

In any case, it would be better to use libiberty's pexecute interface for 
running subprocesses, as used elsewhere in GCC, rather than using POSIX 
interfaces directly, unless there's a clear reason (which needs explaining 
in the patch submission) why it's unsuitable.  Hopefully that would 
eliminate the need for this conditional.

> +static void
> +print_configuration (void)

All the new functions need comments above them giving the semantics of the 
function and any arguments and return values.

> +static int
> +files_equal_p (char *file1, char *file2, char *buf, const int bufsize)
> +{
> +  struct stat st1, st2;
> +  size_t n, len;

> +  for (n = st1.st_size; n; n -= len)

This assignment would silently truncate a large file size on 32-bit 
platforms.  A variable that can store file sizes should be off_t not 
size_t (then you need to be careful about any mixing of signed and 
unsigned types).
Jakub Jelinek Sept. 10, 2014, 4:57 a.m. UTC | #2
On Tue, Sep 09, 2014 at 10:51:23PM +0000, Joseph S. Myers wrote:
> On Thu, 28 Aug 2014, Maxim Ostapenko wrote:
> 
> > 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);
> 
> Why?  This is the case for fatal_error.  FATAL_EXIT_CODE seems right for 
> this, and ICE_EXIT_CODE wrong.

So that the driver can understand the difference between an ICE and other
fatal errors (e.g. sorry etc.).
Users are typically using the driver and for them it matters what exit code
is returned from the driver, not from cc1/cc1plus etc.

	Jakub
Joseph Myers Sept. 10, 2014, 4:37 p.m. UTC | #3
On Wed, 10 Sep 2014, Jakub Jelinek wrote:

> On Tue, Sep 09, 2014 at 10:51:23PM +0000, Joseph S. Myers wrote:
> > On Thu, 28 Aug 2014, Maxim Ostapenko wrote:
> > 
> > > 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);
> > 
> > Why?  This is the case for fatal_error.  FATAL_EXIT_CODE seems right for 
> > this, and ICE_EXIT_CODE wrong.
> 
> So that the driver can understand the difference between an ICE and other
> fatal errors (e.g. sorry etc.).
> Users are typically using the driver and for them it matters what exit code
> is returned from the driver, not from cc1/cc1plus etc.

Well, I think the next revision of the patch submission needs more 
explanation in this area.  What exit codes do cc1 and the driver now 
return for (normal error, fatal error, ICE), and what do they return after 
the patch, and how does the change to the fatal_error case avoid incorrect 
changes if either cc1 or the driver called fatal_error (as opposed to 
either cc1 or the driver having an ICE)?  Maybe that explanation should be 
in the form of a comment on this exit call, explaining why the 
counterintuitive use of ICE_EXIT_CODE in the DK_FATAL case is correct.
diff mbox

Patch

2014-08-04  Jakub Jelinek  <jakub@redhat.com>
	      Max Ostapenko  <m.ostapenko@partner.samsung.com>

	* 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 <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 **);
@@ -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)