123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- /* Copyright (C) 2006-2015 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);
|