Skip to content

Commit 079a9c9

Browse files
Reduce JDWP frequency when resolving variables (#347)
1 parent 94284b0 commit 079a9c9

File tree

5 files changed

+99
-49
lines changed

5 files changed

+99
-49
lines changed

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/DebugSettings.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017-2019 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -41,6 +41,7 @@ public final class DebugSettings {
4141
public StepFilters stepFilters = new StepFilters();
4242
public ClassFilters exceptionFilters = new ClassFilters();
4343
public boolean exceptionFiltersUpdated = false;
44+
public int limitOfVariablesPerJdwpRequest = 100;
4445

4546
public static DebugSettings getCurrent() {
4647
return current;

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/EvaluateRequestHandler.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017-2019 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -32,6 +32,7 @@
3232
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
3333
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
3434
import com.microsoft.java.debug.core.adapter.variables.IVariableFormatter;
35+
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructure;
3536
import com.microsoft.java.debug.core.adapter.variables.JavaLogicalStructureManager;
3637
import com.microsoft.java.debug.core.adapter.variables.StackFrameReference;
3738
import com.microsoft.java.debug.core.adapter.variables.VariableDetailUtils;
@@ -93,13 +94,14 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
9394
Value sizeValue = null;
9495
if (value instanceof ArrayReference) {
9596
indexedVariables = ((ArrayReference) value).length();
96-
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure
97-
&& engine != null
98-
&& JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) {
97+
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && engine != null) {
9998
try {
100-
sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, stackFrameReference.getThread(), engine);
101-
if (sizeValue != null && sizeValue instanceof IntegerValue) {
102-
indexedVariables = ((IntegerValue) sizeValue).value();
99+
JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value);
100+
if (structure != null && structure.getSizeExpression() != null) {
101+
sizeValue = structure.getSize((ObjectReference) value, stackFrameReference.getThread(), engine);
102+
if (sizeValue != null && sizeValue instanceof IntegerValue) {
103+
indexedVariables = ((IntegerValue) sizeValue).value();
104+
}
103105
}
104106
} catch (CancellationException | IllegalArgumentException | InterruptedException
105107
| ExecutionException | UnsupportedOperationException e) {
@@ -108,7 +110,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
108110
}
109111
}
110112
int referenceId = 0;
111-
if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) {
113+
if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) {
112114
referenceId = context.getRecyclableIdPool().addObject(threadId, varProxy);
113115
}
114116

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,14 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
232232
Value sizeValue = null;
233233
if (value instanceof ArrayReference) {
234234
indexedVariables = ((ArrayReference) value).length();
235-
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure
236-
&& evaluationEngine != null
237-
&& JavaLogicalStructureManager.isIndexedVariable((ObjectReference) value)) {
235+
} else if (value instanceof ObjectReference && DebugSettings.getCurrent().showLogicalStructure && evaluationEngine != null) {
238236
try {
239-
sizeValue = JavaLogicalStructureManager.getLogicalSize((ObjectReference) value, containerNode.getThread(), evaluationEngine);
240-
if (sizeValue != null && sizeValue instanceof IntegerValue) {
241-
indexedVariables = ((IntegerValue) sizeValue).value();
237+
JavaLogicalStructure structure = JavaLogicalStructureManager.getLogicalStructure((ObjectReference) value);
238+
if (structure != null && structure.getSizeExpression() != null) {
239+
sizeValue = structure.getSize((ObjectReference) value, containerNode.getThread(), evaluationEngine);
240+
if (sizeValue != null && sizeValue instanceof IntegerValue) {
241+
indexedVariables = ((IntegerValue) sizeValue).value();
242+
}
242243
}
243244
} catch (CancellationException | IllegalArgumentException | InterruptedException | ExecutionException | UnsupportedOperationException e) {
244245
logger.log(Level.INFO,
@@ -267,7 +268,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
267268
}
268269

269270
int referenceId = 0;
270-
if (indexedVariables > 0 || (indexedVariables < 0 && VariableUtils.hasChildren(value, showStaticVariables))) {
271+
if (indexedVariables > 0 || (indexedVariables < 0 && value instanceof ObjectReference)) {
271272
VariableProxy varProxy = new VariableProxy(containerNode.getThread(), containerNode.getScope(), value, containerNode, evaluateName);
272273
referenceId = context.getRecyclableIdPool().addObject(containerNode.getThreadId(), varProxy);
273274
varProxy.setIndexedVariable(indexedVariables >= 0);
@@ -290,6 +291,11 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
290291
}
291292
list.add(typedVariables);
292293
}
294+
295+
if (list.isEmpty() && containerNode.getProxiedVariable() instanceof ObjectReference) {
296+
list.add(new Types.Variable("Class has no fields", "", null, 0, null));
297+
}
298+
293299
response.body = new Responses.VariablesResponseBody(list);
294300

295301
return CompletableFuture.completedFuture(response);

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/JavaLogicalStructure.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public class JavaLogicalStructure {
3333
private final LogicalStructureExpression valueExpression;
3434
private final LogicalStructureExpression sizeExpression;
3535
private final LogicalVariable[] variables;
36+
// Indicates whether the specified type is an interface.
37+
private final boolean isInterface;
3638

3739
/**
3840
* Constructor.
@@ -47,9 +49,15 @@ public JavaLogicalStructure(String type, LogicalStructureExpression valueExpress
4749
*/
4850
public JavaLogicalStructure(String type, String fullyQualifiedName, LogicalStructureExpression valueExpression, LogicalStructureExpression sizeExpression,
4951
LogicalVariable[] variables) {
52+
this(type, type, true, valueExpression, sizeExpression, variables);
53+
}
54+
55+
public JavaLogicalStructure(String type, String fullyQualifiedName, boolean isInterface, LogicalStructureExpression valueExpression,
56+
LogicalStructureExpression sizeExpression, LogicalVariable[] variables) {
5057
this.valueExpression = valueExpression;
5158
this.type = type;
5259
this.fullyQualifiedName = fullyQualifiedName;
60+
this.isInterface = isInterface;
5361
this.sizeExpression = sizeExpression;
5462
this.variables = variables;
5563
}
@@ -84,18 +92,24 @@ public boolean providesLogicalStructure(ObjectReference obj) {
8492
}
8593

8694
ClassType classType = (ClassType) variableType;
87-
while (classType != null) {
88-
if (Objects.equals(type, classType.name())) {
89-
return true;
90-
}
91-
92-
classType = classType.superclass();
95+
if (Objects.equals(type, classType.name())) {
96+
return true;
9397
}
9498

95-
List<InterfaceType> interfaceTypes = ((ClassType) variableType).allInterfaces();
96-
for (InterfaceType interfaceType : interfaceTypes) {
97-
if (Objects.equals(type, interfaceType.name())) {
98-
return true;
99+
if (isInterface) {
100+
List<InterfaceType> interfaceTypes = ((ClassType) variableType).allInterfaces();
101+
for (InterfaceType interfaceType : interfaceTypes) {
102+
if (Objects.equals(type, interfaceType.name())) {
103+
return true;
104+
}
105+
}
106+
} else {
107+
while (classType != null) {
108+
if (Objects.equals(type, classType.name())) {
109+
return true;
110+
}
111+
112+
classType = classType.superclass();
99113
}
100114
}
101115

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/variables/VariableUtils.java

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017-2019 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -15,6 +15,7 @@
1515
import java.util.List;
1616
import java.util.Map;
1717
import java.util.Objects;
18+
import java.util.function.Consumer;
1819
import java.util.logging.Level;
1920
import java.util.logging.Logger;
2021
import java.util.stream.Collectors;
@@ -55,12 +56,11 @@ public static boolean hasChildren(Value value, boolean includeStatic) {
5556
if (value == null || !(value instanceof ObjectReference)) {
5657
return false;
5758
}
58-
Type type = value.type();
59+
ReferenceType type = ((ObjectReference) value).referenceType();
5960
if (type instanceof ArrayType) {
6061
return ((ArrayReference) value).length() > 0;
6162
}
62-
return value.type() instanceof ReferenceType && ((ReferenceType) type).allFields().stream()
63-
.filter(t -> includeStatic || !t.isStatic()).toArray().length > 0;
63+
return type.allFields().stream().anyMatch(t -> includeStatic || !t.isStatic());
6464
}
6565

6666
/**
@@ -74,7 +74,7 @@ public static boolean hasChildren(Value value, boolean includeStatic) {
7474
*/
7575
public static List<Variable> listFieldVariables(ObjectReference obj, boolean includeStatic) throws AbsentInformationException {
7676
List<Variable> res = new ArrayList<>();
77-
Type type = obj.type();
77+
ReferenceType type = obj.referenceType();
7878
if (type instanceof ArrayType) {
7979
int arrayIndex = 0;
8080
boolean isUnboundedArrayType = Objects.equals(type.signature(), "[Ljava/lang/Object;");
@@ -85,7 +85,7 @@ public static List<Variable> listFieldVariables(ObjectReference obj, boolean inc
8585
}
8686
return res;
8787
}
88-
List<Field> fields = obj.referenceType().allFields().stream().filter(t -> includeStatic || !t.isStatic())
88+
List<Field> fields = type.allFields().stream().filter(t -> includeStatic || !t.isStatic())
8989
.sorted((a, b) -> {
9090
try {
9191
boolean v1isStatic = a.isStatic();
@@ -102,11 +102,16 @@ public static List<Variable> listFieldVariables(ObjectReference obj, boolean inc
102102
return -1;
103103
}
104104
}).collect(Collectors.toList());
105-
fields.forEach(f -> {
106-
Variable var = new Variable(f.name(), obj.getValue(f));
107-
var.field = f;
108-
res.add(var);
109-
});
105+
106+
bulkFetchValues(fields, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage -> {
107+
Map<Field, Value> fieldValues = obj.getValues(currentPage);
108+
for (Field currentField : currentPage) {
109+
Variable var = new Variable(currentField.name(), fieldValues.get(currentField));
110+
var.field = currentField;
111+
res.add(var);
112+
}
113+
}));
114+
110115
return res;
111116
}
112117

@@ -155,13 +160,18 @@ public static List<Variable> listLocalVariables(StackFrame stackFrame) throws Ab
155160
return res;
156161
}
157162
try {
158-
List<LocalVariable> localVariables = stackFrame.visibleVariables();
159-
Map<LocalVariable, Value> values = stackFrame.getValues(localVariables);
160-
for (LocalVariable localVariable : localVariables) {
161-
Variable var = new Variable(localVariable.name(), values.get(localVariable));
162-
var.local = localVariable;
163-
res.add(var);
164-
}
163+
List<LocalVariable> visibleVariables = stackFrame.visibleVariables();
164+
// When using the API StackFrame.getValues() to batch fetch the variable values, the JDI
165+
// probably throws timeout exception if the variables to be passed at one time are large.
166+
// So use paging to fetch the values in chunks.
167+
bulkFetchValues(visibleVariables, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage -> {
168+
Map<LocalVariable, Value> values = stackFrame.getValues(currentPage);
169+
for (LocalVariable localVariable : currentPage) {
170+
Variable var = new Variable(localVariable.name(), values.get(localVariable));
171+
var.local = localVariable;
172+
res.add(var);
173+
}
174+
}));
165175
} catch (AbsentInformationException ex) {
166176
// avoid listing variable on native methods
167177

@@ -228,11 +238,16 @@ public static Variable getThisVariable(StackFrame stackFrame) {
228238
public static List<Variable> listStaticVariables(StackFrame stackFrame) {
229239
List<Variable> res = new ArrayList<>();
230240
ReferenceType type = stackFrame.location().declaringType();
231-
type.allFields().stream().filter(TypeComponent::isStatic).forEach(field -> {
232-
Variable staticVar = new Variable(field.name(), type.getValue(field));
233-
staticVar.field = field;
234-
res.add(staticVar);
235-
});
241+
List<Field> fields = type.allFields().stream().filter(TypeComponent::isStatic).collect(Collectors.toList());
242+
bulkFetchValues(fields, DebugSettings.getCurrent().limitOfVariablesPerJdwpRequest, (currentPage -> {
243+
Map<Field, Value> fieldValues = type.getValues(currentPage);
244+
for (Field currentField : currentPage) {
245+
Variable var = new Variable(currentField.name(), fieldValues.get(currentField));
246+
var.field = currentField;
247+
res.add(var);
248+
}
249+
}));
250+
236251
return res;
237252
}
238253

@@ -289,6 +304,18 @@ public static String getEvaluateName(String name, String containerName, boolean
289304
return String.format("%s.%s", containerName, name);
290305
}
291306

307+
private static <T> void bulkFetchValues(List<T> elements, int numberPerPage, Consumer<List<T>> consumer) {
308+
int size = elements.size();
309+
numberPerPage = numberPerPage < 1 ? 1 : numberPerPage;
310+
int page = size / numberPerPage + Math.min(size % numberPerPage, 1);
311+
for (int i = 0; i < page; i++) {
312+
int pageStart = i * numberPerPage;
313+
int pageEnd = Math.min(pageStart + numberPerPage, size);
314+
List<T> currentPage = elements.subList(pageStart, pageEnd);
315+
consumer.accept(currentPage);
316+
}
317+
}
318+
292319
private VariableUtils() {
293320

294321
}

0 commit comments

Comments
 (0)