Fixes for the Debugger to prevent crashes when debugging protected blocks:
Thanks to Manfred Möbus for this!
[codesyntax lang=”smalltalk”]
!Process methods !
dropFrames: frameCount
"Private - discard the top <frameCount> stack frames
from the receiver's stack. Requires that the receiver
not be active. Assumes the receiver is being debugged."
| topFrameBeforeResumption sendFrameBeforeResumption runableBeforeResumption homeFrame |
self == CurrentProcess
ifTrue: [ ^self error: 'cannot drop frames from the active process.' ].
frameCount = 0 ifTrue: [ ^self ].
self class enableInterrupts: false.
sendFrameBeforeResumption := sendFrame.
topFrameBeforeResumption := self topFrame.
runableBeforeResumption := self runable.
self sendFrame: ( self processIndexToStackPointer: topFrameBeforeResumption ).
self debugger notNil
ifTrue: [ self debugger debuggingProcess: CurrentProcess ].
[ self protectionBlock: ( self
firstProtectionBlockWithin: frameCount
removeMark: true ).
self protectionBlock ~~ nil ]
whileTrue: [
self debugger notNil
ifTrue: [ "mm: added 'if > 0' check; expandFrame: would fail, and it is the wrong frame anyway
- see homeFrameOfContext: which in some cases just returns 0 for 'no-valid-result' "
(homeFrame := self homeFrameOfContext: self protectionBlock ) > 0 ifTrue: [
self debugger expandFrame: homeFrame ].].
self evaluateOneProtectionBlock ].
self debugger notNil
ifTrue: [ self debugger debuggingProcess: nil ].
self runable: runableBeforeResumption.
self topFrame = topFrameBeforeResumption ifTrue: [
frameCount timesRepeat: [ self dropFrameWithoutProtection].
sendFrame := sendFrameBeforeResumption]! !
!Debugger methods !
expandFrame: frame
"Private - create a debugging version of a frame."
| process cm dcm offset oldOffset interruptFrame arg |
"mm: get out for frame=0 - this will fail since returnOffsetAt: -1 will return wrong value
and make #convert:to:offset: blow up the image - the real return value is probably
(self at: 2) but this is unconfirmed - so for the while just don't expand this frame"
frame = 0 ifTrue: [ ^self ].
process := self debuggedProcess.
cm := process methodAt: frame.
cm isDebuggable
ifTrue:
[
cm sourceIndex >= 3 "method source in dll"
ifTrue: [ cm sourceObject: ( ( process receiverAt: frame )
methodFor: cm selector in: cm classField ) sourceString ]
]
ifFalse:
[
interruptFrame := false.
oldOffset := (process returnOffsetAt: frame - 1).
(oldOffset = 0) ifTrue: [
interruptFrame := true.
oldOffset := process frameAt: frame - 1 offset: 7].
(oldOffset = 1) ifTrue: [^self].
dcm := cm asDebuggableMethod.
offset := self convert: cm to: dcm offset: oldOffset.
"mm: make block contexts consistent with the replaced compiled method -
this was found necessary only for the protected frame (#ensure: or #ifCurtailed)
as invoked by #hop-ing through a ^ return from block.
The vm gets fooled by old and new return addresses in the next #copyStack in this case,
and when the debugger calls #convert:to:offset with wrong values, a GPF results. "
1 to: (process argumentCount: frame - 1) do: [ :i |
arg := process stackArgAt: frame - 1 number: i.
arg isBlockClosure ifTrue: [
(arg at: 1) == cm ifTrue: [
arg at: 1 put: dcm. ]]].
process methodAt: frame put: dcm.
interruptFrame ifTrue: [
process frameAt: frame - 1 offset: 7 put: offset]
ifFalse: [
process returnOffsetAt: frame - 1 put: offset ]
]! !
!Debugger methods !
hop
"Private - resume process for one hop, i.e., to next expression or
assignment at any method level."
| process protectedFrameIndex |
self resumable
ifFalse: [^self].
process := self debuggedProcess.
realFrame == nil
ifFalse:
[process topFrame: realFrame.
realFrame := nil].
self expandFrame: 2.
self expandFrame: 3.
"mm: ensure hopping (with ^ ) out of a block into protected frame will find its method
already debuggable; otherwise VM would do this in assembler code,
but does not implement the changes to blocks as added to #expandFrame: method"
self inBlock ifTrue: [
protectedFrameIndex := process firstProtectionFrame.
protectedFrameIndex notNil ifTrue: [ self expandFrame: protectedFrameIndex + 1. ].].
self label: 'hopping'.
Process enableInterrupts: false.
process sendFrame: 1.
process debugger: self.
process interruptFrame: 0.
process runable: (process runable bitOr: 1).
BreakPoints:= breakpointArray.
UserInterfaceProcess := (process isUserIF
ifTrue: [process]
ifFalse: [nil]).
CurrentProcess := process.
process resume: 0! !
!Process methods !
firstProtectionFrame
"Private - added by mm: answer the frame number of the first
protection block in this process, or nil if none exists.
(this is the frame where receiverAt: frame will return the FrameMarker,
with the method #setUnwind: )."
| count index |
count := 0.
index := self processIndexFromBPLink: self topFrame.
[(self at: index) ~= 0 ]
whileTrue:
[
FrameMarker == (self at: index + ReceiverOffset)
ifTrue: [^count ].
count := count + 1.
index := self nextFrameFrom: index].
^nil! !
[/codesyntax]