For frame stack, are parent frames of system:setup_call_catcher_cleanup/4 guard frames?

In using prolog_frame_attribute/3 to walk the frame stack there are levels without frames (unidentified frames).
One pattern for unidentified frames is that the next level above (level +1) has the predicate indicator system:setup_call_catcher_cleanup/4. For this pattern the unidentified frames appear to be guard frames.

Is this correct that the parent frames of system:setup_call_catcher_cleanup/4 are guard frames?

If so, how can one get the frame reference as an integer? Can one also get properties for these type of frames?

Guard frames as use by the stack library merely indicate it needs to produce a stack trace. We don’t want to do that by default as it is rather expensive and thus undesirable if the user wants to catch the exception and proceed. Missing levels means last call optimization has removed a frame.

Thanks. :slightly_smiling_face:

After commenting out :- det(_). statements and running with ?- debug,<my goal>. the parent frames for system:setup_call_catcher_cleanup/4 are now appearing as frames with predicate indicator system:setup_call_cleanup/3.


One last unidentified frame remains.

As I don’t know what information is important to understanding the frame without debugging at the C level (which I really don’t want to do at this time as I would have to set up the entire tool chain) here is what I know.

The unidentified frame is

  • the parent of $tabling: $wfs_call/2
  • the child of a choice in the choice stack with choice type of clause.
  • at level 6 which AFAIK will appear in the stacks of every goal run from the top level. In other words any one should be able to find such a frame by just walking a stack of any goal run from the top level.

Any help in understanding this frame would be appreciated. :slightly_smiling_face:

Code used to walk frame stack (Click triangle to expand)

This is a snapshot of the code I used to walk the frame stack. It is not perfect but better than my earlier versions which missed some important details, e.g. alternatives and choice points. There is other code to walk the choice stack but not needed to walk the frame stack.

% :- debug(fr_frame_stack(record_frame_stack_new)).
% :- debug(fr_frame_stack(record_frame_stack_new_data)).

record_frame_stack_new(Frame,frame_stack_new(Frame_stack_new)) :-
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/4 - enter']),
    prolog_frame_attribute(Frame,level,Expected_level),
    (
        prolog_frame_attribute(Frame,has_alternatives,true)
    ->
        prolog_frame_attribute(Frame,alternative,Alternative),
        prolog_frame_attribute(Alternative,level,Alternative_level),
        Alternatives = [alternative(frame(Alternative),level(Alternative_level))]
    ;
        Alternatives = []
    ),

    (
        prolog_frame_attribute(Frame,clause,Clause_ref)
    ->
        clause_property(Clause_ref,predicate(Predicate_indicator))
    ;
        Predicate_indicator = -
    ),

    record_frame_stack_new(Frame,Alternatives,Expected_level,Predicate_indicator,Frame_stack_new),
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/4 - exit']).

% Base case - top reached
record_frame_stack_new(Frame,Alternatives,Expected_level,_Parent_predicate_indicator,[frame_stack_new_entry(type(frame),level(Expected_level),frame(Frame),parent(-),predicate_indicator(Predicate_indicator),alternative(-))]) :-
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 1 - enter']),
    debug(fr_frame_stack(record_frame_stack_new_data),'Frame: ~w, Alternatives: ~w, Expected_level: ~w',[Frame,Alternatives,Expected_level]),
    prolog_frame_attribute(Frame,level,Expected_level),
    prolog_frame_attribute(Frame,top,true),
    !,
    % assertion( Alternatives == [] ),
    assertion( prolog_frame_attribute(Frame,has_alternatives,false) ),

    (
        prolog_frame_attribute(Frame,clause,Clause_ref)
    ->
        clause_property(Clause_ref,predicate(Predicate_indicator))
    ;
        Predicate_indicator = -
    ),

    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 1 - exit']).

% Base case - Lowest possible level reached and top not found.
record_frame_stack_new(_Frame,_Alternatives,Expected_level,_Parent_predicate_indicator,[frame_stack_new_entry(type(unknown_1),level(0),frame(-),parent(-),predicate_indicator(-),alternative(-))]) :-
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 2 - enter']),
    Expected_level =< 0,
    print_message(error,top_not_found),
    !,
    % assertion( Alternatives == [] ),
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 2 - exit']).

% Recursive case - Prolog parent frame found
record_frame_stack_new(Frame,Alternatives0,Expected_level0,_Parent_predicate_indicator,[frame_stack_new_entry(type(frame),level(Expected_level0),frame(Frame),parent(Parent),predicate_indicator(Predicate_indicator),alternative(Alternative))|Frame_stack_new]) :-
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 3 - enter']),
    prolog_frame_attribute(Frame,level,Expected_level0),
    !,
    (
        member(alternative(frame(Frame),level(Expected_level0)),Alternatives0)
    ->
        delete(Alternatives0,alternative(frame(Frame),level(Expected_level0)),Alternatives1)
    ;
        Alternatives1 = Alternatives0
    ),
    % Add level with frame information.
    prolog_frame_attribute(Frame,parent,Parent),
    (
        prolog_frame_attribute(Frame,has_alternatives,true)
    ->
        prolog_frame_attribute(Frame,alternative,New_alternative),
        prolog_frame_attribute(New_alternative,level,New_alternative_level),
        Alternatives = [alternative(frame(New_alternative),level(New_alternative_level))|Alternatives1],
        Alternative = New_alternative
    ;
        Alternative = '-',
        Alternatives = Alternatives1
    ),

    (
        prolog_frame_attribute(Frame,clause,Clause_ref)
    ->
        clause_property(Clause_ref,predicate(Predicate_indicator))
    ;
        Predicate_indicator = -
    ),

    Expected_level is Expected_level0 - 1,
    record_frame_stack_new(Parent,Alternatives,Expected_level,Predicate_indicator,Frame_stack_new),
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 3 - exit']).

