/[ascend]/trunk/ascend/utilities/ascSignal.c
ViewVC logotype

Contents of /trunk/ascend/utilities/ascSignal.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2535 - (show annotations) (download) (as text)
Thu Jan 26 00:19:50 2012 UTC (10 years, 5 months ago) by jpye
File MIME type: text/x-csrc
File size: 17689 byte(s)
New implementation of signal handler stacks, less code.
Fixed one issue with utilities_ascSignal test cases, but can't work out reason for the other.
1 /* ASCEND modelling environment
2 Copyright (C) 1997 Benjamin Andrew Allan
3 Copyright (C) 2006 Carnegie Mellon University
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 *//** @file
20 Signal handling protocol definitions for ASCEND
21
22 Start of making signal handling in ASCEND
23 code somewhat sane. Still needs to somehow
24 support the management of jmp_buf's so that
25 handlers will longjmp to the right place.
26
27 A better alternative is to make all our code check/return
28 status flags based on a variable set by the trap
29 and cleared by recipient.
30 *//*
31 by Benjamin Andrew Allan, May 27, 1997
32 Last in CVS $Revision: 1.9 $ $Date: 1999/01/19 12:23:20 $ $Author: mthomas $
33
34 10/15/2005 - Changed ascresetneeded() so that any previously
35 registered handlers are reset before return.
36 - Added Asc_SignalRecover() to standard handler
37 Asc_SignalTrap() so handlers are reset if necessary. (JDS)
38 12/10/2005 - Changed storage of signal handlers from gl_list's
39 to local arrays. gl_list's can't legally hold
40 function pointers. (JDS)
41 18 Dec 06 - Removed ascresetneeded (moved to SConstruct)
42 25 Jan 12 - Add debug capability for contents of stacks (JDP)
43 */
44
45 #include "ascSignal.h"
46 #ifdef ASC_SIGNAL_TRAPS
47
48 /*-------- this file will do nothing unless ASC_SIGNAL_TRAPS is turned on -----*/
49
50 #include <stdio.h>
51
52
53 #include <signal.h>
54
55 #ifdef HAVE_C99FPE
56 # include <fenv.h>
57 #endif
58
59
60 #ifdef __WIN32__
61 # include <process.h>
62 #else
63 # include <unistd.h>
64 #endif
65
66 #include <ascend/general/ascMalloc.h>
67 #include "ascSignal.h"
68 #include <ascend/general/panic.h>
69
70 //#define SIGNAL_DEBUG
71
72 /*------------------------------------------------------------------------------
73 GLOBALS AND FOWARD DECS
74 */
75
76 /* test buf for initialization */
77 JMP_BUF g_fpe_env;
78 JMP_BUF g_seg_env;
79 JMP_BUF g_int_env;
80
81 #ifdef HAVE_C99FPE
82 fenv_t g_fenv;
83 #endif
84
85 /* for future use */
86 jmp_buf g_foreign_code_call_env;
87
88 /**
89 Struct to store signal stacks, with support for debugging names and
90 location where handlers were pushed.
91 */
92 typedef struct{
93 SigHandlerFn *handler;
94 #ifdef SIGNAL_DEBUG
95 char *name;
96 char *file;
97 int line;
98 #endif
99 } SignalStackItem;
100
101 typedef struct{
102 SignalStackItem fpe_traps[MAX_TRAP_DEPTH];
103 int fpe_top;
104 SignalStackItem int_traps[MAX_TRAP_DEPTH];
105 int int_top;
106 SignalStackItem seg_traps[MAX_TRAP_DEPTH];
107 int seg_top;
108 } SignalStacks;
109
110 static SignalStacks *f_traps = NULL;
111
112 #if 0
113 static SigHandlerFn **f_fpe_traps = NULL; /**< array for pushed SIGFPE handlers */
114 static int f_fpe_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */
115
116 static SigHandlerFn **f_int_traps = NULL; /**< array for pushed SIGINT handlers */
117 static int f_int_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */
118
119 static SigHandlerFn **f_seg_traps = NULL; /**< array for pushed SIGSEGV handlers */
120 static int f_seg_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */
121 #endif
122
123 #ifdef HAVE_C99FPE
124 static fenv_t *f_fenv_stack = NULL;
125 static int f_fenv_stack_top = -1;
126 #endif
127
128 static void initstack(int sig);
129 static int pop_trap(int signum, SigHandlerFn *tp, char *name, char *file, int line);
130 static int push_trap(int signum, SigHandlerFn *func, char *name, char *file, int line);
131 static void reset_trap(int signum);
132
133 #ifdef HAVE_C99FPE
134 static int fenv_pop(fenv_t *stack, int *top);
135 static int fenv_push(fenv_t *stack,int *top, int excepts);
136 #endif
137
138 #define SIGNAME(SIGNUM) (SIGNUM==SIGFPE?"SIGFPE":(SIGNUM==SIGINT?"SIGINT":(SIGNUM==SIGSEGV?"SIGSEGV":"unknown")))
139 /*------------------------------------------------------------------------------
140 API FUNCTIONS
141 */
142
143 /**
144 Initialise ASCEND signal handling
145
146 Does not establish any traps, just the structures for maintaining them.
147 Pushes the existing traps, if any, on the bottom of the created stacks.
148
149 @NOTE Cannot be called twice successfully.
150
151 @return 0 if successful, 1 if out of memory, 2 otherwise.
152 */
153 int Asc_SignalInit(void){
154 /* initialize SIGFPE stack */
155
156 #ifdef SIGNAL_DEBUG
157 # ifdef ASC_RESETNEEDED
158 CONSOLE_DEBUG("Initialising signal stack (with resets needed)");
159 # else
160 CONSOLE_DEBUG("Initialising signal stack (with resets not required)");
161 # endif
162 #endif
163
164 if(f_traps == NULL){
165 f_traps = ASC_NEW(SignalStacks);
166 if(!f_traps){
167 return 1;
168 }
169 f_traps->fpe_top = -1;
170 f_traps->int_top = -1;
171 f_traps->seg_top = -1;
172 }
173
174 #ifdef HAVE_C99FPE
175 if(f_fenv_stack==NULL){ /* if we haven't already initialised this... */
176 f_fenv_stack = ASC_NEW_ARRAY_CLEAR(fenv_t,MAX_TRAP_DEPTH);
177 if(f_fenv_stack == NULL){
178 return 1; /* failed to allocate */
179 }
180 }
181 f_fenv_stack_top = -1;
182 #endif
183
184 /* old signals are *not* stored */
185 initstack(SIGFPE);
186 initstack(SIGINT);
187 initstack(SIGSEGV);
188
189 #if defined(HAVE_C99FPE)
190 CONSOLE_DEBUG("Initialise FPE state to stack (%d)",f_fenv_stack_top);
191 fenv_push(f_fenv_stack,&f_fenv_stack_top,0);
192 #endif
193
194 return 0;
195 }
196
197 /**
198 Clears and destroys the stacks of signal handlers.
199 */
200 void Asc_SignalDestroy(void)
201 {
202 ascfree(f_traps);
203 #ifdef HAVE_C99FPE
204 if(f_fenv_stack){
205 ASC_FREE(f_fenv_stack);
206 f_fenv_stack = NULL;
207 }
208 #endif
209 f_traps = NULL;
210 #ifdef SIGNAL_DEBUG
211 CONSOLE_DEBUG("Destroyed signal stack");
212 #endif
213 }
214
215 /**
216 This function reinstalls all the signal handlers this module
217 has been informed of. This should be called after every
218 trapped exception and at any other time when the status of
219 exception handlers may have become not well defined.
220 The most recently pushed handler is installed for each supported
221 signal. If nothing on stack, SIG_DFL gets installed.
222
223 @NOTE that if somebody installs a handler without going through
224 our push/pop, theirs is liable to be forgotten.
225 */
226 void Asc_SignalRecover(int force){
227 #ifndef ASC_RESETNEEDED
228 if(force){
229 #endif
230 # ifdef SIGNAL_DEBUG
231 CONSOLE_DEBUG("Resetting traps");
232 # endif
233 reset_trap(SIGFPE);
234 reset_trap(SIGINT);
235 reset_trap(SIGSEGV);
236 #ifndef ASC_RESETNEEDED
237 }
238 #endif
239 }
240
241 /**
242 Add a handler to the stack of signal handlers for the given signal.
243
244 There is a maximum stack limit, so returns 1 if limit exceeded.
245 Returns -1 if stack of signal requested does not exist.
246 Pushing a NULL handler does NOT change anything at all.
247 On a successful return, the handler has been installed and will
248 remain installed until a Asc_SignalHandlerPop or another push.
249
250 @return 0 on success, -2 if func is NULL, -1 if unsupported signal is given,
251 -3 if 'signal' returns SIG_ERR.
252 */
253 int Asc_SignalHandlerPush_impl(int signum, SigHandlerFn *func, char *name
254 , char *file, int line
255 ){
256 int err;
257 if (func == NULL) {
258 return -2;
259 }
260
261 #ifdef SIGNAL_DEBUG
262 CONSOLE_DEBUG("Pushing handler %s for signal %s(%d)"
263 ,name,SIGNAME(signum),signum
264 );
265 #endif
266
267 err = push_trap(signum, func, name, file, line);
268
269 if(err != 0){
270 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Error pushing %s to stack (err = %d, signal=%s(%d))."
271 ,name, err, SIGNAME(signum), signum
272 );
273 return err;
274 }
275 return SIG_ERR==SIGNAL(signum, func); /* install */
276 }
277
278
279 int Asc_SignalHandlerPop_impl(int signum, SigHandlerFn *tp, char *name
280 , char *file, int line
281 ){
282 int err;
283 #ifdef SIGNAL_DEBUG
284 CONSOLE_DEBUG("(%s:%d) Popping signal stack for signal %s (%d) (expecting top to be %p '%s')",file,line,SIGNAME(signum),signum,tp,name);
285 #endif
286
287 err = pop_trap(signum, tp, name, file, line);
288
289 if (err != 0 && tp != NULL) {
290 #ifdef SIGNAL_DEBUG
291 CONSOLE_DEBUG("stack pop mismatch");
292 #endif
293 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack pop mismatch.",signum);
294 return err;
295 }
296 SIGNAL(signum,Asc_SignalStackTop(signum));
297 return err;
298 }
299
300 void Asc_SignalTrap(int sigval){
301 #ifdef SIGNAL_DEBUG
302 CONSOLE_DEBUG("Caught signal #%d",sigval);
303 #endif
304 switch(sigval) {
305 case SIGFPE:
306 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Floating point error caught");
307 CONSOLE_DEBUG("SIGFPE caught");
308 #ifdef HAVE_C99FPE
309 FPRESET;
310 #endif
311 LONGJMP(g_fpe_env,sigval);
312 case SIGINT:
313 CONSOLE_DEBUG("SIGINT (Ctrl-C) caught");
314 LONGJMP(g_int_env,sigval);
315 case SIGSEGV:
316 #ifdef SIGNAL_DEBUG
317 CONSOLE_DEBUG("SIGSEGV caught");
318 #endif
319 LONGJMP(g_seg_env,sigval);
320 default:
321 #ifdef SIGNAL_DEBUG
322 CONSOLE_DEBUG("Unrecognised signal %d caught",sigval);
323 #endif
324 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Installed on unexpected signal (sigval = %d). Returning (who knows where...)", sigval);
325 return;
326 }
327 }
328
329 void Asc_SignalPrintStack(int signum){
330 if(!f_traps){
331 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Signal handler not initialised!");
332 return;
333 }
334 SignalStackItem *stack;
335 int *index;
336 switch(signum){
337 case SIGFPE: stack = f_traps->fpe_traps; index = &(f_traps->fpe_top); break;
338 case SIGINT: stack = f_traps->int_traps; index = &(f_traps->int_top); break;
339 case SIGSEGV: stack = f_traps->seg_traps; index = &(f_traps->seg_top); break;
340 default:
341 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d", signum);
342 return;
343 }
344 int i = 0;
345 const char *signame = (signum==SIGFPE?"SIGFPE":SIGNAME(signum));
346
347 #define LMAX 4096
348 char str[LMAX], *end = str;
349 int ch;
350 if(*index == -1){
351 fprintf(stderr,"%s handler stack: empty\n",signame);
352 }else{
353 for(i = 0; i <= *index; ++i){
354 #ifdef SIGNAL_DEBUG
355 ch = SNPRINTF(end, LMAX - (end - str), "%s ", stack[i].name);
356 #else
357 ch = SNPRINTF(end, LMAX - (end - str), "%p ", stack[i].handler);
358 #endif
359 end += ch;
360 if(ch<0)break;
361 }
362 fprintf(stderr,"%s handler stack: %s\n",signame,str);
363 }
364 }
365
366 int Asc_SignalStackLength(int signum){
367 int *index;
368 switch(signum){
369 case SIGFPE: index = &(f_traps->fpe_top); break;
370 case SIGINT: index = &(f_traps->int_top); break;
371 case SIGSEGV: index = &(f_traps->seg_top); break;
372 default:
373 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d",signum);
374 return 0;
375 }
376 return *index + 1;
377 }
378
379 SigHandlerFn *Asc_SignalStackTop(int signum){
380 if(!f_traps)return NULL;
381 SignalStackItem *stack;
382 int *index;
383 switch(signum){
384 case SIGFPE: stack = f_traps->fpe_traps; index = &(f_traps->fpe_top); break;
385 case SIGINT: stack = f_traps->int_traps; index = &(f_traps->int_top); break;
386 case SIGSEGV: stack = f_traps->seg_traps; index = &(f_traps->seg_top); break;
387 default:
388 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d", signum);
389 return NULL;
390 }
391 if(*index < 0)return NULL;
392 if(*index >= MAX_TRAP_DEPTH)return NULL;
393 return stack[*index].handler;
394 }
395
396 /*------------------------------------------------------------------------------
397 UTILITY FUNCTIONS
398 */
399
400 /*
401 Removed ascresetneeded from here. This is a build-time configuration test
402 rather than a runtime test (and causes annoyance when running ASCEND through
403 a debugger).
404
405 So far the following seem to need reset trapped signals after
406 a longjmp, or unconditionally.
407 HPUX cc -Aa -D_HPUX_SOURCE
408 Solaris cc
409 AIX xlc
410 IRIX cc
411 Windows
412
413 The following retain the last trap set with or without a call to longjmp
414 and so don't need resetting of traps.
415 SunOS4 acc
416 OSF32 cc
417 NetBSD gcc 2.4.5 -ansi
418 Linux gcc (i386)
419 */
420
421 //------------------------------------
422 // COMMOM STACK ROUTINES (shared by the three different signal handler stacks)
423
424 static void initstack(int sig){
425 SigHandlerFn *old;
426 SignalStackItem *stack;
427 int *index;
428 switch(sig){
429 case SIGFPE: stack = f_traps->fpe_traps; index = &(f_traps->fpe_top); break;
430 case SIGINT: stack = f_traps->int_traps; index = &(f_traps->int_top); break;
431 case SIGSEGV: stack = f_traps->seg_traps; index = &(f_traps->seg_top); break;
432 default:
433 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d", sig);
434 return;
435 }
436 old = SIGNAL(sig, SIG_DFL);
437 if(old != SIG_ERR && old != SIG_DFL){
438 #ifdef SIGNAL_DEBUG
439 CONSOLE_DEBUG("Initialising stack for signal %d to %p",sig,old);
440 #endif
441 stack[0].handler = old;
442 #ifdef SIGNAL_DEBUG
443 if(old == SIG_DFL){
444 stack[0].name = "SIG_DFL";
445 }else{
446 stack[0].name = "preexisting";
447 }
448 stack[0].file = "unknown";
449 stack[0].line = 0;
450 #endif
451 *index = 0;
452 (void)SIGNAL(sig,old);
453 }else{
454 #ifdef SIGNAL_DEBUG
455 CONSOLE_DEBUG("Initialising stack for signal %d as empty",sig);
456 #endif
457 *index = -1;
458 }
459 }
460
461 static void reset_trap(int signum){
462 SignalStackItem top;
463 SigHandlerFn *oldfn;
464
465 if(f_traps){
466 SignalStackItem *stack;
467 int *index;
468 switch(signum){
469 case SIGFPE: stack = f_traps->fpe_traps; index = &(f_traps->fpe_top); break;
470 case SIGINT: stack = f_traps->int_traps; index = &(f_traps->int_top); break;
471 case SIGSEGV: stack = f_traps->seg_traps; index = &(f_traps->seg_top); break;
472 default:
473 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d", signum);
474 return;
475 }
476 if(*index >= 0 && *index < MAX_TRAP_DEPTH){
477 oldfn = signal(signum,SIG_DFL); (void)oldfn;
478 top = stack[*index];
479 if(top.handler != SIG_ERR && top.handler != SIG_DFL){
480 /* reset the signal, if it's not already set to what we want */
481 #ifdef SIGNAL_DEBUG
482 CONSOLE_DEBUG("Resetting signal %s from %p to %p (%s)"
483 ,SIGNAME(signum)
484 ,oldfn,top.handler,top.name
485 );
486 #endif
487 (void)SIGNAL(signum,top.handler);
488 return;
489 }
490 }
491 #ifdef SIGNAL_DEBUG
492 CONSOLE_DEBUG("Resetting %s handler to SIG_DFL (stack empty or invalid)"
493 ,SIGNAME(signum)
494 );
495 #endif
496 (void)SIGNAL(signum,SIG_DFL);
497 }else{
498 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Signal handler not yet initialised! Setting %s handler to SIG_DFL.",SIGNAME(signum));
499 (void)SIGNAL(signum,SIG_DFL);
500 return;
501 }
502 }
503
504 /**
505 Append a pointer to the list given, if the list is not full.
506 */
507 static int push_trap(int signum, SigHandlerFn *func, char *name, char *file, int line){
508 if(!f_traps){
509 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Signal handler list f_traps not initialised");
510 return -1;
511 }
512 SignalStackItem *stack;
513 int *index;
514 switch(signum){
515 case SIGFPE: stack = f_traps->fpe_traps; index = &(f_traps->fpe_top); break;
516 case SIGINT: stack = f_traps->int_traps; index = &(f_traps->int_top); break;
517 case SIGSEGV: stack = f_traps->seg_traps; index = &(f_traps->seg_top); break;
518 default:
519 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d", signum);
520 return -2;
521 }
522
523 if (*index > MAX_TRAP_DEPTH-1) {
524 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Signal handler stack for %s is full!"
525 ,SIGNAME(signum)
526 );
527 return 1;
528 }
529 ++(*index);
530 stack[*index].handler = func;
531 #ifdef SIGNAL_DEBUG
532 stack[*index].name = name;
533 stack[*index].file = file;
534 stack[*index].line = line;
535 #endif
536 return 0;
537 }
538
539
540 /**
541 @return 0 on success, 2 on NULL tlist or stackptr input, 1 on empty stack
542 or -1 on mismatched input tp and stack data
543
544 Any non-zero return code leaves the stack as it was.
545 */
546 static int pop_trap(int signum, SigHandlerFn *func, char *name, char *file, int line){
547 int err = 0;
548 if(!f_traps){
549 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Signal handler list f_traps not initialised");
550 return 2;
551 }
552 SignalStackItem *stack;
553 int *index;
554 switch(signum){
555 case SIGFPE: stack = f_traps->fpe_traps; index = &(f_traps->fpe_top); break;
556 case SIGINT: stack = f_traps->int_traps; index = &(f_traps->int_top); break;
557 case SIGSEGV: stack = f_traps->seg_traps; index = &(f_traps->seg_top); break;
558 default:
559 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal code %d", signum);
560 return 3;
561 }
562
563 if(*index < 0)return 1;
564
565 if(stack[*index].handler != func){
566 #ifdef SIGNAL_DEBUG
567 error_reporter(ASC_PROG_ERR,file,line,name,"Request to pop '%s' (%p), but top of stack contains '%s' (%p)!"
568 ,name,func,stack[*index].name,stack[*index].handler
569 );
570 #else
571 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Request to pop handler %p, but top of stack contains %p!"
572 ,func,stack[*index].handler
573 );
574 #endif
575 err = 4;
576 }
577 stack[*index].handler = NULL;
578 #ifdef SIGNAL_DEBUG
579 stack[*index].name = NULL;
580 stack[*index].file = NULL;
581 stack[*index].line = 0;
582 #endif
583 --(*index);
584 return err;
585 }
586
587 /*------------------------------------------
588 FPE ENV STACK
589 */
590
591 #ifdef HAVE_C99FPE
592
593 /**
594 Store current FPU state so that we can reset it later (after we've done
595 some stuff)
596
597 return 0 on success
598 */
599 static int fenv_push(fenv_t *stack, int *top, int excepts){
600 #ifdef SIGNAL_DEBUG
601 CONSOLE_DEBUG("Pushing FENV flags %d",excepts);
602 #endif
603
604 if(*top > MAX_TRAP_DEPTH - 1){
605 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is full");
606 return 1;
607 }
608 if(*top < -1){
609 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack top < -1");
610 return 2;
611 }
612 if(stack==NULL){
613 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
614 return 3;
615 }
616 fenv_t *fe = &stack[++(*top)];
617 if(fegetenv(fe)){
618 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to get env");
619 return 4;
620 }
621 fesetexceptflag(&g_fenv, excepts);
622 //CONSOLE_DEBUG("Enabled div-by-zero FPE exception (%d)",*top);
623 return 0;
624 }
625
626 /**
627 Restore a saved FPU state. Return 0 on success.
628 */
629 static int fenv_pop(fenv_t *stack, int *top){
630 #ifdef CONSOLE_DEBUG
631 CONSOLE_DEBUG("Popping FENV flags");
632 #endif
633 if(*top < 0){
634 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is empty");
635 return 1;
636 }
637 if(stack==NULL){
638 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
639 return 2;
640 }
641 fenv_t *fe = &stack[(*top)--];
642 if(fesetenv(fe)){
643 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to set env");
644 return 3;
645 }
646 //CONSOLE_DEBUG("Restorted FPE state");
647 return 0;
648 }
649
650 #endif /* HAVE_C99FPE */
651
652 #endif /* ASC_SIGNAL_TRAPS */

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22