280 lines
6.0 KiB
C
280 lines
6.0 KiB
C
/* Copyright (C) 2006, 2007, 2009, 2011, 2012 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
|
|
show_backtrace (void)
|
|
{
|
|
bt_state state;
|
|
state.frame_number = 0;
|
|
state.error = 0;
|
|
|
|
estr_write ("\nBacktrace for this error:\n");
|
|
|
|
#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]);
|
|
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);
|
|
}
|