I broke the debugger?

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

I broke the debugger?

timrowledge
Weird thing her. I was trying out my old Plumbing example, mostly just as a way to improve the swiki page. Using a 19292 update 5.3 image on Mac.

Found a recursion related bug in the behaviour when there are two faucets. Toggled the break on entry, triggered the recursion. Went to step over the #break and.... whoosh, boom. Some very weird behaviour that is not at all what I'd expect. I also tried stepping into the #break to see if that might show me sometihng but that wasn't any better at all.

Tested in a 198229 image and no problem. Someone done borked it.

The best I could capture any log is -

----------------------------------
Error: subscript is out of bounds: 0
26 January 2020 8:59:18.032815 pm

VM: Mac OS - Smalltalk
Image: Squeak5.3beta [latest update: #19292]

SecurityManager state:
Restricted: false
FileAccess: true
SocketAccess: true
Working Dir /Users/tim/Documents/Squeak
Trusted Dir /Users/tim/Documents/Squeak/secure
Untrusted Dir /Users/tim/Documents/Squeak/My Squeak

Context(Object)>>error:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                aString: 'subscript is out of bounds: 0'
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context(Object)>>errorSubscriptBounds:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)

Context>>at:
        Receiver: FaucetPlumbinTileMorph(Object)>>break
        Arguments and temporary variables:
                index: 0
        Receiver's instance variables:
                sender: BlockClosure>>on:do:
                pc: 44
                stackp: 0
                method: (Object>>#break "a CompiledMethod(2334054)")
                closureOrNil: nil
                receiver: a FaucetPlumbinTileMorph(1038220)


--- The full stack ---
Context(Object)>>error:
Context(Object)>>errorSubscriptBounds:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:
Context>>at:


tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
You never really learn to swear until you learn to drive in Silicon Valley



Reply | Threaded
Open this post in threaded view
|

Code simulation error (was Re: I broke the debugger?)

timrowledge
Something pretty weird is happening when the break is hit. I *finally* got a debugger open on a backtrace that includes the problem with Context>at: failing becasue the argument is 0. It's all a bit strange and unless I managed to do something very odd it looks like a fairly serious bug.

I actually caught this because something went wrong in code I added to try to log the dan initial error. Despite that it does appear to be a trace on the #break problem.


debugger>doStep called from #stepOver.
#handleLabelUpdatesIn:whenExecuting: used and does [interruptedProcess completeStep: currentContext] which uses...
Process>>evaluate:onBehalfOf:
Context>runUntilErrorOrReturnFrom:

Context>jump
 - *we are checking for stackp = 0 which is the very thing that causes problems later with the #pop*

In the #stepToSendOrReturn we use interpretNextInstructionFor: which leads to InterpretV3ClosuresExtension: 7 in: (Object>>break) for: ( aContext sender #on:do:, pc 24 stackp 0 method Object>>break, etc)
 -> doPop
 -> pop  (presumably stackp was 0 here? See above re: #jump)
 -> at: ... but if so why did the error code appear to skip over the first two tests of it?
        <primitive: 210>
        index = 0 ifTrue:[FileStream newFileNamed: 'squeakBreak.log' do:[:f| self errorReportOn: f]].
        index isInteger ifTrue:
                [self errorSubscriptBounds: index].
        index isNumber
                ifTrue: [^self at: index asInteger]"<--- it went here and on the second go around it picked up that index = 0 properly."
                ifFalse: [self errorNonIntegerIndex]


So I *think* that there is an issue in Context>jump where we explicitly check for stackp = 0 but then call code that carefully does a pop via #at:. Something about the primitive: 210 (maybe?) does something weird and the index is both 0 and not 0 - nor even an Integer.

As an interesting bonus, the clause I added to log things when 'index = 0' went very wrong because the 'f' getting passed to the block is apparently the MultiByteFileStream *class* rather than the opened file!

The bit bothering me at the moment is just how this can be a problem that hasn't whacked us before. I haven't been able to cause it with 'normal' code but all I was doing to fall over this was loading & testing the old Plumbing demo code.

Oh, I did just try to see if a newer vm (I am running the 201912311458 ARMv6) would have a fix for the prim 210 but no newer ARM VM will run at all. The latest Mac vm runs but fails with the same huge list of notifiers reporting the error in Context>at: - the fact that there is a *lot* of #at: on the stack is a little more odd.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- If you stand close enough to him, you can hear the ocean



Reply | Threaded
Open this post in threaded view
|

Re: Code simulation error (was Re: I broke the debugger?)

Christoph Thiede

Hi Tim, excellent work!


Coincidentally, I studied the same problem yesterday, but I did not yet complete to report my observations to you. So let me to this hereby:


After many hours of funny debugging, now I could create this minimum failing example:


Processor activeProcess
evaluate: [self error. self inform: #foo]
onBehalfOf: [] newProcess

Expected behavior: First, a debugger is shown, and after proceeding it, a dialog window is shown.

Actual behavior: Both the debugger and the dialog window are shown asynchronously!

Suspicion of someone who did not yet dive deeply into the activeProcess concept: The debugger resumes the wrong process, as the activeProcess concept simulates a different running process for the error, even against the debugger.

If my theory is correct, we would need to find a way to look behind the scenes of the activeProcess and use it in the debugging code. But first, I really need to learn more about this concept.


(Connection to our Context >> #at: problems: Probably no primitive issue at all, just the fact, that #at: calls itself recursively after the error was proceeded - similar like #doesNotUnderstand: does.)


This is an in-midst-of-work message; just did not want us to any duplicate or redundant work. Will have a closer look at this disgusting problem ASAP!

And vice versa, it would be very nice if you could keep me/us up-to-date!


(Oh, what a fun to debug a self-simulating system ...)


Best,

Christoph




Von: Squeak-dev <[hidden email]> im Auftrag von tim Rowledge <[hidden email]>
Gesendet: Dienstag, 28. Januar 2020 03:08 Uhr
An: The general-purpose Squeak developers list
Betreff: [squeak-dev] Code simulation error (was Re: I broke the debugger?)
 
Something pretty weird is happening when the break is hit. I *finally* got a debugger open on a backtrace that includes the problem with Context>at: failing becasue the argument is 0. It's all a bit strange and unless I managed to do something very odd it looks like a fairly serious bug.

I actually caught this because something went wrong in code I added to try to log the dan initial error. Despite that it does appear to be a trace on the #break problem.


debugger>doStep called from #stepOver.
#handleLabelUpdatesIn:whenExecuting: used and does [interruptedProcess completeStep: currentContext] which uses...
Process>>evaluate:onBehalfOf:
Context>runUntilErrorOrReturnFrom:

Context>jump
 - *we are checking for stackp = 0 which is the very thing that causes problems later with the #pop*

In the #stepToSendOrReturn we use interpretNextInstructionFor: which leads to InterpretV3ClosuresExtension: 7 in: (Object>>break) for: ( aContext sender #on:do:, pc 24 stackp 0 method Object>>break, etc)
 -> doPop
 -> pop  (presumably stackp was 0 here? See above re: #jump)
 -> at: ... but if so why did the error code appear to skip over the first two tests of it?
        <primitive: 210>
        index = 0 ifTrue:[FileStream newFileNamed: 'squeakBreak.log' do:[:f| self errorReportOn: f]].
        index isInteger ifTrue:
                [self errorSubscriptBounds: index].
        index isNumber
                ifTrue: [^self at: index asInteger]"<--- it went here and on the second go around it picked up that index = 0 properly."
                ifFalse: [self errorNonIntegerIndex]


So I *think* that there is an issue in Context>jump where we explicitly check for stackp = 0 but then call code that carefully does a pop via #at:. Something about the primitive: 210 (maybe?) does something weird and the index is both 0 and not 0 - nor even an Integer.

As an interesting bonus, the clause I added to log things when 'index = 0' went very wrong because the 'f' getting passed to the block is apparently the MultiByteFileStream *class* rather than the opened file!

The bit bothering me at the moment is just how this can be a problem that hasn't whacked us before. I haven't been able to cause it with 'normal' code but all I was doing to fall over this was loading & testing the old Plumbing demo code.

Oh, I did just try to see if a newer vm (I am running the 201912311458 ARMv6) would have a fix for the prim 210 but no newer ARM VM will run at all. The latest Mac vm runs but fails with the same huge list of notifiers reporting the error in Context>at: - the fact that there is a *lot* of #at: on the stack is a little more odd.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- If you stand close enough to him, you can hear the ocean





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

Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede

Hi all,


I took a closer look at the ActiveProcess concept and I think I found a solution approach to fix all these nasty infinite debugger chains that occurred when any bytecode simulation error occurred. Please see the attached changeset.


The main cognition was the issue that any bytecode simulation error, which is executed behind an #evaluate:onBehalfOf: call, should *not* be debugged on the "behalf-of" process but indeed on the "basic" active process who called #evaluate:onBehalfOf:. Otherwise, the simulating process would not have been stopped by the debugger. See Process >> #step:, for example.

So I added Process >> #basicActiveProcess, which returns the real activeProcess without regard to #effectiveProcess. This method is used by StandardToolSet & debugger in order to identify the process to debug. At other places, #effectiveProcess must be never used as this would take the idea of ActiveProcess ad absurdum and make it impossible to debug certain processing logic.

The changeset also consists of a new test method in DebuggerTests that tests this regression.


So this is a list of recent issues that *won't* crash your image after loading this changeset:


They still may raise a "usual" error because the context simulation is buggy in some respects, but so far I did not manage to find another bug that crashes your image. And this should make it so much easier to debug and fix the remaining simulation bugs!

In any case, please review! :-) I'm almost sure you will have some criticism, but how do you think about the approach in general? I wonder whether there will be any situation where we cannot debug the senders of #basicActiveProcess properly because they don't follow the ActiveProcess concept. But in general, I think it's clearly an improvement against the current state of Trunk. I'm looking forward to your feedback!

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von Thiede, Christoph
Gesendet: Dienstag, 28. Januar 2020 09:17 Uhr
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] Code simulation error (was Re: I broke the debugger?)
 

Hi Tim, excellent work!


Coincidentally, I studied the same problem yesterday, but I did not yet complete to report my observations to you. So let me to this hereby:


After many hours of funny debugging, now I could create this minimum failing example:


Processor activeProcess
evaluate: [self error. self inform: #foo]
onBehalfOf: [] newProcess

Expected behavior: First, a debugger is shown, and after proceeding it, a dialog window is shown.

Actual behavior: Both the debugger and the dialog window are shown asynchronously!

Suspicion of someone who did not yet dive deeply into the activeProcess concept: The debugger resumes the wrong process, as the activeProcess concept simulates a different running process for the error, even against the debugger.

If my theory is correct, we would need to find a way to look behind the scenes of the activeProcess and use it in the debugging code. But first, I really need to learn more about this concept.


(Connection to our Context >> #at: problems: Probably no primitive issue at all, just the fact, that #at: calls itself recursively after the error was proceeded - similar like #doesNotUnderstand: does.)


This is an in-midst-of-work message; just did not want us to any duplicate or redundant work. Will have a closer look at this disgusting problem ASAP!

And vice versa, it would be very nice if you could keep me/us up-to-date!


(Oh, what a fun to debug a self-simulating system ...)


Best,

Christoph




Von: Squeak-dev <[hidden email]> im Auftrag von tim Rowledge <[hidden email]>
Gesendet: Dienstag, 28. Januar 2020 03:08 Uhr
An: The general-purpose Squeak developers list
Betreff: [squeak-dev] Code simulation error (was Re: I broke the debugger?)
 
Something pretty weird is happening when the break is hit. I *finally* got a debugger open on a backtrace that includes the problem with Context>at: failing becasue the argument is 0. It's all a bit strange and unless I managed to do something very odd it looks like a fairly serious bug.

I actually caught this because something went wrong in code I added to try to log the dan initial error. Despite that it does appear to be a trace on the #break problem.


debugger>doStep called from #stepOver.
#handleLabelUpdatesIn:whenExecuting: used and does [interruptedProcess completeStep: currentContext] which uses...
Process>>evaluate:onBehalfOf:
Context>runUntilErrorOrReturnFrom:

Context>jump
 - *we are checking for stackp = 0 which is the very thing that causes problems later with the #pop*

In the #stepToSendOrReturn we use interpretNextInstructionFor: which leads to InterpretV3ClosuresExtension: 7 in: (Object>>break) for: ( aContext sender #on:do:, pc 24 stackp 0 method Object>>break, etc)
 -> doPop
 -> pop  (presumably stackp was 0 here? See above re: #jump)
 -> at: ... but if so why did the error code appear to skip over the first two tests of it?
        <primitive: 210>
        index = 0 ifTrue:[FileStream newFileNamed: 'squeakBreak.log' do:[:f| self errorReportOn: f]].
        index isInteger ifTrue:
                [self errorSubscriptBounds: index].
        index isNumber
                ifTrue: [^self at: index asInteger]"<--- it went here and on the second go around it picked up that index = 0 properly."
                ifFalse: [self errorNonIntegerIndex]


So I *think* that there is an issue in Context>jump where we explicitly check for stackp = 0 but then call code that carefully does a pop via #at:. Something about the primitive: 210 (maybe?) does something weird and the index is both 0 and not 0 - nor even an Integer.

As an interesting bonus, the clause I added to log things when 'index = 0' went very wrong because the 'f' getting passed to the block is apparently the MultiByteFileStream *class* rather than the opened file!

The bit bothering me at the moment is just how this can be a problem that hasn't whacked us before. I haven't been able to cause it with 'normal' code but all I was doing to fall over this was loading & testing the old Plumbing demo code.

Oh, I did just try to see if a newer vm (I am running the 201912311458 ARMv6) would have a fix for the prim 210 but no newer ARM VM will run at all. The latest Mac vm runs but fails with the same huge list of notifiers reporting the error in Context>at: - the fact that there is a *lot* of #at: on the stack is a little more odd.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- If you stand close enough to him, you can hear the ocean






fix-infinite-debuggers.2.cs (6K) Download Attachment
Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

timrowledge
>
> <fix-infinite-debuggers.2.cs>


Interesting. I filed that into a 19230 image (because that's what I had running) and tried my PlumbingDemo-crash again. It *doesn't( make stepping over the break work but it *does* result in a clean new debugger on the debugger that shows the problem I reported with the Context>jump leading to Context>at: 0. More interestingly the #at: 0 problem is now a simple case of doPop->pop->at:->errorSubscriptBounds with no strange #at: failing to notice index = 0.

I'm still baffled by how this can be breaking so strangely; it's just the usual 'self break' at the beginning of a perfectly ordinary looking method. Maybe it's the whole Morphic drawing method thing. And clearly there is some interaction with Eliot's work on process-faithful debugging.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- One clown short of a circus.



Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede

Here is a second approach to fix the bug:


Process >> basicEvaluate: aBlock onBehalfOf: aProcess

| range savedVariables |
"range accesses everything after myList, e.g. threadId, effectiveProcess, name, island, env"
range := 5 to: Process instSize.
savedVariables := range collect: [:i| self instVarAt: i].
range do:
[:i| self instVarAt: i put: (aProcess instVarAt: i)].
effectiveProcess := aProcess.
^aBlock ensure:
["write back any assigned-to variables."
range do:
[:i| | v |
((v := self instVarAt: i) ~~ (aProcess instVarAt: i)
and: [v notNil]) ifTrue:
[aProcess instVarAt: i put: v]].
"restore old values"
range with: savedVariables do:
[:i :var| self instVarAt: i put: var]]

Process >> evaluate: aBlock onBehalfOf: aProcess
"Evaluate aBlock setting effectiveProcess to aProcess, and all other variables other than
the scheduling ones to those of aProcess.  Used in the execution simulation machinery
to ensure that Processor activeProcess evaluates correctly when debugging."

^ self
basicEvaluate: [aBlock
on: UnhandledError
do: [:ex |
self
basicEvaluate: [ex pass]
onBehalfOf: self]]
onBehalfOf: aProcess


To summarize, in #evaluate:onBehalfOf:, catch UnhandledErrors that are raised by aBlock and handle them on behalf of the original process. Maybe this would be a less invasive solution. Unfortunately, for reasons I did not yet figure out, this fixes all recursions listed in my latest post, with exception of Generator>>#nextPut:.

Best,
Christoph


Von: Squeak-dev <[hidden email]> im Auftrag von tim Rowledge <[hidden email]>
Gesendet: Dienstag, 28. Januar 2020 21:24 Uhr
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))
 
>
> <fix-infinite-debuggers.2.cs>


Interesting. I filed that into a 19230 image (because that's what I had running) and tried my PlumbingDemo-crash again. It *doesn't( make stepping over the break work but it *does* result in a clean new debugger on the debugger that shows the problem I reported with the Context>jump leading to Context>at: 0. More interestingly the #at: 0 problem is now a simple case of doPop->pop->at:->errorSubscriptBounds with no strange #at: failing to notice index = 0.

I'm still baffled by how this can be breaking so strangely; it's just the usual 'self break' at the beginning of a perfectly ordinary looking method. Maybe it's the whole Morphic drawing method thing. And clearly there is some interaction with Eliot's work on process-faithful debugging.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- One clown short of a circus.





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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede
> Unfortunately, for reasons I did not yet figure out, this fixes all
recursions listed in my latest post, with exception of Generator>>#nextPut:.

Short update: The latter can be explained by looking at Context >>
#cannotReturn:, which spawns a debugger on the activeProcess (!) manually.
Not sure why we cannot simply raise a regular exception here? This would
also be analogous to BlockCannotReturn ...

Best,
Christoph



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

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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Jakob Reschke
Hi all,

Want another easy way to still get endless debuggers?

    [^ self halt] ensure: [1 asString]

Do it, then step over the return (I have to click twice actually) -->
endless debuggers on findContextSuchThat:.

Triggers in both current Trunk and in 5.3, and 5.2, and 5.0. In 5.1
and 4.6 I get repeated emergency evaluators instead.

If you proceed in the debugger after the halt, the expression
evaluates flawlessly.

If this example is not actually a new one, just be reminded that the
problem still exists... ;-)

Kind regards,
Jakob

Am So., 23. Feb. 2020 um 23:45 Uhr schrieb Christoph Thiede
<[hidden email]>:

>
> > Unfortunately, for reasons I did not yet figure out, this fixes all
> recursions listed in my latest post, with exception of Generator>>#nextPut:.
>
> Short update: The latter can be explained by looking at Context >>
> #cannotReturn:, which spawns a debugger on the activeProcess (!) manually.
> Not sure why we cannot simply raise a regular exception here? This would
> also be analogous to BlockCannotReturn ...
>
> Best,
> Christoph
>
>
>
> --
> Sent from: http://forum.world.st/Squeak-Dev-f45488.html
>

Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Jakob Reschke
Oh, my 4.5 images open only one debugger for findContextSuchThat:.
That is, one debugger per further click on "Over".

Am Do., 18. Juni 2020 um 01:14 Uhr schrieb Jakob Reschke
<[hidden email]>:

>
> Hi all,
>
> Want another easy way to still get endless debuggers?
>
>     [^ self halt] ensure: [1 asString]
>
> Do it, then step over the return (I have to click twice actually) -->
> endless debuggers on findContextSuchThat:.
>
> Triggers in both current Trunk and in 5.3, and 5.2, and 5.0. In 5.1
> and 4.6 I get repeated emergency evaluators instead.
>
> If you proceed in the debugger after the halt, the expression
> evaluates flawlessly.
>
> If this example is not actually a new one, just be reminded that the
> problem still exists... ;-)
>
> Kind regards,
> Jakob
>
> Am So., 23. Feb. 2020 um 23:45 Uhr schrieb Christoph Thiede
> <[hidden email]>:
> >
> > > Unfortunately, for reasons I did not yet figure out, this fixes all
> > recursions listed in my latest post, with exception of Generator>>#nextPut:.
> >
> > Short update: The latter can be explained by looking at Context >>
> > #cannotReturn:, which spawns a debugger on the activeProcess (!) manually.
> > Not sure why we cannot simply raise a regular exception here? This would
> > also be analogous to BlockCannotReturn ...
> >
> > Best,
> > Christoph
> >
> >
> >
> > --
> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
> >

Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede

Nice finding. The basisActiveProcess patch catches this, but we still get a BlockCannotReturn there. I guess this has the same reason as http://forum.world.st/BUG-REGRESSION-while-debugging-Generator-gt-gt-nextPut-td5108125.html#a5109109 ...

forum.world.st
BUG/REGRESSION while debugging Generator >> #nextPut:. Hi all, I discovered a bug when stepping over a call to Generator >> #nextPut:. !! Save your image before trying the following...
Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von Jakob Reschke <[hidden email]>
Gesendet: Donnerstag, 18. Juni 2020 01:44:28
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))
 
Oh, my 4.5 images open only one debugger for findContextSuchThat:.
That is, one debugger per further click on "Over".

Am Do., 18. Juni 2020 um 01:14 Uhr schrieb Jakob Reschke
<[hidden email]>:
>
> Hi all,
>
> Want another easy way to still get endless debuggers?
>
>     [^ self halt] ensure: [1 asString]
>
> Do it, then step over the return (I have to click twice actually) -->
> endless debuggers on findContextSuchThat:.
>
> Triggers in both current Trunk and in 5.3, and 5.2, and 5.0. In 5.1
> and 4.6 I get repeated emergency evaluators instead.
>
> If you proceed in the debugger after the halt, the expression
> evaluates flawlessly.
>
> If this example is not actually a new one, just be reminded that the
> problem still exists... ;-)
>
> Kind regards,
> Jakob
>
> Am So., 23. Feb. 2020 um 23:45 Uhr schrieb Christoph Thiede
> <[hidden email]>:
> >
> > > Unfortunately, for reasons I did not yet figure out, this fixes all
> > recursions listed in my latest post, with exception of Generator>>#nextPut:.
> >
> > Short update: The latter can be explained by looking at Context >>
> > #cannotReturn:, which spawns a debugger on the activeProcess (!) manually.
> > Not sure why we cannot simply raise a regular exception here? This would
> > also be analogous to BlockCannotReturn ...
> >
> > Best,
> > Christoph
> >
> >
> >
> > --
> > Sent from: http://forum.world.st/Squeak-Dev-f45488.html
> >



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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede
Hi all! :-)

After a lot of time, I would like to give this issue another push. These
bugs are really serious and we should finally fix them.

Short summary for everyone who would not like to re-read the whole thread:
/
Over the last months, several issues have been reported about infinite
chains of debuggers popping up in your image and, most of the time,
eventually making it unusable. Some months ago, I traced these issues down
to several simulation errors that occurred while pressing the Over button in
the debugger. The simulation errors can be fixed separately, but the
debugger chains are caused by a second phenomenon which I could break down
in the following snippet:

    Processor activeProcess
        evaluate: [self error: #first. self inform: #second]
        onBehalfOf: [] newProcess
   
    You would expect to see first a debugger window for the #error, and
second, after you resume this error, a dialog window for the #inform:.
However, surprisingly, both windows appear at the same time. The opening
debugger does not suspend the process correctly.
/


I also had a talk about it with Marcel and Patrick and we ended up with the
finding that the semantics of Process >> #evaluate:onBehalfOf: are not 100%
clear at the moment. Its method comment states:

    "Evaluate aBlock setting effectiveProcess to aProcess, and all other
variables other than the scheduling ones to those of aProcess."

However, the situation is unclear when an exception is raised from aBlock.
Should it be handled still on behalf of the behalfOf process or rather on
the original receiver process instead?
At the moment, the former is the case, and this leads to the problem that
the behalfOf process is debugged (i.e. interrupted) when an error is raised
from aBlock while the - technically actually causing - receiver process
keeps running, eventually ignoring the just-raised exception. See
StandardToolSet class >> #handleError: for the place where "Processor
activeProcess" is determined for debugging, which is redirected to the
onBehalf process during the execution of aBlock.
At this place, we actually would like to see something like this:

    thisContext process
        debug: anError signalerContext
        title: anError description

Which, however, is not possible because a stack frame cannot know its
containing process.
So in my first changeset (fix-infinite-debuggers.2.cs below), I had proposed
to rewrite that method like this:

    Processor basicActiveProcess
        debug: anError signalerContext
        title: anError description

where basicActiveProcess would return the "true" active process neglecting
the onBehalfOf redirection.
However, *could there be any situation where you would like to respect the
process redirection indeed in #handleError:?* (highlighting this as the open
key question of this thread)
We did not find a plausible example, the only case I could imagine is the
following situation (which, admittedly, is not an every-day use case):

    Debug the expression "self halt", step down until in StandardToolSet
class>>handleError:, step into #activeProcess and then step over repeatedly
until #debug:title: is sent.
    In Trunk, this opens a second debugger on the same process. When using
#basicActiveProcess, however, the first debugger stops working (dead
process).


So, your comments and thoughts are important: How would you think about the
proposed #basicActiveProcess solution? Do you see any scenario where the
onBehalfOf redirection needs to be perpetuated in ToolSet >> #handleError:?
Or would you prefer another approach to fix the problem?
Looking forward to an interesting discussion! :-)

