summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Haberler <git@mah.priv.at>2011-07-05 22:31:05 +0200
committerMichael Haberler <git@mah.priv.at>2011-10-28 08:25:51 +0200
commit01675bfd29acd891bf8e07f1ccea513136e492ba (patch)
tree04db052cb8864d583639fa14277b5bdf016f8f95
parent23fedb30d3a0d0e4752e471f841b92c00c712287 (diff)
downloadlinuxcnc-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.h1
-rw-r--r--src/emc/task/Submakefile1
-rw-r--r--src/emc/task/emctaskmain.cc9
-rw-r--r--src/emc/task/signalhandler.cc128
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