From patchwork Sun May 8 16:53:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Janne Blomqvist X-Patchwork-Id: 94594 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 1E0AE1007D5 for ; Mon, 9 May 2011 02:54:07 +1000 (EST) Received: (qmail 11604 invoked by alias); 8 May 2011 16:54:06 -0000 Received: (qmail 11578 invoked by uid 22791); 8 May 2011 16:54:01 -0000 X-SWARE-Spam-Status: No, hits=-2.3 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, RFC_ABUSE_POST, TW_BG, TW_TP, TW_VF X-Spam-Check-By: sourceware.org Received: from mail-px0-f172.google.com (HELO mail-px0-f172.google.com) (209.85.212.172) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 08 May 2011 16:53:43 +0000 Received: by pxi6 with SMTP id 6so3639265pxi.17 for ; Sun, 08 May 2011 09:53:43 -0700 (PDT) MIME-Version: 1.0 Received: by 10.68.36.234 with SMTP id t10mr7845098pbj.361.1304873623155; Sun, 08 May 2011 09:53:43 -0700 (PDT) Received: by 10.68.42.197 with HTTP; Sun, 8 May 2011 09:53:43 -0700 (PDT) In-Reply-To: References: Date: Sun, 8 May 2011 19:53:43 +0300 Message-ID: Subject: Re: [Patch, libfortran] Thread safety and simplification of error printing From: Janne Blomqvist To: Fortran List , GCC Patches Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org On Sun, May 8, 2011 at 14:40, Janne Blomqvist wrote: > On Sat, May 7, 2011 at 19:35, Janne Blomqvist wrote: >> Hi, >> >> the error printing functionality (in io/unix.c) st_printf and >> st_vprintf are not thread-safe as they use a static buffer. However, >> since these routines are used when something has gone wrong, we >> shouldn't use malloc() to allocate thread-specific buffers or anything >> fancy like that. The way the buffer is used is that it is filled with >> vs(n)printf() and then the buffer is written using write(); the >> simplest way to fix this is to just print directly using vfprintf(), >> which is what this patch does. Also, the patch uses >> flockfile/funlockfile in a few places to increase the likelihood of >> multiple related messages to end up together on the output. >> >> As stderr is unbuffered and stdout is buffered, this patch also gets >> rid of the functionality to choose where to send the error output, as >> we don't want to deal with buffering in the error handling path. IMHO >> this is no loss, as all targets/shells/batch schedulers/ etc. where >> this might be relevant, offer functionality to redirect stderr to >> wherever stdout goes. So there is no need for a gfortran-specific >> mechanism for this. > > While this patch makes error printing thread-safe, it's no longer > async-signal-safe as the stderr lock might lead to a deadlock. So I'm > retracting this patch and thinking some more about this problem. So here is an updated patch. Compared to the status quo the main differences are that it uses a stack allocated buffer to do the formatting instead of a static one. This prevents conflicts from multiple threads and in contrast to the approach in the previous patch should also be async-signal-safe (or at least, less likely to go belly-up when called from within a signal handler than using vfprintf() directly). It also introduces a new function to write a string to standard error without any formatting (estr_write), which is simpler and less likely to cause problems than the general purpose vs(n)printf() used before. This can be used in lieu of st_printf in the majority of cases where no formatting is done. Regtested on x86_64-unknown-linux-gnu, Ok for trunk? frontend ChangeLog: 2011-05-08 Janne Blomqvist * gfortran.texi: Remove GFORTRAN_USE_STDERR documentation. library ChangeLog: 2011-05-08 Janne Blomqvist * io/unix.c (st_vprintf,st_printf): Move to runtime/error.c. * libgfortran.h (struct options_t): Remove use_stderr field. (st_vprintf,st_printf): Move prototypes. (estr_write): New prototype. * runtime/error.c (sys_exit): Use estr_write instead of st_printf. (estr_write): New function. (st_vprintf): Move from io/unix.c, use stack allocated buffer, always output to stderr. (st_printf): Move from io/unix.c. (show_locus): Use a local variable instead of static. (os_error): Use estr_write instead of st_printf. (runtime_error): Likewise. (runtime_error_at): Likewise. (runtime_warning_at): Likewise. (internal_error): Likewise. (generate_error): Likewise. (generate_warning): Likewise. (notify_std): Likewise. * runtime/pause.c (do_pause): Likewise. (pause_string): Likewise. * runtime/stop.c (stop_string): Likewise. (error_stop_string): Likewise. * config/fpu_aix.h (set_fpu): Likewise. * config/fpu_generic.h (set_fpu): Likewise. * config/fpu_glibc.h (set_fpu): Likewise. * config/fpu-sysv.h (set_fpu): Likewise. * runtime/backtrace.c (dump_glibc_backtrace): Likewise. (show_backtrace): Likewise. * runtime/environ.c (print_spaces): Likewise. (show_string): Likewise. (show_variables): Likewise. (variable_table[]): Remove GFORTRAN_USE_STDERR entry. diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi index 1284c3d..c810fe2 100644 --- a/gcc/fortran/gfortran.texi +++ b/gcc/fortran/gfortran.texi @@ -579,7 +579,6 @@ Malformed environment variables are silently ignored. * GFORTRAN_STDIN_UNIT:: Unit number for standard input * GFORTRAN_STDOUT_UNIT:: Unit number for standard output * GFORTRAN_STDERR_UNIT:: Unit number for standard error -* GFORTRAN_USE_STDERR:: Send library output to standard error * GFORTRAN_TMPDIR:: Directory for scratch files * GFORTRAN_UNBUFFERED_ALL:: Don't buffer I/O for all units. * GFORTRAN_UNBUFFERED_PRECONNECTED:: Don't buffer I/O for preconnected units. @@ -613,14 +612,6 @@ This environment variable can be used to select the unit number preconnected to standard error. This must be a positive integer. The default value is 0. -@node GFORTRAN_USE_STDERR -@section @env{GFORTRAN_USE_STDERR}---Send library output to standard error - -This environment variable controls where library output is sent. -If the first letter is @samp{y}, @samp{Y} or @samp{1}, standard -error is used. If the first letter is @samp{n}, @samp{N} or -@samp{0}, standard output is used. - @node GFORTRAN_TMPDIR @section @env{GFORTRAN_TMPDIR}---Directory for scratch files diff --git a/libgfortran/config/fpu-aix.h b/libgfortran/config/fpu-aix.h index 7d6f8df..262557b 100644 --- a/libgfortran/config/fpu-aix.h +++ b/libgfortran/config/fpu-aix.h @@ -1,8 +1,8 @@ /* AIX FPU-related code. - Copyright 2005, 2007, 2009 Free Software Foundation, Inc. + Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc. Contributed by Francois-Xavier Coudert -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -38,44 +38,44 @@ set_fpu (void) #ifdef TRP_INVALID mode |= TRP_INVALID; #else - st_printf ("Fortran runtime warning: IEEE 'invalid operation' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'invalid operation' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_DENORMAL) - st_printf ("Fortran runtime warning: IEEE 'denormal number' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'denormal number' " + "exception not supported.\n"); if (options.fpe & GFC_FPE_ZERO) #ifdef TRP_DIV_BY_ZERO mode |= TRP_DIV_BY_ZERO; #else - st_printf ("Fortran runtime warning: IEEE 'division by zero' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'division by zero' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_OVERFLOW) #ifdef TRP_OVERFLOW mode |= TRP_OVERFLOW; #else - st_printf ("Fortran runtime warning: IEEE 'overflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'overflow' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_UNDERFLOW) #ifdef TRP_UNDERFLOW mode |= TRP_UNDERFLOW; #else - st_printf ("Fortran runtime warning: IEEE 'underflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'underflow' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_PRECISION) #ifdef TRP_UNDERFLOW mode |= TRP_UNDERFLOW; #else - st_printf ("Fortran runtime warning: IEEE 'loss of precision' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'loss of precision' " + "exception not supported.\n"); #endif fp_trap(FP_TRAP_SYNC); diff --git a/libgfortran/config/fpu-generic.h b/libgfortran/config/fpu-generic.h index 234e6e2..72de91b 100644 --- a/libgfortran/config/fpu-generic.h +++ b/libgfortran/config/fpu-generic.h @@ -1,8 +1,8 @@ /* Fallback FPU-related code (for systems not otherwise supported). - Copyright 2005, 2007, 2009 Free Software Foundation, Inc. + Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc. Contributed by Francois-Xavier Coudert -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -32,21 +32,21 @@ void set_fpu (void) { if (options.fpe & GFC_FPE_INVALID) - st_printf ("Fortran runtime warning: IEEE 'invalid operation' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'invalid operation' " + "exception not supported.\n"); if (options.fpe & GFC_FPE_DENORMAL) - st_printf ("Fortran runtime warning: IEEE 'denormal number' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'denormal number' " + "exception not supported.\n"); if (options.fpe & GFC_FPE_ZERO) - st_printf ("Fortran runtime warning: IEEE 'division by zero' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'division by zero' " + "exception not supported.\n"); if (options.fpe & GFC_FPE_OVERFLOW) - st_printf ("Fortran runtime warning: IEEE 'overflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'overflow' " + "exception not supported.\n"); if (options.fpe & GFC_FPE_UNDERFLOW) - st_printf ("Fortran runtime warning: IEEE 'underflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'underflow' " + "exception not supported.\n"); if (options.fpe & GFC_FPE_PRECISION) - st_printf ("Fortran runtime warning: IEEE 'loss of precision' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'loss of precision' " + "exception not supported.\n"); } diff --git a/libgfortran/config/fpu-glibc.h b/libgfortran/config/fpu-glibc.h index 807f094..669b7ad 100644 --- a/libgfortran/config/fpu-glibc.h +++ b/libgfortran/config/fpu-glibc.h @@ -1,8 +1,8 @@ /* FPU-related code for systems with GNU libc. - Copyright 2005, 2007, 2009 Free Software Foundation, Inc. + Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc. Contributed by Francois-Xavier Coudert -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -40,8 +40,8 @@ void set_fpu (void) #ifdef FE_INVALID feenableexcept (FE_INVALID); #else - st_printf ("Fortran runtime warning: IEEE 'invalid operation' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'invalid operation' " + "exception not supported.\n"); #endif /* glibc does never have a FE_DENORMAL. */ @@ -49,39 +49,39 @@ void set_fpu (void) #ifdef FE_DENORMAL feenableexcept (FE_DENORMAL); #else - st_printf ("Fortran runtime warning: IEEE 'denormal number' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'denormal number' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_ZERO) #ifdef FE_DIVBYZERO feenableexcept (FE_DIVBYZERO); #else - st_printf ("Fortran runtime warning: IEEE 'division by zero' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'division by zero' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_OVERFLOW) #ifdef FE_OVERFLOW feenableexcept (FE_OVERFLOW); #else - st_printf ("Fortran runtime warning: IEEE 'overflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'overflow' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_UNDERFLOW) #ifdef FE_UNDERFLOW feenableexcept (FE_UNDERFLOW); #else - st_printf ("Fortran runtime warning: IEEE 'underflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'underflow' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_PRECISION) #ifdef FE_INEXACT feenableexcept (FE_INEXACT); #else - st_printf ("Fortran runtime warning: IEEE 'loss of precision' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'loss of precision' " + "exception not supported.\n"); #endif } diff --git a/libgfortran/config/fpu-sysv.h b/libgfortran/config/fpu-sysv.h index 85ca252..4770089 100644 --- a/libgfortran/config/fpu-sysv.h +++ b/libgfortran/config/fpu-sysv.h @@ -1,8 +1,8 @@ /* SysV FPU-related code (for systems not otherwise supported). - Copyright 2005, 2007, 2009 Free Software Foundation, Inc. + Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc. Contributed by Francois-Xavier Coudert -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -34,48 +34,48 @@ set_fpu (void) #ifdef FP_X_INV cw |= FP_X_INV; #else - st_printf ("Fortran runtime warning: IEEE 'invalid operation' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'invalid operation' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_DENORMAL) #ifdef FP_X_DNML cw |= FP_X_DNML; #else - st_printf ("Fortran runtime warning: IEEE 'denormal number' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'denormal number' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_ZERO) #ifdef FP_X_DZ cw |= FP_X_DZ; #else - st_printf ("Fortran runtime warning: IEEE 'division by zero' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'division by zero' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_OVERFLOW) #ifdef FP_X_OFL cw |= FP_X_OFL; #else - st_printf ("Fortran runtime warning: IEEE 'overflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'overflow' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_UNDERFLOW) #ifdef FP_X_UFL cw |= FP_X_UFL; #else - st_printf ("Fortran runtime warning: IEEE 'underflow' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'underflow' " + "exception not supported.\n"); #endif if (options.fpe & GFC_FPE_PRECISION) #ifdef FP_X_IMP cw |= FP_X_IMP; #else - st_printf ("Fortran runtime warning: IEEE 'loss of precision' " - "exception not supported.\n"); + estr_write ("Fortran runtime warning: IEEE 'loss of precision' " + "exception not supported.\n"); #endif fpsetmask(cw); diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index 4e4bc3b..c257766 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -1353,61 +1353,6 @@ error_stream (void) } -/* st_vprintf()-- vprintf function for error output. To avoid buffer - overruns, we limit the length of the buffer to ST_VPRINTF_SIZE. 2k - is big enough to completely fill a 80x25 terminal, so it shuld be - OK. We use a direct write() because it is simpler and least likely - to be clobbered by memory corruption. Writing an error message - longer than that is an error. */ - -#define ST_VPRINTF_SIZE 2048 - -int -st_vprintf (const char *format, va_list ap) -{ - static char buffer[ST_VPRINTF_SIZE]; - int written; - int fd; - - fd = options.use_stderr ? STDERR_FILENO : STDOUT_FILENO; -#ifdef HAVE_VSNPRINTF - written = vsnprintf(buffer, ST_VPRINTF_SIZE, format, ap); -#else - written = vsprintf(buffer, format, ap); - - if (written >= ST_VPRINTF_SIZE-1) - { - /* The error message was longer than our buffer. Ouch. Because - we may have messed up things badly, report the error and - quit. */ -#define ERROR_MESSAGE "Internal error: buffer overrun in st_vprintf()\n" - write (fd, buffer, ST_VPRINTF_SIZE-1); - write (fd, ERROR_MESSAGE, strlen(ERROR_MESSAGE)); - sys_exit(2); -#undef ERROR_MESSAGE - - } -#endif - - written = write (fd, buffer, written); - return written; -} - -/* st_printf()-- printf() function for error output. This just calls - st_vprintf() to do the actual work. */ - -int -st_printf (const char *format, ...) -{ - int written; - va_list ap; - va_start (ap, format); - written = st_vprintf(format, ap); - va_end (ap); - return written; -} - - /* compare_file_filename()-- Given an open stream and a fortran string * that is a filename, figure out if the file is the same as the * filename. */ diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h index 6cccaca..ae86a02 100644 --- a/libgfortran/libgfortran.h +++ b/libgfortran/libgfortran.h @@ -508,7 +508,7 @@ typedef struct int separator_len; const char *separator; - int use_stderr, all_unbuffered, unbuffered_preconnected, default_recl; + int all_unbuffered, unbuffered_preconnected, default_recl; int fpe, dump_core, backtrace; } options_t; @@ -691,6 +691,16 @@ internal_proto(show_backtrace); extern void sys_exit (int) __attribute__ ((noreturn)); internal_proto(sys_exit); +extern ssize_t estr_write (const char *); +internal_proto(estr_write); + +extern int st_vprintf (const char *, va_list); +internal_proto(st_vprintf); + +extern int st_printf (const char *, ...) + __attribute__((format (gfc_printf, 1, 2))); +internal_proto(st_printf); + extern const char *gfc_xtoa (GFC_UINTEGER_LARGEST, char *, size_t); internal_proto(gfc_xtoa); @@ -792,13 +802,6 @@ internal_proto(close_units); extern int unit_to_fd (int); internal_proto(unit_to_fd); -extern int st_printf (const char *, ...) - __attribute__ ((format (gfc_printf, 1, 2))); -internal_proto(st_printf); - -extern int st_vprintf (const char *, va_list); -internal_proto(st_vprintf); - extern char * filename_from_unit (int); internal_proto(filename_from_unit); diff --git a/libgfortran/runtime/backtrace.c b/libgfortran/runtime/backtrace.c index 5e4f15c..aa77025 100644 --- a/libgfortran/runtime/backtrace.c +++ b/libgfortran/runtime/backtrace.c @@ -95,7 +95,11 @@ dump_glibc_backtrace (int depth, char *str[]) int i; for (i = 0; i < depth; i++) - st_printf (" + %s\n", str[i]); + { + estr_write (" + "); + estr_write (str[i]); + estr_write ("\n"); + } free (str); } @@ -192,7 +196,7 @@ show_backtrace (void) if (fgets (func, sizeof(func), output)) { - st_printf ("\nBacktrace for this error:\n"); + estr_write ("\nBacktrace for this error:\n"); do { @@ -222,7 +226,9 @@ show_backtrace (void) if (func[0] == '?' && func[1] == '?' && file[0] == '?' && file[1] == '?') { - st_printf (" + %s\n", str[i]); + estr_write (" + "); + estr_write (str[i]); + estr_write ("\n"); continue; } @@ -239,15 +245,25 @@ show_backtrace (void) line = -1; if (strcmp (func, "MAIN__") == 0) - st_printf (" + in the main program\n"); + estr_write (" + in the main program\n"); else - st_printf (" + function %s (0x%s)\n", func, addr[i]); + { + estr_write (" + function "); + estr_write (func); + estr_write (" (0x"); + estr_write (addr[i]); + estr_write (")\n"); + } if (line <= 0 && strcmp (file, "??") == 0) continue; if (line <= 0) - st_printf (" from file %s\n", file); + { + estr_write (" from file "); + estr_write (file); + estr_write ("\n"); + } else st_printf (" at line %d of file %s\n", line, file); } @@ -257,8 +273,8 @@ show_backtrace (void) return; fallback: - st_printf ("** Something went wrong while running addr2line. **\n" - "** Falling back to a simpler backtrace scheme. **\n"); + estr_write ("** Something went wrong while running addr2line. **\n" + "** Falling back to a simpler backtrace scheme. **\n"); } } while (0); @@ -288,7 +304,7 @@ fallback: char *arg[NUM_ARGS+1]; char buf[20]; - st_printf ("\nBacktrace for this error:\n"); + estr_write ("\nBacktrace for this error:\n"); arg[0] = (char *) "pstack"; snprintf (buf, sizeof(buf), "%d", (int) getppid ()); arg[1] = buf; @@ -301,7 +317,7 @@ fallback: #if GLIBC_BACKTRACE dump_glibc_backtrace (depth, str); #else - st_printf (" unable to produce a backtrace, sorry!\n"); + estr_write (" unable to produce a backtrace, sorry!\n"); #endif _exit (0); @@ -316,7 +332,7 @@ fallback: #if GLIBC_BACKTRACE /* Fallback to the glibc backtrace. */ - st_printf ("\nBacktrace for this error:\n"); + estr_write ("\nBacktrace for this error:\n"); dump_glibc_backtrace (depth, str); #endif } diff --git a/libgfortran/runtime/environ.c b/libgfortran/runtime/environ.c index a6ce645..7695f0d 100644 --- a/libgfortran/runtime/environ.c +++ b/libgfortran/runtime/environ.c @@ -71,7 +71,7 @@ print_spaces (int n) buffer[i] = '\0'; - st_printf (buffer); + estr_write (buffer); } @@ -261,7 +261,10 @@ show_string (variable * v) if (p == NULL) p = ""; - st_printf ("%s \"%s\"\n", var_source (v), p); + estr_write (var_source (v)); + estr_write (" \""); + estr_write (p); + estr_write ("\"\n"); } @@ -281,10 +284,6 @@ static variable variable_table[] = { "Unit number that will be preconnected to standard error\n" "(No preconnection if negative)", 0}, - {"GFORTRAN_USE_STDERR", 1, &options.use_stderr, init_boolean, - show_boolean, - "Sends library output to standard error instead of standard output.", 0}, - {"GFORTRAN_TMPDIR", 0, NULL, init_string, show_string, "Directory for scratch files. Overrides the TMP environment variable\n" "If TMP is not set " DEFAULT_TEMPDIR " is used.", 0}, @@ -352,32 +351,33 @@ show_variables (void) int n; /* TODO: print version number. */ - st_printf ("GNU Fortran 95 runtime library version " + estr_write ("GNU Fortran runtime library version " "UNKNOWN" "\n\n"); - st_printf ("Environment variables:\n"); - st_printf ("----------------------\n"); + estr_write ("Environment variables:\n"); + estr_write ("----------------------\n"); for (v = variable_table; v->name; v++) { - n = st_printf ("%s", v->name); + n = estr_write (v->name); print_spaces (25 - n); if (v->show == show_integer) - st_printf ("Integer "); + estr_write ("Integer "); else if (v->show == show_boolean) - st_printf ("Boolean "); + estr_write ("Boolean "); else - st_printf ("String "); + estr_write ("String "); v->show (v); - st_printf ("%s\n\n", v->desc); + estr_write (v->desc); + estr_write ("\n\n"); } /* System error codes */ - st_printf ("\nRuntime error codes:"); - st_printf ("\n--------------------\n"); + estr_write ("\nRuntime error codes:"); + estr_write ("\n--------------------\n"); for (n = LIBERROR_FIRST + 1; n < LIBERROR_LAST; n++) if (n < 0 || n > 9) @@ -385,10 +385,8 @@ show_variables (void) else st_printf (" %d %s\n", n, translate_error (n)); - st_printf ("\nCommand line arguments:\n"); - st_printf (" --help Print this list\n"); - - /* st_printf(" --resume Resume program execution from dropfile\n"); */ + estr_write ("\nCommand line arguments:\n"); + estr_write (" --help Print this list\n"); sys_exit (0); } diff --git a/libgfortran/runtime/error.c b/libgfortran/runtime/error.c index 06c144a..e619044 100644 --- a/libgfortran/runtime/error.c +++ b/libgfortran/runtime/error.c @@ -81,7 +81,7 @@ sys_exit (int code) struct rlimit core_limit; if (getrlimit (RLIMIT_CORE, &core_limit) == 0 && core_limit.rlim_cur == 0) - st_printf ("** Warning: a core dump was requested, but the core size" + estr_write ("** Warning: a core dump was requested, but the core size" "limit\n** is currently zero.\n\n"); #endif @@ -89,7 +89,7 @@ sys_exit (int code) #if defined(HAVE_KILL) && defined(HAVE_GETPID) && defined(SIGQUIT) kill (getpid (), SIGQUIT); #else - st_printf ("Core dump not possible, sorry."); + estr_write ("Core dump not possible, sorry."); #endif } @@ -112,6 +112,67 @@ sys_exit (int code) * Other error returns are reserved for the STOP statement with a numeric code. */ + +/* Write a null-terminated C string to standard error. This function + is async-signal-safe. */ + +ssize_t +estr_write (const char *str) +{ + return write (STDERR_FILENO, str, strlen (str)); +} + + +/* st_vprintf()-- vsnprintf-like function for error output. We use a + stack allocated buffer for formatting; since this function might be + called from within a signal handler, printing directly to stderr + with vfprintf is not safe since the stderr locking might lead to a + deadlock. */ + +#define ST_VPRINTF_SIZE 512 + +int +st_vprintf (const char *format, va_list ap) +{ + int written; + char buffer[ST_VPRINTF_SIZE]; + +#ifdef HAVE_VSNPRINTF + written = vsnprintf(buffer, ST_VPRINTF_SIZE, format, ap); +#else + written = vsprintf(buffer, format, ap); + + if (written >= ST_VPRINTF_SIZE - 1) + { + /* The error message was longer than our buffer. Ouch. Because + we may have messed up things badly, report the error and + quit. */ +#define ERROR_MESSAGE "Internal error: buffer overrun in st_vprintf()\n" + write (STDERR_FILENO, buffer, ST_VPRINTF_SIZE - 1); + write (STDERR_FILENO, ERROR_MESSAGE, strlen(ERROR_MESSAGE)); + sys_exit(2); +#undef ERROR_MESSAGE + + } +#endif + + written = write (STDERR_FILENO, buffer, written); + return written; +} + + +int +st_printf (const char * format, ...) +{ + int written; + va_list ap; + va_start (ap, format); + written = st_vprintf (format, ap); + va_end (ap); + return written; +} + + /* gfc_xtoa()-- Integer to hexadecimal conversion. */ const char * @@ -177,7 +238,7 @@ gf_strerror (int errnum, void show_locus (st_parameter_common *cmp) { - static char *filename; + char *filename; if (!options.locus || cmp == NULL || cmp->filename == NULL) return; @@ -185,6 +246,7 @@ show_locus (st_parameter_common *cmp) if (cmp->unit > 0) { filename = filename_from_unit (cmp->unit); + if (filename != NULL) { st_printf ("At line %d of file %s (unit = %d, file = '%s')\n", @@ -233,8 +295,11 @@ os_error (const char *message) { char errmsg[STRERR_MAXSZ]; recursion_check (); - st_printf ("Operating system error: %s\n%s\n", - gf_strerror (errno, errmsg, STRERR_MAXSZ), message); + estr_write ("Operating system error: "); + estr_write (gf_strerror (errno, errmsg, STRERR_MAXSZ)); + estr_write ("\n"); + estr_write (message); + estr_write ("\n"); sys_exit (1); } iexport(os_error); @@ -249,11 +314,11 @@ runtime_error (const char *message, ...) va_list ap; recursion_check (); - st_printf ("Fortran runtime error: "); + estr_write ("Fortran runtime error: "); va_start (ap, message); st_vprintf (message, ap); va_end (ap); - st_printf ("\n"); + estr_write ("\n"); sys_exit (2); } iexport(runtime_error); @@ -267,12 +332,12 @@ runtime_error_at (const char *where, const char *message, ...) va_list ap; recursion_check (); - st_printf ("%s\n", where); - st_printf ("Fortran runtime error: "); + estr_write (where); + estr_write ("\nFortran runtime error: "); va_start (ap, message); st_vprintf (message, ap); va_end (ap); - st_printf ("\n"); + estr_write ("\n"); sys_exit (2); } iexport(runtime_error_at); @@ -283,12 +348,12 @@ runtime_warning_at (const char *where, const char *message, ...) { va_list ap; - st_printf ("%s\n", where); - st_printf ("Fortran runtime warning: "); + estr_write (where); + estr_write ("\nFortran runtime warning: "); va_start (ap, message); st_vprintf (message, ap); va_end (ap); - st_printf ("\n"); + estr_write ("\n"); } iexport(runtime_warning_at); @@ -301,7 +366,9 @@ internal_error (st_parameter_common *cmp, const char *message) { recursion_check (); show_locus (cmp); - st_printf ("Internal Error: %s\n", message); + estr_write ("Internal Error: "); + estr_write (message); + estr_write ("\n"); /* This function call is here to get the main.o object file included when linking statically. This works because error.o is supposed to @@ -474,7 +541,9 @@ generate_error (st_parameter_common *cmp, int family, const char *message) recursion_check (); show_locus (cmp); - st_printf ("Fortran runtime error: %s\n", message); + estr_write ("Fortran runtime error: "); + estr_write (message); + estr_write ("\n"); sys_exit (2); } iexport(generate_error); @@ -489,7 +558,9 @@ generate_warning (st_parameter_common *cmp, const char *message) message = " "; show_locus (cmp); - st_printf ("Fortran runtime warning: %s\n", message); + estr_write ("Fortran runtime warning: "); + estr_write (message); + estr_write ("\n"); } @@ -532,13 +603,17 @@ notify_std (st_parameter_common *cmp, int std, const char * message) { recursion_check (); show_locus (cmp); - st_printf ("Fortran runtime error: %s\n", message); + estr_write ("Fortran runtime error: "); + estr_write (message); + estr_write ("\n"); sys_exit (2); } else { show_locus (cmp); - st_printf ("Fortran runtime warning: %s\n", message); + estr_write ("Fortran runtime warning: "); + estr_write (message); + estr_write ("\n"); } return FAILURE; } diff --git a/libgfortran/runtime/pause.c b/libgfortran/runtime/pause.c index 61ab4db..0e3c8ae 100644 --- a/libgfortran/runtime/pause.c +++ b/libgfortran/runtime/pause.c @@ -1,8 +1,8 @@ -/* Implementation of the STOP statement. - Copyright 2002, 2005, 2007, 2009, 2010 Free Software Foundation, Inc. +/* Implementation of the PAUSE statement. + Copyright 2002, 2005, 2007, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Paul Brook -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -25,18 +25,19 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #include "libgfortran.h" #include +#include static void do_pause (void) { char buff[4]; - st_printf ("To resume execution, type go. " - "Other input will terminate the job.\n"); + estr_write ("To resume execution, type go. " + "Other input will terminate the job.\n"); fgets(buff, 4, stdin); if (strncmp(buff, "go\n", 3) != 0) stop_string ('\0', 0); - st_printf ("RESUMED\n"); + estr_write ("RESUMED\n"); } /* A numeric PAUSE statement. */ @@ -59,10 +60,11 @@ export_proto(pause_string); void pause_string (char *string, GFC_INTEGER_4 len) { - st_printf ("PAUSE "); - while (len--) - st_printf ("%c", *(string++)); - st_printf ("\n"); + estr_write ("PAUSE "); + ssize_t w = write (STDERR_FILENO, string, len); + (void) sizeof (w); /* Avoid compiler warning about not using write + return val. */ + estr_write ("\n"); do_pause (); } diff --git a/libgfortran/runtime/stop.c b/libgfortran/runtime/stop.c index 29f5031..b6f61ff 100644 --- a/libgfortran/runtime/stop.c +++ b/libgfortran/runtime/stop.c @@ -1,8 +1,8 @@ /* Implementation of the STOP statement. - Copyright 2002, 2005, 2007, 2009, 2010 Free Software Foundation, Inc. + Copyright 2002, 2005, 2007, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Paul Brook -This file is part of the GNU Fortran 95 runtime library (libgfortran). +This file is part of the GNU Fortran runtime library (libgfortran). Libgfortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public @@ -25,6 +25,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see #include "libgfortran.h" #include +#include /* A numeric STOP statement. */ @@ -65,10 +66,10 @@ stop_string (const char *string, GFC_INTEGER_4 len) { if (string) { - st_printf ("STOP "); - while (len--) - st_printf ("%c", *(string++)); - st_printf ("\n"); + estr_write ("STOP "); + ssize_t w = write (STDERR_FILENO, string, len); + (void) sizeof (w); /* Avoid compiler warning about not using w. */ + estr_write ("\n"); } sys_exit (0); } @@ -86,10 +87,10 @@ export_proto(error_stop_string); void error_stop_string (const char *string, GFC_INTEGER_4 len) { - st_printf ("ERROR STOP "); - while (len--) - st_printf ("%c", *(string++)); - st_printf ("\n"); + estr_write ("ERROR STOP "); + ssize_t w = write (STDERR_FILENO, string, len); + (void) sizeof (w); /* Avoid compiler warning about not using w. */ + estr_write ("\n"); sys_exit (1); }