Best,
Christoph



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

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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Eliot Miranda-2
Hi all,

> On Oct 28, 2020, at 5:50 AM, Christoph Thiede <[hidden email]> wrote:
>
> Hi all! :-)
>
> After a lot of time, I would like to give this issue another push. These
> bugs are really serious and we should finally fix them.
>
> Short summary for everyone who would not like to re-read the whole thread:
> /
> Over the last months, several issues have been reported about infinite
> chains of debuggers popping up in your image and, most of the time,
> eventually making it unusable. Some months ago, I traced these issues down
> to several simulation errors that occurred while pressing the Over button in
> the debugger. The simulation errors can be fixed separately, but the
> debugger chains are caused by a second phenomenon which I could break down
> in the following snippet:
>
>    Processor activeProcess
>        evaluate: [self error: #first. self inform: #second]
>        onBehalfOf: [] newProcess
>
>    You would expect to see first a debugger window for the #error, and
> second, after you resume this error, a dialog window for the #inform:.
> However, surprisingly, both windows appear at the same time. The opening
> debugger does not suspend the process correctly.
> /
>
>
> I also had a talk about it with Marcel and Patrick and we ended up with the
> finding that the semantics of Process >> #evaluate:onBehalfOf: are not 100%
> clear at the moment. Its method comment states:
>
>    "Evaluate aBlock setting effectiveProcess to aProcess, and all other
> variables other than the scheduling ones to those of aProcess."
>
> However, the situation is unclear when an exception is raised from aBlock.
> Should it be handled still on behalf of the behalfOf process or rather on
> the original receiver process instead?

Conceptually Process >> #evaluate:onBehalfOf: is a private method if the debugger, which exists only to ensure that while simulating code on behalf of done process being debugged, the right process is found by the simulated code even though another process is actually executing the code.

Therefore the block being evaluated within Process >> #evaluate:onBehalfOf: is always evaluating some simulation on behalf of the debugger.  Therefore, *any* error which occurs during the block is not an error in the process being debugged (that error delivery should be simulated, and delivered within the process being debugged), but an error in the simulation, and should stop the simulation.

Therefore, an error within the evaluate block of Process >> #evaluate:onBehalfOf: could be caught within Process >> #evaluate:onBehalfOf: but delivered outside.  One could experiment with adding an exception handler around the evaluation block that would copy the stack of the exception, terminate the block, and then open a debugger on the copy of the stack once the effectiveProcess has been restored.

[P.S. I think the right way to implement this is to use Context>>cut: to snip the section of the stack containing the simulation error and stitch this back in after the evaluate block, but I *think* I am seeing bugs in the context-to-stack mapping machinery in the VM which is causing the stitching back of the stack to fail, which is why I’m trying (successfully does far) to simulate the simulator.  So hopefully in a few days the stitching approach will be viable, but did the moment the stack copying approach will work, even though it introduces unnecessary overhead.]

I hope this helps, and I hope I’m understanding the issue correctly.  If I’m not I thank you for your patience :-).

