/* 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 . */ #include "libgfortran.h" #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_INTTYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_EXECINFO_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #include /* 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 }