diff options
Diffstat (limited to 'libgupc/portals4/gupcr_backtrace.c')
-rw-r--r-- | libgupc/portals4/gupcr_backtrace.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/libgupc/portals4/gupcr_backtrace.c b/libgupc/portals4/gupcr_backtrace.c new file mode 100644 index 00000000000..8d482adc2e8 --- /dev/null +++ b/libgupc/portals4/gupcr_backtrace.c @@ -0,0 +1,400 @@ +/* Copyright (C) 2012-2016 Free Software Foundation, Inc. + This file is part of the UPC runtime Library. + Written by Gary Funck <gary@intrepid.com> + and Nenad Vukicevic <nenad@intrepid.com> + +This file is part of GCC. + +GCC 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. + +GCC 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 "config.h" + +#include "gupcr_config.h" +#include "gupcr_defs.h" +#include "gupcr_sup.h" +#include "gupcr_utils.h" +#include "gupcr_backtrace.h" +#include "gupcr_barrier.h" +#include <signal.h> +#include <string.h> +#if HAVE_EXECINFO_H +#include <execinfo.h> +#endif +#if HAVE_LIMITS_H +#include <limits.h> +#endif + +/** Skip over frames belonging to the backtrace code itself. */ +#define GUPCR_BT_SKIP_FRAME_CNT 3 +/** Maximum number of stack frames to display. */ +#define GUPCR_BT_DEPTH_CNT 128 + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +/** Default backtrace file name prefix. */ +#define UPC_BACKTRACE_PREFIX "backtrace" + +/** Full path of the executable program. */ +static char *gupcr_abs_execname; + +/** Backtrace on faults enabled flag. */ +static int bt_enabled = 0; + +/** + * GLIBC backtrace. + * + * Show backtrace by using the GLIBC backtrace functionality. + * Backtrace is improved with the source file/line numbers if + * addr2line is available. + * + * By default backtrace lines are sent to the 'stderr' file + * descriptor. However, an environment variable + * UPC_BACKTRACEFILE can be used to redirect the backtrace + * to an actual file and it is used as a simple prefix for + * the backtrace file. For example, if it is set to "/tmp/trace-upc", + * the actual trace file is going to be "/tmp/trace-upc-PID.MYTHREAD". + * If empty environment variable is provided, a simple "trace" prefix + * is used. + * + */ +void +gupcr_backtrace (void) +{ + void *strace[GUPCR_BT_DEPTH_CNT]; + size_t size,i; + char **strace_str; + char *file_env; + int under_upc_main = 1; + FILE *traceout = stderr; + + file_env = getenv (GUPCR_BACKTRACE_FILE_ENV); + if (file_env) + { + #define MAX_INT_STRING ".2147483647" + char *tracefile; + int len, lenw; + /* Use default trace file name if one not specified by the user. */ + if (!strlen (file_env)) + file_env = (char *) UPC_BACKTRACE_PREFIX; + len = strlen (file_env) + strlen (MAX_INT_STRING) + 1; + tracefile = malloc (len); + if (!tracefile) + gupcr_fatal_error ("cannot allocate (%d) memory for backtrace file %s", + len, file_env); + lenw = snprintf (tracefile, len, "%s.%d", file_env, MYTHREAD); + if ((lenw >= len) || (lenw < 0)) + gupcr_fatal_error ("cannot create backtrace file name: %s", file_env); + traceout = fopen (tracefile, "w"); + if (!traceout) + gupcr_fatal_error ("cannot open backtrace file: %s", tracefile); + free (tracefile); + } + else + fprintf (traceout, "Thread %d backtrace:\n", MYTHREAD); + + /* Use "backtrace" functionality of glibc to receive + backtrace addresses. */ + size = backtrace (strace, GUPCR_BT_DEPTH_CNT); + /* Add symbolic information to each address + and print the stack trace. */ + for (i = GUPCR_BT_SKIP_FRAME_CNT; i < size; i++) + { + if (under_upc_main) + { +# if HAVE_UPC_BACKTRACE_ADDR2LINE + /* Call addr2line to generate source files, line numbers, + and functions. In case of any error (malloc, snprintf) + do not abort the program. */ + FILE *a2l; + #define CMD_TMPL "%s -f -e %s %p" + /* Allow space for addr2line, filename, command line options, + and address argument for addr2line. */ + int cmd_size = strlen (GUPCR_BACKTRACE_ADDR2LINE) + + strlen (gupcr_abs_execname) + + strlen (CMD_TMPL) + + strlen ("0x1234567812345678"); + int sz; + char *cmd = malloc (cmd_size); + /* Create an actual addr2line command. */ + sz = snprintf (cmd, cmd_size, CMD_TMPL, GUPCR_BACKTRACE_ADDR2LINE, + gupcr_abs_execname, strace[i]); + if ((sz >= cmd_size) || (sz < 0)) + { + fprintf (traceout, "unable to create addr2line " + "command line\n"); + return; + } + /* Execute addr2line. */ + a2l = popen (cmd, "r"); + free (cmd); + if (a2l) + { + /* addr2line responds with two lines: procedure name and + the file name with line number. */ + int max_rep = 2 * FILENAME_MAX; + /* Build a data structure that is identical to the + structure returned by the glibc backtrace_symbol(). */ + struct back_trace { + char *addr; + char data[1]; + }; + struct back_trace *rep = malloc (max_rep); + int index = 0; + if (!rep) + { + fprintf (traceout, "unable to acquire memory " + "for backtracing\n"); + return; + } + rep->data[0] = '\0'; + /* Read addr2line response. */ + while (fgets(&rep->data[index], max_rep-index, a2l)) + { + /* Remove all the new lines, as addr2line returns + info in multiple lines. */ + index = strlen (&rep->data[0]); + if (rep->data[index - 1] == '\n') + rep->data[index - 1] = ' '; + } + pclose (a2l); + rep->addr = &rep->data[0]; + strace_str = &rep->addr; + } + else + { + /* Somehow we failed to invoke addr2line, fall back + to glibc. */ + strace_str = backtrace_symbols (&strace[i], 1); + } +# else + strace_str = backtrace_symbols (&strace[i], 1); +# endif + fprintf (traceout, "[%4d][%lld] %s\n", MYTHREAD, + (long long int) (i - GUPCR_BT_SKIP_FRAME_CNT), *strace_str); + /* Extra info for the barrier. */ + if (strstr( *strace_str, "__upc_wait")) + { + fprintf (traceout, "[%4d] BARRIER ID: %d\n", MYTHREAD, + gupcr_barrier_id); + } + if (strstr (*strace_str, "upc_main")) + under_upc_main = 0; + /* Symbol trace buffer must be released. */ + free (strace_str); + } + } + fflush (traceout); + if (file_env) + fclose (traceout); +} + +#define GUPCR_BACKTRACE_PID_BUFLEN 16 + +/** + * Backtrace on fatal errors. + * + * Print backtrace (stack frames) on fatal errors: run-time + * fatal error or segmentation fault. + * + * Only print backtrace if environment variable UPC_BACKTRACE + * is set to 1. The following order of backtrace capabilities + * is searched and executed: + * + * (1) Use GDB for backtrace (if enabled) + * (2) Use GLIBC backtrace with source file/line display (if + * addr2line is available) + * (3) Use GLIBC backtrace with raw addresses (display is + * improved if -rdynamic option is supported by the linker) + * + */ +void +gupcr_fatal_error_backtrace (void) +{ + if (bt_enabled) + { +#ifdef HAVE_UPC_BACKTRACE_GDB + { + char *env; + const char *gdb; + char pid_buf[GUPCR_BACKTRACE_PID_BUFLEN]; + int child_pid; + /* Which gdb to use? */ + env = getenv (GUPCR_BACKTRACE_GDB_ENV); + if (!env || (strlen (env) == 0)) + gdb = GUPCR_BACKTRACE_GDB; + else + gdb = (const char *) env; + if (strcmp (gdb, "none")) + { + const char *err_msg = 0; + char tmpf[PATH_MAX]; + int fbt; + const char *btcmd = "backtrace 30\n"; + fprintf (stderr, "Thread %d GDB backtrace:\n", MYTHREAD); + /* Get pid and name of the running program. */ + sprintf(pid_buf, "%d", getpid()); + /* Create temp file for GDB commands. */ + if ((fbt = gupcr_create_temp_file + ("upc_bt_gdb.XXXXXX", tmpf, &err_msg)) == -1) + { + fprintf (stderr, "cannot open gdb command - %s\n", err_msg); + return; + } + if (write (fbt, btcmd, sizeof (btcmd)) == -1) + { + perror ("cannot write gdb command file for backtrace"); + return; + } + if (close (fbt)) + { + perror ("cannot close gdb command file for backtrace"); + return; + } + child_pid = fork(); + if (!child_pid) + { + dup2(2,1); + execlp(gdb, gdb, "-nx", "-batch", "-x", tmpf, + gupcr_abs_execname, pid_buf, NULL); + fprintf (stderr, "cannot start GDB - %s\n", gdb); + abort(); /* If gdb failed to start */ + } + else + waitpid(child_pid,NULL,0); + unlink (tmpf); + return; + } + } +#endif /* GUPCR_BACKTRACE_GDB */ + + /* Simple backtrace only. */ + gupcr_backtrace (); + } +} + +/** + * Backtrace signal handler. + * + * Display stack frames on a request. In case of the + * monitor thread only print the mappings between the + * UPC threads and processes. + */ +static void +gupcr_backtrace_handler (int sig __attribute__ ((unused)), + siginfo_t *siginfo __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + gupcr_backtrace (); +} + +/** + * Backtrace fault handler. + * + * A fault happened and backtrace is enabled. Allow for only + * one thread to print the backtrace. The restore signal + * handlers to their default and return ensures that + * signal terminates the thread and allows for the monitor + * thread to terminate all the other threads.. + */ +static void +gupcr_fault_handler (int sig __attribute__ ((unused)), + siginfo_t *siginfo __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + gupcr_backtrace_restore_handlers (); + gupcr_fatal_error_backtrace (); +} + +/** + * Initialize UPC backtrace. + */ +void +gupcr_backtrace_init (const char *execname) +{ + /* Find the full path for the executable. On linux systems we + might be able to read "/proc/self/exe" to the get the full + executable path. But, it is not portable. */ + int slen = sizeof (gupcr_abs_execname) - strlen (execname) - 2; + gupcr_abs_execname = malloc (PATH_MAX + 1); + if (!gupcr_abs_execname) + gupcr_fatal_error ("cannot allocate space for executable file name"); + *gupcr_abs_execname = '\0'; + if (execname[0] != '/') + { + if (!getcwd (gupcr_abs_execname, slen)) + strcpy (gupcr_abs_execname, "/BT_CANNOT_CREATE_ABS_PATH"); + strcat (gupcr_abs_execname, "/"); + } + strcat (gupcr_abs_execname, execname); + +#ifdef HAVE_UPC_BACKTRACE_SIGNAL + { + /* Install backtrace signal handler (backtrace on request). */ + struct sigaction act; + memset (&act, '\0', sizeof(act)); + act.sa_sigaction = &gupcr_backtrace_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(GUPCR_BACKTRACE_SIGNAL, &act, NULL) < 0) { + perror ("was not able to install backtrace handler"); + } + } +#endif + + /* Install signal handlers only if backtrace is enabled. */ + bt_enabled = gupcr_is_backtrace_enabled (); + + if (bt_enabled) + { + struct sigaction act; + memset (&act, '\0', sizeof(act)); + act.sa_sigaction = &gupcr_fault_handler; + act.sa_flags = SA_SIGINFO; + if (sigaction(SIGABRT, &act, NULL) < 0) + perror ("unable to install SIGABRT handler"); + if (sigaction(SIGILL, &act, NULL) < 0) + perror ("unable to install SIGILL handler"); + if (sigaction(SIGSEGV, &act, NULL) < 0) + perror ("unable to install SIGSEGV handler"); + if (sigaction(SIGBUS, &act, NULL) < 0) + perror ("unable to install SIGBUS handler"); + if (sigaction(SIGFPE, &act, NULL) < 0) + perror ("unable to install SIGFPE handler"); + } +} + +/** + * Restore default handlers. + * + * Has to be called once the run-time discovered + * a fatal error. + */ +void +gupcr_backtrace_restore_handlers (void) +{ + /* Don't handle any signals with backtrace code. Install + default handlers. */ + signal (SIGABRT, SIG_DFL); + signal (SIGILL, SIG_DFL); + signal (SIGSEGV, SIG_DFL); + signal (SIGBUS, SIG_DFL); + signal (SIGFPE, SIG_DFL); +} |