> At the moment, the former is the case, and this leads to the problem that
> the behalfOf process is debugged (i.e. interrupted) when an error is raised
> from aBlock while the - technically actually causing - receiver process
> keeps running, eventually ignoring the just-raised exception. See
> StandardToolSet class >> #handleError: for the place where "Processor
> activeProcess" is determined for debugging, which is redirected to the
> onBehalf process during the execution of aBlock.
> At this place, we actually would like to see something like this:
>
>    thisContext process
>        debug: anError signalerContext
>        title: anError description
>
> Which, however, is not possible because a stack frame cannot know its
> containing process.
> So in my first changeset (fix-infinite-debuggers.2.cs below), I had proposed
> to rewrite that method like this:
>
>    Processor basicActiveProcess
>        debug: anError signalerContext
>        title: anError description
>
> where basicActiveProcess would return the "true" active process neglecting
> the onBehalfOf redirection.
> However, *could there be any situation where you would like to respect the
> process redirection indeed in #handleError:?* (highlighting this as the open
> key question of this thread)
> We did not find a plausible example, the only case I could imagine is the
> following situation (which, admittedly, is not an every-day use case):
>
>    Debug the expression "self halt", step down until in StandardToolSet
> class>>handleError:, step into #activeProcess and then step over repeatedly
> until #debug:title: is sent.
>    In Trunk, this opens a second debugger on the same process. When using
> #basicActiveProcess, however, the first debugger stops working (dead
> process).
>
>
> So, your comments and thoughts are important: How would you think about the
> proposed #basicActiveProcess solution? Do you see any scenario where the
> onBehalfOf redirection needs to be perpetuated in ToolSet >> #handleError:?
> Or would you prefer another approach to fix the problem?
> Looking forward to an interesting discussion! :-)
>
> Best,
> Christoph