% Recursive case - Alternative only frame found
record_frame_stack_new(Frame,Alternatives0,Expected_level0,_Parent_predicate_indicator,[frame_stack_new_entry(type(alternative),level(Expected_level0),frame(Alternative),parent(Parent),predicate_indicator(Predicate_indicator),alternative(-))|Frame_stack_new]) :-
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - enter']),
    debug(fr_frame_stack(record_frame_stack_new_data),'Frame: ~w, Alternatives0: ~w, Expected_level0: ~w',[Frame,Alternatives0,Expected_level0]),
    member(alternative(frame(Alternative),level(Expected_level0)),Alternatives0),
    !,
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @1']),
    delete(Alternatives0,alternative(frame(Alternative),level(Expected_level0)),Alternatives1),
    debug(fr_frame_stack(record_frame_stack_new_data),'Alternatives1: ~w',[Alternatives1]),
    % Add level with frame information.
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @2']),
    prolog_frame_attribute(Alternative,parent,Parent),
    (
        debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @3']),
        prolog_frame_attribute(Alternative,has_alternatives,true)
    ->
        debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @4']),
        debug(fr_frame_stack(record_frame_stack_new_data),'Alternative: ~w',[Alternative]),
        % prolog_frame_attribute(Alternative,alternative,New_alternative),  % TODO: Is this a bug?
        % debug(fr_frame_stack(record_frame_stack_new_data),'New_alternative: ~w',[New_alternative]),
        % debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @5']),
        % prolog_frame_attribute(New_alternative,level,New_alternative_level),
        % debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @6']),
        % Alternatives = [alternative(frame(New_alternative),level(New_alternative_level))|Alternatives1]
        Alternatives = Alternatives1
    ;
        debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @7']),
        Alternatives = Alternatives1
    ),

    (
        prolog_frame_attribute(Alternative,clause,Clause_ref)
    ->
        clause_property(Clause_ref,predicate(Predicate_indicator))
    ;
        Predicate_indicator = -
    ),

    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @8']),
    Expected_level is Expected_level0 - 1,
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - @9']),
    record_frame_stack_new(Frame,Alternatives,Expected_level,Predicate_indicator,Frame_stack_new),
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 4 - exit']).

% % Recursive case - No prolog parent frame or alternative found
% record_frame_stack_new(Frame,Alternatives,Expected_level0,Parent_predicate_indicator,[frame_stack_new_entry(type(stack_guard),level(Expected_level0),frame(-),parent(-),predicate_indicator(-),alternative(-))|Frame_stack_new]) :-
%     debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 5 - enter']),
%     Parent_predicate_indicator = system:setup_call_catcher_cleanup/4,
%     !,
%     % Add missing level with less details
%     Expected_level is Expected_level0 - 1,
%     debug(fr_frame_stack(record_frame_stack_new_data),'For stack_guard - Parent_predicate_indicator: ~w~n',[Parent_predicate_indicator]),
%     record_frame_stack_new(Frame,Alternatives,Expected_level,-,Frame_stack_new),
%     debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 5 - exit']).

% Recursive case - No prolog parent frame or alternative found
record_frame_stack_new(Frame,Alternatives,Expected_level0,Parent_predicate_indicator,[frame_stack_new_entry(type(unknown_2),level(Expected_level0),frame(-),parent(-),predicate_indicator(-),alternative(-))|Frame_stack_new]) :-
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 6 - enter']),
    % Add missing level with less details
    Expected_level is Expected_level0 - 1,
    debug(fr_frame_stack(record_frame_stack_new_data),'For unknown_2 - Parent_predicate_indicator: ~w~n',[Parent_predicate_indicator]),
    record_frame_stack_new(Frame,Alternatives,Expected_level,-,Frame_stack_new),
    debug(fr_frame_stack(record_frame_stack_new),'~a',['record_frame_stack_new/5 - 6 - exit']).

:- multifile
    prolog:message/3.

prolog:message(top_not_found) -->
    [ 'Top not found.'-[] ].

The question of course is, why bother? The level has very little use except for some indication of the stack depth for debugging and supporting skip in the debugger.

The more I dig into the parts of SWI-Prolog I don’t know the more I understand and use. :slightly_smiling_face:

Until today I don’t recall ever seeing mention of a guard frame, now if I see it mentioned I don’t scratch my head and even have some idea of where and when to use it.