Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/c_lib/src/interrupt.c
4024 views
1
/*
2
Interrupt and signal handling for Sage
3
4
AUTHORS:
5
6
- William Stein, Martin Albrecht (2006): initial version
7
8
- Jeroen Demeyer (2010-10-03): almost complete rewrite (#9678)
9
10
*/
11
12
/*****************************************************************************
13
* Copyright (C) 2006 William Stein <[email protected]>
14
* 2006 Martin Albrecht <[email protected]>
15
* 2010 Jeroen Demeyer <[email protected]>
16
*
17
* Distributed under the terms of the GNU General Public License (GPL)
18
* as published by the Free Software Foundation; either version 2 of
19
* the License, or (at your option) any later version.
20
* http://www.gnu.org/licenses/
21
****************************************************************************/
22
23
#include <stdio.h>
24
#include <string.h>
25
#include <limits.h>
26
/* glibc has a backtrace() command since version 2.1 */
27
#ifdef __GLIBC__
28
#if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)
29
#define HAVE_BACKTRACE 1
30
#include <execinfo.h>
31
#endif
32
#endif
33
#include "stdsage.h"
34
#include "interrupt.h"
35
36
37
struct sage_signals_t _signals;
38
39
/* The default signal mask during normal operation,
40
* initialized by setup_sage_signal_handler(). */
41
static sigset_t default_sigmask;
42
43
/* default_sigmask with SIGINT and SIGALRM added. */
44
static sigset_t sigmask_with_sigint;
45
46
/* Does this processor support the x86 EMMS instruction? */
47
#if defined(__i386__) || defined(__x86_64__)
48
#define CPU_ARCH_x86
49
static int cpu_has_emms = 0;
50
#endif
51
52
/* Do whatever is needed to reset the CPU to a sane state after
53
* handling a signal. In particular on x86 CPUs, we need to clear
54
* the FPU (this is needed after MMX instructions have been used or
55
* if an interrupt occurs during an FPU computation).
56
* Linux and OS X 10.6 do this as part of their signals implementation,
57
* but Solaris doesn't. Since this code is called only when handling a
58
* signal (which should be very rare), it's better to play safe and
59
* always execute this instead of special-casing based on the operating
60
* system.
61
* See http://trac.sagemath.org/sage_trac/ticket/12873
62
*/
63
static inline void reset_CPU()
64
{
65
#ifdef CPU_ARCH_x86
66
/* Clear FPU tag word */
67
if (cpu_has_emms)
68
{
69
asm("emms");
70
}
71
else
72
{
73
asm("ffree %st(0)");
74
asm("ffree %st(1)");
75
asm("ffree %st(2)");
76
asm("ffree %st(3)");
77
asm("ffree %st(4)");
78
asm("ffree %st(5)");
79
asm("ffree %st(6)");
80
asm("ffree %st(7)");
81
}
82
#endif
83
}
84
85
86
/* Handler for SIGINT */
87
void sage_interrupt_handler(int sig)
88
{
89
#if ENABLE_DEBUG_INTERRUPT
90
fprintf(stderr, "\n*** SIGINT *** %s sig_on\n", (_signals.sig_on_count > 0) ? "inside" : "outside");
91
print_backtrace();
92
#endif
93
94
if (_signals.sig_on_count > 0)
95
{
96
if (_signals.block_sigint)
97
{
98
/* SIGINT is blocked, so simply set _signals.interrupt_received. */
99
_signals.interrupt_received = 1;
100
return;
101
}
102
103
/* Raise KeyboardInterrupt */
104
PyErr_SetNone(PyExc_KeyboardInterrupt);
105
106
/* Jump back to sig_on() (the first one if there is a stack) */
107
reset_CPU();
108
siglongjmp(_signals.env, sig);
109
}
110
else
111
{
112
/* Set an internal Python flag that an interrupt has been
113
* raised. This will not immediately raise an exception, only
114
* on the next call of PyErr_CheckSignals(). We cannot simply
115
* raise an exception here because of Python's "global
116
* interpreter lock" -- Jeroen Demeyer */
117
PyErr_SetInterrupt();
118
_signals.interrupt_received = 1;
119
}
120
}
121
122
/* Call sage_interrupt_handler() "by hand". */
123
void call_sage_interrupt_handler(int sig)
124
{
125
/* Block SIGINT, SIGALRM */
126
sigprocmask(SIG_BLOCK, &sigmask_with_sigint, NULL);
127
sage_interrupt_handler(sig);
128
}
129
130
131
132
/* Handler for SIGILL, SIGABRT, SIGFPE, SIGBUS, SIGSEGV */
133
void sage_signal_handler(int sig)
134
{
135
sig_atomic_t inside = _signals.inside_signal_handler;
136
_signals.inside_signal_handler = 1;
137
138
if (inside == 0 && _signals.sig_on_count > 0)
139
{
140
/* We are inside sig_on(), so we can handle the signal! */
141
142
/* Message to be printed in the Python exception */
143
const char* msg = _signals.s;
144
145
if (!msg)
146
{
147
/* Default: a message depending on which signal we got */
148
switch(sig)
149
{
150
case SIGILL: msg = "Illegal instruction"; break;
151
case SIGABRT: msg = "Aborted"; break;
152
case SIGFPE: msg = "Floating point exception"; break;
153
case SIGBUS: msg = "Bus error"; break;
154
case SIGSEGV: msg = "Segmentation fault"; break;
155
default: msg = "";
156
}
157
}
158
159
/* Raise RuntimeError */
160
PyErr_SetString(PyExc_RuntimeError, msg);
161
162
/* Jump back to sig_on() (the first one if there is a stack) */
163
reset_CPU();
164
siglongjmp(_signals.env, sig);
165
}
166
else
167
{
168
/* We are outside sig_on() and have no choice but to terminate Sage */
169
170
/* Reset all signals to their default behaviour and unblock
171
* them in case something goes wrong as of now. */
172
signal(SIGILL, SIG_DFL);
173
signal(SIGABRT, SIG_DFL);
174
signal(SIGFPE, SIG_DFL);
175
signal(SIGBUS, SIG_DFL);
176
signal(SIGSEGV, SIG_DFL);
177
sigprocmask(SIG_SETMASK, &sigmask_with_sigint, NULL);
178
179
if (inside) sigdie(sig, "An error occured during signal handling.");
180
181
/* Quit Sage with an appropriate message. */
182
switch(sig)
183
{
184
case SIGILL:
185
sigdie(sig, "Unhandled SIGILL: An illegal instruction occurred in Sage.");
186
break; /* This will not be reached */
187
case SIGABRT:
188
sigdie(sig, "Unhandled SIGABRT: An abort() occurred in Sage.");
189
break; /* This will not be reached */
190
case SIGFPE:
191
sigdie(sig, "Unhandled SIGFPE: An unhandled floating point exception occurred in Sage.");
192
break; /* This will not be reached */
193
case SIGBUS:
194
sigdie(sig, "Unhandled SIGBUS: A bus error occurred in Sage.");
195
break; /* This will not be reached */
196
case SIGSEGV:
197
sigdie(sig, "Unhandled SIGSEGV: A segmentation fault occurred in Sage.");
198
break; /* This will not be reached */
199
};
200
sigdie(sig, "Unknown signal received.\n");
201
}
202
}
203
204
/* Check whether we received an interrupt before sig_on().
205
* Return 0 if there was an interrupt, 1 otherwise. */
206
int _sig_on_interrupt_received()
207
{
208
_signals.interrupt_received = 0;
209
if (PyErr_CheckSignals())
210
{
211
_signals.sig_on_count = 0;
212
return 0;
213
}
214
return 1;
215
}
216
217
/* Recover after siglongjmp() */
218
void _sig_on_recover()
219
{
220
_signals.block_sigint = 0;
221
_signals.sig_on_count = 0;
222
/* Reset signal mask */
223
sigprocmask(SIG_SETMASK, &default_sigmask, NULL);
224
_signals.inside_signal_handler = 0;
225
}
226
227
void _sig_off_warning(const char* file, int line)
228
{
229
char buf[320];
230
snprintf(buf, sizeof(buf), "sig_off() without sig_on() at %s:%i", file, line);
231
PyErr_WarnEx(PyExc_RuntimeWarning, buf, 2);
232
print_backtrace();
233
}
234
235
236
void set_sage_signal_handler_message(const char* s)
237
{
238
_signals.s = s;
239
}
240
241
242
void setup_sage_signal_handler()
243
{
244
/* Reset the _signals structure */
245
memset(&_signals, 0, sizeof(_signals));
246
247
/* Save the default signal mask */
248
sigprocmask(SIG_BLOCK, NULL, &default_sigmask);
249
250
/* Save the signal mask with SIGINT and SIGALRM */
251
sigprocmask(SIG_BLOCK, NULL, &sigmask_with_sigint);
252
sigaddset(&sigmask_with_sigint, SIGINT);
253
sigaddset(&sigmask_with_sigint, SIGALRM);
254
255
/* Install signal handlers */
256
struct sigaction sa;
257
memset(&sa, 0, sizeof(sa));
258
/* Block SIGINT and SIGALRM during the signal handlers */
259
sigemptyset(&sa.sa_mask);
260
sigaddset(&sa.sa_mask, SIGINT);
261
sigaddset(&sa.sa_mask, SIGALRM);
262
263
sa.sa_handler = sage_interrupt_handler;
264
if (sigaction(SIGINT, &sa, NULL)) {perror("sigaction"); exit(1);}
265
sa.sa_handler = sage_signal_handler;
266
/* Allow signals during signal handling, we have code to deal with
267
* this case. */
268
sa.sa_flags |= SA_NODEFER;
269
if (sigaction(SIGILL, &sa, NULL)) {perror("sigaction"); exit(1);}
270
if (sigaction(SIGABRT, &sa, NULL)) {perror("sigaction"); exit(1);}
271
if (sigaction(SIGFPE, &sa, NULL)) {perror("sigaction"); exit(1);}
272
if (sigaction(SIGBUS, &sa, NULL)) {perror("sigaction"); exit(1);}
273
if (sigaction(SIGSEGV, &sa, NULL)) {perror("sigaction"); exit(1);}
274
275
/* If the CPU architecture is x86, check whether the EMMS
276
* instruction is supported by executing it and catching a
277
* possible SIGILL (illegal instruction signal). */
278
#ifdef CPU_ARCH_x86
279
if (!cpu_has_emms)
280
{
281
if (sig_on_no_except()) /* try: */
282
{
283
asm("emms");
284
sig_off();
285
cpu_has_emms = 1;
286
}
287
else /* except: */
288
{
289
PyErr_Clear(); /* Clear Python exception */
290
}
291
}
292
#endif
293
}
294
295
296
void print_backtrace()
297
{
298
void* backtracebuffer[1024];
299
fflush(stderr);
300
#ifdef HAVE_BACKTRACE
301
int btsize = backtrace(backtracebuffer, 1024);
302
backtrace_symbols_fd(backtracebuffer, btsize, 2);
303
#endif
304
}
305
306
307
void sigdie(int sig, const char* s)
308
{
309
print_backtrace();
310
fprintf(stderr, "\n"
311
"------------------------------------------------------------------------\n"
312
"%s\n"
313
"This probably occurred because a *compiled* component of Sage has a bug\n"
314
"in it and is not properly wrapped with sig_on(), sig_off(). You might\n"
315
"want to run Sage under gdb with 'sage -gdb' to debug this.\n"
316
"Sage will now terminate.\n"
317
"------------------------------------------------------------------------\n",
318
s);
319
fflush(stderr);
320
321
/* Suicide with signal ``sig`` */
322
kill(getpid(), sig);
323
324
/* We should be dead! */
325
exit(128 + sig);
326
}
327
328