Eliot
_,,,^..^,,,_ (phone)

Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede

Hi Eliot,


thanks for your reply!


Therefore, an error within the evaluate block of Process >> #evaluate:onBehalfOf: could be caught within Process >> #evaluate:onBehalfOf: but delivered outside.  One could experiment with adding an exception handler around the evaluation block that would copy the stack of the exception, terminate the block, and then open a debugger on the copy of the stack once the effectiveProcess has been restored.

This sounds pretty much like what I proposed here: http://forum.world.st/I-broke-the-debugger-tp5110752p5111016.html
This solution would also have the advantage of a lower footprint than any #basicActiveProcess solution.
How would you think about it? :-)

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von Eliot Miranda <[hidden email]>
Gesendet: Mittwoch, 28. Oktober 2020 14:43:51
An: The general-purpose Squeak developers list
Betreff: Re: [squeak-dev] Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))
 
Hi all,

> On Oct 28, 2020, at 5:50 AM, Christoph Thiede <[hidden email]> wrote:
>
> Hi all! :-)
>
> After a lot of time, I would like to give this issue another push. These
> bugs are really serious and we should finally fix them.
>
> Short summary for everyone who would not like to re-read the whole thread:
> /
> Over the last months, several issues have been reported about infinite
> chains of debuggers popping up in your image and, most of the time,
> eventually making it unusable. Some months ago, I traced these issues down
> to several simulation errors that occurred while pressing the Over button in
> the debugger. The simulation errors can be fixed separately, but the
> debugger chains are caused by a second phenomenon which I could break down
> in the following snippet:
>
>    Processor activeProcess
>        evaluate: [self error: #first. self inform: #second]
>        onBehalfOf: [] newProcess
>
>    You would expect to see first a debugger window for the #error, and
> second, after you resume this error, a dialog window for the #inform:.
> However, surprisingly, both windows appear at the same time. The opening
> debugger does not suspend the process correctly.
> /
>
>
> I also had a talk about it with Marcel and Patrick and we ended up with the
> finding that the semantics of Process >> #evaluate:onBehalfOf: are not 100%
> clear at the moment. Its method comment states:
>
>    "Evaluate aBlock setting effectiveProcess to aProcess, and all other
> variables other than the scheduling ones to those of aProcess."
>
> However, the situation is unclear when an exception is raised from aBlock.
> Should it be handled still on behalf of the behalfOf process or rather on
> the original receiver process instead?

Conceptually Process >> #evaluate:onBehalfOf: is a private method if the debugger, which exists only to ensure that while simulating code on behalf of done process being debugged, the right process is found by the simulated code even though another process is actually executing the code.

Therefore the block being evaluated within Process >> #evaluate:onBehalfOf: is always evaluating some simulation on behalf of the debugger.  Therefore, *any* error which occurs during the block is not an error in the process being debugged (that error delivery should be simulated, and delivered within the process being debugged), but an error in the simulation, and should stop the simulation.

Therefore, an error within the evaluate block of Process >> #evaluate:onBehalfOf: could be caught within Process >> #evaluate:onBehalfOf: but delivered outside.  One could experiment with adding an exception handler around the evaluation block that would copy the stack of the exception, terminate the block, and then open a debugger on the copy of the stack once the effectiveProcess has been restored.

[P.S. I think the right way to implement this is to use Context>>cut: to snip the section of the stack containing the simulation error and stitch this back in after the evaluate block, but I *think* I am seeing bugs in the context-to-stack mapping machinery in the VM which is causing the stitching back of the stack to fail, which is why I’m trying (successfully does far) to simulate the simulator.  So hopefully in a few days the stitching approach will be viable, but did the moment the stack copying approach will work, even though it introduces unnecessary overhead.]

I hope this helps, and I hope I’m understanding the issue correctly.  If I’m not I thank you for your patience :-).

