mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			280 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
/* Copyright (C) 2006-2013 Free Software Foundation, Inc.
 | 
						|
   Contributed by François-Xavier Coudert
 | 
						|
 | 
						|
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 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>
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
#ifdef HAVE_UNISTD_H
 | 
						|
#include <unistd.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_SYS_WAIT_H
 | 
						|
#include <sys/wait.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <limits.h>
 | 
						|
 | 
						|
#include "unwind.h"
 | 
						|
 | 
						|
 | 
						|
/* Macros for common sets of capabilities: can we fork and exec, and
 | 
						|
   can we use pipes to communicate with the subprocess.  */
 | 
						|
#define CAN_FORK (defined(HAVE_FORK) && defined(HAVE_EXECVE) \
 | 
						|
		  && defined(HAVE_WAIT))
 | 
						|
#define CAN_PIPE (CAN_FORK && defined(HAVE_PIPE) \
 | 
						|
		  && defined(HAVE_DUP2) && defined(HAVE_CLOSE))
 | 
						|
 | 
						|
#ifndef PATH_MAX
 | 
						|
#define PATH_MAX 4096
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
/* GDB style #NUM index for each stack frame.  */
 | 
						|
 | 
						|
static void 
 | 
						|
bt_header (int num)
 | 
						|
{
 | 
						|
  st_printf ("#%d  ", num);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* fgets()-like function that reads a line from a fd, without
 | 
						|
   needing to malloc() a buffer, and does not use locks, hence should
 | 
						|
   be async-signal-safe.  */
 | 
						|
 | 
						|
static char *
 | 
						|
fd_gets (char *s, int size, int fd)
 | 
						|
{
 | 
						|
  for (int i = 0; i < size; i++)
 | 
						|
    {
 | 
						|
      char c;
 | 
						|
      ssize_t nread = read (fd, &c, 1);
 | 
						|
      if (nread == 1)
 | 
						|
	{
 | 
						|
	  s[i] = c;
 | 
						|
	  if (c == '\n')
 | 
						|
	    {
 | 
						|
	      if (i + 1 < size)
 | 
						|
		s[i+1] = '\0';
 | 
						|
	      else
 | 
						|
		s[i] = '\0';
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  s[i] = '\0';
 | 
						|
	  if (i == 0)
 | 
						|
	    return NULL;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
extern char *addr2line_path;
 | 
						|
 | 
						|
/* Struct containing backtrace state.  */
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  int frame_number;
 | 
						|
  int direct_output;
 | 
						|
  int outfd;
 | 
						|
  int infd;
 | 
						|
  int error;
 | 
						|
}
 | 
						|
bt_state;
 | 
						|
 | 
						|
static _Unwind_Reason_Code
 | 
						|
trace_function (struct _Unwind_Context *context, void *state_ptr)
 | 
						|
{
 | 
						|
  bt_state* state = (bt_state*) state_ptr;
 | 
						|
  _Unwind_Ptr ip;
 | 
						|
#ifdef HAVE_GETIPINFO
 | 
						|
  int ip_before_insn = 0;
 | 
						|
  ip = _Unwind_GetIPInfo (context, &ip_before_insn);
 | 
						|
  
 | 
						|
  /* If the unwinder gave us a 'return' address, roll it back a little
 | 
						|
     to ensure we get the correct line number for the call itself.  */
 | 
						|
  if (! ip_before_insn)
 | 
						|
    --ip;
 | 
						|
#else  
 | 
						|
  ip = _Unwind_GetIP (context);
 | 
						|
#endif
 | 
						|
 | 
						|
  if (state->direct_output)
 | 
						|
    {
 | 
						|
      bt_header(state->frame_number);
 | 
						|
      st_printf ("%p\n", (void*) ip);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      char addr_buf[GFC_XTOA_BUF_SIZE], func[1024], file[PATH_MAX];
 | 
						|
      char *p;
 | 
						|
      const char* addr = gfc_xtoa (ip, addr_buf, sizeof (addr_buf));
 | 
						|
      write (state->outfd, addr, strlen (addr));
 | 
						|
      write (state->outfd, "\n", 1);
 | 
						|
 | 
						|
      if (! fd_gets (func, sizeof(func), state->infd))
 | 
						|
	{
 | 
						|
	  state->error = 1;
 | 
						|
	  goto done;
 | 
						|
	}
 | 
						|
      if (! fd_gets (file, sizeof(file), state->infd))
 | 
						|
	{
 | 
						|
	  state->error = 1;
 | 
						|
	  goto done;
 | 
						|
	}
 | 
						|
	    
 | 
						|
	for (p = func; *p != '\n' && *p != '\r'; p++)
 | 
						|
	  ;
 | 
						|
	*p = '\0';
 | 
						|
	
 | 
						|
	/* _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)
 | 
						|
	  return _URC_END_OF_STACK;
 | 
						|
	
 | 
						|
	bt_header (state->frame_number);
 | 
						|
	estr_write ("0x");
 | 
						|
	estr_write (addr);
 | 
						|
 | 
						|
	if (func[0] != '?' && func[1] != '?')
 | 
						|
	  {
 | 
						|
	    estr_write (" in ");
 | 
						|
	    estr_write (func);
 | 
						|
	  }
 | 
						|
	
 | 
						|
	if (strncmp (file, "??", 2) == 0)
 | 
						|
	  estr_write ("\n");
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    estr_write (" at ");
 | 
						|
	    estr_write (file);
 | 
						|
	  }
 | 
						|
    }
 | 
						|
 | 
						|
 done:
 | 
						|
 | 
						|
  state->frame_number++;
 | 
						|
  
 | 
						|
  return _URC_NO_REASON;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Display the backtrace.  */
 | 
						|
 | 
						|
void
 | 
						|
backtrace (void)
 | 
						|
{
 | 
						|
  bt_state state;
 | 
						|
  state.frame_number = 0;
 | 
						|
  state.error = 0;
 | 
						|
 | 
						|
#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, inp[2];
 | 
						|
 | 
						|
    /* Don't output an error message if something goes wrong, we'll simply
 | 
						|
       fall back to printing the addresses.  */
 | 
						|
    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]);
 | 
						|
 | 
						|
    state.outfd = inp[1];
 | 
						|
    state.infd = f[0];
 | 
						|
    state.direct_output = 0;
 | 
						|
    _Unwind_Backtrace (trace_function, &state);
 | 
						|
    if (state.error)
 | 
						|
      goto fallback;
 | 
						|
    close (inp[1]);
 | 
						|
    close (f[0]);
 | 
						|
    wait (NULL);
 | 
						|
    return;
 | 
						|
 | 
						|
fallback:
 | 
						|
    estr_write ("** Something went wrong while running addr2line. **\n"
 | 
						|
		"** Falling back to a simpler backtrace scheme. **\n");
 | 
						|
  }
 | 
						|
  while (0);
 | 
						|
 | 
						|
fallback_noerr:
 | 
						|
#endif /* CAN_PIPE */
 | 
						|
 | 
						|
  /* Fallback to the simple backtrace without addr2line.  */
 | 
						|
  state.direct_output = 1;
 | 
						|
  _Unwind_Backtrace (trace_function, &state);
 | 
						|
}
 | 
						|
iexport(backtrace);
 |