Beispiel #1
0
/* 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
}