> At the moment, the former is the case, and this leads to the problem that
> the behalfOf process is debugged (i.e. interrupted) when an error is raised
> from aBlock while the - technically actually causing - receiver process
> keeps running, eventually ignoring the just-raised exception. See
> StandardToolSet class >> #handleError: for the place where "Processor
> activeProcess" is determined for debugging, which is redirected to the
> onBehalf process during the execution of aBlock.
> At this place, we actually would like to see something like this:
>
>    thisContext process
>        debug: anError signalerContext
>        title: anError description
>
> Which, however, is not possible because a stack frame cannot know its
> containing process.
> So in my first changeset (fix-infinite-debuggers.2.cs below), I had proposed
> to rewrite that method like this:
>
>    Processor basicActiveProcess
>        debug: anError signalerContext
>        title: anError description
>
> where basicActiveProcess would return the "true" active process neglecting
> the onBehalfOf redirection.
> However, *could there be any situation where you would like to respect the
> process redirection indeed in #handleError:?* (highlighting this as the open
> key question of this thread)
> We did not find a plausible example, the only case I could imagine is the
> following situation (which, admittedly, is not an every-day use case):
>
>    Debug the expression "self halt", step down until in StandardToolSet
> class>>handleError:, step into #activeProcess and then step over repeatedly
> until #debug:title: is sent.
>    In Trunk, this opens a second debugger on the same process. When using
> #basicActiveProcess, however, the first debugger stops working (dead
> process).
>
>
> So, your comments and thoughts are important: How would you think about the
> proposed #basicActiveProcess solution? Do you see any scenario where the
> onBehalfOf redirection needs to be perpetuated in ToolSet >> #handleError:?
> Or would you prefer another approach to fix the problem?
> Looking forward to an interesting discussion! :-)
>
> Best,
> Christoph

