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