From 820c9f330d8336a29ee0845a6ec71f6f09b39fec Mon Sep 17 00:00:00 2001 From: Shuhei Takahashi Date: Mon, 27 Jan 2025 22:28:59 +0900 Subject: [PATCH] Avoid scanning all classes on attach Clients try to enable all breakpoints on attach, but currently it ends up with querying all classes loaded in the VM today. This is very slow; it takes ~5ms per class on my environment. This patch workarounds the problem by skipping classes that are clearly irrelevant. --- .../org/javacs/debug/JavaDebugServer.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/org/javacs/debug/JavaDebugServer.java b/src/main/java/org/javacs/debug/JavaDebugServer.java index 53d36ce68..b19af4bf1 100644 --- a/src/main/java/org/javacs/debug/JavaDebugServer.java +++ b/src/main/java/org/javacs/debug/JavaDebugServer.java @@ -182,6 +182,10 @@ private boolean matchesLine(BreakpointRequest b, int line) { private List loadedTypesMatching(String absolutePath) { var matches = new ArrayList(); for (var type : vm.allClasses()) { + // ReferenceType#relativePath is slow. Avoid them when possible. + if (!mayProvideType(absolutePath, type)) { + continue; + } var path = relativePath(type); if (!path.isEmpty() && absolutePath.endsWith(path)) { matches.add(type); @@ -362,6 +366,18 @@ private void enablePendingBreakpointsInLoadedClasses() { } private void enablePendingBreakpointsIn(ReferenceType type) { + // ReferenceType#relativePath is slow. Avoid them when possible. + boolean found = false; + for (var b : pendingBreakpoints) { + if (mayProvideType(b.source.path, type)) { + found = true; + break; + } + } + if (!found) { + return; + } + // Check that class has source information var path = relativePath(type); if (path.isEmpty()) return; @@ -705,5 +721,26 @@ public EvaluateResponseBody evaluate(EvaluateArguments req) { throw new UnsupportedOperationException(); } + /** Determines if a given type might be provided by a source file. */ + private static boolean mayProvideType(String absolutePath, ReferenceType type) { + var lastSlashPos = absolutePath.lastIndexOf('/'); + if (lastSlashPos < 0) { + return true; + } + var absoluteDir = absolutePath.substring(0, lastSlashPos); + + var fullName = type.name(); + var lastDotPos = fullName.lastIndexOf('.'); + if (lastDotPos < 0) { + return true; + } + var packageName = fullName.substring(0, lastDotPos); + + // If a type belongs to the package "x.y.z", its source directory should end with "x/y/z". + // Unfortunately it's difficult to consider class names because of anonymous classes and + // non-public classes. + return absoluteDir.endsWith(packageName.replace('.', '/')); + } + private static final Logger LOG = Logger.getLogger("debug"); }