Eliot
_,,,^..^,,,_ (phone)



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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

marcel.taeumel
In reply to this post by Eliot Miranda-2
Hi Eliot.

> Therefore, *any* error which occurs during the block is not an error in the process
> being debugged (that error delivery should be simulated, and delivered within the
> process being debugged), but an error in the simulation, and should stop the simulation.

Well, unfortunately, one cannot know. Think of simulating "7/0".

Best,
Marcel

Am 28.10.2020 14:44:06 schrieb Eliot Miranda <[hidden email]>:

Hi all,

> On Oct 28, 2020, at 5:50 AM, Christoph Thiede wrote:
>
> Hi all! :-)
>
> After a lot of time, I would like to give this issue another push. These
> bugs are really serious and we should finally fix them.
>
> Short summary for everyone who would not like to re-read the whole thread:
> /
> Over the last months, several issues have been reported about infinite
> chains of debuggers popping up in your image and, most of the time,
> eventually making it unusable. Some months ago, I traced these issues down
> to several simulation errors that occurred while pressing the Over button in
> the debugger. The simulation errors can be fixed separately, but the
> debugger chains are caused by a second phenomenon which I could break down
> in the following snippet:
>
> Processor activeProcess
> evaluate: [self error: #first. self inform: #second]
> onBehalfOf: [] newProcess
>
> You would expect to see first a debugger window for the #error, and
> second, after you resume this error, a dialog window for the #inform:.
> However, surprisingly, both windows appear at the same time. The opening
> debugger does not suspend the process correctly.
> /
>
>
> I also had a talk about it with Marcel and Patrick and we ended up with the
> finding that the semantics of Process >> #evaluate:onBehalfOf: are not 100%
> clear at the moment. Its method comment states:
>
> "Evaluate aBlock setting effectiveProcess to aProcess, and all other
> variables other than the scheduling ones to those of aProcess."
>
> However, the situation is unclear when an exception is raised from aBlock.
> Should it be handled still on behalf of the behalfOf process or rather on
> the original receiver process instead?

Conceptually Process >> #evaluate:onBehalfOf: is a private method if the debugger, which exists only to ensure that while simulating code on behalf of done process being debugged, the right process is found by the simulated code even though another process is actually executing the code.

Therefore the block being evaluated within Process >> #evaluate:onBehalfOf: is always evaluating some simulation on behalf of the debugger. Therefore, *any* error which occurs during the block is not an error in the process being debugged (that error delivery should be simulated, and delivered within the process being debugged), but an error in the simulation, and should stop the simulation.

Therefore, an error within the evaluate block of Process >> #evaluate:onBehalfOf: could be caught within Process >> #evaluate:onBehalfOf: but delivered outside. One could experiment with adding an exception handler around the evaluation block that would copy the stack of the exception, terminate the block, and then open a debugger on the copy of the stack once the effectiveProcess has been restored.

[P.S. I think the right way to implement this is to use Context>>cut: to snip the section of the stack containing the simulation error and stitch this back in after the evaluate block, but I *think* I am seeing bugs in the context-to-stack mapping machinery in the VM which is causing the stitching back of the stack to fail, which is why I’m trying (successfully does far) to simulate the simulator. So hopefully in a few days the stitching approach will be viable, but did the moment the stack copying approach will work, even though it introduces unnecessary overhead.]

I hope this helps, and I hope I’m understanding the issue correctly. If I’m not I thank you for your patience :-).

