/************************************************************************* * * * YAP Prolog * * * * Yap Prolog was developed at NCCUP - Universidade do Porto * * * * Copyright L.Damas, V. Santos Costa and Universidade do Porto 1985-- * * * ************************************************************************** * * * File: signal.c * * comments: Signal Handling & Debugger Support * * * * * * * *************************************************************************/ #ifdef SCCS static char SccsId[] = "%W% %G%"; #endif #define HAS_CACHE_REGS 1 #include "Yap.h" #if HAVE_UNISTD_H #include #endif #if _WIN32 #include #include #endif #include "Yatom.h" #include "YapHeap.h" #include "eval.h" #include "yapio.h" #ifdef TABLING #include "tab.macros.h" #endif /* TABLING */ #include #include #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if HAVE_MALLOC_H #include #endif #include #ifdef LOW_LEVEL_TRACER #include #endif /* * The InteractSIGINT function is called after a normal interrupt had been caught. * It allows 6 possibilities: abort, continue, trace, debug, help, exit. */ static yap_signals InteractSIGINT(int ch) { #ifdef HAVE_SETBUF /* make sure we are not waiting for the end of line */ setbuf (stdin, NULL); #endif switch (ch) { case 'a': /* abort computation */ return YAP_ABORT_SIGNAL; case 'b': /* continue */ return YAP_BREAK_SIGNAL; case 'c': /* continue */ return YAP_NO_SIGNAL; case 'd': /* enter debug mode */ return YAP_DEBUG_SIGNAL; case 'e': /* exit */ Yap_exit(1); return YAP_EXIT_SIGNAL; case 'g': /* stack dump */ return YAP_STACK_DUMP_SIGNAL; case 't': /* start tracing */ return YAP_TRACE_SIGNAL; #ifdef LOW_LEVEL_TRACER case 'T': toggle_low_level_trace(); return YAP_NO_SIGNAL; #endif case 's': /* show some statistics */ return YAP_STATISTICS_SIGNAL; case EOF: return YAP_NO_SIGNAL; case 'h': case '?': default: /* show an helpful message */ fprintf(stderr, "Please press one of:\n"); fprintf(stderr, " a for abort\n c for continue\n d for debug\n"); fprintf(stderr, " e for exit\n g for stack dump\n s for statistics\n t for trace\n"); fprintf(stderr, " b for break\n"); return YAP_NO_SIGNAL; } } /* This function talks to the user about a signal. We assume we are in the context of the main Prolog thread (trivial in Unix, but hard in WIN32) */ static yap_signals ProcessSIGINT(void) { CACHE_REGS int ch, out; #if _WIN32 if (!_isatty(0)) { return YAP_INT_SIGNAL; } #elif HAVE_ISATTY if (!isatty(0)) { return YAP_INT_SIGNAL; } #endif LOCAL_PrologMode |= AsyncIntMode; do { ch = Yap_GetCharForSIGINT(); } while (!(out = InteractSIGINT(ch))); LOCAL_PrologMode &= ~AsyncIntMode; return(out); } inline static void do_signal(int wid, yap_signals sig USES_REGS) { #if THREADS __sync_fetch_and_or ( &REMOTE(wid)->Signals_, SIGNAL_TO_BIT(sig)); if (!REMOTE_InterruptsDisabled(wid)) { REMOTE_ThreadHandle(wid).current_yaam_regs->CreepFlag_ = Unsigned(REMOTE_ThreadHandle(wid).current_yaam_regs->LCL0_); } #else LOCAL_Signals |= SIGNAL_TO_BIT(sig); if (!LOCAL_InterruptsDisabled) { CreepFlag = Unsigned(LCL0); } #endif } inline static int get_signal(yap_signals sig USES_REGS) { #if THREADS uint64_t old; // first, clear the Creep Flag, now if someone sets it it is their problem CalculateStackGap( PASS_REGS1 ); // reset the flag if ( (old =__sync_fetch_and_and( &LOCAL_Signals, ~SIGNAL_TO_BIT(sig) ) ) != SIGNAL_TO_BIT(sig)) { if (!LOCAL_InterruptsDisabled && LOCAL_Signals != 0) { CreepFlag = (CELL)LCL0; } if (!(old & SIGNAL_TO_BIT(sig)) ) { // not there? return FALSE; } // more likely case, we have other interrupts. return TRUE; } // success, we are good return TRUE; // should we set the flag? #else if (LOCAL_Signals & SIGNAL_TO_BIT(sig)) { LOCAL_Signals &= ~SIGNAL_TO_BIT(sig); if (!LOCAL_InterruptsDisabled && LOCAL_Signals != 0) { CreepFlag = (CELL)LCL0; } else { CalculateStackGap( PASS_REGS1 ); } return TRUE; } else { return FALSE; } #endif } /** Function called to handle delayed interrupts. */ int Yap_HandleInterrupts( void ) { CACHE_REGS yap_signals sig; if ( get_signal( YAP_INT_SIGNAL PASS_REGS )) { if ( (sig = ProcessSIGINT()) != YAP_NO_SIGNAL ) do_signal(worker_id, sig PASS_REGS); LOCAL_PrologMode &= ~InterruptMode; return 1; } return 0; } static Int p_creep( USES_REGS1 ) { Atom at; PredEntry *pred; at = AtomCreep; pred = RepPredProp(PredPropByFunc(Yap_MkFunctor(at, 1),0)); CreepCode = pred; do_signal(worker_id, YAP_CREEP_SIGNAL PASS_REGS); return TRUE; } static Int p_creep_fail( USES_REGS1 ) { Atom at; PredEntry *pred; at = AtomCreep; pred = RepPredProp(PredPropByFunc(Yap_MkFunctor(at, 1),0)); CreepCode = pred; do_signal(worker_id, YAP_CREEP_SIGNAL PASS_REGS); return FALSE; } static Int stop_creeping( USES_REGS1 ) { get_signal( YAP_CREEP_SIGNAL PASS_REGS ); return true; } static Int creep_allowed( USES_REGS1 ) { if (PP != NULL) { get_signal(YAP_CREEP_SIGNAL PASS_REGS); return true; } return false; } void Yap_signal(yap_signals sig) { CACHE_REGS do_signal(worker_id, sig PASS_REGS); } #ifdef DEBUG static Int p_debug( USES_REGS1 ); #endif void Yap_external_signal(int wid, yap_signals sig) { #if THREADS REGSTORE *regcache = REMOTE_ThreadHandle(wid).current_yaam_regs; #endif do_signal(wid, sig PASS_REGS); LOCAL_PrologMode &= ~InterruptMode; } int Yap_get_signal__(yap_signals sig USES_REGS) { return get_signal(sig PASS_REGS); } // the caller holds the lock. int Yap_has_signals__(yap_signals sig1, yap_signals sig2 USES_REGS) { return LOCAL_Signals & (SIGNAL_TO_BIT(sig1)|SIGNAL_TO_BIT(sig2)); } int Yap_only_has_signals__(yap_signals sig1, yap_signals sig2 USES_REGS) { uint64_t sigs = LOCAL_Signals; return sigs & (SIGNAL_TO_BIT(sig1) | SIGNAL_TO_BIT(sig2)) && ! (sigs & ~(SIGNAL_TO_BIT(sig1) | SIGNAL_TO_BIT(sig2))) ; } #ifdef DEBUG volatile int volat = 0; static Int p_debug( USES_REGS1 ) { /* $debug(+Flag) */ int i = IntOfTerm(Deref(ARG1)); while (volat == 0) { } if (i >= 'a' && i <= 'z') GLOBAL_Option[i - 96] = !GLOBAL_Option[i - 96]; return (1); } void Yap_loop(void); void Yap_debug_end_loop(void); void Yap_loop(void) { while (volat == 0); } void Yap_debug_end_loop(void) { volat = 1; } #endif static Int p_first_signal( USES_REGS1 ) { Atom at; yap_signals sig; while (TRUE) { uint64_t mask = LOCAL_Signals; if (mask == 0) return FALSE; #if HAVE___BUILTIN_FFSLL sig = __builtin_ffsll(mask); #elif HAVE_FFSLL sig = ffsll(mask); #else sig = Yap_msb( mask PASS_REGS)+1; #endif if (get_signal(sig PASS_REGS)) { break; } } loop: switch (sig) { case YAP_INT_SIGNAL: sig = ProcessSIGINT(); if (sig == YAP_INT_SIGNAL) { at = AtomSigInt; break; } if (sig != YAP_NO_SIGNAL) goto loop; return FALSE; case YAP_ABORT_SIGNAL: /* abort computation */ LOCAL_PrologMode &= ~AsyncIntMode; if (LOCAL_PrologMode & (GCMode|ConsoleGetcMode|CritMode)) { LOCAL_PrologMode |= AbortMode; return -1; } else { Yap_Error(ABORT_EVENT, TermNil, "abort from console"); } Yap_RestartYap( 1 ); return FALSE; case YAP_CREEP_SIGNAL: at = AtomSigCreep; break; case YAP_TRACE_SIGNAL: at = AtomSigTrace; break; case YAP_DEBUG_SIGNAL: at = AtomSigDebug; break; case YAP_BREAK_SIGNAL: at = AtomSigBreak; break; case YAP_FAIL_SIGNAL: at = AtomFail; break; case YAP_STACK_DUMP_SIGNAL: at = AtomSigStackDump; break; case YAP_STATISTICS_SIGNAL: at = AtomSigStatistics; break; #ifdef SIGALRM case YAP_ALARM_SIGNAL: #endif case YAP_WINTIMER_SIGNAL: at = AtomSigAlarm; break; #ifdef SIGVTALRM case YAP_VTALARM_SIGNAL: at = AtomSigVTAlarm; break; #endif case YAP_EXIT_SIGNAL: Yap_exit(1); return FALSE; case YAP_WAKEUP_SIGNAL: at = AtomSigWakeUp; break; case YAP_ITI_SIGNAL: at = AtomSigIti; break; #ifdef SIGPIPE case YAP_PIPE_SIGNAL: at = AtomSigPipe; break; #endif #ifdef SIGHUP case YAP_HUP_SIGNAL: at = AtomSigHup; break; #endif #ifdef SIGUSR1 case YAP_USR1_SIGNAL: at = AtomSigUsr1; break; #endif #ifdef SIGUSR2 case YAP_USR2_SIGNAL: at = AtomSigUsr2; break; #endif #ifdef SIGFPE case YAP_FPE_SIGNAL: at = AtomSigFPE; break; #endif default: return FALSE; } return Yap_unify(ARG1, MkAtomTerm(at)); } static Int p_continue_signals( USES_REGS1 ) { return p_first_signal( PASS_REGS1 ); } void Yap_InitSignalCPreds(void) { /* Basic predicates for the debugger */ Yap_InitCPred("$creep", 0, p_creep, SafePredFlag); Yap_InitCPred("$creep_fail", 0, p_creep_fail, SafePredFlag); Yap_InitCPred("$stop_creeping", 0, stop_creeping, NoTracePredFlag|HiddenPredFlag|SafePredFlag); Yap_InitCPred("$disable_debugging", 0, stop_creeping, NoTracePredFlag|HiddenPredFlag|SafePredFlag); Yap_InitCPred ("$first_signal", 1, p_first_signal, SafePredFlag|SyncPredFlag); Yap_InitCPred ("$continue_signals", 0, p_continue_signals, SafePredFlag|SyncPredFlag); Yap_InitCPred("creep_allowed", 0, creep_allowed, 0); #ifdef DEBUG Yap_InitCPred("sys_debug", 1, p_debug, SafePredFlag|SyncPredFlag); #endif } void *Yap_InitSignals(int wid) { void *ptr = (void *)malloc(sizeof(UInt)*REMOTE_MaxActiveSignals(wid)); return ptr; }