Trapping fatal exceptions in a PEngines instance and producing a stack trace?

I’m using: SWI-Prolog version 8.0.2

Occasionally I get fatal errors in my code running under PEngines, usually with the predicate atomics-to_string due to an Arguments are not sufficiently instantiated error.

PEngines catches the error without crashing, that’s good. But I am wondering if there is a way to do the equivalent of a try/catch around functions like atomics_to_string that can cause a fatal error like that, so I can handle the error myself, or at least add some context to help with debugging?

Also, is there a way to get PEngines to deliver a full stack trace instead of just an error message?:

Object received: {
    "code": "instantiation_error",
    "data": "atomics_to_string/2: Arguments are not sufficiently instantiated",
    "event": "error",
    "id": "21b36912-e160-4898-9a40-d83b3d10b410",
    "pengine": {
        "options": {
            "server": "http://localhost:3030/pengine",
            "application": "tsll",
            "destroy": false,
            "src_text": "(removed)",
            "format": "json"
        },
        "id": "21b36912-e160-4898-9a40-d83b3d10b410",
        "request": {}
    }
}

In the meantime I am using code like that below to help my debugging efforts, but I am hoping there are things intrinsic to SWi-Prolog to replace or augment what the code below does:

% ----->>>>> is_any_list_element_var

% This predicate succeeds if any element in the list is not ground.

% Did not find any var() list elements.  Fail the function.
is_any_list_element_var_1([]) :- !, fail.

is_any_list_element_var_1([H | _]) :-
    var(H),
    !.

is_any_list_element_var_1([_ | T]) :-
    !,
    is_any_list_element_var_1(T).

is_any_list_element_var(List) :-
    is_list(List),
    !,
    is_any_list_element_var_1(List).

is_any_list_element_var(_) :-
    x_write('(is_any_list_element_var) ERROR: The list parameter is not a list.'),
    !.

% ----->>>>> wrap_atomics_to_string

% Wrap the atomics_to_string predicate in an attempt to achieve better debugging
%   capabilities when calling that predicate.

% Validate the Caller parameter.
wrap_atomics_to_string(Caller, _, _) :-
    var(Caller),
    x_write('(wrap_atomics_to_string) ERROR: The caller parameter should be ground.'),
    !.

% Validate the List parameter as being a list.
wrap_atomics_to_string(Caller, List, _) :-
    is_list(List),
    x_write('(wrap_atomics_to_string) ERROR: The list parameter should be ground.  Called by: '),
    x_write(Caller),
    !.

% Check for an empty list.
wrap_atomics_to_string(Caller, List, _) :-
    is_list(List),
    length(List, 0),
    x_write('(wrap_atomics_to_string) ERROR: The list parameter is empty. Called by: '),
    x_write(Caller),
    !.

% Make sure every element in the list to be passed to atomics_to_string is ground.
wrap_atomics_to_string(Caller, List, _) :-
    is_any_list_element_var(List),
    x_write('(wrap_atomics_to_string) ERROR: The list parameter contains one or more elements that are not ground.  Called by: '),
    x_write(Caller),
    !.

% Should be OK to call atomics_to_string now.
wrap_atomics_to_string(Caller, List, StrOut) :-
    x_write('(wrap_atomics_to_string) Calling atomics_to_string from: '),
    x_write(Caller),
    x_write(', with list content: '),
    x_write(List),
    atomics_to_string(List, StrOut),
    x_write('(wrap_atomics_to_string) Successful call.'),
    !.

See catch/3 and catch_with_backtrace/3. The latter is quite recent and should probably be used in the pengines library.

2 Likes