> At the moment, the former is the case, and this leads to the problem that
> the behalfOf process is debugged (i.e. interrupted) when an error is raised
> from aBlock while the - technically actually causing - receiver process
> keeps running, eventually ignoring the just-raised exception. See
> StandardToolSet class >> #handleError: for the place where "Processor
> activeProcess" is determined for debugging, which is redirected to the
> onBehalf process during the execution of aBlock.
> At this place, we actually would like to see something like this:
>
> thisContext process
> debug: anError signalerContext
> title: anError description
>
> Which, however, is not possible because a stack frame cannot know its
> containing process.
> So in my first changeset (fix-infinite-debuggers.2.cs below), I had proposed
> to rewrite that method like this:
>
> Processor basicActiveProcess
> debug: anError signalerContext
> title: anError description
>
> where basicActiveProcess would return the "true" active process neglecting
> the onBehalfOf redirection.
> However, *could there be any situation where you would like to respect the
> process redirection indeed in #handleError:?* (highlighting this as the open
> key question of this thread)
> We did not find a plausible example, the only case I could imagine is the
> following situation (which, admittedly, is not an every-day use case):
>
> Debug the expression "self halt", step down until in StandardToolSet
> class>>handleError:, step into #activeProcess and then step over repeatedly
> until #debug:title: is sent.
> In Trunk, this opens a second debugger on the same process. When using
> #basicActiveProcess, however, the first debugger stops working (dead
> process).
>
>
> So, your comments and thoughts are important: How would you think about the
> proposed #basicActiveProcess solution? Do you see any scenario where the
> onBehalfOf redirection needs to be perpetuated in ToolSet >> #handleError:?
> Or would you prefer another approach to fix the problem?
> Looking forward to an interesting discussion! :-)
>
> Best,
> Christoph

Eliot
_,,,^..^,,,_ (phone)



Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede
Hi Marcel!

> Think of simulating "7/0".

