Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support step filters when stepping #106

Merged
merged 23 commits into from
Dec 6, 2017
Merged

Support step filters when stepping #106

merged 23 commits into from
Dec 6, 2017

Conversation

testforstephen
Copy link
Contributor

Expose debugFilters options in launch.json to allow user to customize the filters to debug "Just My Code".

The peer PR in vscode side is microsoft/vscode-java-debug#155

stepfilter

andxu
andxu previously approved these changes Nov 17, 2017
@@ -312,6 +316,34 @@ public static void resumeThread(ThreadReference thread) {
}
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to put another class ThreadUtility. There will be StackFrameUtility definitely. If all utility in DebugUtility, this file will be bloated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are many places to reference to these methods. It would cause many changed files in this PR. Prefer to use a separate PR to do this.

@@ -118,9 +118,7 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
debugEvent.shouldResume = false;
}
} else if (event instanceof StepEvent) {
ThreadReference stepThread = ((StepEvent) event).thread();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If do nothing, just remove this condition switch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice.
done.

@@ -51,6 +51,7 @@
context.setAttached(true);
context.setSourcePaths(attachArguments.sourcePaths);
context.setDebuggeeEncoding(StandardCharsets.UTF_8); // Use UTF-8 as debuggee's default encoding format.
context.setDebugFilters(attachArguments.debugFilters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A thought about AttachRequesthandler & LanuchRequestHandler. There are lots of duplicate code from these two classes. We need refactor out a common base in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. Will add some preProcess and postProcess function to handle the common logic.

Copy link
Contributor Author

@testforstephen testforstephen Nov 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When implementing launchInTerminal, i have changed the launch logic, would prefer to refactor common base of the attach/launch handler there.
Will send out new PR later.

return threadStates.get(threadId);
}

private void setPendingStepKind(ThreadReference thread, int kind) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be another ThreadUtility class if you already put something into the DebugUtility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the ThreadsRequestHandler constructor, i create a thread state map private Map<Long, ThreadState> threadStates = new HashMap<>();. This method is used to update the state map.

@@ -90,7 +110,10 @@
private CompletableFuture<Response> stepIn(Requests.StepInArguments arguments, Response response, IDebugAdapterContext context) {
ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId);
if (thread != null) {
DebugUtility.stepInto(thread, context.getDebugSession().getEventHub());
setPendingStepKind(thread, StepRequest.STEP_INTO);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already use StepRequest.STEP_INOT/STEP_OVER in the DebugUtility. I would suggest put the same logic either on inside DebugUtility.stepxxx or all on here. But not on different method call stacks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is to record the thread's stepping state, and putting the code in ThreadRequestHandler is reasonable because ThreadRequestHandler life cycle is debug session dependent.

return !methodShouldBeFiltered(originalLocation.method(), context) && methodShouldBeFiltered(currentLocation.method(), context);
}

private boolean methodShouldBeFiltered(Method method, IDebugAdapterContext context) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming ShouldFilterMethod for concice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

/**
* Return true if the StepEvent's location is a Method that the user has indicated to filter.
*/
private boolean locationShouldBeFiltered(ThreadReference thread, IDebugAdapterContext context) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ShouldFilterLocation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

*/
public static CompletableFuture<Location> stepOver(ThreadReference thread, IEventHub eventHub) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to change the return value?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One common practice of changing interface is to overload the method by adding parameters. So the old method can call the new method. In this way, the change happens internally without affecting other parts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, a new method can be added:
public static CompletableFuture<Location> stepOver(ThreadReference thread, IEventHub eventHub, String[] stepFilters)
And in the old method, we change the implementation to call the new one with null or empty string array.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good suggestion.

yaohaizh
yaohaizh previously approved these changes Nov 24, 2017
context.getDebugSession().getEventHub().stepEvents().subscribe(debugEvent -> {
handleDebugEvent(debugEvent, context.getDebugSession(), context);
});
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just return here so the "else" branch is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice. fixed.

setOriginalStackDepth(thread);
setOriginalStepLocation(thread);
if (command == Command.STEPIN) {
DebugUtility.stepInto(thread, context.getDebugSession().getEventHub(), context.getDebugFilters().stepFilters);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't anyone using the returned request object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed. the latest change has used the stepRequest info.

import com.sun.jdi.request.StepRequest;

public class StepRequestHandler implements IDebugRequestHandler {
private Map<Long, ThreadState> threadStates;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who is responsible for removing thread state objects from the collection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add logic to remove thread state when ThreadDeathEvent occurs.

logger.log(Level.SEVERE, failureMessage);
return AdapterUtils.createAsyncErrorResponse(response, ErrorCode.STEP_FAILURE, failureMessage);
} catch (IndexOutOfBoundsException ex) {
final String failureMessage = String.format("Failed to step because the thread %d doesn't contain any stack frame", threadId);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would this happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first, thread.frame(i) claims it could throw IndexOutOfBoundsException. And i found a special case that could get this exception. That are system thread Attach Listener and Signal Dispatcher, when pausing them, you could find their stack frames are empty.

image

ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), threadId);
if (thread != null) {
try {
setPendingStepKind(threadId, command);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to check the thread status before setting it? What if the thread state is already set by other requests?

if (pendingStepRequest != null) {
DebugUtility.deleteEventRequestSafely(debugSession.getVM().eventRequestManager(), pendingStepRequest);
}
setPendingStepRequest(threadId, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just removeThreadState()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice.

removeThreadState(threadId);
} else if (event instanceof StepEvent) {
debugEvent.shouldResume = false;
ThreadReference thread = ((StepEvent) event).thread();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The space before "event" is not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the whitespace between (StepEvent) and event? checkstyle will need add a space there.

debugEvent.shouldResume = false;
ThreadReference thread = ((StepEvent) event).thread();
long threadId = thread.uniqueID();
setPendingStepRequest(threadId, null); // clean up the pending status.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to dispose the request before setting it to null, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to dispose it because DebugUtility.stepXXX method also subscribes StepEvent and it will dispose the StepRequest via JDI.

try {
if (getPendingStepKind(threadId) == Command.STEPIN) {
// Check if the step into operation stepped through the filtered code and stopped at an un-filtered location.
if (getOriginalStackDepth(threadId) + 1 < thread.frameCount()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't quite get the logic here. Let's sync more tomorrow.

}
// If the ending step location is filtered, or same as the original location where the step into operation is originated,
// do another step of the same kind.
if (shouldFilterLocation(thread, context) || shouldDoExtraStepInto(thread)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't quite get the logic here. Let's sync more tomorrow.

}
}

private void setPendingStepKind(long threadId, Command kind) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind -> Type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice.

@@ -38,6 +38,13 @@
public boolean supportsRunInTerminalRequest;
}

public static class DebugFilters {
public String[] stepFilters = new String[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stepFilters -> classNameFilters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

@@ -38,6 +38,13 @@
public boolean supportsRunInTerminalRequest;
}

public static class DebugFilters {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DebugFilters -> StepFilters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.

context.getStepFilters().classNameFilters);
}
threadState.pendingStepRequest.enable();
thread.resume();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use DebugUtility.resumeThread

}

private boolean shouldFilterMethod(Method method, IDebugAdapterContext context) {
if ((context.getStepFilters().skipStaticInitializers && method.isStaticInitializer())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use simple return

StepRequest pendingStepRequest = null;
int stackDepth = -1;
Location stepLocation = null;
Disposable disposable = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

disposable -> eventSubscription

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants