This is an automated email from the ASF dual-hosted git repository.
entl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 8a328a3 Performance optimizations to speed up remote debugging.
8a328a3 is described below
commit 8a328a371bed55f5f488bb3d0d89ae8a8c5b84bc
Author: Martin Entlicher <[email protected]>
AuthorDate: Tue Sep 7 12:13:34 2021 +0200
Performance optimizations to speed up remote debugging.
---
.../debugger/jpda/DeadlockDetectorImpl.java | 44 ++--
.../debugger/jpda/JDIExceptionReporter.java | 16 +-
.../modules/debugger/jpda/JPDADebuggerImpl.java | 223 ++++++++++++---------
.../modules/debugger/jpda/SingleThreadWatcher.java | 5 +-
.../debugger/jpda/actions/StepActionProvider.java | 3 +
.../debugger/jpda/models/JPDAThreadGroupImpl.java | 11 +-
.../debugger/jpda/models/JPDAThreadImpl.java | 122 ++++++-----
.../debugger/jpda/models/ObjectTranslation.java | 115 +++++++++--
.../modules/debugger/jpda/models/ThreadsCache.java | 17 --
java/java.lsp.server/manifest.mf | 1 +
.../modules/debugger/jpda/Bundle.properties | 18 ++
.../lsp/server/debugging/NbProtocolServer.java | 154 +++++++-------
.../variables/NbVariablesRequestHandler.java | 104 +++++-----
.../org/netbeans/modules/java/lsp/server/layer.xml | 30 +++
14 files changed, 529 insertions(+), 334 deletions(-)
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/DeadlockDetectorImpl.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/DeadlockDetectorImpl.java
index 8018fe8..9f1a938 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/DeadlockDetectorImpl.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/DeadlockDetectorImpl.java
@@ -34,15 +34,15 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+
import org.netbeans.api.debugger.jpda.CallStackFrame;
import org.netbeans.api.debugger.jpda.DeadlockDetector;
import org.netbeans.api.debugger.jpda.JPDADebugger;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.api.debugger.jpda.ObjectVariable;
-import org.netbeans.api.debugger.jpda.This;
import org.netbeans.modules.debugger.jpda.expr.JDIVariable;
import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
-import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.RequestProcessor.Task;
import org.openide.util.WeakListeners;
@@ -52,6 +52,9 @@ import org.openide.util.WeakSet;
*
* @author martin
*/
[email protected]({
+ "USE_JPDA_DEADLOCK_DETECTOR=true"
+})
public class DeadlockDetectorImpl extends DeadlockDetector implements
PropertyChangeListener {
private final Set<JPDAThread> suspendedThreads = new WeakSet<JPDAThread>();
@@ -61,6 +64,9 @@ public class DeadlockDetectorImpl extends DeadlockDetector
implements PropertyCh
private Map<Long, Node> monitorToNode;
DeadlockDetectorImpl(JPDADebugger debugger) {
+ if (!Boolean.valueOf(Bundle.USE_JPDA_DEADLOCK_DETECTOR())) {
+ return;
+ }
debugger.addPropertyChangeListener(this);
List<JPDAThread> threads =
debugger.getThreadsCollector().getAllThreads();
for (JPDAThread thread : threads) {
@@ -85,24 +91,26 @@ public class DeadlockDetectorImpl extends DeadlockDetector
implements PropertyCh
final Set<JPDAThread> tempSuspThreads;
synchronized(suspendedThreads) {
suspendedThreads.add(thread);
- tempSuspThreads = new
HashSet<JPDAThread>(suspendedThreads);
+ tempSuspThreads = suspendedThreads.size() > 1 ? new
HashSet<JPDAThread>(suspendedThreads) : null;
}
- final Task[] taskPtr = new Task[] { null };
- synchronized (unfinishedTasks) {
- Task task = rp.post(new Runnable() {
- public void run() {
- Set<Deadlock> deadlocks;
- deadlocks = findDeadlockedThreads(tempSuspThreads);
- if (deadlocks != null) {
- setDeadlocks(deadlocks);
- }
- synchronized (unfinishedTasks) {
- unfinishedTasks.remove(taskPtr[0]);
+ if (tempSuspThreads != null) {
+ final Task[] taskPtr = new Task[] { null };
+ synchronized (unfinishedTasks) {
+ Task task = rp.post(new Runnable() {
+ public void run() {
+ Set<Deadlock> deadlocks;
+ deadlocks =
findDeadlockedThreads(tempSuspThreads);
+ if (deadlocks != null) {
+ setDeadlocks(deadlocks);
+ }
+ synchronized (unfinishedTasks) {
+ unfinishedTasks.remove(taskPtr[0]);
+ }
}
- }
- });
- unfinishedTasks.add(task);
- taskPtr[0] = task;
+ });
+ unfinishedTasks.add(task);
+ taskPtr[0] = task;
+ }
}
} else {
synchronized (suspendedThreads) {
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JDIExceptionReporter.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JDIExceptionReporter.java
index a5c75ab..947f3a5 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JDIExceptionReporter.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JDIExceptionReporter.java
@@ -31,7 +31,8 @@ import org.openide.util.Exceptions;
*/
public final class JDIExceptionReporter {
- private static final Logger logger =
Logger.getLogger("org.netbeans.modules.debugger.jpda.jdi"); // NOI18N
+ private static final Logger LOGGER =
Logger.getLogger("org.netbeans.modules.debugger.jpda.jdi"); // NOI18N
+ private static final Level LOGLEVEL = Level.FINER;
public static final Object RET_VOID = new Object();
@@ -46,14 +47,14 @@ public final class JDIExceptionReporter {
}
public static boolean isLoggable() {
- return logger.isLoggable(java.util.logging.Level.FINER);
+ return LOGGER.isLoggable(java.util.logging.Level.SEVERE);
}
public static void logCallStart(String className, String methodName,
String msg, Object[] args) {
try {
- logger.log(java.util.logging.Level.FINER, msg, args);
+ LOGGER.log(LOGLEVEL, msg, args);
} catch (Exception exc) {
- logger.log(java.util.logging.Level.FINER, "Logging of
"+className+"."+methodName+"() threw exception:", exc);
+ LOGGER.log(LOGLEVEL, "Logging of "+className+"."+methodName+"()
threw exception:", exc);
}
callStartTime.set(System.nanoTime());
}
@@ -62,10 +63,13 @@ public final class JDIExceptionReporter {
long t2 = System.nanoTime();
long t1 = callStartTime.get();
callStartTime.remove();
- logger.log(java.util.logging.Level.FINER, " {0}.{1}()
returned after {2} ns, return value = {3}",
+ LOGGER.log(LOGLEVEL, " {0}.{1}() returned after {2} ns,
return value = {3}",
new Object[] {className, methodName, (t2 - t1), (RET_VOID
== ret) ? "void" : ret});
if (ret instanceof Throwable) {
- logger.log(Level.FINER, "", (Throwable) ret);
+ LOGGER.log(LOGLEVEL, "", (Throwable) ret);
+ }
+ if ((t2 - t1) > 100_000_000) {
+ LOGGER.log(LOGLEVEL, "", new RuntimeException("TooLongCall"));
}
}
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
index c84c44d..1085334 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/JPDADebuggerImpl.java
@@ -60,8 +60,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.Refreshable;
@@ -107,10 +110,12 @@ import
org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper
import org.netbeans.modules.debugger.jpda.jdi.IntegerValueWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import
org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.LocalVariableWrapper;
import org.netbeans.modules.debugger.jpda.jdi.MirrorWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.StackFrameWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.TypeComponentWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ValueWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
@@ -184,6 +189,8 @@ public class JPDADebuggerImpl extends JPDADebugger {
private final Map<Long, String> markedObjects = new
LinkedHashMap<Long, String>();
private final Map<String, ObjectVariable> markedObjectLabels = new
LinkedHashMap<String, ObjectVariable>();
private final Map<ClassType, List> allInterfacesMap = new
WeakHashMap<ClassType, List>();
+ private static final int RP_THROUGHPUT = 10;
+ private final RequestProcessor rpCreateThreads = new
RequestProcessor(JPDADebuggerImpl.class.getName()+"_newJPDAThreads",
RP_THROUGHPUT); // NOI18N
private StackFrame altCSF = null; //PATCH 48174
@@ -1163,11 +1170,21 @@ public class JPDADebuggerImpl extends JPDADebugger {
}
}
+ private Method stringLengthMethod;
+ private Method stringSubstringMethod;
+ private final Object stringMethodsLock = new Object();
+
private Value cutLength(StringReference sr, int maxLength, ThreadReference
tr) throws InvalidExpressionException {
try {
Method stringLengthMethod;
- stringLengthMethod = ClassTypeWrapper.concreteMethodByName(
- (ClassType) ValueWrapper.type (sr), "length", "()I");
// NOI18N
+ synchronized (stringMethodsLock) {
+ stringLengthMethod = this.stringLengthMethod;
+ if (stringLengthMethod == null) {
+ stringLengthMethod = ClassTypeWrapper.concreteMethodByName(
+ (ClassType) ValueWrapper.type (sr), "length",
"()I"); // NOI18N
+ this.stringLengthMethod = stringLengthMethod;
+ }
+ }
List<Value> emptyArgs = Collections.emptyList();
IntegerValue lengthValue = (IntegerValue)
org.netbeans.modules.debugger.jpda.expr.TreeEvaluator.
invokeVirtual (
@@ -1178,8 +1195,15 @@ public class JPDADebuggerImpl extends JPDADebugger {
this
);
if (IntegerValueWrapper.value(lengthValue) > maxLength) {
- Method subStringMethod =
ClassTypeWrapper.concreteMethodByName(
- (ClassType) ValueWrapper.type (sr), "substring",
"(II)Ljava/lang/String;"); // NOI18N
+ Method subStringMethod;
+ synchronized (stringMethodsLock) {
+ subStringMethod = this.stringSubstringMethod;
+ if (subStringMethod == null) {
+ subStringMethod =
ClassTypeWrapper.concreteMethodByName(
+ (ClassType) ValueWrapper.type (sr),
"substring", "(II)Ljava/lang/String;"); // NOI18N
+ this.stringSubstringMethod = subStringMethod;
+ }
+ }
if (subStringMethod != null) {
sr = (StringReference)
org.netbeans.modules.debugger.jpda.expr.TreeEvaluator.
invokeVirtual (
@@ -1201,34 +1225,25 @@ public class JPDADebuggerImpl extends JPDADebugger {
}
public static String getGenericSignature (TypeComponent component) {
- if (tcGenericSignatureMethod == null) {
- return null;
- }
try {
- return (String) tcGenericSignatureMethod.invoke
- (component, new Object[0]);
- } catch (IllegalAccessException e) {
- Exceptions.printStackTrace(e);
- return null; // should not happen
- } catch (InvocationTargetException e) {
- Exceptions.printStackTrace(e);
- return null; // should not happen
+ return TypeComponentWrapper.genericSignature(component);
+ } catch (InternalExceptionWrapper ex) {
+ Exceptions.printStackTrace(ex);
+ } catch (VMDisconnectedExceptionWrapper ex) {
+ // Disconnected
}
+ return null;
}
public static String getGenericSignature (LocalVariable component) {
- if (lvGenericSignatureMethod == null) {
- return null;
- }
try {
- return (String) lvGenericSignatureMethod.invoke(component, new
Object[0]);
- } catch (IllegalAccessException e) {
- Exceptions.printStackTrace(e);
- return null; // should not happen
- } catch (InvocationTargetException e) {
- Exceptions.printStackTrace(e);
- return null; // should not happen
+ return LocalVariableWrapper.genericSignature(component);
+ } catch (InternalExceptionWrapper ex) {
+ Exceptions.printStackTrace(ex);
+ } catch (VMDisconnectedExceptionWrapper ex) {
+ // Disconnected
}
+ return null;
}
public VirtualMachine getVirtualMachine () {
@@ -1270,7 +1285,6 @@ public class JPDADebuggerImpl extends JPDADebugger {
canBeModified = null; // Reset the can be modified flag
}
- initGenericsSupport ();
EditorContextBridge.getContext().createTimeStamp(this);
@@ -1854,20 +1868,21 @@ public class JPDADebuggerImpl extends JPDADebugger {
}
public void notifyToBeResumedAll() {
- Collection threads = threadsTranslation.getTranslated();
- for (Iterator it = threads.iterator(); it.hasNext(); ) {
- Object threadOrGroup = it.next();
- if (threadOrGroup instanceof JPDAThreadImpl) {
- int status = ((JPDAThreadImpl) threadOrGroup).getState();
+ List<? extends JPDAThreadImpl> threads =
threadsTranslation.getTranslated((o) -> (o instanceof JPDAThreadImpl) ?
(JPDAThreadImpl) o : null);
+ int n = threads.size();
+ if (n > 0) {
+ processInParallel(n, (i) -> {
+ JPDAThreadImpl thread = threads.get(i);
+ int status = thread.getState();
boolean invalid = (status == JPDAThread.STATE_NOT_STARTED ||
status == JPDAThread.STATE_UNKNOWN ||
status == JPDAThread.STATE_ZOMBIE);
if (!invalid) {
- ((JPDAThreadImpl) threadOrGroup).notifyToBeResumed();
+ thread.notifyToBeResumed();
} else if (status == JPDAThread.STATE_UNKNOWN || status ==
JPDAThread.STATE_ZOMBIE) {
- threadsTranslation.remove(((JPDAThreadImpl)
threadOrGroup).getThreadReference());
+ threadsTranslation.remove(thread.getThreadReference());
}
- }
+ });
}
}
@@ -1876,21 +1891,22 @@ public class JPDADebuggerImpl extends JPDADebugger {
}
public void notifyToBeResumedAllNoFire(Set<ThreadReference>
ignoredThreads) {
- Collection threads = threadsTranslation.getTranslated();
- for (Iterator it = threads.iterator(); it.hasNext(); ) {
- Object threadOrGroup = it.next();
- if (threadOrGroup instanceof JPDAThreadImpl &&
- (ignoredThreads == null ||
!ignoredThreads.contains(threadOrGroup))) {
- int status = ((JPDAThreadImpl) threadOrGroup).getState();
+ List<? extends JPDAThreadImpl> threads =
threadsTranslation.getTranslated((o) ->
+ (o instanceof JPDAThreadImpl && (ignoredThreads == null ||
!ignoredThreads.contains(o))) ? (JPDAThreadImpl) o : null);
+ int n = threads.size();
+ if (n > 0) {
+ processInParallel(n, (i) -> {
+ JPDAThreadImpl thread = threads.get(i);
+ int status = thread.getState();
boolean invalid = (status == JPDAThread.STATE_NOT_STARTED ||
status == JPDAThread.STATE_UNKNOWN ||
status == JPDAThread.STATE_ZOMBIE);
if (!invalid) {
- ((JPDAThreadImpl) threadOrGroup).notifyToBeResumedNoFire();
+ thread.notifyToBeResumedNoFire();
} else if (status == JPDAThread.STATE_UNKNOWN || status ==
JPDAThread.STATE_ZOMBIE) {
- threadsTranslation.remove(((JPDAThreadImpl)
threadOrGroup).getThreadReference());
+ threadsTranslation.remove(thread.getThreadReference());
}
- }
+ });
}
}
@@ -1916,16 +1932,24 @@ public class JPDADebuggerImpl extends JPDADebugger {
// Re-fire the changes
@Override
public void propertyChange(PropertyChangeEvent evt) {
- String propertyName = evt.getPropertyName();
- if
(ThreadsCache.PROP_THREAD_STARTED.equals(propertyName)) {
- firePropertyChange(PROP_THREAD_STARTED, null,
getThread((ThreadReference) evt.getNewValue()));
- }
- if
(ThreadsCache.PROP_THREAD_DIED.equals(propertyName)) {
- firePropertyChange(PROP_THREAD_DIED,
getThread((ThreadReference) evt.getOldValue()), null);
- }
- if
(ThreadsCache.PROP_GROUP_ADDED.equals(propertyName)) {
- firePropertyChange(PROP_THREAD_GROUP_ADDED, null,
getThreadGroup((ThreadGroupReference) evt.getNewValue()));
- }
+ rpCreateThreads.post(() -> {
+ String propertyName = evt.getPropertyName();
+ if
(ThreadsCache.PROP_THREAD_STARTED.equals(propertyName)) {
+ JPDAThreadImpl thread =
getThread((ThreadReference) evt.getNewValue());
+ if
(!thread.getName().contains(ThreadsCache.THREAD_NAME_FILTER_PATTERN)) {
+ firePropertyChange(PROP_THREAD_STARTED,
null, thread);
+ }
+ }
+ if
(ThreadsCache.PROP_THREAD_DIED.equals(propertyName)) {
+ JPDAThreadImpl thread =
getThread((ThreadReference) evt.getOldValue());
+ if
(!thread.getName().contains(ThreadsCache.THREAD_NAME_FILTER_PATTERN)) {
+ firePropertyChange(PROP_THREAD_DIED,
thread, null);
+ }
+ }
+ if
(ThreadsCache.PROP_GROUP_ADDED.equals(propertyName)) {
+ firePropertyChange(PROP_THREAD_GROUP_ADDED,
null, getThreadGroup((ThreadGroupReference) evt.getNewValue()));
+ }
+ });
}
});
}
@@ -1961,13 +1985,63 @@ public class JPDADebuggerImpl extends JPDADebugger {
}
}
int n = threadList.size();
- List<JPDAThread> threads = new ArrayList<JPDAThread>(n);
- for (int i = 0; i < n; i++) {
- threads.add(getThread(threadList.get(i)));
+ final List<ThreadReference> tl = threadList;
+
+ // Create threads in parallel.
+ // Constructor of JPDAThreadImpl calls getName() and getStatus(),
which take time on slow connection during remote debugging.
+ List<JPDAThread> threads = new ArrayList<>(n);
+ if (n > 0) {
+ AtomicBoolean filteredThreads = new AtomicBoolean(false);
+ processInParallel(n, (i) -> {
+ JPDAThreadImpl thread = getThread(tl.get(i));
+ if
(!thread.getName().contains(ThreadsCache.THREAD_NAME_FILTER_PATTERN)) {
+ threads.set(i, thread);
+ } else {
+ threads.set(i, null);
+ filteredThreads.set(true);
+ }
+ });
+ if (filteredThreads.get()) {
+ Iterator<JPDAThread> it = threads.iterator();
+ while (it.hasNext()) {
+ if (it.next() == null) {
+ it.remove();
+ }
+ }
+ }
}
return Collections.unmodifiableList(threads);
}
+ private void processInParallel(int n, Consumer<Integer> task) {
+ // Run by chunks:
+ int chunks = Math.min(RP_THROUGHPUT, n);
+ int chn = n / chunks + ((n % chunks) > 0 ? 1 : 0);
+ class TranslateThreads implements Runnable {
+
+ private final int ch;
+
+ private TranslateThreads(int ch) {
+ this.ch = ch;
+ }
+
+ @Override
+ public void run() {
+ int i0 = ch*chn;
+ for (int i = i0; i < i0 + chn && i < n; i++) {
+ task.accept(i);
+ }
+ }
+ }
+ RequestProcessor.Task[] tasks = new RequestProcessor.Task[chunks];
+ for (int ch = 0; ch < chunks; ch++) {
+ tasks[ch] = rpCreateThreads.post(new TranslateThreads(ch));
+ }
+ for (int ch = 0; ch < chunks; ch++) {
+ tasks[ch].waitFinished();
+ }
+ }
+
public JPDAThreadGroup[] getTopLevelThreadGroups() {
ThreadsCache tc = getThreadsCache();
if (tc == null) {
@@ -2135,43 +2209,6 @@ public class JPDADebuggerImpl extends JPDADebugger {
// private helper methods
..................................................
- private static final java.util.regex.Pattern jvmVersionPattern =
- java.util.regex.Pattern.compile
("(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?(-\\w+)?");
- private static java.lang.reflect.Method tcGenericSignatureMethod;
- private static java.lang.reflect.Method lvGenericSignatureMethod;
-
-
- private void initGenericsSupport () {
- tcGenericSignatureMethod = null;
- if (Bootstrap.virtualMachineManager ().minorInterfaceVersion () >= 5) {
- VirtualMachine vm;
- synchronized (virtualMachineLock) {
- vm = virtualMachine;
- }
- if (vm == null) {
- return ;
- }
- try {
- java.util.regex.Matcher m =
jvmVersionPattern.matcher(VirtualMachineWrapper.version(vm));
- if (m.matches ()) {
- int minor = Integer.parseInt (m.group (2));
- if (minor >= 5) {
- try {
- tcGenericSignatureMethod = TypeComponent.class.
- getMethod ("genericSignature", new Class [0]);
- lvGenericSignatureMethod = LocalVariable.class.
- getMethod ("genericSignature", new Class [0]);
- } catch (NoSuchMethodException e) {
- // the method is not available, ignore generics
- }
- }
- }
- } catch (InternalExceptionWrapper e) {
- } catch (VMDisconnectedExceptionWrapper e) {
- }
- }
- }
-
private PropertyChangeEvent setStateNoFire (int state) {
int o;
synchronized (stateLock) {
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/SingleThreadWatcher.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/SingleThreadWatcher.java
index f5d6bcf..df2dee2 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/SingleThreadWatcher.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/SingleThreadWatcher.java
@@ -32,11 +32,14 @@ public class SingleThreadWatcher implements Runnable {
private static final int DELAY = 3000;
- private JPDAThreadImpl t;
+ private final JPDAThreadImpl t;
private Task watchTask;
public SingleThreadWatcher(JPDAThreadImpl t) {
this.t = t;
+ if (!Boolean.valueOf(Bundle.USE_JPDA_DEADLOCK_DETECTOR())) {
+ return;
+ }
//System.err.println("\nnew SingleThreadWatcher("+t+")");
watchTask = t.getDebugger().getRequestProcessor().post(this, DELAY);
}
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
index 44bcdf0..a770ea5 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/actions/StepActionProvider.java
@@ -605,6 +605,9 @@ implements Executor {
return 0;
} catch (InternalExceptionWrapper iex) {
return 0;
+ } catch (IndexOutOfBoundsException iobe) {
+ // no frames on stack
+ return 0;
} catch (InvalidStackFrameExceptionWrapper iex) {
return 0;
} catch (ObjectCollectedExceptionWrapper iex) {
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadGroupImpl.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadGroupImpl.java
index c42257f..1a05a1a 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadGroupImpl.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadGroupImpl.java
@@ -24,6 +24,7 @@ import com.sun.jdi.ThreadGroupReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VMDisconnectedException;
+import java.util.Arrays;
import java.util.List;
import org.netbeans.api.debugger.jpda.JPDAThread;
@@ -84,8 +85,14 @@ public class JPDAThreadGroupImpl implements JPDAThreadGroup {
List<ThreadReference> l = tc.getThreads(tgr);
int i, k = l.size ();
JPDAThreadImpl[] ts = new JPDAThreadImpl[k];
- for (i = 0; i < k; i++) {
- ts [i] = debugger.getThread(l.get (i));
+ i = 0;
+ for (ThreadReference t : l) {
+ JPDAThreadImpl thread = debugger.getThread(t);
+ if
(thread.getName().contains(ThreadsCache.THREAD_NAME_FILTER_PATTERN)) {
+ ts = Arrays.copyOf(ts, k - 1);
+ } else {
+ ts[i++] = thread;
+ }
}
return ts;
}
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadImpl.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadImpl.java
index 0e4b4f0..c430d9b 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadImpl.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/JPDAThreadImpl.java
@@ -626,35 +626,31 @@ public final class JPDAThreadImpl implements JPDAThread,
Customizer, BeanContext
}
List l;
CallStackFrame[] theCachedFrames = null;
- int max;
- synchronized (cachedFramesLock) {
- if (stackDepth < 0) {
- stackDepth =
ThreadReferenceWrapper.frameCount(threadReference);
- }
- max = stackDepth;
+ CallStackFrame[] frames;
+ // synchronize the whole retrieval of frames to prevent from
concurrent retrieval of the same frames.
+ synchronized (cachedFramesLock) {
+ if (stackDepth < 0) {
+ stackDepth =
ThreadReferenceWrapper.frameCount(threadReference);
}
+ int max = stackDepth;
if (to < 0) to = max; // Fight strange negative frame counts
from http://www.netbeans.org/issues/show_bug.cgi?id=162448
from = Math.min(from, max);
to = Math.min(to, max);
- if (to - from > 1) { /*TODO: Frame caching cause problems
with invalid frames. Some fix is necessary...
- * as a workaround, frames caching is disabled.*/
- synchronized (cachedFramesLock) {
- if (from == cachedFramesFrom && to == cachedFramesTo) {
- return cachedFrames;
- }
- if (from >= cachedFramesFrom && to <= cachedFramesTo) {
- // TODO: Arrays.copyOfRange(cachedFrames, from -
cachedFramesFrom, to);
- return copyOfRange(cachedFrames, from -
cachedFramesFrom, to - cachedFramesFrom);
- }
- if (cachedFramesFrom >= 0 && cachedFramesTo >
cachedFramesFrom) {
- int length = to - from;
- theCachedFrames = new CallStackFrame[length];
- for (int i = 0; i < length; i++) {
- if (i >= cachedFramesFrom && i <
cachedFramesTo) {
- theCachedFrames[i] = cachedFrames[i -
cachedFramesFrom];
- } else {
- theCachedFrames[i] = null;
- }
+ if (to - from > 1) {
+ if (from == cachedFramesFrom && to == cachedFramesTo) {
+ return cachedFrames;
+ }
+ if (from >= cachedFramesFrom && to <= cachedFramesTo) {
+ return Arrays.copyOfRange(cachedFrames, from -
cachedFramesFrom, to - cachedFramesFrom);
+ }
+ if (cachedFramesFrom >= 0 && cachedFramesTo >
cachedFramesFrom) {
+ int length = to - from;
+ theCachedFrames = new CallStackFrame[length];
+ for (int i = 0; i < length; i++) {
+ if (i >= cachedFramesFrom && i < cachedFramesTo) {
+ theCachedFrames[i] = cachedFrames[i -
cachedFramesFrom];
+ } else {
+ theCachedFrames[i] = null;
}
}
}
@@ -694,25 +690,22 @@ public final class JPDAThreadImpl implements JPDAThread,
Customizer, BeanContext
if (l == null) {
l = java.util.Collections.emptyList();
}
- int n = l.size();
- CallStackFrame[] frames = new CallStackFrame[n];
- for (int i = 0; i < n; i++) {
- if (theCachedFrames != null && theCachedFrames[i] != null) {
- frames[i] = theCachedFrames[i];
- } else {
- frames[i] = new CallStackFrameImpl(this, (StackFrame)
l.get(i), from + i, debugger);
- }
- if (from == 0 && i == 0 && currentOperation != null) {
- ((CallStackFrameImpl)
frames[i]).setCurrentOperation(currentOperation);
+ int n = l.size();
+ frames = new CallStackFrame[n];
+ for (int i = 0; i < n; i++) {
+ if (theCachedFrames != null && theCachedFrames[i] != null)
{
+ frames[i] = theCachedFrames[i];
+ } else {
+ frames[i] = new CallStackFrameImpl(this, (StackFrame)
l.get(i), from + i, debugger);
+ }
+ if (from == 0 && i == 0 && currentOperation != null) {
+ ((CallStackFrameImpl)
frames[i]).setCurrentOperation(currentOperation);
+ }
}
+ cachedFrames = frames;
+ cachedFramesFrom = from;
+ cachedFramesTo = to;
}
- //if (to - from > 1) {
- synchronized (cachedFramesLock) {
- cachedFrames = frames;
- cachedFramesFrom = from;
- cachedFramesTo = to;
- }
- //}
return frames;
} catch (IncompatibleThreadStateException ex) {
String msg = ex.getLocalizedMessage() + " " + getThreadStateLog();
@@ -745,17 +738,6 @@ public final class JPDAThreadImpl implements JPDAThread,
Customizer, BeanContext
}
}
- private static CallStackFrame[] copyOfRange(CallStackFrame[] original, int
from, int to) {
- // TODO: Use Arrays.copyOfRange(cachedFrames, from, to);
- int newLength = to - from;
- if (newLength < 0)
- throw new IllegalArgumentException(from + " > " + to);
- CallStackFrame[] copy = new CallStackFrame[newLength];
- System.arraycopy(original, from, copy, 0,
- Math.min(original.length - from, newLength));
- return copy;
- }
-
private void cleanCachedFrames() {
synchronized (cachedFramesLock) {
stackDepth = -1;
@@ -1229,6 +1211,10 @@ public final class JPDAThreadImpl implements JPDAThread,
Customizer, BeanContext
}
public PropertyChangeEvent notifySuspended(boolean doFire, boolean
explicitelyPaused) {
+ return notifySuspended(doFire, explicitelyPaused, true);
+ }
+
+ private PropertyChangeEvent notifySuspended(boolean doFire, boolean
explicitelyPaused, boolean verifyStatusAndName) {
if (loggerS.isLoggable(Level.FINE)) {
loggerS.fine("["+threadName+"]: "+"notifySuspended(doFire =
"+doFire+", explicitelyPaused = "+explicitelyPaused+")");
}
@@ -1246,22 +1232,26 @@ public final class JPDAThreadImpl implements
JPDAThread, Customizer, BeanContext
}
return null;
}
- try {
- suspendCount =
ThreadReferenceWrapper.suspendCount(threadReference);
- threadName = ThreadReferenceWrapper.name(threadReference);
- } catch (IllegalThreadStateExceptionWrapper ex) {
- return null; // Thrown when thread has exited
- } catch (ObjectCollectedExceptionWrapper ocex) {
- return null; // The thread is gone
- } catch (VMDisconnectedExceptionWrapper ex) {
- return null; // The VM is gone
- } catch (InternalExceptionWrapper ex) {
- return null; // Something is gone
+ if (verifyStatusAndName) {
+ try {
+ suspendCount =
ThreadReferenceWrapper.suspendCount(threadReference);
+ threadName = ThreadReferenceWrapper.name(threadReference);
+ } catch (IllegalThreadStateExceptionWrapper ex) {
+ return null; // Thrown when thread has exited
+ } catch (ObjectCollectedExceptionWrapper ocex) {
+ return null; // The thread is gone
+ } catch (VMDisconnectedExceptionWrapper ex) {
+ return null; // The VM is gone
+ } catch (InternalExceptionWrapper ex) {
+ return null; // Something is gone
+ }
+ } else {
+ suspendCount = 1;
}
//System.err.println("notifySuspended("+getName()+") suspendCount
= "+suspendCount+", var suspended = "+suspended);
suspendedNoFire = false;
debugger.setCurrentSuspendedNoFireThread(null);
- if ((!suspended || suspendedNoFire && doFire) &&
isThreadSuspended()) {
+ if ((!suspended || suspendedNoFire && doFire) &&
(!verifyStatusAndName || isThreadSuspended())) {
//System.err.println(" setting suspended = true");
suspended = true;
suspendedToFire = Boolean.TRUE;
@@ -1479,7 +1469,7 @@ public final class JPDAThreadImpl implements JPDAThread,
Customizer, BeanContext
}
// Do not notify suspended state when was already unsuspended when
started invoking.
if (!wasUnsuspendedStateWhenInvoking) {
- PropertyChangeEvent evt = notifySuspended(false, false);
+ PropertyChangeEvent evt = notifySuspended(false, false, false);
if (evt != null) {
evt.setPropagationId("methodInvoke"); // NOI18N
pch.firePropertyChange(evt);
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ObjectTranslation.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ObjectTranslation.java
index cd96fa7..9d05e6c 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ObjectTranslation.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ObjectTranslation.java
@@ -30,14 +30,20 @@ import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Function;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.openide.util.Exceptions;
/**
* Helps to translate one tree to another.
@@ -140,7 +146,8 @@ public final class ObjectTranslation {
}
/**
- * Translates a debuggee Mirror to a wrapper object.
+ * Translates a debuggee Mirror to a wrapper object. It allows thread safe
+ * concurrent translation.
*
* @param o the Mirror object in the debuggee
* @return translated object or <code>null</code> when the argument
@@ -148,13 +155,28 @@ public final class ObjectTranslation {
*/
public Object translate (Mirror o) {
Object r = null;
+ boolean create = false;
synchronized (cache) {
- WeakReference wr = cache.get (o);
- if (wr != null)
- r = wr.get ();
+ WeakReference wr = cache.get(o);
+ if (wr != null) {
+ r = wr.get();
+ }
if (r == null) {
- r = createTranslation (o);
- cache.put (o, new WeakReference<Object>(r));
+ r = new TranslationFuture();
+ create = true;
+ cache.put (o, new WeakReference<>(r));
+ }
+ }
+ if (r instanceof TranslationFuture) {
+ TranslationFuture tf = (TranslationFuture) r;
+ if (create) {
+ r = createTranslation(o);
+ tf.complete(r);
+ synchronized (cache) {
+ cache.put(o, new WeakReference<>(r));
+ }
+ } else {
+ r = tf.get();
}
}
return r;
@@ -170,9 +192,13 @@ public final class ObjectTranslation {
public Object translateExisting(Mirror o) {
Object r = null;
synchronized (cache) {
- WeakReference wr = cache.get (o);
- if (wr != null)
- r = wr.get ();
+ WeakReference wr = cache.get(o);
+ if (wr != null) {
+ r = wr.get();
+ if (r instanceof TranslationFuture) {
+ r = ((TranslationFuture) r).get();
+ }
+ }
}
return r;
}
@@ -187,6 +213,9 @@ public final class ObjectTranslation {
for (Iterator it = references.iterator(); it.hasNext(); ) {
WeakReference wr = (WeakReference) it.next();
Object r = wr.get();
+ if (r instanceof TranslationFuture) {
+ r = ((TranslationFuture) r).get();
+ }
if (r != null) {
translated.add(r);
}
@@ -196,7 +225,35 @@ public final class ObjectTranslation {
}
/**
- * Translates a debuggee Mirror to a wrapper object.
+ * Get existing translated objects.
+ *
+ * @param <T> the type of translated objects we're looking for
+ * @param test a test function that returns non-null objects of type
+ * <code>T</code> that we're looking for
+ * @return a list of translated objects selected by the test function.
+ */
+ public <T> List<? extends T> getTranslated(Function<Object, T> test) {
+ List<T> translated = new ArrayList<>();
+ synchronized (cache) {
+ Collection references = cache.values();
+ for (Iterator it = references.iterator(); it.hasNext(); ) {
+ WeakReference wr = (WeakReference) it.next();
+ Object r = wr.get();
+ if (r instanceof TranslationFuture) {
+ r = ((TranslationFuture) r).get();
+ }
+ T t;
+ if (r != null && (t = test.apply(r)) != null) {
+ translated.add(t);
+ }
+ }
+ }
+ return translated;
+ }
+
+ /**
+ * Translates a debuggee Mirror to a wrapper object. It allows thread safe
+ * concurrent translation.
*
* @param o the Mirror object in the debuggee
* @param v an additional argument used for the translation
@@ -206,17 +263,32 @@ public final class ObjectTranslation {
public Object translate (Mirror o, Object v) {
Object r = null;
boolean verify = false;
+ boolean create = false;
synchronized (cache) {
- WeakReference wr = cache.get (o);
- if (wr != null)
- r = wr.get ();
+ WeakReference wr = cache.get(o);
+ if (wr != null) {
+ r = wr.get();
+ }
if (r == null) {
- r = createTranslation (o, v);
- cache.put (o, new WeakReference<Object>(r));
+ r = new TranslationFuture();
+ create = true;
+ cache.put(o, new WeakReference<>(r));
} else {
verify = true;
}
}
+ if (r instanceof TranslationFuture) {
+ TranslationFuture tf = (TranslationFuture) r;
+ if (create) {
+ r = createTranslation(o, v);
+ tf.complete(r);
+ synchronized (cache) {
+ cache.put(o, new WeakReference<>(r));
+ }
+ } else {
+ r = tf.get();
+ }
+ }
if (verify) {
verifyTranslation(r, o, v);
}
@@ -273,5 +345,16 @@ public final class ObjectTranslation {
public static ObjectTranslation createLocalsTranslation(JPDADebuggerImpl
debugger) {
return new ObjectTranslation(debugger, LOCALS_ID);
}
-
+
+ private static final class TranslationFuture extends
CompletableFuture<Object> {
+
+ @Override
+ public Object get() {
+ try {
+ return super.get();
+ } catch (ExecutionException | InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
}
diff --git
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ThreadsCache.java
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ThreadsCache.java
index f6c428a..d2fb2f9 100644
---
a/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ThreadsCache.java
+++
b/java/debugger.jpda/src/org/netbeans/modules/debugger/jpda/models/ThreadsCache.java
@@ -149,28 +149,12 @@ public class ThreadsCache implements Executor {
private synchronized void init() throws VMDisconnectedExceptionWrapper,
InternalExceptionWrapper {
allThreads = new
ArrayList<ThreadReference>(VirtualMachineWrapper.allThreads(vm));
- filterThreads(allThreads);
}
- private void filterThreads(List<ThreadReference> threads) {
- for (int i = 0; i < threads.size(); i++) {
- ThreadReference tr = threads.get(i);
- try {
- if
(ThreadReferenceWrapper.name(tr).contains(THREAD_NAME_FILTER_PATTERN)) {
- threads.remove(i);
- i--;
- }
- } catch (Exception ex) {
- // continue
- }
- }
- }
-
private void initGroups(ThreadGroupReference group) {
try {
List<ThreadGroupReference> groups = new
ArrayList<>(ThreadGroupReferenceWrapper.threadGroups0(group));
List<ThreadReference> threads = new
ArrayList<>(ThreadGroupReferenceWrapper.threads0(group));
- filterThreads(threads);
groupMap.put(group, groups);
threadMap.put(group, threads);
for (ThreadGroupReference g : groups) {
@@ -562,7 +546,6 @@ public class ThreadsCache implements Executor {
} catch (VMDisconnectedExceptionWrapper vmdex) {
return ;
}
- filterThreads(allThreadsNew);
newThreads = new ArrayList<ThreadReference>(allThreadsNew);
newThreads.removeAll(allThreads);
diff --git a/java/java.lsp.server/manifest.mf b/java/java.lsp.server/manifest.mf
index 32384b0..78c39b3 100644
--- a/java/java.lsp.server/manifest.mf
+++ b/java/java.lsp.server/manifest.mf
@@ -3,4 +3,5 @@ AutoUpdate-Show-In-Client: true
OpenIDE-Module: org.netbeans.modules.java.lsp.server
OpenIDE-Module-Implementation-Version: 1
OpenIDE-Module-Localizing-Bundle:
org/netbeans/modules/java/lsp/server/Bundle.properties
+OpenIDE-Module-Layer: org/netbeans/modules/java/lsp/server/layer.xml
diff --git
a/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-debugger-jpda.jar/org/netbeans/modules/debugger/jpda/Bundle.properties
b/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-debugger-jpda.jar/org/netbeans/modules/debugger/jpda/Bundle.properties
new file mode 100644
index 0000000..98e59d7
--- /dev/null
+++
b/java/java.lsp.server/nbcode/branding/modules/org-netbeans-modules-debugger-jpda.jar/org/netbeans/modules/debugger/jpda/Bundle.properties
@@ -0,0 +1,18 @@
+# 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.
+
+USE_JPDA_DEADLOCK_DETECTOR=false
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
index d22b718..08b133b 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
@@ -31,6 +31,8 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.lsp4j.debug.Capabilities;
@@ -100,6 +102,9 @@ import org.openide.util.RequestProcessor;
*/
public final class NbProtocolServer implements IDebugProtocolServer,
LspSession.ScheduledServer {
+ private static final Logger LOGGER =
Logger.getLogger(NbProtocolServer.class.getName());
+ private static final Level LOGLEVEL = Level.FINE;
+
private final DebugAdapterContext context;
private final NbLaunchRequestHandler launchRequestHandler = new
NbLaunchRequestHandler();
private final NbAttachRequestHandler attachRequestHandler = new
NbAttachRequestHandler();
@@ -107,6 +112,7 @@ public final class NbProtocolServer implements
IDebugProtocolServer, LspSession.
private final NbBreakpointsRequestHandler breakpointsRequestHandler = new
NbBreakpointsRequestHandler();
private final NbVariablesRequestHandler variablesRequestHandler = new
NbVariablesRequestHandler();
private final RequestProcessor evaluationRP = new
RequestProcessor(NbProtocolServer.class.getName(), 3);
+ private final RequestProcessor threadsRP = new
RequestProcessor(NbProtocolServer.class.getName(), 1);
private boolean initialized = false;
private Future<Void> runningServer;
@@ -284,72 +290,78 @@ public final class NbProtocolServer implements
IDebugProtocolServer, LspSession.
@Override
public CompletableFuture<StackTraceResponse>
stackTrace(StackTraceArguments args) {
- CompletableFuture<StackTraceResponse> future = new
CompletableFuture<>();
if (context.getDebugSession() == null) {
+ CompletableFuture<StackTraceResponse> future = new
CompletableFuture<>();
ErrorUtilities.completeExceptionally(future, "Debug Session
doesn't exist.", ResponseErrorCode.InvalidParams);
+ return future;
} else {
- List<StackFrame> result = new ArrayList<>();
- int cnt = 0;
- DVThread dvThread =
context.getThreadsProvider().getThread(args.getThreadId());
- if (dvThread != null) {
- cnt = dvThread.getFrameCount();
- int from = args.getStartFrame() != null ? args.getStartFrame()
: 0;
- int to = args.getLevels() != null ? from + args.getLevels() :
Integer.MAX_VALUE;
- List<DVFrame> stackFrames = dvThread.getFrames(from, to);
- for (DVFrame frame : stackFrames) {
- int frameId =
context.getThreadsProvider().getThreadObjects().addObject(args.getThreadId(),
new NbFrame(args.getThreadId(), frame));
- int line = frame.getLine();
- if (line < 0) { // unknown
- line = 0;
- }
- int column = frame.getColumn();
- if (column < 0) { // unknown
- column = 0;
- }
- StackFrame stackFrame = new StackFrame();
- stackFrame.setId(frameId);
- stackFrame.setName(frame.getName());
- URI sourceURI = frame.getSourceURI();
- if (sourceURI != null) {
- Source source = new Source();
- String scheme = sourceURI.getScheme();
- if (null == scheme || scheme.isEmpty() ||
"file".equalsIgnoreCase(scheme)) {
-
source.setName(Paths.get(sourceURI).getFileName().toString());
- source.setPath(sourceURI.getPath());
- source.setSourceReference(0);
- } else {
- int ref = context.createSourceReference(sourceURI,
frame.getSourceMimeType());
- String path = sourceURI.getPath();
- if (path == null) {
- path = sourceURI.getSchemeSpecificPart();
- }
- if (path != null) {
- int sepIndex = Math.max(path.lastIndexOf('/'),
path.lastIndexOf(File.separatorChar));
- source.setName(path.substring(sepIndex + 1));
- if ("jar".equalsIgnoreCase(scheme)) {
- try {
- path = new URI(path).getPath();
- } catch (URISyntaxException ex) {
- // ignore, we just tried
+ return CompletableFuture.supplyAsync(() -> {
+ LOGGER.log(LOGLEVEL, "stackTrace() START");
+ long t1 = System.nanoTime();
+ List<StackFrame> result = new ArrayList<>();
+ int cnt = 0;
+ DVThread dvThread =
context.getThreadsProvider().getThread(args.getThreadId());
+ if (dvThread != null) {
+ cnt = dvThread.getFrameCount();
+ int from = args.getStartFrame() != null ?
args.getStartFrame() : 0;
+ int to = args.getLevels() != null ? from +
args.getLevels() : Integer.MAX_VALUE;
+ List<DVFrame> stackFrames = dvThread.getFrames(from, to);
+ for (DVFrame frame : stackFrames) {
+ int frameId =
context.getThreadsProvider().getThreadObjects().addObject(args.getThreadId(),
new NbFrame(args.getThreadId(), frame));
+ int line = frame.getLine();
+ if (line < 0) { // unknown
+ line = 0;
+ }
+ int column = frame.getColumn();
+ if (column < 0) { // unknown
+ column = 0;
+ }
+ StackFrame stackFrame = new StackFrame();
+ stackFrame.setId(frameId);
+ stackFrame.setName(frame.getName());
+ URI sourceURI = frame.getSourceURI();
+ if (sourceURI != null) {
+ Source source = new Source();
+ String scheme = sourceURI.getScheme();
+ if (null == scheme || scheme.isEmpty() ||
"file".equalsIgnoreCase(scheme)) {
+
source.setName(Paths.get(sourceURI).getFileName().toString());
+ source.setPath(sourceURI.getPath());
+ source.setSourceReference(0);
+ } else {
+ int ref =
context.createSourceReference(sourceURI, frame.getSourceMimeType());
+ String path = sourceURI.getPath();
+ if (path == null) {
+ path = sourceURI.getSchemeSpecificPart();
+ }
+ if (path != null) {
+ int sepIndex =
Math.max(path.lastIndexOf('/'), path.lastIndexOf(File.separatorChar));
+ source.setName(path.substring(sepIndex +
1));
+ if ("jar".equalsIgnoreCase(scheme)) {
+ try {
+ path = new URI(path).getPath();
+ } catch (URISyntaxException ex) {
+ // ignore, we just tried
+ }
}
+ source.setPath(path);
}
- source.setPath(path);
+ source.setSourceReference(ref);
}
- source.setSourceReference(ref);
+ stackFrame.setSource(source);
}
- stackFrame.setSource(source);
+ stackFrame.setLine(line);
+ stackFrame.setColumn(column);
+ result.add(stackFrame);
}
- stackFrame.setLine(line);
- stackFrame.setColumn(column);
- result.add(stackFrame);
}
- }
- StackTraceResponse response = new StackTraceResponse();
- response.setStackFrames(result.toArray(new
StackFrame[result.size()]));
- response.setTotalFrames(cnt);
- future.complete(response);
+ StackTraceResponse response = new StackTraceResponse();
+ response.setStackFrames(result.toArray(new
StackFrame[result.size()]));
+ response.setTotalFrames(cnt);
+ long t2 = System.nanoTime();
+ LOGGER.log(LOGLEVEL, "stackTrace() END after {0} ns", (t2 -
t1));
+ return response;
+ }, threadsRP);
}
- return future;
}
@Override
@@ -427,22 +439,28 @@ public final class NbProtocolServer implements
IDebugProtocolServer, LspSession.
@Override
public CompletableFuture<ThreadsResponse> threads() {
- CompletableFuture<ThreadsResponse> future = new CompletableFuture<>();
if (context.getDebugSession() == null) {
+ CompletableFuture<ThreadsResponse> future = new
CompletableFuture<>();
ErrorUtilities.completeExceptionally(future, "Debug Session
doesn't exist.", ResponseErrorCode.InvalidParams);
+ return future;
} else {
- List<org.eclipse.lsp4j.debug.Thread> result = new ArrayList<>();
- context.getThreadsProvider().visitThreads((id, dvThread) -> {
- org.eclipse.lsp4j.debug.Thread thread = new
org.eclipse.lsp4j.debug.Thread();
- thread.setId(id);
- thread.setName(dvThread.getName());
- result.add(thread);
- });
- ThreadsResponse response = new ThreadsResponse();
- response.setThreads(result.toArray(new
org.eclipse.lsp4j.debug.Thread[result.size()]));
- future.complete(response);
+ return CompletableFuture.supplyAsync(() -> {
+ LOGGER.log(LOGLEVEL, "threads() START");
+ long t1 = System.nanoTime();
+ List<org.eclipse.lsp4j.debug.Thread> result = new
ArrayList<>();
+ context.getThreadsProvider().visitThreads((id, dvThread) -> {
+ org.eclipse.lsp4j.debug.Thread thread = new
org.eclipse.lsp4j.debug.Thread();
+ thread.setId(id);
+ thread.setName(dvThread.getName());
+ result.add(thread);
+ });
+ ThreadsResponse response = new ThreadsResponse();
+ response.setThreads(result.toArray(new
org.eclipse.lsp4j.debug.Thread[result.size()]));
+ long t2 = System.nanoTime();
+ LOGGER.log(LOGLEVEL, "threads() END after {0} ns", (t2 - t1));
+ return response;
+ }, threadsRP);
}
- return future;
}
private EvaluateResponse passToApplication(String args) {
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
index b1627d2..7da7ca5 100644
---
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
+++
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
@@ -21,6 +21,8 @@ package
org.netbeans.modules.java.lsp.server.debugging.variables;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.lsp4j.debug.SetVariableArguments;
import org.eclipse.lsp4j.debug.SetVariableResponse;
@@ -31,11 +33,13 @@ import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
import org.netbeans.api.debugger.Session;
import org.netbeans.modules.java.lsp.server.debugging.DebugAdapterContext;
+import org.netbeans.modules.java.lsp.server.debugging.NbProtocolServer;
import org.netbeans.modules.java.lsp.server.debugging.NbScope;
import org.netbeans.modules.java.lsp.server.debugging.launch.NbDebugSession;
import org.netbeans.modules.java.lsp.server.debugging.utils.ErrorUtilities;
import org.netbeans.spi.viewmodel.Models;
import org.netbeans.spi.viewmodel.UnknownTypeException;
+import org.openide.util.RequestProcessor;
/**
*
@@ -43,72 +47,78 @@ import org.netbeans.spi.viewmodel.UnknownTypeException;
*/
public final class NbVariablesRequestHandler {
+ private static final Logger LOGGER =
Logger.getLogger(NbProtocolServer.class.getName());
+ private static final Level LOGLEVEL = Level.FINE;
+
private static final String LOCALS_VIEW_NAME = "LocalsView";
private static final String LOCALS_VALUE_COLUMN_ID = "LocalsValue";
private static final String LOCALS_TO_STRING_COLUMN_ID = "LocalsToString";
private static final String LOCALS_TYPE_COLUMN_ID = "LocalsType";
private final ViewModel.Provider localsModelProvider;
+ private final RequestProcessor variablesRP = new
RequestProcessor(NbVariablesRequestHandler.class.getName(), 2);
public NbVariablesRequestHandler() {
this.localsModelProvider = new ViewModel.Provider(LOCALS_VIEW_NAME);
}
public CompletableFuture<VariablesResponse> variables(VariablesArguments
arguments, DebugAdapterContext context) {
- CompletableFuture<VariablesResponse> future = new
CompletableFuture<>();
-
- VariablesResponse response = new VariablesResponse();
- Object container =
context.getThreadsProvider().getThreadObjects().getObject(arguments.getVariablesReference());
- if (container == null) {
- // Nothing, or an old container
- response.setVariables(new Variable[0]);
- } else {
- Session session = context.getDebugSession().getSession();
- Models.CompoundModel localsModel =
localsModelProvider.getModel(session);
- int threadId;
- if (container instanceof NbScope) {
- threadId = ((NbScope) container).getFrame().getThreadId();
- container = localsModel.getRoot();
+ return CompletableFuture.supplyAsync(() -> {
+ LOGGER.log(LOGLEVEL, "variables() START");
+ long t1 = System.nanoTime();
+ VariablesResponse response = new VariablesResponse();
+ Object container =
context.getThreadsProvider().getThreadObjects().getObject(arguments.getVariablesReference());
+ if (container == null) {
+ // Nothing, or an old container
+ response.setVariables(new Variable[0]);
} else {
- threadId =
context.getThreadsProvider().getThreadObjects().findObjectThread(arguments.getVariablesReference());
- }
- List<Variable> list = new ArrayList<>();
- try {
- Object[] children;
- int count = arguments.getCount() != null ?
arguments.getCount() : 0;
- if (count > 0) {
- int start = arguments.getStart() != null ?
arguments.getStart() : 0;
- children = localsModel.getChildren(container, start, start
+ count);
+ Session session = context.getDebugSession().getSession();
+ Models.CompoundModel localsModel =
localsModelProvider.getModel(session);
+ int threadId;
+ if (container instanceof NbScope) {
+ threadId = ((NbScope) container).getFrame().getThreadId();
+ container = localsModel.getRoot();
} else {
- children = localsModel.getChildren(container, 0,
Integer.MAX_VALUE);
+ threadId =
context.getThreadsProvider().getThreadObjects().findObjectThread(arguments.getVariablesReference());
}
- for (Object child : children) {
- String name = localsModel.getDisplayName(child);
- String value;
- try {
- value = String.valueOf(localsModel.getValueAt(child,
LOCALS_TO_STRING_COLUMN_ID));
- } catch (UnknownTypeException ex) {
- value = String.valueOf(localsModel.getValueAt(child,
LOCALS_VALUE_COLUMN_ID));
+ List<Variable> list = new ArrayList<>();
+ try {
+ Object[] children;
+ int count = arguments.getCount() != null ?
arguments.getCount() : 0;
+ if (count > 0) {
+ int start = arguments.getStart() != null ?
arguments.getStart() : 0;
+ children = localsModel.getChildren(container, start,
start + count);
+ } else {
+ children = localsModel.getChildren(container, 0,
Integer.MAX_VALUE);
}
- String type = String.valueOf(localsModel.getValueAt(child,
LOCALS_TYPE_COLUMN_ID));
- Variable variable = new Variable();
- variable.setName(name);
- variable.setValue(value);
- variable.setType(type);
- if (!localsModel.isLeaf(child)) {
- int id =
context.getThreadsProvider().getThreadObjects().addObject(threadId, child);
- variable.setVariablesReference(id);
+ for (Object child : children) {
+ String name = localsModel.getDisplayName(child);
+ String value;
+ try {
+ value =
String.valueOf(localsModel.getValueAt(child, LOCALS_TO_STRING_COLUMN_ID));
+ } catch (UnknownTypeException ex) {
+ value =
String.valueOf(localsModel.getValueAt(child, LOCALS_VALUE_COLUMN_ID));
+ }
+ String type =
String.valueOf(localsModel.getValueAt(child, LOCALS_TYPE_COLUMN_ID));
+ Variable variable = new Variable();
+ variable.setName(name);
+ variable.setValue(value);
+ variable.setType(type);
+ if (!localsModel.isLeaf(child)) {
+ int id =
context.getThreadsProvider().getThreadObjects().addObject(threadId, child);
+ variable.setVariablesReference(id);
+ }
+ list.add(variable);
}
- list.add(variable);
+ } catch (UnknownTypeException e) {
+ throw
ErrorUtilities.createResponseErrorException(e.getMessage(),
ResponseErrorCode.InternalError);
}
- } catch (UnknownTypeException e) {
- ErrorUtilities.completeExceptionally(future, e.getMessage(),
ResponseErrorCode.InternalError);
- return future;
+ response.setVariables(list.toArray(new Variable[list.size()]));
}
- response.setVariables(list.toArray(new Variable[list.size()]));
- }
- future.complete(response);
- return future;
+ long t2 = System.nanoTime();
+ LOGGER.log(LOGLEVEL, "variables() END after {0} ns", (t2 - t1));
+ return response;
+ }, variablesRP);
}
public CompletableFuture<SetVariableResponse>
setVariable(SetVariableArguments args, DebugAdapterContext context) {
diff --git
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/layer.xml
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/layer.xml
new file mode 100644
index 0000000..311b57a
--- /dev/null
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/layer.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN"
"http://www.netbeans.org/dtds/filesystem-1_2.dtd">
+<filesystem>
+ <folder name="Debugger">
+ <file
name="org-netbeans-modules-debugger-jpda-ui-CurrentThreadAnnotationListener.instance_hidden"/>
+ <folder name="netbeans-JPDASession">
+ <!--file
name="org-netbeans-modules-debugger-jpda-ui-focus-FocusSuspendController.instance_hidden"/-->
+ </folder>
+ </folder>
+</filesystem>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists