diff options
author | Michael Haberler <git@mah.priv.at> | 2011-07-05 22:31:05 +0200 |
---|---|---|
committer | Michael Haberler <git@mah.priv.at> | 2011-10-28 08:25:51 +0200 |
commit | 01675bfd29acd891bf8e07f1ccea513136e492ba (patch) | |
tree | 04db052cb8864d583639fa14277b5bdf016f8f95 | |
parent | 23fedb30d3a0d0e4752e471f841b92c00c712287 (diff) | |
download | linuxcnc-01675bfd29acd891bf8e07f1ccea513136e492ba.tar.gz linuxcnc-01675bfd29acd891bf8e07f1ccea513136e492ba.zip |
task: add backtrace/gdb startup on signal support
SIGSEGV or SIGUSR1 traps milltask into gdb in a new window
SIGUSR2 creates a backtrace in /tmp/backtrace.<pid>
configurable on new EMC_DEBUG_GDBONSIGNAL flag (0x00020000)
-rw-r--r-- | src/emc/nml_intf/debugflags.h | 1 | ||||
-rw-r--r-- | src/emc/task/Submakefile | 1 | ||||
-rw-r--r-- | src/emc/task/emctaskmain.cc | 9 | ||||
-rw-r--r-- | src/emc/task/signalhandler.cc | 128 |
4 files changed, 139 insertions, 0 deletions
diff --git a/src/emc/nml_intf/debugflags.h b/src/emc/nml_intf/debugflags.h index 5b542e884..31f9a964c 100644 --- a/src/emc/nml_intf/debugflags.h +++ b/src/emc/nml_intf/debugflags.h @@ -16,6 +16,7 @@ #define EMC_DEBUG_REMAP 0x00004000 #define EMC_DEBUG_PYTHON 0x00008000 #define EMC_DEBUG_NAMEDPARAM 0x00010000 +#define EMC_DEBUG_GDBONSIGNAL 0x00020000 #define EMC_DEBUG_UNCONDITIONAL 0x40000000 // always logged #define EMC_DEBUG_ALL 0x7FFFFFFF /* it's an int for %i to work diff --git a/src/emc/task/Submakefile b/src/emc/task/Submakefile index c8801c582..1c04d809f 100644 --- a/src/emc/task/Submakefile +++ b/src/emc/task/Submakefile @@ -21,6 +21,7 @@ MILLTASKSRCS := \ emc/motion/stashf.c \ emc/rs274ngc/tool_parse.cc \ emc/task/taskmodule.cc \ + emc/task/signalhandler.cc \ $(ULAPISRCS) USERSRCS += $(MILLTASKSRCS) diff --git a/src/emc/task/emctaskmain.cc b/src/emc/task/emctaskmain.cc index fa0795261..f13dd73bf 100644 --- a/src/emc/task/emctaskmain.cc +++ b/src/emc/task/emctaskmain.cc @@ -136,6 +136,8 @@ static int pseudoMdiLineNumber = INT_MIN; // %d receives io.reason static const char *io_error = "toolchanger error %d"; +extern void setup_signal_handlers(); // backtrace, gdb-in-new-window supportx + static int all_homed(void) { for(int i=0; i<9; i++) { unsigned int mask = 1<<i; @@ -2977,6 +2979,11 @@ static int iniLoad(const char *filename) // set_rcs_print_flag(PRINT_EVERYTHING); max_rcs_errors_to_print = -1; } + if (EMC_DEBUG & EMC_DEBUG_GDBONSIGNAL) { + // enable backtrace on USR2 + // enable gdb in new window on SEGV or USR2 + setup_signal_handlers(); + } if (EMC_DEBUG & EMC_DEBUG_VERSIONS) { if (NULL != (inistring = inifile.Find("VERSION", "EMC"))) { @@ -3079,6 +3086,8 @@ static int iniLoad(const char *filename) return 0; } + + /* syntax: a.out {-d -ini <inifile>} {-nml <nmlfile>} {-shm <key>} */ diff --git a/src/emc/task/signalhandler.cc b/src/emc/task/signalhandler.cc new file mode 100644 index 000000000..881ce3bba --- /dev/null +++ b/src/emc/task/signalhandler.cc @@ -0,0 +1,128 @@ +// generate a backtrace from a signal handler, +// or alternatively start gdb in a new window +// +// start gdb with command script and have it connect to a gdbserver instance +// then start gdbserver, attach it to our pid and let gdb connect to it + +#include <stdio.h> +#include <limits.h> +#include <signal.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +static const char *progname; +static const char *dir_prefix = "/tmp/"; +static const char *gdbserver = "gdbserver"; +static int port = 2345; + +static void call_gdb(int sig, int start_gdb_in_window) +{ + FILE *f; + char tmp_gdbrc[PATH_MAX]; + + sprintf(tmp_gdbrc, "/tmp/gdbrc.%d",getpid()); + + if ((f = fopen(tmp_gdbrc,"w")) == NULL) { + perror(tmp_gdbrc); + abort(); + } + fprintf(f,"set tcp auto-retry on\nset tcp connect-timeout 3600\n" + "file %s\ntarget remote :%d\n", progname, port); + + // this should be configurable + fprintf(f,start_gdb_in_window ? "backtrace\n" : + "backtrace full\ninfo source\nquit\n"); + fclose(f); + char cmd[PATH_MAX]; + if (start_gdb_in_window) { + sprintf(cmd, "gnome-terminal --title 'GDB - %s backtrace' -x gdb -x %s", + progname, tmp_gdbrc); + fprintf(stderr, "signal_handler: got signal %d, starting debugger window (pid %d)\n",sig, getpid()); + + } else { + sprintf(cmd, "gdb --batch -x %s > %sbacktrace.%d &", + tmp_gdbrc, dir_prefix, getpid()); + fprintf(stderr, "signal_handler: got signal %d, generating backtrace in %sbacktrace.%d\n",sig, dir_prefix, getpid()); + } + int rc = system(cmd); + if (rc == -1) { + perror(cmd); + } else if (rc) { + fprintf(stderr,"system(%s) returned %d", cmd, rc); + } + sprintf(cmd,"%s --once --attach :%d %d &", gdbserver, port, getpid()); + rc = system(cmd); + if (rc == -1) { + perror(cmd); + } else if (rc) { + fprintf(stderr,"system(%s) returned %d", cmd, rc); + } + // gdb needs a bit of time to connect and do its thing + sleep(3); + unlink(tmp_gdbrc); + fprintf(stderr, "signal_handler: sig %d - done\n", sig); + if (sig == SIGSEGV) + exit(0); +} + + +void gdb_backtrace(int sig) +{ + call_gdb(sig, 0); +} + + +void gdb_in_window(int sig) +{ + call_gdb(sig, 1); +} + +void setup_signal_handlers() +{ + struct sigaction backtrace_action, gdb_action; + char path[PATH_MAX]; + char exe[PATH_MAX]; + + // determine pathname of running program for gdb + sprintf(path,"/proc/%d/exe", getpid()); + if (readlink(path, exe, sizeof(exe)) < 0) { + fprintf(stderr, "signal_handler: cant readlink(%s): %s\n",path,strerror(errno)); + return; + } + progname = strdup(exe); + + sigemptyset( &gdb_action.sa_mask ); + gdb_action.sa_handler = gdb_in_window; + gdb_action.sa_flags = 0; + + sigemptyset( &backtrace_action.sa_mask ); + backtrace_action.sa_handler = gdb_backtrace; + backtrace_action.sa_flags = 0; + + // trap into gdb in new window on SEGV, USR1 + sigaction( SIGSEGV, &gdb_action, (struct sigaction *) NULL ); + sigaction( SIGUSR1, &gdb_action, (struct sigaction *) NULL ); + + // generate a backtrace on USR2 signal + sigaction( SIGUSR2, &backtrace_action, (struct sigaction *) NULL ); +} + + +#ifdef TEST + +int main(int argc, const char *argv[]) { + + + signal_handlers(); + + sleep(10); // during which a SIGUSR2 will generate a backtrace + + void *foo = 0; + memset(foo,0,47); // this segfault whould warp us into the gdb window + + return 0; +} +#endif |