/ src / emc / task / signalhandler.cc
signalhandler.cc
  1  /*    This is a component of LinuxCNC
  2   *    Copyright 2011 Michael Haberler <git@mah.priv.at>
  3   *
  4   *    This program is free software; you can redistribute it and/or modify
  5   *    it under the terms of the GNU General Public License as published by
  6   *    the Free Software Foundation; either version 2 of the License, or
  7   *    (at your option) any later version.
  8   *
  9   *    This program is distributed in the hope that it will be useful,
 10   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 11   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12   *    GNU General Public License for more details.
 13   *
 14   *    You should have received a copy of the GNU General Public License
 15   *    along with this program; if not, write to the Free Software
 16   *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 17   */
 18  // generate a backtrace from a signal handler,
 19  // or alternatively start gdb in a new window
 20  //
 21  // start gdb with command script and have it connect to a gdbserver instance
 22  // then start gdbserver, attach it to our pid and let gdb connect to it
 23  
 24  #include <stdio.h>
 25  #include <limits.h>
 26  #include <signal.h>
 27  #include <sys/types.h>
 28  #include <unistd.h>
 29  #include <stdlib.h>
 30  #include <errno.h>
 31  #include <string.h>
 32  
 33  static const char *progname;
 34  static const char *dir_prefix = "/tmp/";
 35  static const char *gdbserver = "gdbserver";
 36  static int port = 2345;
 37  
 38  static void call_gdb(int sig, int start_gdb_in_window)
 39  {
 40      FILE *f;
 41      char tmp_gdbrc[PATH_MAX];
 42  
 43      sprintf(tmp_gdbrc, "/tmp/gdbrc.%d",getpid());
 44  
 45      if ((f = fopen(tmp_gdbrc,"w")) == NULL) {
 46  	perror(tmp_gdbrc);
 47  	abort();
 48      }
 49      fprintf(f,"set tcp auto-retry on\nset tcp connect-timeout 3600\n"
 50  	    "file %s\ntarget remote :%d\n", progname, port);
 51  
 52      // this should be configurable
 53      fprintf(f,start_gdb_in_window ? "backtrace\n" :
 54  	    "backtrace full\ninfo source\nquit\n");
 55      fclose(f);
 56      char cmd[PATH_MAX];
 57      if (start_gdb_in_window) {
 58  	sprintf(cmd, "gnome-terminal --title 'GDB - %s backtrace' -x gdb -x %s",
 59  		progname, tmp_gdbrc);
 60  	fprintf(stderr, "signal_handler: got signal %d, starting debugger window (pid %d)\n",sig, getpid());
 61  
 62      } else {
 63  	sprintf(cmd, "gdb --batch -x %s > %sbacktrace.%d &",
 64  		tmp_gdbrc, dir_prefix, getpid());
 65  	fprintf(stderr, "signal_handler: got signal %d, generating backtrace in %sbacktrace.%d\n",sig,  dir_prefix, getpid());
 66      }
 67      int rc = system(cmd);
 68      if (rc == -1) {
 69  	perror(cmd);
 70      } else if (rc) {
 71  	fprintf(stderr,"system(%s) returned %d", cmd,  rc);
 72      }
 73      sprintf(cmd,"%s --once --attach :%d %d &", gdbserver, port, getpid());
 74      rc = system(cmd);
 75      if (rc == -1) {
 76  	perror(cmd);
 77      } else if (rc) {
 78  	fprintf(stderr,"system(%s) returned %d", cmd,  rc);
 79      }
 80      // gdb needs a bit of time to connect and do its thing
 81      sleep(3);
 82      unlink(tmp_gdbrc);
 83      fprintf(stderr, "signal_handler: sig %d -  done\n", sig);
 84      if (sig == SIGSEGV)
 85  	exit(0);
 86  }
 87  
 88  
 89  void gdb_backtrace(int sig)
 90  {
 91      call_gdb(sig, 0);
 92  }
 93  
 94  
 95  void gdb_in_window(int sig)
 96  {
 97      call_gdb(sig, 1);
 98  }
 99  
100  void setup_signal_handlers()
101  {
102      struct sigaction backtrace_action, gdb_action;
103      char path[PATH_MAX];
104      char exe[PATH_MAX];
105  
106      // determine pathname of running program for gdb
107      sprintf(path,"/proc/%d/exe", getpid());
108      if (readlink(path, exe, sizeof(exe)) < 0) {
109  	fprintf(stderr, "signal_handler: cant readlink(%s): %s\n",path,strerror(errno));
110  	return;
111      }
112      progname = strdup(exe);
113  
114      sigemptyset( &gdb_action.sa_mask );
115      gdb_action.sa_handler = gdb_in_window;
116      gdb_action.sa_flags   = 0;
117  
118      sigemptyset( &backtrace_action.sa_mask );
119      backtrace_action.sa_handler = gdb_backtrace;
120      backtrace_action.sa_flags   = 0;
121  
122      // trap into gdb in new window on SEGV, USR1
123      sigaction( SIGSEGV, &gdb_action, (struct sigaction *) NULL );
124      sigaction( SIGUSR1, &gdb_action, (struct sigaction *) NULL );
125  
126      // generate a backtrace on USR2 signal
127      sigaction( SIGUSR2,  &backtrace_action, (struct sigaction *) NULL );
128  }
129  
130  
131  #ifdef TEST
132  
133  int main(int argc, const char *argv[]) {
134  
135  
136      signal_handlers();
137  
138      sleep(10);  // during which a SIGUSR2 will generate a backtrace
139  
140      void *foo = 0;
141      memset(foo,0,47); // this segfault  whould warp us into the gdb window
142  
143      return 0;
144  }
145  #endif