/* 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] = 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 }
void show_backtrace (void) { #if GLIBC_BACKTRACE #define DEPTH 50 #define BUFSIZE 1024 void *trace[DEPTH]; int depth; depth = backtrace (trace, DEPTH); if (depth <= 0) return; #if CAN_PIPE if (addr2line_path == NULL) goto fallback_noerr; /* We attempt to extract file and line information from addr2line. */ do { /* Local variables. */ int f[2], pid, bt[2], inp[2]; char addr_buf[GFC_XTOA_BUF_SIZE], func[BUFSIZE], file[BUFSIZE]; char *p; /* 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 (pipe (inp) != 0) break; if ((pid = fork ()) == -1) break; if (pid == 0) { /* Child process. */ #define NUM_FIXEDARGS 7 char *arg[NUM_FIXEDARGS]; char *newenv[] = { NULL }; close (f[0]); close (inp[1]); if (dup2 (inp[0], STDIN_FILENO) == -1) _exit (1); close (inp[0]); close (STDERR_FILENO); if (dup2 (f[1], STDOUT_FILENO) == -1) _exit (1); close (f[1]); arg[0] = addr2line_path; arg[1] = (char *) "-e"; arg[2] = full_exe_path (); arg[3] = (char *) "-f"; arg[4] = (char *) "-s"; arg[5] = (char *) "-C"; arg[6] = NULL; execve (addr2line_path, arg, newenv); _exit (1); #undef NUM_FIXEDARGS } /* Father process. */ close (f[1]); close (inp[0]); if (pipe (bt) != 0) break; backtrace_symbols_fd (trace, depth, bt[1]); close (bt[1]); estr_write ("\nBacktrace for this error:\n"); for (int j = 0; j < depth; j++) { const char *addr = gfc_xtoa ((GFC_UINTEGER_LARGEST) (intptr_t) trace[j], addr_buf, sizeof (addr_buf)); write (inp[1], addr, strlen (addr)); write (inp[1], "\n", 1); if (! fd_gets (func, sizeof(func), f[0])) goto fallback; if (! fd_gets (file, sizeof(file), f[0])) goto fallback; for (p = func; *p != '\n' && *p != '\r'; p++) ; *p = '\0'; /* If we only have the address, use the glibc backtrace. */ if (func[0] == '?' && func[1] == '?' && file[0] == '?' && file[1] == '?') { bt_header (j); while (1) { char bc; ssize_t nread = read (bt[0], &bc, 1); if (nread != 1 || bc == '\n') break; write (STDERR_FILENO, &bc, 1); } estr_write ("\n"); continue; } else { /* Forward to the next entry in the backtrace. */ while (1) { char bc; ssize_t nread = read (bt[0], &bc, 1); if (nread != 1 || bc == '\n') break; } } /* _start is a setup routine that calls main(), and main() is the frontend routine that calls some setup stuff and then calls MAIN__, so at this point we should stop. */ if (strcmp (func, "_start") == 0 || strcmp (func, "main") == 0) break; bt_header (j); estr_write (full_exe_path ()); estr_write ("[0x"); estr_write (addr); estr_write ("] in "); estr_write (func); if (strncmp (file, "??", 2) == 0) estr_write ("\n"); else { estr_write (" at "); estr_write (file); } } /* Loop over each hex address. */ close (inp[1]); close (bt[0]); wait (NULL); return; fallback: estr_write ("** Something went wrong while running addr2line. **\n" "** Falling back to a simpler backtrace scheme. **\n"); } while (0); #undef DEPTH #undef BUFSIZE #endif /* CAN_PIPE */ fallback_noerr: /* Fallback to the glibc backtrace. */ estr_write ("\nBacktrace for this error:\n"); backtrace_symbols_fd (trace, depth, STDERR_FILENO); return; #elif defined(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]; estr_write ("\nBacktrace for this error:\n"); arg[0] = (char *) "pstack"; snprintf (buf, sizeof(buf), "%d", (int) getppid ()); arg[1] = buf; arg[2] = NULL; execvp (arg[0], arg); #undef NUM_ARGS /* pstack didn't work. */ estr_write (" unable to produce a backtrace, sorry!\n"); _exit (1); } /* Father process. */ wait (NULL); return; } while(0); #else estr_write ("\nBacktrace not yet available on this platform, sorry!\n"); #endif }