From 87b49d65b26d21d63d1d3bb142ddee37ae20a812 Mon Sep 17 00:00:00 2001 From: Egor Ushakov Date: Wed, 2 Oct 2024 14:54:35 +0200 Subject: [PATCH] [debugger] use optimized allLineLocationsSync to reduce number of sync debugger commands GitOrigin-RevId: 9d0735eb837b705456872ff5f11cbd3c4587f012 --- .../intellij/debugger/engine/JavaValue.java | 3 ++- .../debugger/impl/DebuggerUtilsAsync.java | 25 ++++++++++++++++++- .../debugger/impl/DebuggerUtilsEx.java | 2 +- .../debugger/impl/SourceCodeChecker.java | 4 +-- .../debugger/GroovyPositionManager.java | 5 ++-- .../org/jetbrains/eval4j/jdi/test/jdiTest.kt | 5 ++-- .../debugger/test/AbstractFileRankingTest.kt | 5 ++-- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index c33acae04e15..fa131b02e351 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -11,6 +11,7 @@ import com.intellij.debugger.engine.evaluation.expression.Modifier; import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; +import com.intellij.debugger.impl.DebuggerUtilsAsync; import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.MethodBytecodeUtil; import com.intellij.debugger.memory.agent.MemoryAgent; @@ -773,7 +774,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV Location location = lambdaMethod != null ? ContainerUtil.getFirstItem(DebuggerUtilsEx.allLineLocations(lambdaMethod)) : null; if (location == null) { - location = ContainerUtil.getFirstItem(clsType.allLineLocations()); + location = ContainerUtil.getFirstItem(DebuggerUtilsAsync.allLineLocationsSync(clsType)); } if (location != null) { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsAsync.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsAsync.java index f86d2ef85253..35b5b6b2535e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsAsync.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsAsync.java @@ -175,7 +175,30 @@ public final class DebuggerUtilsAsync { return type.locationsOfLine(stratum, sourceName, lineNumber); } - public static CompletableFuture> allLineLocationsAsync(Method method) { + public static List allLineLocationsSync(ReferenceType type) throws AbsentInformationException { + return allLineLocationsSync(type, type.virtualMachine().getDefaultStratum(), null); + } + + /** + * Drop-in replacement for the standard jdi version, but "parallel" inside, so a lot faster when type has lots of methods + */ + public static List allLineLocationsSync(ReferenceType type, String stratum, String sourceName) + throws AbsentInformationException { + if (type instanceof ReferenceTypeImpl && isAsyncEnabled()) { + try { + return ((ReferenceTypeImpl)type).allLineLocationsAsync(stratum, sourceName).get(); + } + catch (Exception e) { + if (e.getCause() instanceof AbsentInformationException) { + throw (AbsentInformationException)e.getCause(); + } + LOG.warn(e); + } + } + return type.allLineLocations(stratum, sourceName); + } + + public static CompletableFuture> allLineLocations(Method method) { if (method instanceof MethodImpl && isAsyncEnabled()) { return reschedule(((MethodImpl)method).allLineLocationsAsync()); } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 13ded66377b7..fcfaad5f2808 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -559,7 +559,7 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { @Nullable public static List allLineLocations(ReferenceType cls) { try { - return cls.allLineLocations(); + return DebuggerUtilsAsync.allLineLocationsSync(cls); } catch (AbsentInformationException ignored) { return null; diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/SourceCodeChecker.java b/java/debugger/impl/src/com/intellij/debugger/impl/SourceCodeChecker.java index 41f1ee5fa052..89dd1eb25ec9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/SourceCodeChecker.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/SourceCodeChecker.java @@ -86,7 +86,7 @@ public final class SourceCodeChecker { DebuggerUtilsEx.isLambda(method)) { return CompletableFuture.completedFuture(ThreeState.UNSURE); } - return DebuggerUtilsAsync.allLineLocationsAsync(method).thenApply(locations -> { + return DebuggerUtilsAsync.allLineLocations(method).thenApply(locations -> { if (ContainerUtil.isEmpty(locations)) { return ThreeState.UNSURE; } @@ -171,7 +171,7 @@ public final class SourceCodeChecker { for (ReferenceType type : types) { i++; try { - for (Location loc : type.allLineLocations()) { + for (Location loc : DebuggerUtilsAsync.allLineLocationsSync(type)) { SourcePosition position = ReadAction.compute(() -> { try { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java index c2b627e8f3ce..1fdb30aa2cf3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/debugger/GroovyPositionManager.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.groovy.debugger; @@ -464,8 +464,7 @@ public class GroovyPositionManager extends PositionManagerEx { if (!DebuggerUtilsAsync.locationsOfLineSync(fromClass, lineNumber).isEmpty()) { return fromClass; } - //noinspection LoopStatementThatDoesntLoop - for (Location location : fromClass.allLineLocations()) { + for (Location location : DebuggerUtilsAsync.allLineLocationsSync(fromClass)) { final SourcePosition candidateFirstPosition = SourcePosition.createFromLine( toFind.getContainingFile(), location.lineNumber() - 1 ); diff --git a/plugins/kotlin/jvm-debugger/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt b/plugins/kotlin/jvm-debugger/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt index 0e8563386b21..dad4d2b7a418 100644 --- a/plugins/kotlin/jvm-debugger/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt +++ b/plugins/kotlin/jvm-debugger/eval4j/test/org/jetbrains/eval4j/jdi/test/jdiTest.kt @@ -1,7 +1,8 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.eval4j.jdi.test +import com.intellij.debugger.impl.DebuggerUtilsAsync import com.sun.jdi.* import com.sun.jdi.event.BreakpointEvent import com.sun.jdi.event.ClassPrepareEvent @@ -55,7 +56,7 @@ fun suite(): TestSuite { is ClassPrepareEvent -> { val _class = event.referenceType()!! if (_class.name() == debugeeName) { - for (l in _class.allLineLocations()) { + for (l in DebuggerUtilsAsync.allLineLocationsSync(_class)) { if (l.method().name() == "main") { classLoader = l.method().declaringType().classLoader() val breakpointRequest = vm.eventRequestManager().createBreakpointRequest(l) diff --git a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/AbstractFileRankingTest.kt b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/AbstractFileRankingTest.kt index 0f09cc545ea2..d504fa4d67c1 100644 --- a/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/AbstractFileRankingTest.kt +++ b/plugins/kotlin/jvm-debugger/test/test/org/jetbrains/kotlin/idea/debugger/test/AbstractFileRankingTest.kt @@ -1,7 +1,8 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.debugger.test +import com.intellij.debugger.impl.DebuggerUtilsAsync import com.intellij.openapi.application.runReadAction import com.sun.jdi.ThreadReference import org.jetbrains.kotlin.config.JvmClosureGenerationScheme @@ -70,7 +71,7 @@ abstract class AbstractFileRankingTest : LowLevelDebuggerTestBase() { val jdiClass = mainThread.virtualMachine().classesByName(className).singleOrNull() ?: error("Class '$className' was not found in the debuggee process class loader") - val locations = jdiClass.allLineLocations() + val locations = DebuggerUtilsAsync.allLineLocationsSync(jdiClass) assert(locations.isNotEmpty()) { "There are no locations for class $className" } val allFilesWithSameName = files.filter { it.name == expectedFile.name }