[BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

[BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)

Christoph Thiede

Hi all,


I would like to report a bug I found related to debugging the debugger. I actually found the issue when debugging the context simulation of "Over", but let's start with a simple example.


Scenario 1

Steps to reproduce:

  • (Save your image, if it contained any valuable information.)
  • Type the following into a Workspace:

x := 5.

thisContext insertSender: (Context contextEnsure: [x := 4]).

  • Debug it and step:
    • (into) #contextEnsure
    • (through) #ensure:
    • (into) #jump
    • step over the implementation, and press (into) while returning.

Expected behavior:
The same should happen as if you would not debug, but normally do the code: It should run without raising any error, and x should finally contain the value 4.

Actual behavior:
  • In Trunk:
    • An infinite chain of debuggers appears, making it impossible to reuse the image. If you gain the chance to see any debugger, or to receive a non-empty debug log, you can see there is #errorSubscriptBounds: called from within a context. However, it is not possible to find out the complete stack of this error. (At least, I did not manage after a few attempts.)
  • In Squeak 5.1 (Note: You will need to replace here with MethodContext, for compatibility):
    • The same error is raised, but instead of endless debuggers, you get endless emergency evaluators:


Workaround:
Modify the #contextEnsure: implementation in the following way:


Reason:

As the comment in Context>>#jump states, the receiver context "MUST BE a top context (ie. a suspended context or a abandoned [sic] context that was jumped out of)", which, in particular, "already has its return value on its stack".1

However, in the #contextEnsure: example, ctxt is not a top context because is it still running, and on its top, it does not have a return value but the closure vector2 {ctxt. chain}. This leads to the fact that #jump pops away this closure vector so that any attempt to access this vector will result in Context>>#at: to call #errorSubscriptBounds:. I'm not sure where this attempt is made actually, but I suppose each debugger's ContextVariableInspector could do this and so raise another debugger and so forth ...


Considerations:

A simple solution would be the workaround presented above. However, we would not only need to patch #contextOn:do: the same way, we also wondered why the same problem does not also has had any visible impacts on the other senders of #jump in past, which are #restart and #runUntilErrorOrReturnFrom:. The first refreshes the context and then jumps into it, and from theory, I think it should be possible to call it from a foreign sender in a similar minimal example in order to invalidate the context register. However, I did not manage to construct an appropriate example yet. The same goes for #runUntilErrorOrReturnFrom:, because when "here jump" is executed, "here" again is neither a sender of the block context, nor it has is return value on stack.

In a nutshell, to us, it has been keeping an open question so far whether the other senders of #jump are legitime.

Should we maybe add a method like "Context >> jumpWith: aReturnValue" which would push aReturnValue on the receiver first and then perform the jump? Or could this have any negative impacts if there is indeed already a return value on stack of the receiver?


Scenario 2

Steps to reproduce:

The same as Scenario 1, but instead of stepping into #jump, just step over it.


Expected behavior:

The same expected behavior as in Scenario 1.


Actual behavior: (diff to Actual behavior of Scenario 1 highlighted for ease of reading)

  • In Trunk:
    • An infinite chain of debuggers appears, making it impossible to reuse the image. If you gain the chance to see any debugger, or to receive a non-empty debug log, you can see there is #cannotReturn:to: called from within a new process.
  • In Squeak 5.1 (Note: You will need to replace here with MethodContext, for compatibility):
    • The same error is raised, but instead of endless debuggers, you get endless emergency evaluators:


Note: This issue continues to exist after applying the workaround patch from Scenario 1.


Reason:

No idea. After applying the workaround from above, I tried to debug #doStep before stepping over the #jump per button, but this lead me to some kind of infinite loop: When I debug the whole debugging logic via Process>>#completeStep: and Process>>#complete: down to Context>>#runUntilErrorOrReturnFrom: and there step *into* the "self jump" statement, the error does not occur. When I step *over* this line instead, the same error occurs. I also tried to debug that #doStep in the second debugger window again, but this bug seems to be a real Heisenbug - it only occurs when you do *not* step into the relevant #jump call.


Considerations:

As mentioned above, we also could not yet explain whether all calls to #jump in #runUntilErrorOrReturnFrom: are valid with regard to the "return value on stack" precondition. Maybe there is a connection?



We will keep investigating the issue, but any help is appreciated, of course!


1 I'm afraid the example method "Interpreter>>primitiveSuspend" further mentioned in the comment does no longer exist.

2 Is "closure vector" an official term? If not, what would be the official term? :)


Best,

Christoph



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: [BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)

Christoph Thiede

For trace:

Scenario 1 should have been fixed via Kernel-ct.1296. Also, the general debugger chain issue could be solved via the #basicEvaluate:onBehalfOf: approach I proposed here: Fixing the infinite debugger chains?

Scenario 2 appears, imo, to be caused by the same issue as BUG/REGRESSION while debugging Generator >> #nextPut: (that is, #runUntilErrorOrReturnFrom: might need to detect sender swaps). See changeset in that thread.


Best,

Christoph



Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Freitag, 22. November 2019 15:15:05
An: Squeak Dev
Betreff: [squeak-dev] [BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)
 

Hi all,


I would like to report a bug I found related to debugging the debugger. I actually found the issue when debugging the context simulation of "Over", but let's start with a simple example.


Scenario 1

Steps to reproduce:

  • (Save your image, if it contained any valuable information.)
  • Type the following into a Workspace:

x := 5.

thisContext insertSender: (Context contextEnsure: [x := 4]).

  • Debug it and step:
    • (into) #contextEnsure
    • (through) #ensure:
    • (into) #jump
    • step over the implementation, and press (into) while returning.

Expected behavior:
The same should happen as if you would not debug, but normally do the code: It should run without raising any error, and x should finally contain the value 4.