I don't understand what you mean. :-)
[7 / 0] ifError: [#error] returns #error.
[[7 / 0] newProcess runUntil: [:c | c isDead]] ifError: [#error] does not.
The only exception is signaled *in* the simulation process, it shows up a
debugger, but the exception does not arrive at the simulating process.

Best,
Christoph



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

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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

marcel.taeumel
Hi Christoph.

Sounds good then. :-)

Best,
Marcel

Am 12.11.2020 11:09:52 schrieb Christoph Thiede <[hidden email]>:

Hi Marcel!

> Think of simulating "7/0".

I don't understand what you mean. :-)
[7 / 0] ifError: [#error] returns #error.
[[7 / 0] newProcess runUntil: [:c | c isDead]] ifError: [#error] does not.
The only exception is signaled *in* the simulation process, it shows up a
debugger, but the exception does not arrive at the simulating process.

Best,
Christoph



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



Reply | Threaded
Open this post in threaded view
|

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede
Hi all, hi Eliot,

I have to revise my previous recommendation of the
#basicEvaluate:onBehalfOf: solution. After taking another look at Jakob's
example from above ([^ self halt] ensure: [1 asString]), I recognized that
this approach would not fix the infinite debuggers in this situation. Here's
the reason behind it:


When stepping over the '^ self halt' statement in the example, a
#cannotReturn: message is invoked by the VM at some point where the stack
looks somehow like this:

Context>>return:through:
Context>>aboutToReturn:through:
...
Context>>runUntilErrorOrReturnFrom:
...
Process>>evaluate:onBehalfOf:
...
Process>>complete:
...
Debugger>>doStep

However, the fact that a #cannotReturn: has been invoked means that the
stack has got corrupted during the execution, so no exception can be passed
back to any exception handler that we might install in
#evaluate:onBehalfOf:. For this reason, when the BlockCannotReturn handling
reaches StandardToolSet >> #handleError:, the effectiveProcess is suspended
instead of the original activeProcess, and the debuggers are bouncing again
...

Now you might argue that this situation is only the consequence of another
error that occurs during the simulated execution of
Context>>aboutToReturn:through: (because of the #runUntilErrorOrReturnFrom:
hack, see
http://forum.world.st/BUG-REGRESSION-while-debugging-Generator-gt-gt-nextPut-td5108125.html#a5109109),
but I constructed another example that is immune to the
basicEvaluate:onBehalfOf: approach, too, but in this example the fault in
this example is clearly located in the user code so a powerful debugger
framework should not struggle about it:

[] ensure: [thisContext privSender: nil] "step over #ensure:"

And there is even a second problem with the basicEvaluate:onBehalfOf:
process because I found two calls on Process >> #debug:title:full: from the
simulator (Context) that do not even signal any exception before invoking
the debugger on the activeProcess, so again, an exception handler in
#basicEvaluate:onBehalfOf: would not be activated at all.


tl;dr: The basicEvaluate:onBehalfOf: approach, in spite of its simplicity,
would not cover all situations that can lead to an infinite debugger
situation.
For this reason, I recommend to use my first approach instead, which was to
define #basicActiveProcess and #isBasicActiveProcess on Process that ignore
the effectiveProcess mock-up for the single purpose of suspending the
correct process when a debugger is spawned in whatever way inside of an
#evaluate:onBehalfOf: block.

Does this argument sound reasonable to you? :-)
I have prepared some tests and an updated changeset and am looking forward
to uploading it with your agreement.

Best,
Christoph



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

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

Re: Fixing the infinite debugger chains? (was: Code simulation error (was Re: I broke the debugger?))

Christoph Thiede
Hi all, hi Eliot,

sorry for double-posting on purpose, but this issue and my thoughts on it
have become so complicated that I needed to separate two different subtopics
from each other:


I just identified another limitation in the process-faithful debugging
model, it cannot be applied nestable:

Consider the following expression:
Processor activeProcess environmentAt: #foo put: 42.
Processor halt activeProcess environmentAt: #foo.

This snippet returns 42 independently of whether you click Proceed in the
Halt Debugger or instead click Debug and then step over the second
#activeProcess send. However, you cannot select the Over or Through button
in the debugger and debug its action on a third process - if you try to do
so, an environmentKeyNotFound error will be raised eventually from the DoIt.

The reason for this is that the effectiveProcess approach is not nestable. I
find this quite a pity in a self-supporting system ...

Here is a very small patch that would eliminate this restriction:

 Processor >> effectiveProcess
     "effectiveProcess is a mechanism to allow process-faithful debugging.
The debugger executes code on behalf of processes, so unless some effort is
made the identity of Processor activeProcess is not correctly maintained
when debugging code. The debugger uses evaluate:onBehalfOf: to assign the
debugged process as the effectiveProcess of the process executing the code,
preserving process identity."

+ ^effectiveProcess ifNil: [self] ifNotNil: [:process | process
effectiveProcess]
- ^effectiveProcess ifNil: [self]

Would it be possible to apply this change or are there any good arguments
against it? :-) If it would be possible, I would be glad to upload the patch
to the inbox, and I also have prepared some tests for it.

I'm proposing this change in the same thread as the #basicActiveProcess
discussion because if we decide to make process-faithful debugging nestable
(please! :D), #basicActiveProcess (probably #executingProcess would be a
better name for it ...) as some kind of reciprocal operation for
#effectiveProcess should be nestable in this case, too.


Also, here are two more related questions for you (sorry for the walls of
text! ;-)):

1.) Could you maybe explain the motivation for Kernel-eem.894/Kernel-eem.895
to me? Why is it necessary to modify the non-effective process? In a current
Trunk image, ProcessorScheduler does never expose activeProcess itself
(without wrapping it with an #effectiveProcess send). Are there any other
references that need to be taken into account? I reverted to eem 9/7/2009
11:10 of #evaluate:onBehalfOf: and the environments example from above is
still working. I have read [this
thread](http://forum.world.st/Process-specific-state-difficult-to-debug-td4802422.html#a4802507)
but I cannot reproduce your error and also System-eem.699 seems to be the
wrong reference, at least I don't see how MessageTally is related to
process-faithful debugging.

2.) This might be or might not be a question of coding styles, but when I
browse the senders of #evaluate:onBehalfOf:, there are some methods that
eventually make nested calls to #evaluate:onBehalfOf:, for instance,
#popTo:/#return:value:/#activateReturn:value:. I assume this was not an
intentional decision but kept for simplicity? Should we maybe eliminate
these redundant nestings (and maybe speed up some things) by introducing a
few basic selectors, or would this not be worth the time?


I am looking forward to your help! :-)

Best,
Christoph



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

Carpe Squeak!