8193879: Java debugger hangs on method invocation
Reviewed-by: sspitsyn, amenkov, gadams
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java Tue Oct 09 20:38:13 2018 -0400
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/InvokableTypeImpl.java Tue Oct 09 19:11:09 2018 -0700
@@ -236,15 +236,7 @@
final MethodImpl method,
final ValueImpl[] args,
final int options) {
- /*
- * Cache the values of args when TRACE_SENDS is enabled, for later printing.
- * If not cached, printing causes a remote call while synchronized, and deadlock.
- */
- if ((vm.traceFlags & VirtualMachine.TRACE_SENDS) != 0) {
- for (ValueImpl arg: args) {
- arg.toString();
- }
- }
+
CommandSender sender = getInvokeMethodSender(thread, method, args, options);
PacketStream stream;
if ((options & ClassType.INVOKE_SINGLE_THREADED) != 0) {
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/VMState.java Tue Oct 09 20:38:13 2018 -0400
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/VMState.java Tue Oct 09 19:11:09 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,10 +26,7 @@
package com.sun.tools.jdi;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
import com.sun.jdi.ThreadGroupReference;
import com.sun.jdi.ThreadReference;
@@ -44,12 +41,10 @@
/*
* Certain information can be cached only when the entire VM is
- * suspended and there are no pending resumes. The fields below
- * are used to track whether there are pending resumes. (There
- * is an assumption that JDWP command ids are increasing over time.)
+ * suspended and there are no pending resumes. The field below
+ * is used to track whether there are pending resumes.
*/
- private int lastCompletedCommandId = 0; // synchronized (this)
- private int lastResumeCommandId = 0; // synchronized (this)
+ private final Set<Integer> pendingResumeCommands = Collections.synchronizedSet(new HashSet<>());
// This is cached only while the VM is suspended
private static class Cache {
@@ -97,12 +92,12 @@
* A JDWP command has been completed (reply has been received).
* Update data that tracks pending resume commands.
*/
- synchronized void notifyCommandComplete(int id) {
- lastCompletedCommandId = id;
+ void notifyCommandComplete(int id) {
+ pendingResumeCommands.remove(id);
}
synchronized void freeze() {
- if (cache == null && (lastCompletedCommandId >= lastResumeCommandId)) {
+ if (cache == null && (pendingResumeCommands.isEmpty())) {
/*
* No pending resumes to worry about. The VM is suspended
* and additional state can be cached. Notify all
@@ -115,7 +110,7 @@
synchronized PacketStream thawCommand(CommandSender sender) {
PacketStream stream = sender.send();
- lastResumeCommandId = stream.id();
+ pendingResumeCommands.add(stream.id());
thaw();
return stream;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/MethodInvokeWithTraceOnTest.java Tue Oct 09 19:11:09 2018 -0700
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8193879 8193801 8129348
+ * @summary Invokes static and instance methods when debugger trace
+ * mode is on.
+ * @library /test/lib
+ * @run build TestScaffold VMConnection TargetListener TargetAdapter
+ * @run compile -g MethodInvokeWithTraceOnTest.java
+ * @run driver MethodInvokeWithTraceOnTest
+ */
+
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+import com.sun.jdi.request.*;
+
+import java.util.*;
+
+import static lib.jdb.JdbTest.*;
+
+/********** target program **********/
+
+class MethodInvokeWithTraceOnTestTarg {
+ public static void main(String[] args) {
+ new MethodInvokeWithTraceOnTestTarg().test();
+ }
+
+ private void test() {
+ Thread thread = Thread.currentThread();
+ print(thread); // @1 breakpoint
+ String str = "test";
+ printStatic(str); // @2 breakpoint
+
+ }
+
+ public void print(Object obj) {
+ System.out.println(obj);
+ }
+
+ public static void printStatic(Object obj) {
+ System.out.println(obj);
+ }
+
+}
+
+
+/********** test program **********/
+
+public class MethodInvokeWithTraceOnTest extends TestScaffold {
+
+ MethodInvokeWithTraceOnTest(String args[]) {
+ super(args);
+ }
+
+ public static void main(String[] args)
+ throws Exception {
+ new MethodInvokeWithTraceOnTest(args).startTests();
+ }
+
+ /********** test core **********/
+
+ protected void runTests() throws Exception {
+ init();
+
+ // Test with suspend policy set to SUSPEND_EVENT_THREAD
+ BreakpointEvent be = resumeToBreakpoint(true, 1);
+ System.out.println("Breakpoint 1 is hit, suspendPolicy:" + be.request().suspendPolicy());
+ testMethods(be);
+
+ // Test with suspend policy set to SUSPEND_ALL
+ be = resumeToBreakpoint(false, 2);
+ System.out.println("Breakpoint 2 is hit, suspendPolicy:" + be.request().suspendPolicy());
+ testMethods(be);
+
+ listenUntilVMDisconnect();
+ }
+
+ private void init() throws Exception {
+ startToMain("MethodInvokeWithTraceOnTestTarg");
+ vm().setDebugTraceMode(VirtualMachine.TRACE_ALL);
+ }
+
+ private BreakpointEvent resumeToBreakpoint(boolean suspendThread, int breakpointId) throws Exception {
+ int bkpLine = parseBreakpoints(getTestSourcePath("MethodInvokeWithTraceOnTest.java"), breakpointId).get(0);
+ System.out.println("Running to line: " + bkpLine);
+ return resumeTo("MethodInvokeWithTraceOnTestTarg", bkpLine, suspendThread);
+ }
+
+ private void testMethods(BreakpointEvent be) throws Exception {
+ System.out.println("Testing methods...");
+ ThreadReference thread = be.thread();
+ StackFrame frame = thread.frame(0);
+ ObjectReference thisObj = frame.thisObject();
+ LocalVariable threadVar = frame.visibleVariableByName("thread");
+ ThreadReference threadObj = (ThreadReference) frame.getValue(threadVar);
+ StringReference stringObj = vm().mirrorOf("test string");
+ int invokeOptions = getMethodInvokeOptions(be);
+
+ testInstanceMethod1(thread, thisObj, stringObj, threadObj, invokeOptions);
+ testStaticMethod1(thread, thisObj, stringObj, threadObj, invokeOptions);
+ testStaticMethod2(thread, invokeOptions);
+ }
+
+ private void testInstanceMethod1(ThreadReference thread, ObjectReference thisObj, StringReference stringObj,
+ ThreadReference threadObj, int invokeOptions) throws Exception {
+ ClassType classType = (ClassType) thisObj.referenceType();
+ Method printMethod = classType.methodsByName("print",
+ "(Ljava/lang/Object;)V").get(0);
+
+ System.out.println("Passing StringReference to instance method...");
+ thisObj.invokeMethod(thread, printMethod, Collections.singletonList(stringObj), invokeOptions);
+
+ System.out.println("Passing ThreadReference to instance method...");
+ thisObj.invokeMethod(thread, printMethod, Collections.singletonList(threadObj), invokeOptions);
+ }
+
+ private void testStaticMethod1(ThreadReference thread, ObjectReference thisObj, StringReference stringObj,
+ ThreadReference threadObj, int invokeOptions) throws Exception {
+ ClassType classType = (ClassType) thisObj.referenceType();
+ Method printMethod = classType.methodsByName("printStatic",
+ "(Ljava/lang/Object;)V").get(0);
+
+ System.out.println("Passing StringReference to static method...");
+ classType.invokeMethod(thread, printMethod, Collections.singletonList(stringObj), invokeOptions);
+
+ System.out.println("Passing ThreadReference to static method...");
+ classType.invokeMethod(thread, printMethod, Collections.singletonList(threadObj), invokeOptions);
+ }
+
+ private void testStaticMethod2(ThreadReference thread, int invokeOptions) throws Exception {
+ ClassType classType = getClassType("java.lang.Class");
+ Method forNameMethod = classType.methodsByName("forName",
+ "(Ljava/lang/String;)Ljava/lang/Class;").get(0);
+ StringReference classNameParam = vm().mirrorOf("java.lang.String");
+ classType.invokeMethod(thread, forNameMethod, Collections.singletonList(classNameParam), invokeOptions);
+ }
+
+ private ClassType getClassType(String className) {
+ List classes = vm().classesByName(className);
+ return (ClassType) classes.get(0);
+ }
+
+ private int getMethodInvokeOptions(BreakpointEvent be) {
+ return be.request().suspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD ?
+ ObjectReference.INVOKE_SINGLE_THREADED : 0;
+ }
+}
--- a/test/jdk/com/sun/jdi/TestScaffold.java Tue Oct 09 20:38:13 2018 -0400
+++ b/test/jdk/com/sun/jdi/TestScaffold.java Tue Oct 09 19:11:09 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -784,9 +784,16 @@
}
public BreakpointEvent resumeTo(Location loc) {
+ return resumeTo(loc, false);
+ }
+
+ public BreakpointEvent resumeTo(Location loc, boolean suspendThread) {
final BreakpointRequest request =
- requestManager.createBreakpointRequest(loc);
+ requestManager.createBreakpointRequest(loc);
request.addCountFilter(1);
+ if (suspendThread) {
+ request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
+ }
request.enable();
return (BreakpointEvent)waitForRequestedEvent(request);
}
@@ -845,12 +852,16 @@
}
public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException {
+ return resumeTo(clsName, lineNumber, false);
+ }
+
+ public BreakpointEvent resumeTo(String clsName, int lineNumber, boolean suspendThread) throws AbsentInformationException {
ReferenceType rt = findReferenceType(clsName);
if (rt == null) {
rt = resumeToPrepareOf(clsName).referenceType();
}
- return resumeTo(findLocation(rt, lineNumber));
+ return resumeTo(findLocation(rt, lineNumber), suspendThread);
}
public ClassPrepareEvent resumeToPrepareOf(String className) {
--- a/test/jdk/com/sun/jdi/lib/jdb/JdbTest.java Tue Oct 09 20:38:13 2018 -0400
+++ b/test/jdk/com/sun/jdi/lib/jdb/JdbTest.java Tue Oct 09 19:11:09 2018 -0700
@@ -238,7 +238,7 @@
}
// gets full test source path for the given test filename
- protected static String getTestSourcePath(String fileName) {
+ public static String getTestSourcePath(String fileName) {
return Paths.get(System.getProperty("test.src")).resolve(fileName).toString();
}