FYI - using prolog_frame_attribute/3 and parent

After reading reply by Jan W. this is no longer considered a possible bug.

Following is the original post.


Using SWI-Prolog (threaded, 64 bits, version 8.5.0-49-g2f46def0c) on Windows 10.

At present it might take me more than a day to whittle down the code to a minimal reproducible example, I tried creating the minimal reproducible example from a clean slate with a few lines of code and it did not reproduce. So hopefully I can explain what I think it is and it will be obvious. Also if the root cause is what I suspect, it has been there for a very very long time but luckily what the cause is so far from normal usage that would explain why it has survived for so long.

The code uses prolog_trace_interception/4 persisting the data as facts. For each call to prolog_trace_interception/4 a set of facts are added. The code compares the current set of facts to the previous set of facts to identify when frames were removed. The facts contain the entire stack of frames for each set (based on what can be collected using just Prolog). When a frame (child frame) that has been removed from the stack is identified, the code uses prolog_frame_attribute/3 with parent to get the parent frame.

Now for the speculation of what I think is happening. Since the child frame has been removed but the frame ref (number) has been remember, when the frame ref (number) is used with prolog_frame_attribute/3 and parent the code can not convert the frame ref (number) to the internal C structures and then the code just never returns.

This is not a show stopper for my needs as am pretty sure I can use the recorded information to get the parent frame. But since the problem is reproducible with my current code and the only way out is to kill the swipl task, noting it here.

I don’t expect this to fixed anytime soon or ever if my speculation is correct.

I also suspect that because the recording of the frame is from one prolog_trace_interception/4 and the use of prolog_frame_attribute/3 is in the next call from prolog_trace_interception/4 and thousands of statements are executed in between, the frame pointers at the C level are removed.

If you have questions please ask. If you need a minimal reproducible example I can’t guarantee I can get to one but ask if you need me to try. :slightly_smiling_face:

I don’t know the details of your code. A quick read hints that you are using a frame reference (much) later. You are responsible that it is still valid. The reference is just an integer that goes through some very elementary checks. Surely not something fool proof. A fool proof solution would generate unique ids and keep track of those. That is a lot of complicated admin. Typically you get a frame reference from the trace interception and a few more predicates, do you analysis and forget about it. If you want to do anything different you have to be very careful about the scoping rules.

These predicates are for a reason in the Hacker’s corner :slight_smile:

Yes.


That is along the lines of what I expected. :slightly_smiling_face:

Hackers corner - caveat emptor!

The predicates described here should be handled with some care as it is easy to corrupt the consistency of the Prolog system by misusing them.

Neither is sensitive to GC. The environment (local) stack is not compacted by GC. It may be shifted. The references are relative to the base of the stack, so this doesn’t do any harm.

Strange. Are local variables never reclaimed? Only when det and the tail is reached?

I find this layout, sandwiched between lBase and lTop (from the C file pl-wam.c):

	lTop  -->| first free location |
		 -----------------------
		 | local variables     |
		 |        ...	       |
		 | arguments for goal  |
		 | localFrame struct   |
		 | queryFrame struct   |
		 -----------------------
		 |        ...	       |
		 | term-references     |
		 -----------------------
	lBase -->| # fliFrame struct   |
		 -----------------------

Last call optimization applies. Stack frames as a whole cannot be discarded as GC is not involved in the reasoning whether or not we can return to them. Stack triming, allocating variables required only in earlier goals of a clause body is not implemented. It surely helps saving memory. Several systems separate the environment stack and choice point stack. That makes sense as one can end up with death choice points sandwiched between environment frames. Local stack GC could fix that too. All in all I’ve not seen applications in the wild where all this matters. The environment stack is typically small anyway.