Actual behavior:
  • In Trunk:
    • An infinite chain of debuggers appears, making it impossible to reuse the image. If you gain the chance to see any debugger, or to receive a non-empty debug log, you can see there is #errorSubscriptBounds: called from within a context. However, it is not possible to find out the complete stack of this error. (At least, I did not manage after a few attempts.)
  • In Squeak 5.1 (Note: You will need to replace here with MethodContext, for compatibility):
    • The same error is raised, but instead of endless debuggers, you get endless emergency evaluators:


Workaround:
Modify the #contextEnsure: implementation in the following way:


Reason:

As the comment in Context>>#jump states, the receiver context "MUST BE a top context (ie. a suspended context or a abandoned [sic] context that was jumped out of)", which, in particular, "already has its return value on its stack".1

However, in the #contextEnsure: example, ctxt is not a top context because is it still running, and on its top, it does not have a return value but the closure vector2 {ctxt. chain}. This leads to the fact that #jump pops away this closure vector so that any attempt to access this vector will result in Context>>#at: to call #errorSubscriptBounds:. I'm not sure where this attempt is made actually, but I suppose each debugger's ContextVariableInspector could do this and so raise another debugger and so forth ...


Considerations:

A simple solution would be the workaround presented above. However, we would not only need to patch #contextOn:do: the same way, we also wondered why the same problem does not also has had any visible impacts on the other senders of #jump in past, which are #restart and #runUntilErrorOrReturnFrom:. The first refreshes the context and then jumps into it, and from theory, I think it should be possible to call it from a foreign sender in a similar minimal example in order to invalidate the context register. However, I did not manage to construct an appropriate example yet. The same goes for #runUntilErrorOrReturnFrom:, because when "here jump" is executed, "here" again is neither a sender of the block context, nor it has is return value on stack.

In a nutshell, to us, it has been keeping an open question so far whether the other senders of #jump are legitime.

Should we maybe add a method like "Context >> jumpWith: aReturnValue" which would push aReturnValue on the receiver first and then perform the jump? Or could this have any negative impacts if there is indeed already a return value on stack of the receiver?


Scenario 2

Steps to reproduce:

The same as Scenario 1, but instead of stepping into #jump, just step over it.


Expected behavior:

The same expected behavior as in Scenario 1.


Actual behavior: (diff to Actual behavior of Scenario 1 highlighted for ease of reading)

  • In Trunk:
    • An infinite chain of debuggers appears, making it impossible to reuse the image. If you gain the chance to see any debugger, or to receive a non-empty debug log, you can see there is #cannotReturn:to: called from within a new process.
  • In Squeak 5.1 (Note: You will need to replace here with MethodContext, for compatibility):
    • The same error is raised, but instead of endless debuggers, you get endless emergency evaluators:


Note: This issue continues to exist after applying the workaround patch from Scenario 1.


Reason:

No idea. After applying the workaround from above, I tried to debug #doStep before stepping over the #jump per button, but this lead me to some kind of infinite loop: When I debug the whole debugging logic via Process>>#completeStep: and Process>>#complete: down to Context>>#runUntilErrorOrReturnFrom: and there step *into* the "self jump" statement, the error does not occur. When I step *over* this line instead, the same error occurs. I also tried to debug that #doStep in the second debugger window again, but this bug seems to be a real Heisenbug - it only occurs when you do *not* step into the relevant #jump call.


Considerations:

As mentioned above, we also could not yet explain whether all calls to #jump in #runUntilErrorOrReturnFrom: are valid with regard to the "return value on stack" precondition. Maybe there is a connection?



We will keep investigating the issue, but any help is appreciated, of course!


1 I'm afraid the example method "Interpreter>>primitiveSuspend" further mentioned in the comment does no longer exist.

2 Is "closure vector" an official term? If not, what would be the official term? :)


Best,

Christoph



Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: [BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)

Jaromir Matas
Hi Christoph,

has there been a progress on this issue? I've tried scenario2 with your
changeset from [1] and the problem is gone :) Is this news?

best,

[1]
http://forum.world.st/BUG-REGRESSION-while-debugging-Generator-gt-gt-nextPut-td5108125i20.html#a5129721



-----
^[^ Jaromir
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

^[^ Jaromir
Reply | Threaded
Open this post in threaded view
|

Re: [BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)

Christoph Thiede
Hi Jaromir,

I was actually aware of this, but thanks for the follow-up! :-) Now we need
some more feedback on the changeset in [1] ...

[1]
http://forum.world.st/BUG-REGRESSION-while-debugging-Generator-gt-gt-nextPut-td5108125i20.html#a5129721

Best,
Christoph



-----
Carpe Squeak!
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: [BUG(s)] in Context control (#jump, #runUntilErrorOrReturnFrom:)

Jaromir Matas
In reply to this post by Christoph Thiede
Hi Christoph,


Christoph Thiede wrote
> The same goes for #runUntilErrorOrReturnFrom:, because when "here jump" is
> executed, "here" again is neither a sender of the block context, nor it
> has is return value on stack.
>
> In a nutshell, to us, it has been keeping an open question so far whether
> the other senders of #jump are legitime.

Just a quick answer to this one as I was just looking at it:
#runUntilErrorOrReturnFrom: is a different story - in this case "here" is
the context the #jump jumps from, i.e. "here" is the sender and #jump will
push nil to here's stack and make it a top context. Thus no fix needed.

In your case of #contextEnsure: the #jump happened from a context higher
above and "ctxt" wasn't the sender thus your fix was necessary.

best,



-----
^[^ Jaromir
--
Sent from: http://forum.world.st/Squeak-Dev-f45488.html

^[^ Jaromir