This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/C/errors.c
2015-09-29 23:09:12 +01:00

566 lines
15 KiB
C
Executable File

/*************************************************************************
* *
* Yap Prolog *
* *
* Yap Prolog Was Developed At Nccup - Universidade Do Porto *
* *
* Copyright L.Damas, V.S.Costa And Universidade Do Porto 1985-1997 *
* *
**************************************************************************
* *
* File: errors.c *
* Last Rev: *
* Mods: *
* Comments: Yap'S error handlers *
* *
*************************************************************************/
#include "absmi.h"
#include "yapio.h"
#if HAVE_STDARG_H
#include <stdarg.h>
#endif
#include <stdlib.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#endif
#include "Foreign.h"
#if DEBUG
void Yap_PrintPredName(PredEntry *ap) {
CACHE_REGS
Term tmod = ap->ModuleOfPred;
if (!tmod)
tmod = TermProlog;
#if THREADS
Yap_DebugPlWrite(MkIntegerTerm(worker_id));
Yap_DebugPutc(stderr, ' ');
#endif
Yap_DebugPutc(stderr, '>');
Yap_DebugPutc(stderr, '\t');
Yap_DebugPlWrite(tmod);
Yap_DebugPutc(stderr, ':');
if (ap->ModuleOfPred == IDB_MODULE) {
Term t = Deref(ARG1);
if (IsAtomTerm(t)) {
Yap_DebugPlWrite(t);
} else if (IsIntegerTerm(t)) {
Yap_DebugPlWrite(t);
} else {
Functor f = FunctorOfTerm(t);
Atom At = NameOfFunctor(f);
Yap_DebugPlWrite(MkAtomTerm(At));
Yap_DebugPutc(stderr, '/');
Yap_DebugPlWrite(MkIntegerTerm(ArityOfFunctor(f)));
}
} else {
if (ap->ArityOfPE == 0) {
Atom At = (Atom)ap->FunctorOfPred;
Yap_DebugPlWrite(MkAtomTerm(At));
} else {
Functor f = ap->FunctorOfPred;
Atom At = NameOfFunctor(f);
Yap_DebugPlWrite(MkAtomTerm(At));
Yap_DebugPutc(stderr, '/');
Yap_DebugPlWrite(MkIntegerTerm(ArityOfFunctor(f)));
}
}
char s[1024];
if (ap->PredFlags & StandardPredFlag)
fprintf(stderr, "S");
if (ap->PredFlags & CPredFlag)
fprintf(stderr, "C");
if (ap->PredFlags & UserCPredFlag)
fprintf(stderr, "U");
if (ap->PredFlags & SyncPredFlag)
fprintf(stderr, "Y");
if (ap->PredFlags & LogUpdatePredFlag)
fprintf(stderr, "Y");
if (ap->PredFlags & HiddenPredFlag)
fprintf(stderr, "H");
sprintf(s, " %llx\n", ap->PredFlags);
Yap_DebugPuts(stderr, s);
}
#endif
bool Yap_Warning(const char *s, ...) {
va_list args;
va_start(args, s);
fprintf(stderr, "warning: %s\n", s);
va_end(args);
return true;
}
bool Yap_PrintWarning(Term twarning) {
CACHE_REGS
PredEntry *pred = RepPredProp(PredPropByFunc(
FunctorPrintMessage, PROLOG_MODULE)); // PROCEDURE_print_message2;
bool rc;
Term ts[2];
if (LOCAL_within_print_message) {
/* error within error */
fprintf(stderr, "%% WARNING WITHIN WARNING\n");
Yap_RestartYap(1);
}
LOCAL_DoingUndefp = true;
LOCAL_within_print_message = true;
if (pred->OpcodeOfPred == UNDEF_OPCODE) {
fprintf(stderr, "warning message:\n");
Yap_DebugPlWrite(twarning);
fprintf(stderr, "\n");
LOCAL_DoingUndefp = false;
LOCAL_within_print_message = false;
return true;
}
ts[0] = MkAtomTerm(AtomWarning);
ts[1] = twarning;
rc = Yap_execute_pred(pred, ts, true PASS_REGS);
LOCAL_within_print_message = false;
LOCAL_DoingUndefp = false;
return rc;
}
int Yap_HandleError(const char *s, ...) {
CACHE_REGS
yap_error_number err = LOCAL_Error_TYPE;
const char *serr;
LOCAL_Error_TYPE = YAP_NO_ERROR;
if (LOCAL_ErrorMessage) {
serr = LOCAL_ErrorMessage;
} else {
serr = s;
}
switch (err) {
case RESOURCE_ERROR_STACK:
if (!Yap_gc(2, ENV, gc_P(P, CP))) {
Yap_Error(RESOURCE_ERROR_STACK, ARG1, serr);
return (FALSE);
}
return TRUE;
case RESOURCE_ERROR_AUXILIARY_STACK:
if (LOCAL_MAX_SIZE < (char *)AuxSp - AuxBase) {
LOCAL_MAX_SIZE += 1024;
}
if (!Yap_ExpandPreAllocCodeSpace(0, NULL, TRUE)) {
/* crash in flames */
Yap_Error(RESOURCE_ERROR_AUXILIARY_STACK, ARG1, serr);
return FALSE;
}
return TRUE;
case RESOURCE_ERROR_HEAP:
if (!Yap_growheap(FALSE, 0, NULL)) {
Yap_Error(RESOURCE_ERROR_HEAP, ARG2, serr);
return FALSE;
}
default:
Yap_Error(err, LOCAL_Error_Term, serr);
return (FALSE);
}
}
int Yap_SWIHandleError(const char *s, ...) {
CACHE_REGS
yap_error_number err = LOCAL_Error_TYPE;
char *serr;
LOCAL_Error_TYPE = YAP_NO_ERROR;
if (LOCAL_ErrorMessage) {
serr = LOCAL_ErrorMessage;
} else {
serr = (char *)s;
}
switch (err) {
case RESOURCE_ERROR_STACK:
if (!Yap_gc(2, ENV, gc_P(P, CP))) {
Yap_Error(RESOURCE_ERROR_STACK, TermNil, serr);
return (FALSE);
}
return TRUE;
case RESOURCE_ERROR_AUXILIARY_STACK:
if (LOCAL_MAX_SIZE < (char *)AuxSp - AuxBase) {
LOCAL_MAX_SIZE += 1024;
}
if (!Yap_ExpandPreAllocCodeSpace(0, NULL, TRUE)) {
/* crash in flames */
Yap_Error(RESOURCE_ERROR_AUXILIARY_STACK, ARG1, serr);
return FALSE;
}
return TRUE;
case RESOURCE_ERROR_HEAP:
if (!Yap_growheap(FALSE, 0, NULL)) {
Yap_Error(RESOURCE_ERROR_HEAP, ARG2, serr);
return FALSE;
}
default:
Yap_Error(err, LOCAL_Error_Term, serr);
return (FALSE);
}
}
void Yap_RestartYap(int flag) {
CACHE_REGS
#if PUSH_REGS
restore_absmi_regs(&Yap_standard_regs);
#endif
siglongjmp(LOCAL_RestartEnv, 1);
}
static void error_exit_yap(int value) {
CACHE_REGS
if (!(LOCAL_PrologMode & BootMode)) {
#if DEBUG
#endif
}
fprintf(stderr, "\n Exiting ....\n");
Yap_exit(value);
}
/* This needs to be a static because I can't trust the stack (WIN32), and
I can't trust the Yap stacks (error) */
#define YAP_BUF_SIZE 512
static char tmpbuf[YAP_BUF_SIZE];
// error classes: based on OSI errors.
//
// - The extra argument says whether there different instances
//
// - Events are treated within the same pipeline as errors.
//
#undef BEGIN_ERROR_CLASSES
#undef ECLASS
#undef END_ERROR_CLASSES
#undef BEGIN_ERRORS
#undef E0
#undef E
#undef E2
#undef END_ERRORS
#define BEGIN_ERROR_CLASSES() \
static Term mkerrorct(yap_error_class_number c, Term *ts) { \
switch (c) {
#define ECLASS(CL, A, B) \
case CL: \
if (A == 0) \
return MkAtomTerm(Yap_LookupAtom(A)); \
else { \
return Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom(A), B), B, ts); \
}
#define END_ERROR_CLASSES() \
} \
}
#define BEGIN_ERRORS() \
static Term mkerrort(yap_error_number e, Term *ts) { \
switch (e) {
#define E0(A, B) \
case A: \
return mkerrorct(B, ts);
#define E(A, B, C) \
case A: \
ts -= 1; \
ts[0] = MkAtomTerm(Yap_LookupAtom(C)); \
return mkerrorct(B, ts);
#define E2(A, B, C, D) \
case A: \
ts -= 2; \
ts[0] = MkAtomTerm(Yap_LookupAtom(C)); \
ts[1] = MkAtomTerm(Yap_LookupAtom(D)); \
return mkerrorct(B, ts);
#define END_ERRORS() \
} \
}
#include "YapErrors.h"
/**
* @brief Yap_Errorp
* This function handles errors in the C code. Check errors.yap for the
*corresponding Prolog code.
*
* @param file C source
* @param function C function
* @param lineno C exact line
* @param type the error ID (in YAP this is a single integer)
* @param where the culprit
* @return usually FAILCODE
*
* In a good day, the error handler's job is to generate a throw. This includes:
* - constructing an ISO style error term;
* - constructing a list with all available info on the bug
* - generating the throw
* - forcing backtracking in order to restart.
*
* In a bad day, it has to deal with OOM, abort, and errors within errorts.
*
* The list includes the following options:
* + c=c(file, line, function): where the bug was detected;
*
* + e=p(mod, name, arity, cl, file, lin): where the code was entered;
*
* + p=p(mod, name, arity, cl, file, line): the prolog procedure that caused the bug,
*and optionally,
*
* + g=g(Goal): the goal that created this mess
*
* + i=i(Comment): an user-written comment on this bug.
*/
yamop *Yap_Error__(const char *file, const char *function, int lineno,
yap_error_number type, Term where, ...) {
CACHE_REGS
va_list ap;
CELL nt[3];
Functor fun;
bool serious;
Term tf, error_t, comment, culprit;
/* disallow recursive error handling */
if (LOCAL_PrologMode & InErrorMode) {
fprintf(stderr, "%% ERROR WITHIN ERROR %d: %s\n", LOCAL_CurrentError,
tmpbuf);
Yap_RestartYap(1);
}
LOCAL_PrologMode |= InErrorMode;
LOCAL_Error_TYPE = YAP_NO_ERROR;
Yap_ClearExs();
if (where == 0L)
where = TermNil;
// first, obtain current location
sprintf(LOCAL_FileNameBuf, "%s:%d in C-function %s ", file, lineno, function);
tf = MkAtomTerm(Yap_LookupAtom(LOCAL_FileNameBuf));
#if DEBUG_STRICT
if (Yap_heap_regs && !(LOCAL_PrologMode & BootMode))
fprintf(stderr, "***** Processing Error %d (%lx,%x) %s***\n", type,
(unsigned long int)LOCAL_Signals, LOCAL_PrologMode, format);
else
fprintf(stderr, "***** Processing Error %d (%x) %s***\n", type,
LOCAL_PrologMode, format);
#endif
if (type == INTERRUPT_EVENT) {
fprintf(stderr, "%% YAP exiting: cannot handle signal %d\n",
(int)IntOfTerm(where));
Yap_exit(1);
}
if (LOCAL_within_print_message) {
/* error within error */
fprintf(stderr, "%% ERROR WITHIN WARNING %d: %s\n", LOCAL_CurrentError,
tmpbuf);
Yap_exit(1);
}
if (LOCAL_PrologMode == BootMode) {
Yap_detect_bug_location(P, FIND_PRED_FROM_ANYWHERE, (char *)HR, 256);
fprintf(stderr, "%%\n%% PC: %s\n", (char *)HR);
Yap_detect_bug_location(CP, FIND_PRED_FROM_ANYWHERE, (char *)HR, 256);
fprintf(stderr, "%% Continuation: %s\n", (char *)HR);
error_exit_yap(1);
}
if (P == (yamop *)(FAILCODE))
return P;
/* PURE_ABORT may not have set where correctly, BootMode may not have the data
* terms ready */
if (type == ABORT_EVENT || LOCAL_PrologMode & BootMode) {
where = TermNil;
LOCAL_PrologMode &= ~AbortMode;
LOCAL_CurrentError = type;
LOCAL_PrologMode |= InErrorMode;
/* make sure failure will be seen at next port */
// no need to lock & unlock
if (LOCAL_PrologMode & AsyncIntMode)
Yap_signal(YAP_FAIL_SIGNAL);
P = FAILCODE;
} else {
if (IsVarTerm(where)) {
/* we must be careful someone gave us a copy to a local variable */
Term t = MkVarTerm();
Yap_unify(t, where);
where = Deref(where);
}
/* Exit Abort Mode, if we were there */
LOCAL_PrologMode &= ~AbortMode;
LOCAL_CurrentError = type;
LOCAL_PrologMode |= InErrorMode;
if (!(where = Yap_CopyTerm(where))) {
where = TermNil;
}
}
{
char *format;
va_start(ap, where);
format = va_arg(ap, char *);
if (format != NULL) {
#if HAVE_VSNPRINTF
(void)vsnprintf(tmpbuf, YAP_BUF_SIZE, format, ap);
#else
(void)vsprintf(tmpbuf, format, ap);
#endif
comment = MkAtomTerm(Yap_LookupAtom(tmpbuf));
} else {
tmpbuf[0] = '\0';
comment = TermNil;
}
va_end(ap);
}
if (LOCAL_PrologMode & BootMode) {
/* crash in flames! */
fprintf(stderr, "%% YAP Fatal Error: %s exiting....\n", tmpbuf);
error_exit_yap(1);
}
#ifdef DEBUG
// DumpActiveGoals( USES_REGS1 );
#endif /* DEBUG */
switch (type) {
case SYSTEM_ERROR_INTERNAL: {
fprintf(stderr, "%% Internal YAP Error: %s exiting....\n", tmpbuf);
serious = TRUE;
if (LOCAL_PrologMode & BootMode) {
fprintf(stderr, "%% YAP crashed while booting %s\n", tmpbuf);
} else {
Yap_detect_bug_location(P, FIND_PRED_FROM_ANYWHERE, tmpbuf, YAP_BUF_SIZE);
fprintf(stderr, "%% Bug found while executing %s\n", tmpbuf);
}
error_exit_yap(1);
}
case SYSTEM_ERROR_FATAL: {
fprintf(stderr, "%% Fatal YAP Error: %s exiting....\n", tmpbuf);
error_exit_yap(1);
}
case INTERRUPT_EVENT: {
error_exit_yap(1);
}
case ABORT_EVENT:
nt[0] = MkAtomTerm(AtomDAbort);
fun = FunctorVar;
serious = TRUE;
break;
case CALL_COUNTER_UNDERFLOW_EVENT:
/* Do a long jump */
LOCAL_ReductionsCounterOn = FALSE;
LOCAL_PredEntriesCounterOn = FALSE;
LOCAL_RetriesCounterOn = FALSE;
Yap_JumpToEnv(MkAtomTerm(AtomCallCounter));
P = (yamop *)FAILCODE;
LOCAL_PrologMode &= ~InErrorMode;
return (P);
case PRED_ENTRY_COUNTER_UNDERFLOW_EVENT:
/* Do a long jump */
LOCAL_ReductionsCounterOn = FALSE;
LOCAL_PredEntriesCounterOn = FALSE;
LOCAL_RetriesCounterOn = FALSE;
Yap_JumpToEnv(MkAtomTerm(AtomCallAndRetryCounter));
P = (yamop *)FAILCODE;
LOCAL_PrologMode &= ~InErrorMode;
return (P);
case RETRY_COUNTER_UNDERFLOW_EVENT:
/* Do a long jump */
LOCAL_ReductionsCounterOn = FALSE;
LOCAL_PredEntriesCounterOn = FALSE;
LOCAL_RetriesCounterOn = FALSE;
Yap_JumpToEnv(MkAtomTerm(AtomRetryCounter));
P = (yamop *)FAILCODE;
LOCAL_PrologMode &= ~InErrorMode;
return (P);
default: {
Term ts[3];
ts[2] = where;
nt[0] = mkerrort(type, ts + 2);
}
}
if (type != ABORT_EVENT) {
/* This is used by some complex procedures to detect there was an error */
if (IsAtomTerm(nt[0])) {
strncpy(LOCAL_ErrorSay, (char *)RepAtom(AtomOfTerm(nt[0]))->StrOfAE,
MAX_ERROR_MSG_SIZE);
LOCAL_ErrorMessage = LOCAL_ErrorSay;
} else {
strncpy(LOCAL_ErrorSay,
(char *)RepAtom(NameOfFunctor(FunctorOfTerm(nt[0])))->StrOfAE,
MAX_ERROR_MSG_SIZE);
LOCAL_ErrorMessage = LOCAL_ErrorSay;
}
if (LOCAL_ErrorSay && LOCAL_ErrorSay[0])
comment = MkAtomTerm(Yap_LookupAtom( LOCAL_ErrorSay ) );
else
comment = TermNil;
}
switch (type) {
case RESOURCE_ERROR_HEAP:
case RESOURCE_ERROR_STACK:
case RESOURCE_ERROR_TRAIL:
comment = MkAtomTerm(Yap_LookupAtom(tmpbuf));
default:
nt[1] = TermNil;
if (comment != TermNil)
nt[1] = MkPairTerm(MkPairTerm(MkAtomTerm(Yap_LookupAtom("i")),comment), nt[1]);
if (file && function) {
Term ts[3], t3;
ts[0] = MkAtomTerm(Yap_LookupAtom(file));
ts[1] = MkIntegerTerm(lineno);
ts[2] = MkAtomTerm(Yap_LookupAtom(function));
t3 = Yap_MkApplTerm(Yap_MkFunctor(Yap_LookupAtom("c"),3),3,ts);
nt[1] = MkPairTerm(MkPairTerm(MkAtomTerm(Yap_LookupAtom("c")),t3), nt[1]);
}
if ((culprit=Yap_pc_location( P, B, ENV)) != TermNil ) {
nt[1] = MkPairTerm(MkPairTerm(MkAtomTerm(Yap_LookupAtom("p")),culprit), nt[1]);
}
if ((culprit=Yap_env_location( CP, B, ENV, 0)) != TermNil ) {
nt[1] = MkPairTerm(MkPairTerm(MkAtomTerm(Yap_LookupAtom("e")),culprit), nt[1]);
}
}
Yap_DebugPlWrite(nt[1]);
/* disable active signals at this point */
LOCAL_Signals = 0;
CalculateStackGap(PASS_REGS1);
LOCAL_PrologMode &= ~InErrorMode;
/* we might be in the middle of a critical region */
if (LOCAL_InterruptsDisabled) {
LOCAL_InterruptsDisabled = 0;
LOCAL_UncaughtThrow = TRUE;
Yap_RestartYap(1);
}
#if DEBUG
// DumpActiveGoals( PASS_REGS1 );
#endif
/* wait if we we are in user code,
it's up to her to decide */
fun = FunctorError;
if (LOCAL_PrologMode & UserCCallMode) {
error_t = Yap_MkApplTerm(fun, 2, nt);
if (!(EX = Yap_StoreTermInDB(error_t, 2))) {
/* fat chance */
Yap_RestartYap(1);
}
} else {
if (type == ABORT_EVENT) {
error_t = MkAtomTerm(AtomDAbort);
} else {
error_t = Yap_MkApplTerm(fun, 2, nt);
}
Yap_JumpToEnv(error_t);
P = (yamop *)FAILCODE;
}
LOCAL_PrologMode &= ~InErrorMode;
return P;
}