aboutsummaryrefslogtreecommitdiffstats
path: root/src/emc/task/signalhandler.cc
blob: b933ada5850e7b8bc2865cc31cd139e71bab3420 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*    This is a component of LinuxCNC
 *    Copyright 2011 Michael Haberler <git@mah.priv.at>
 *
 *    This program 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 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program 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.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
// 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];

    snprintf(tmp_gdbrc, sizeof(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) {
	snprintf(cmd, sizeof(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 {
	snprintf(cmd, sizeof(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);
    }
    snprintf(cmd, sizeof(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
    snprintf(path, sizeof(path),"/proc/%d/exe", getpid());
    if (readlink(path, exe, sizeof(exe)) < 0) {
	fprintf(stderr, "signal_handler: can\'t 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 would warp us into the gdb window

    return 0;
}
#endif
bues.ch cgit interface