Skip to content

Commit

Permalink
Step and Continue after breakpoint suspends at wrong place after eval…
Browse files Browse the repository at this point in the history
…. [NETBEANS-6123]
  • Loading branch information
entlicher committed Oct 12, 2021
1 parent 6fb7a62 commit 1b12ab7
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import org.netbeans.modules.debugger.jpda.actions.CompoundSmartSteppingListener;
import org.netbeans.modules.debugger.jpda.actions.SmartSteppingFilterImpl;
import org.netbeans.modules.debugger.jpda.actions.StepIntoActionProvider;
import org.netbeans.modules.debugger.jpda.impl.StepUtils;
import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
Expand Down Expand Up @@ -225,6 +226,7 @@ public void addStep(JPDAThread tr) {
size,
getDepth()
);
StepUtils.markOriginalStepDepth(stepRequest, trImpl.getThreadReference());
//stepRequest.addCountFilter(1); - works bad with exclusion filters!
String[] exclusionPatterns = applyExclusionPatterns(stepRequest);
debuggerImpl.getOperator().register(stepRequest, this);
Expand Down Expand Up @@ -468,6 +470,7 @@ private boolean setUpBoundaryStepRequest(EventRequestManager erm,
StepRequest.STEP_LINE,
StepRequest.STEP_OVER
);
StepUtils.markOriginalStepDepth(boundaryStepRequest, trRef);
if (isNextOperationFromDifferentExpression) {
EventRequestWrapper.addCountFilter(boundaryStepRequest, 2);
} else {
Expand Down Expand Up @@ -632,6 +635,7 @@ public boolean exec (final Event event) {
StepRequest.STEP_LINE,
StepRequest.STEP_OUT
);
StepUtils.markOriginalStepDepth(stepRequest, tr.getThreadReference());
EventRequestWrapper.addCountFilter(stepRequest, 1);
String[] exclusionPatterns = getCurrentExclusionPatterns();
// JDI is inconsistent!!! Step into steps *through* filters, but step out does *NOT*
Expand Down Expand Up @@ -946,6 +950,7 @@ private boolean shouldNotStopHere(StepEvent event, int stepDepthDiff) {
doStepSize,
doStepDepth
);
StepUtils.markOriginalStepDepth(stepRequest, tr);
//EventRequestWrapper.addCountFilter(stepRequest, 1);
String[] exclusionPatterns = applyExclusionPatterns(stepRequest);
debuggerImpl.getOperator ().register (stepRequest, this);
Expand Down Expand Up @@ -1002,6 +1007,7 @@ private boolean shouldNotStopHere(StepEvent event, int stepDepthDiff) {
doStepSize,
depth
);
StepUtils.markOriginalStepDepth(stepRequest, tr);
if (logger.isLoggable(Level.FINE)) {
try {
logger.fine("Can not stop at " + ThreadReferenceWrapper.frame(tr, 0) + ", smart-stepping. Submitting step = " + stepRequest + "; depth = " + depth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.netbeans.modules.debugger.jpda.JPDAStepImpl;
import org.netbeans.modules.debugger.jpda.JPDAStepImpl.MethodExitBreakpointListener;
import org.netbeans.modules.debugger.jpda.SourcePath;
import org.netbeans.modules.debugger.jpda.impl.StepUtils;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidRequestStateExceptionWrapper;
Expand Down Expand Up @@ -221,6 +222,7 @@ private void runAction(final int stepDepth, final int stepSize, boolean doResume
stepSize,
stepDepth
);
StepUtils.markOriginalStepDepth(stepRequest, tr);
EventRequestWrapper.addCountFilter (stepRequest, 1);
getDebuggerImpl ().getOperator ().register (stepRequest, StepActionProvider.this);
EventRequestWrapper.setSuspendPolicy (stepRequest, suspendPolicy);
Expand Down Expand Up @@ -416,6 +418,7 @@ public boolean exec (Event ev) {
StepRequest.STEP_LINE,
step
);
StepUtils.markOriginalStepDepth(stepRequest, tr);
EventRequestWrapper.addCountFilter(stepRequest, 1);
getDebuggerImpl ().getOperator ().register (stepRequest, this);
EventRequestWrapper.setSuspendPolicy(stepRequest, suspendPolicy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.netbeans.modules.debugger.jpda.JPDAStepImpl;
import org.netbeans.modules.debugger.jpda.SourcePath;
import static org.netbeans.modules.debugger.jpda.actions.StepActionProvider.getTopFrame;
import org.netbeans.modules.debugger.jpda.impl.StepUtils;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidRequestStateExceptionWrapper;
Expand Down Expand Up @@ -509,6 +510,7 @@ private StepRequest setStepRequest (int step, int stepSize, JPDAThreadImpl[] res
tr,
stepSize,
step);
StepUtils.markOriginalStepDepth(stepRequest, tr);
getDebuggerImpl ().getOperator ().register (stepRequest, this);
suspendPolicy = getDebuggerImpl().getSuspend();
EventRequestWrapper.setSuspendPolicy (stepRequest, suspendPolicy);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.debugger.jpda.impl;

import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.request.StepRequest;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;

import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.request.EventRequestWrapper;

public final class StepUtils {

private static final String STEP_PROP_DEPTH = "originalThreadDepth"; // NOI18N

private StepUtils() {}

/**
* Mark the frame depth of the thread when the step is created. It's to be retrieved
* by {@link #getOriginalStepDepth(com.sun.jdi.request.StepRequest)}.
*/
public static void markOriginalStepDepth(StepRequest stepRequest, ThreadReference threadReference) {
try {
EventRequestWrapper.putProperty(stepRequest, STEP_PROP_DEPTH, ThreadReferenceWrapper.frameCount(threadReference));
} catch (IllegalThreadStateExceptionWrapper | IncompatibleThreadStateException | InternalExceptionWrapper |
InvalidStackFrameExceptionWrapper | ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
// Not successful, ignore.
}
}

/**
* Get the frame depth of the thread when the step was submitted, or <code>-1</code> when unknown.
*/
public static int getOriginalStepDepth(StepRequest stepRequest) {
Object depth;
try {
depth = EventRequestWrapper.getProperty(stepRequest, STEP_PROP_DEPTH);
} catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
return -1;
}
if (depth instanceof Integer) {
return (Integer) depth;
}
return -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
import org.netbeans.modules.debugger.jpda.SingleThreadWatcher;
import org.netbeans.modules.debugger.jpda.impl.StepUtils;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidRequestStateExceptionWrapper;
Expand Down Expand Up @@ -128,8 +129,8 @@ public final class JPDAThreadImpl implements JPDAThread, Customizer, BeanContext
private static final Logger logger = Logger.getLogger(JPDAThreadImpl.class.getName()); // NOI18N
private static final Logger loggerS = Logger.getLogger(JPDAThreadImpl.class.getName()+".suspend"); // NOI18N

private ThreadReference threadReference;
private JPDADebuggerImpl debugger;
private final ThreadReference threadReference;
private final JPDADebuggerImpl debugger;
/** Thread is suspended and everybody know about this. */
private boolean suspended;
private boolean suspendedOnAnEvent; // Suspended by an event that occured in this thread
Expand Down Expand Up @@ -1357,7 +1358,9 @@ public void notifyMethodInvoking() throws PropertyVetoException {
if (stepsToDelete == null) {
stepsToDelete = new ArrayList<StepRequest>();
}
stepsToDelete.add(sr);
if (checkToDisableStep(sr, t)) {
stepsToDelete.add(sr);
}
}
}
if (stepsToDelete != null) {
Expand Down Expand Up @@ -1404,6 +1407,39 @@ public void notifyMethodInvoking() throws PropertyVetoException {
}
}

private static boolean checkToDisableStep(StepRequest sr, ThreadReference t) {
int stepKind;
try {
stepKind = StepRequestWrapper.depth(sr);
} catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
// A wrong state, do nothing
return false;
}
if (stepKind == StepRequest.STEP_INTO) {
// Disable step into as method invocation will suspend on it
return true;
}
int threadDepth;
try {
threadDepth = ThreadReferenceWrapper.frameCount(t);
} catch (IllegalThreadStateExceptionWrapper | IncompatibleThreadStateException |
InternalExceptionWrapper | InvalidStackFrameExceptionWrapper ex) {
// We can not retrieve the frame depth
return true;
} catch (ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
// A wrong state, do nothing
return false;
}
int stepDepth = StepUtils.getOriginalStepDepth(sr);
if (stepDepth > 0) {
// If the depth at which the step was submitted is less than the current depth,
// do not disable the step as it will not interfere with the method invocation.
// The invocation will not go up the stack.
return stepDepth >= threadDepth;
}
return true;
}

public void notifyMethodInvokeDone() {
synchronized (suspendToCheckForMonitorsLock) {
canSuspendToCheckForMonitors = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,56 @@ public void testStepOut () throws Exception {
}
}

public void testStepAndContinueOnBP() throws Exception {
String app = "org.netbeans.api.debugger.jpda.testapps.StepAndContinueApp";
try {
JPDASupport.removeAllBreakpoints();
Utils.BreakPositions bp = Utils.getBreakPositions(sourceRoot + app.replace('.', '/') + ".java");
LineBreakpoint lb = bp.getLineBreakpoints().get(0);
dm.addBreakpoint(lb);
support = JPDASupport.attach(app);
support.waitState (JPDADebugger.STATE_STOPPED);
dm.removeBreakpoint(lb);
assertEquals (
"Execution stopped in wrong class",
support.getDebugger().getCurrentCallStackFrame().getClassName(),
app
);
assertEquals (
"Execution stopped at wrong line",
lb.getLineNumber(),
support.getDebugger().getCurrentCallStackFrame().getLineNumber(null)
);
lb = bp.getLineBreakpoints().get(1);
dm.addBreakpoint(lb);
support.stepOver();
suspendedLineCheck(bp.getStopLine("Over1"));
support.stepOver();
breakpointCheckEvalCont(lb.getLineNumber());
suspendedLineCheck(bp.getStopLine("Over2"));
support.stepOver();
breakpointCheckEvalCont(lb.getLineNumber());
suspendedLineCheck(bp.getStopLine("Over3"));
support.stepOver();
suspendedLineCheck(bp.getStopLine("Over4"));
support.stepOver();
suspendedLineCheck(bp.getStopLine("Over5"));
support.stepInto();
suspendedLineCheck(bp.getStopLine("Into6"));
support.stepOut();
breakpointCheckEvalCont(lb.getLineNumber());
support.stepOver();
suspendedLineCheck(bp.getStopLine("Out7"));
support.stepInto();
suspendedLineCheck(bp.getStopLine("Into8"));
support.stepOut();
breakpointCheckEvalCont(lb.getLineNumber());
suspendedLineCheck(bp.getStopLine("Out7"));
} finally {
support.doFinish ();
}
}

private void stepCheck (
Object stepType,
String clsExpected,
Expand All @@ -247,4 +297,25 @@ private void stepCheck (
getLineNumber (null)
);
}

private void breakpointCheckEvalCont(int lineExpected) {
suspendedLineCheck(lineExpected);
// We invoke a method:
try {
assertEquals("10", support.getDebugger().evaluate("m1()").getValue());
} catch (InvalidExpressionException ex) {
throw new AssertionError(ex);
}
// Then we do Continue to finish the original step:
support.doContinue();
support.waitState(JPDADebugger.STATE_STOPPED);
}

private void suspendedLineCheck(int lineExpected) {
assertEquals (
"Execution stopped at wrong line",
lineExpected,
support.getDebugger().getCurrentCallStackFrame().getLineNumber(null)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.api.debugger.jpda.testapps;

/**
* A sample application where we combine stepping and breakpoints with continue.
*
* @author Martin Entlicher
*/
public class StepAndContinueApp {

public static void main(String[] args) {
StepAndContinueApp sa = new StepAndContinueApp();
int x = sa.m1(); // STOP Over4
x += sa.m2(); // STOP Over5
x += sa.m3(); // STOP Out7
}

private int m1() {
int im1 = 10; // LBREAKPOINT
m2(); // STOP Over1
m3(); // STOP Over2
return im1; // STOP Over3
}

private int m2() {
int im2 = 20; // STOP Into6
m3();
return im2;
}

private int m3() {
int im3 = 30; // STOP Into8
return im3; // LBREAKPOINT
}

}

0 comments on commit 1b12ab7

Please sign in to comment.