| /* Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc. |
| Contributed by François-Xavier Coudert |
| |
| This file is part of the GNU Fortran 95 runtime library (libgfortran). |
| |
| Libgfortran is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| Libgfortran is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "libgfortran.h" |
| |
| #include <string.h> |
| |
| #ifdef HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| |
| #ifdef HAVE_INTTYPES_H |
| #include <inttypes.h> |
| #endif |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| |
| #ifdef HAVE_EXECINFO_H |
| #include <execinfo.h> |
| #endif |
| |
| #ifdef HAVE_SYS_WAIT_H |
| #include <sys/wait.h> |
| #endif |
| |
| #include <ctype.h> |
| |
| |
| /* Macros for common sets of capabilities: can we fork and exec, can |
| we use glibc-style backtrace functions, and can we use pipes. */ |
| #define CAN_FORK (defined(HAVE_FORK) && defined(HAVE_EXECVP) \ |
| && defined(HAVE_WAIT)) |
| #define GLIBC_BACKTRACE (defined(HAVE_BACKTRACE) \ |
| && defined(HAVE_BACKTRACE_SYMBOLS)) |
| #define CAN_PIPE (CAN_FORK && defined(HAVE_PIPE) \ |
| && defined(HAVE_DUP2) && defined(HAVE_FDOPEN) \ |
| && defined(HAVE_CLOSE)) |
| |
| |
| #if GLIBC_BACKTRACE && CAN_PIPE |
| static char * |
| local_strcasestr (const char *s1, const char *s2) |
| { |
| #ifdef HAVE_STRCASESTR |
| return strcasestr (s1, s2); |
| #else |
| |
| const char *p = s1; |
| const size_t len = strlen (s2); |
| const char u = *s2, v = isupper((int) *s2) ? tolower((int) *s2) |
| : (islower((int) *s2) ? toupper((int) *s2) |
| : *s2); |
| |
| while (1) |
| { |
| while (*p != u && *p != v && *p) |
| p++; |
| if (*p == 0) |
| return NULL; |
| if (strncasecmp (p, s2, len) == 0) |
| return (char *)p; |
| } |
| #endif |
| } |
| #endif |
| |
| |
| #if GLIBC_BACKTRACE |
| static void |
| dump_glibc_backtrace (int depth, char *str[]) |
| { |
| int i; |
| |
| for (i = 0; i < depth; i++) |
| st_printf (" + %s\n", str[i]); |
| |
| free (str); |
| } |
| #endif |
| |
| /* show_backtrace displays the backtrace, currently obtained by means of |
| the glibc backtrace* functions. */ |
| void |
| show_backtrace (void) |
| { |
| #if GLIBC_BACKTRACE |
| |
| #define DEPTH 50 |
| #define BUFSIZE 1024 |
| |
| void *trace[DEPTH]; |
| char **str; |
| int depth; |
| |
| depth = backtrace (trace, DEPTH); |
| if (depth <= 0) |
| return; |
| |
| str = backtrace_symbols (trace, depth); |
| |
| #if CAN_PIPE |
| |
| #ifndef STDIN_FILENO |
| #define STDIN_FILENO 0 |
| #endif |
| |
| #ifndef STDOUT_FILENO |
| #define STDOUT_FILENO 1 |
| #endif |
| |
| #ifndef STDERR_FILENO |
| #define STDERR_FILENO 2 |
| #endif |
| |
| /* We attempt to extract file and line information from addr2line. */ |
| do |
| { |
| /* Local variables. */ |
| int f[2], pid, line, i; |
| FILE *output; |
| char addr_buf[DEPTH][GFC_XTOA_BUF_SIZE], func[BUFSIZE], file[BUFSIZE]; |
| char *p, *end; |
| const char *addr[DEPTH]; |
| |
| /* Write the list of addresses in hexadecimal format. */ |
| for (i = 0; i < depth; i++) |
| addr[i] = gfc_xtoa ((GFC_UINTEGER_LARGEST) (intptr_t) trace[i], addr_buf[i], |
| sizeof (addr_buf[i])); |
| |
| /* Don't output an error message if something goes wrong, we'll simply |
| fall back to the pstack and glibc backtraces. */ |
| if (pipe (f) != 0) |
| break; |
| if ((pid = fork ()) == -1) |
| break; |
| |
| if (pid == 0) |
| { |
| /* Child process. */ |
| #define NUM_FIXEDARGS 5 |
| char *arg[DEPTH+NUM_FIXEDARGS+1]; |
| |
| close (f[0]); |
| close (STDIN_FILENO); |
| close (STDERR_FILENO); |
| |
| if (dup2 (f[1], STDOUT_FILENO) == -1) |
| _exit (0); |
| close (f[1]); |
| |
| arg[0] = (char *) "addr2line"; |
| arg[1] = (char *) "-e"; |
| arg[2] = full_exe_path (); |
| arg[3] = (char *) "-f"; |
| arg[4] = (char *) "-s"; |
| for (i = 0; i < depth; i++) |
| arg[NUM_FIXEDARGS+i] = (char *) addr[i]; |
| arg[NUM_FIXEDARGS+depth] = NULL; |
| execvp (arg[0], arg); |
| _exit (0); |
| #undef NUM_FIXEDARGS |
| } |
| |
| /* Father process. */ |
| close (f[1]); |
| wait (NULL); |
| output = fdopen (f[0], "r"); |
| i = -1; |
| |
| if (fgets (func, sizeof(func), output)) |
| { |
| st_printf ("\nBacktrace for this error:\n"); |
| |
| do |
| { |
| if (! fgets (file, sizeof(file), output)) |
| goto fallback; |
| |
| i++; |
| |
| for (p = func; *p != '\n' && *p != '\r'; p++) |
| ; |
| |
| *p = '\0'; |
| |
| /* Try to recognize the internal libgfortran functions. */ |
| if (strncasecmp (func, "*_gfortran", 10) == 0 |
| || strncasecmp (func, "_gfortran", 9) == 0 |
| || strcmp (func, "main") == 0 || strcmp (func, "_start") == 0 |
| || strcmp (func, "_gfortrani_handler") == 0) |
| continue; |
| |
| if (local_strcasestr (str[i], "libgfortran.so") != NULL |
| || local_strcasestr (str[i], "libgfortran.dylib") != NULL |
| || local_strcasestr (str[i], "libgfortran.a") != NULL) |
| continue; |
| |
| /* If we only have the address, use the glibc backtrace. */ |
| if (func[0] == '?' && func[1] == '?' && file[0] == '?' |
| && file[1] == '?') |
| { |
| st_printf (" + %s\n", str[i]); |
| continue; |
| } |
| |
| /* Extract the line number. */ |
| for (end = NULL, p = file; *p; p++) |
| if (*p == ':') |
| end = p; |
| if (end != NULL) |
| { |
| *end = '\0'; |
| line = atoi (++end); |
| } |
| else |
| line = -1; |
| |
| if (strcmp (func, "MAIN__") == 0) |
| st_printf (" + in the main program\n"); |
| else |
| st_printf (" + function %s (0x%s)\n", func, addr[i]); |
| |
| if (line <= 0 && strcmp (file, "??") == 0) |
| continue; |
| |
| if (line <= 0) |
| st_printf (" from file %s\n", file); |
| else |
| st_printf (" at line %d of file %s\n", line, file); |
| } |
| while (fgets (func, sizeof(func), output)); |
| |
| free (str); |
| return; |
| |
| fallback: |
| st_printf ("** Something went wrong while running addr2line. **\n" |
| "** Falling back to a simpler backtrace scheme. **\n"); |
| } |
| } |
| while (0); |
| |
| #undef DEPTH |
| #undef BUFSIZE |
| |
| #endif |
| #endif |
| |
| #if CAN_FORK && defined(HAVE_GETPPID) |
| /* Try to call pstack. */ |
| do |
| { |
| /* Local variables. */ |
| int pid; |
| |
| /* Don't output an error message if something goes wrong, we'll simply |
| fall back to the pstack and glibc backtraces. */ |
| if ((pid = fork ()) == -1) |
| break; |
| |
| if (pid == 0) |
| { |
| /* Child process. */ |
| #define NUM_ARGS 2 |
| char *arg[NUM_ARGS+1]; |
| char buf[20]; |
| |
| st_printf ("\nBacktrace for this error:\n"); |
| arg[0] = (char *) "pstack"; |
| #ifdef HAVE_SNPRINTF |
| snprintf (buf, sizeof(buf), "%d", (int) getppid ()); |
| #else |
| sprintf (buf, "%d", (int) getppid ()); |
| #endif |
| arg[1] = buf; |
| arg[2] = NULL; |
| execvp (arg[0], arg); |
| #undef NUM_ARGS |
| |
| /* pstack didn't work, so we fall back to dumping the glibc |
| backtrace if we can. */ |
| #if GLIBC_BACKTRACE |
| dump_glibc_backtrace (depth, str); |
| #else |
| st_printf (" unable to produce a backtrace, sorry!\n"); |
| #endif |
| |
| _exit (0); |
| } |
| |
| /* Father process. */ |
| wait (NULL); |
| return; |
| } |
| while(0); |
| #endif |
| |
| #if GLIBC_BACKTRACE |
| /* Fallback to the glibc backtrace. */ |
| st_printf ("\nBacktrace for this error:\n"); |
| dump_glibc_backtrace (depth, str); |
| #endif |
| } |