/************************************************************************* * * * 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 "YapHeap.h" #include "Yatom.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 */ #if PUSH_REGS // restore_absmi_regs(&Yap_standard_regs); #endif siglongjmp(LOCAL_RestartEnv, 4); 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 bool 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 } bool Yap_DisableInterrupts(int wid) { LOCAL_InterruptsDisabled = true; YAPEnterCriticalSection(); return true; } bool Yap_EnableInterrupts(int wid) { LOCAL_InterruptsDisabled = false; YAPLeaveCriticalSection(); return true; } /** Function called to handle delayed interrupts. */ bool Yap_HandleSIGINT(void) { CACHE_REGS yap_signals sig; do { if ((sig = ProcessSIGINT()) != YAP_NO_SIGNAL) do_signal(worker_id, sig PASS_REGS); LOCAL_PrologMode &= ~InterruptMode; return true; } while (get_signal(YAP_INT_SIGNAL PASS_REGS)); return false; } 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) { if (get_signal(YAP_CREEP_SIGNAL PASS_REGS)) { return Yap_unify(ARG1, TermTrue); } return Yap_unify(ARG1, TermFalse); } static Int disable_debugging(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 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 continue_signals(USES_REGS1) { return 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", 1, stop_creeping, NoTracePredFlag | HiddenPredFlag | SafePredFlag); Yap_InitCPred("$disable_debugging", 0, disable_debugging, NoTracePredFlag | HiddenPredFlag | SafePredFlag); Yap_InitCPred("$first_signal", 1, first_signal, SafePredFlag | SyncPredFlag); Yap_InitCPred("$continue_signals", 0, 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; }