summaryrefslogtreecommitdiff
path: root/libgfortran/runtime/backtrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgfortran/runtime/backtrace.c')
-rw-r--r--libgfortran/runtime/backtrace.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/libgfortran/runtime/backtrace.c b/libgfortran/runtime/backtrace.c
new file mode 100644
index 0000000000..77dd4d5f0a
--- /dev/null
+++ b/libgfortran/runtime/backtrace.c
@@ -0,0 +1,179 @@
+/* Copyright (C) 2006-2017 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 <gthr.h>
+
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "backtrace-supported.h"
+#include "backtrace.h"
+
+
+/* Store our own state while backtracing. */
+struct mystate
+{
+ int frame;
+ bool try_simple;
+ bool in_signal_handler;
+};
+
+
+/* Does a function name have "_gfortran_" or "_gfortrani_" prefix, possibly
+ with additional underscore(s) at the beginning? Cannot use strncmp()
+ because we might be called from a signal handler. */
+
+static int
+has_gfortran_prefix (const char *s)
+{
+ if (!s)
+ return 0;
+
+ while (*s == '_')
+ s++;
+
+ return (s[0] == 'g' && s[1] == 'f' && s[2] == 'o' && s[3] == 'r'
+ && s[4] == 't' && s[5] == 'r' && s[6] == 'a' && s[7] == 'n'
+ && (s[8] == '_' || (s[8] == 'i' && s[9] == '_')));
+}
+
+static void
+error_callback (void *data, const char *msg, int errnum)
+{
+ struct mystate *state = (struct mystate *) data;
+#define ERRHDR "\nCould not print backtrace: "
+
+ if (errnum < 0)
+ {
+ state->try_simple = true;
+ return;
+ }
+ else if (errnum == 0)
+ {
+ estr_write (ERRHDR);
+ estr_write (msg);
+ estr_write ("\n");
+ }
+ else
+ {
+ char errbuf[256];
+ if (state->in_signal_handler)
+ {
+ estr_write (ERRHDR);
+ estr_write (msg);
+ estr_write (", errno: ");
+ const char *p = gfc_itoa (errnum, errbuf, sizeof (errbuf));
+ estr_write (p);
+ estr_write ("\n");
+ }
+ else
+ st_printf (ERRHDR "%s: %s\n", msg,
+ gf_strerror (errnum, errbuf, sizeof (errbuf)));
+ }
+}
+
+static int
+simple_callback (void *data, uintptr_t pc)
+{
+ struct mystate *state = (struct mystate *) data;
+ st_printf ("#%d 0x%lx\n", state->frame, (unsigned long) pc);
+ (state->frame)++;
+ return 0;
+}
+
+static int
+full_callback (void *data, uintptr_t pc, const char *filename,
+ int lineno, const char *function)
+{
+ struct mystate *state = (struct mystate *) data;
+
+ if (has_gfortran_prefix (function))
+ return 0;
+
+ st_printf ("#%d 0x%lx in %s\n", state->frame,
+ (unsigned long) pc, function == NULL ? "???" : function);
+ if (filename || lineno != 0)
+ st_printf ("\tat %s:%d\n", filename == NULL ? "???" : filename, lineno);
+ (state->frame)++;
+
+ if (function != NULL && strcmp (function, "main") == 0)
+ return 1;
+
+ return 0;
+}
+
+
+/* Display the backtrace. */
+
+void
+show_backtrace (bool in_signal_handler)
+{
+ struct backtrace_state *lbstate;
+ struct mystate state = { 0, false, in_signal_handler };
+
+ lbstate = backtrace_create_state (NULL, __gthread_active_p (),
+ error_callback, NULL);
+
+ if (lbstate == NULL)
+ return;
+
+ if (!BACKTRACE_SUPPORTED || (in_signal_handler && BACKTRACE_USES_MALLOC))
+ {
+ /* If symbolic backtrace is not supported on this target, or would
+ require malloc() and we are in a signal handler, go with a
+ simple backtrace. */
+
+ backtrace_simple (lbstate, 0, simple_callback, error_callback, &state);
+ }
+ else
+ {
+ /* libbacktrace uses mmap, which is safe to call from a signal handler
+ (in practice, if not in theory). Thus we can generate a symbolic
+ backtrace, if debug symbols are available. */
+
+ backtrace_full (lbstate, 0, full_callback, error_callback, &state);
+ if (state.try_simple)
+ backtrace_simple (lbstate, 0, simple_callback, error_callback, &state);
+ }
+}
+
+
+
+/* Function called by the front-end translating the BACKTRACE intrinsic. */
+
+extern void backtrace (void);
+export_proto (backtrace);
+
+void
+backtrace (void)
+{
+ show_backtrace (false);
+}
+