Blame SOURCES/8153711-pr3313-rh1284948.patch

045ef6
# HG changeset patch
045ef6
# User sgehwolf
045ef6
# Date 1458555849 -3600
045ef6
#      Mon Mar 21 11:24:09 2016 +0100
045ef6
# Node ID 9f6a0864a734ae3fd0eb198768db7cdee53ba0ed
045ef6
# Parent  1179be40f1e3b59a890e96a5a9d3ff6fc18a2846
045ef6
8153711, PR3313: [REDO] JDWP: Memory Leak: GlobalRefs never deleted when processing invokeMethod command
045ef6
Summary: Delete global references in invoker_completeInvokeRequest()
045ef6
Reviewed-by: sspitsyn, dsamersoff
045ef6
045ef6
diff --git a/src/share/back/invoker.c b/src/share/back/invoker.c
045ef6
--- openjdk/jdk/src/share/back/invoker.c
045ef6
+++ openjdk/jdk/src/share/back/invoker.c
045ef6
@@ -211,6 +211,62 @@
045ef6
     return error;
045ef6
 }
045ef6
 
045ef6
+/*
045ef6
+ * Delete saved global references - if any - for:
045ef6
+ * - a potentially thrown Exception
045ef6
+ * - a returned refernce/array value
045ef6
+ * See invoker_doInvoke() and invoke* methods where global references
045ef6
+ * are being saved.
045ef6
+ */
045ef6
+static void
045ef6
+deletePotentiallySavedGlobalRefs(JNIEnv *env, InvokeRequest *request)
045ef6
+{
045ef6
+    /* Delete potentially saved return value */
045ef6
+    if ((request->invokeType == INVOKE_CONSTRUCTOR) ||
045ef6
+        (returnTypeTag(request->methodSignature) == JDWP_TAG(OBJECT)) ||
045ef6
+        (returnTypeTag(request->methodSignature) == JDWP_TAG(ARRAY))) {
045ef6
+        if (request->returnValue.l != NULL) {
045ef6
+            tossGlobalRef(env, &(request->returnValue.l));
045ef6
+        }
045ef6
+    }
045ef6
+    /* Delete potentially saved exception */
045ef6
+    if (request->exception != NULL) {
045ef6
+        tossGlobalRef(env, &(request->exception));
045ef6
+    }
045ef6
+}
045ef6
+
045ef6
+/*
045ef6
+ * Delete global argument references from the request which got put there before a
045ef6
+ * invoke request was carried out. See fillInvokeRequest().
045ef6
+ */
045ef6
+static void
045ef6
+deleteGlobalArgumentRefs(JNIEnv *env, InvokeRequest *request)
045ef6
+{
045ef6
+    void *cursor;
045ef6
+    jint argIndex = 0;
045ef6
+    jvalue *argument = request->arguments;
045ef6
+    jbyte argumentTag = firstArgumentTypeTag(request->methodSignature, &cursor);
045ef6
+
045ef6
+    if (request->clazz != NULL) {
045ef6
+        tossGlobalRef(env, &(request->clazz));
045ef6
+    }
045ef6
+    if (request->instance != NULL) {
045ef6
+        tossGlobalRef(env, &(request->instance));
045ef6
+    }
045ef6
+    /* Delete global argument references */
045ef6
+    while (argIndex < request->argumentCount) {
045ef6
+        if ((argumentTag == JDWP_TAG(OBJECT)) ||
045ef6
+            (argumentTag == JDWP_TAG(ARRAY))) {
045ef6
+            if (argument->l != NULL) {
045ef6
+                tossGlobalRef(env, &(argument->l));
045ef6
+            }
045ef6
+        }
045ef6
+        argument++;
045ef6
+        argIndex++;
045ef6
+        argumentTag = nextArgumentTypeTag(&cursor);
045ef6
+    }
045ef6
+}
045ef6
+
045ef6
 static jvmtiError
045ef6
 fillInvokeRequest(JNIEnv *env, InvokeRequest *request,
045ef6
                   jbyte invokeType, jbyte options, jint id,
045ef6
@@ -320,6 +376,8 @@
045ef6
 invokeConstructor(JNIEnv *env, InvokeRequest *request)
045ef6
 {
045ef6
     jobject object;
045ef6
+
045ef6
+    JDI_ASSERT_MSG(request->clazz, "Request clazz null");
045ef6
     object = JNI_FUNC_PTR(env,NewObjectA)(env, request->clazz,
045ef6
                                      request->method,
045ef6
                                      request->arguments);
045ef6
@@ -336,6 +394,7 @@
045ef6
         case JDWP_TAG(OBJECT):
045ef6
         case JDWP_TAG(ARRAY): {
045ef6
             jobject object;
045ef6
+            JDI_ASSERT_MSG(request->clazz, "Request clazz null");
045ef6
             object = JNI_FUNC_PTR(env,CallStaticObjectMethodA)(env,
045ef6
                                        request->clazz,
045ef6
                                        request->method,
045ef6
@@ -424,6 +483,7 @@
045ef6
         case JDWP_TAG(OBJECT):
045ef6
         case JDWP_TAG(ARRAY): {
045ef6
             jobject object;
045ef6
+            JDI_ASSERT_MSG(request->instance, "Request instance null");
045ef6
             object = JNI_FUNC_PTR(env,CallObjectMethodA)(env,
045ef6
                                  request->instance,
045ef6
                                  request->method,
045ef6
@@ -511,6 +571,8 @@
045ef6
         case JDWP_TAG(OBJECT):
045ef6
         case JDWP_TAG(ARRAY): {
045ef6
             jobject object;
045ef6
+            JDI_ASSERT_MSG(request->clazz, "Request clazz null");
045ef6
+            JDI_ASSERT_MSG(request->instance, "Request instance null");
045ef6
             object = JNI_FUNC_PTR(env,CallNonvirtualObjectMethodA)(env,
045ef6
                                            request->instance,
045ef6
                                            request->clazz,
045ef6
@@ -607,6 +669,8 @@
045ef6
     JNIEnv *env;
045ef6
     jboolean startNow;
045ef6
     InvokeRequest *request;
045ef6
+    jbyte options;
045ef6
+    jbyte invokeType;
045ef6
 
045ef6
     JDI_ASSERT(thread);
045ef6
 
045ef6
@@ -623,6 +687,9 @@
045ef6
     if (startNow) {
045ef6
         request->started = JNI_TRUE;
045ef6
     }
045ef6
+    options = request->options;
045ef6
+    invokeType = request->invokeType;
045ef6
+
045ef6
     debugMonitorExit(invokerLock);
045ef6
 
045ef6
     if (!startNow) {
045ef6
@@ -637,7 +704,7 @@
045ef6
 
045ef6
         JNI_FUNC_PTR(env,ExceptionClear)(env);
045ef6
 
045ef6
-        switch (request->invokeType) {
045ef6
+        switch (invokeType) {
045ef6
             case INVOKE_CONSTRUCTOR:
045ef6
                 invokeConstructor(env, request);
045ef6
                 break;
045ef6
@@ -645,7 +712,7 @@
045ef6
                 invokeStatic(env, request);
045ef6
                 break;
045ef6
             case INVOKE_INSTANCE:
045ef6
-                if (request->options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) {
045ef6
+                if (options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) {
045ef6
                     invokeNonvirtual(env, request);
045ef6
                 } else {
045ef6
                     invokeVirtual(env, request);
045ef6
@@ -723,12 +790,23 @@
045ef6
     }
045ef6
 
045ef6
     /*
045ef6
+     * At this time, there's no need to retain global references on
045ef6
+     * arguments since the reply is processed. No one will deal with
045ef6
+     * this request ID anymore, so we must call deleteGlobalArgumentRefs().
045ef6
+     *
045ef6
+     * We cannot delete saved exception or return value references
045ef6
+     * since otherwise a deleted handle would escape when writing
045ef6
+     * the response to the stream. Instead, we clean those refs up
045ef6
+     * after writing the respone.
045ef6
+     */
045ef6
+    deleteGlobalArgumentRefs(env, request);
045ef6
+
045ef6
+    /*
045ef6
      * Give up the lock before I/O operation
045ef6
      */
045ef6
     debugMonitorExit(invokerLock);
045ef6
     eventHandler_unlock();
045ef6
 
045ef6
-
045ef6
     if (!detached) {
045ef6
         outStream_initReply(&out, id);
045ef6
         (void)outStream_writeValue(env, &out, tag, returnValue);
045ef6
@@ -736,6 +814,16 @@
045ef6
         (void)outStream_writeObjectRef(env, &out, exc);
045ef6
         outStream_sendReply(&out;;
045ef6
     }
045ef6
+
045ef6
+    /*
045ef6
+     * Delete potentially saved global references of return value
045ef6
+     * and exception
045ef6
+     */
045ef6
+    eventHandler_lock(); // for proper lock order
045ef6
+    debugMonitorEnter(invokerLock);
045ef6
+    deletePotentiallySavedGlobalRefs(env, request);
045ef6
+    debugMonitorExit(invokerLock);
045ef6
+    eventHandler_unlock();
045ef6
 }
045ef6
 
045ef6
 jboolean
045ef6
diff --git a/test/com/sun/jdi/oom/@debuggeeVMOptions b/test/com/sun/jdi/oom/@debuggeeVMOptions
045ef6
new file mode 100644
045ef6
--- /dev/null
045ef6
+++ openjdk/jdk/test/com/sun/jdi/oom/@debuggeeVMOptions
045ef6
@@ -0,0 +1,1 @@
045ef6
+-Xmx40m
045ef6
\ No newline at end of file
045ef6
diff --git a/test/com/sun/jdi/oom/OomDebugTest.java b/test/com/sun/jdi/oom/OomDebugTest.java
045ef6
new file mode 100644
045ef6
--- /dev/null
045ef6
+++ openjdk/jdk/test/com/sun/jdi/oom/OomDebugTest.java
045ef6
@@ -0,0 +1,417 @@
045ef6
+/*
045ef6
+ * Copyright (c) 2016 Red Hat Inc.
045ef6
+ *
045ef6
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
045ef6
+ *
045ef6
+ * This code is free software; you can redistribute it and/or modify it
045ef6
+ * under the terms of the GNU General Public License version 2 only, as
045ef6
+ * published by the Free Software Foundation.
045ef6
+ *
045ef6
+ * This code is distributed in the hope that it will be useful, but WITHOUT
045ef6
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
045ef6
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
045ef6
+ * version 2 for more details (a copy is included in the LICENSE file that
045ef6
+ * accompanied this code).
045ef6
+ *
045ef6
+ * You should have received a copy of the GNU General Public License version
045ef6
+ * 2 along with this work; if not, write to the Free Software Foundation,
045ef6
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
045ef6
+ *
045ef6
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
045ef6
+ * or visit www.oracle.com if you need additional information or have any
045ef6
+ * questions.
045ef6
+ */
045ef6
+
045ef6
+/**
045ef6
+ *  @test
045ef6
+ *  @bug 8153711
045ef6
+ *  @summary JDWP: Memory Leak (global references not deleted after invokeMethod).
045ef6
+ *
045ef6
+ *  @author Severin Gehwolf <sgehwolf@redhat.com>
045ef6
+ *
045ef6
+ *  @library ..
045ef6
+ *  @run build TestScaffold VMConnection TargetListener TargetAdapter
045ef6
+ *  @run compile -g OomDebugTest.java
045ef6
+ *  @run shell OomDebugTestSetup.sh
045ef6
+ *  @run main OomDebugTest OomDebugTestTarget test1
045ef6
+ *  @run main OomDebugTest OomDebugTestTarget test2
045ef6
+ *  @run main OomDebugTest OomDebugTestTarget test3
045ef6
+ *  @run main OomDebugTest OomDebugTestTarget test4
045ef6
+ *  @run main OomDebugTest OomDebugTestTarget test5
045ef6
+ */
045ef6
+import java.io.File;
045ef6
+import java.io.FileInputStream;
045ef6
+import java.io.FileNotFoundException;
045ef6
+import java.io.FileOutputStream;
045ef6
+import java.io.IOException;
045ef6
+import java.util.ArrayList;
045ef6
+import java.util.Arrays;
045ef6
+import java.util.Collections;
045ef6
+import java.util.HashSet;
045ef6
+import java.util.List;
045ef6
+import java.util.Properties;
045ef6
+import java.util.Set;
045ef6
+
045ef6
+import com.sun.jdi.ArrayReference;
045ef6
+import com.sun.jdi.ArrayType;
045ef6
+import com.sun.jdi.ClassType;
045ef6
+import com.sun.jdi.Field;
045ef6
+import com.sun.jdi.InvocationException;
045ef6
+import com.sun.jdi.Method;
045ef6
+import com.sun.jdi.ObjectReference;
045ef6
+import com.sun.jdi.ReferenceType;
045ef6
+import com.sun.jdi.StackFrame;
045ef6
+import com.sun.jdi.VMOutOfMemoryException;
045ef6
+import com.sun.jdi.Value;
045ef6
+import com.sun.jdi.event.BreakpointEvent;
045ef6
+import com.sun.jdi.event.ExceptionEvent;
045ef6
+
045ef6
+/***************** Target program **********************/
045ef6
+
045ef6
+class OomDebugTestTarget {
045ef6
+
045ef6
+    OomDebugTestTarget() {
045ef6
+        System.out.println("DEBUG: invoked constructor");
045ef6
+    }
045ef6
+    static class FooCls {
045ef6
+        @SuppressWarnings("unused")
045ef6
+        private byte[] bytes = new byte[3000000];
045ef6
+    };
045ef6
+
045ef6
+    FooCls fooCls = new FooCls();
045ef6
+    byte[] byteArray = new byte[0];
045ef6
+
045ef6
+    void testMethod(FooCls foo) {
045ef6
+        System.out.println("DEBUG: invoked 'void testMethod(FooCls)', foo == " + foo);
045ef6
+    }
045ef6
+
045ef6
+    void testPrimitive(byte[] foo) {
045ef6
+        System.out.println("DEBUG: invoked 'void testPrimitive(byte[])', foo == " + foo);
045ef6
+    }
045ef6
+
045ef6
+    byte[] testPrimitiveArrRetval() {
045ef6
+        System.out.println("DEBUG: invoked 'byte[] testPrimitiveArrRetval()'");
045ef6
+        return new byte[3000000];
045ef6
+    }
045ef6
+
045ef6
+    FooCls testFooClsRetval() {
045ef6
+        System.out.println("DEBUG: invoked 'FooCls testFooClsRetval()'");
045ef6
+        return new FooCls();
045ef6
+    }
045ef6
+
045ef6
+    public void entry() {}
045ef6
+
045ef6
+    public static void main(String[] args){
045ef6
+        System.out.println("DEBUG: OomDebugTestTarget.main");
045ef6
+        new OomDebugTestTarget().entry();
045ef6
+    }
045ef6
+}
045ef6
+
045ef6
+/***************** Test program ************************/
045ef6
+
045ef6
+public class OomDebugTest extends TestScaffold {
045ef6
+
045ef6
+    private static final String[] ALL_TESTS = new String[] {
045ef6
+            "test1", "test2", "test3", "test4", "test5"
045ef6
+    };
045ef6
+    private static final Set<String> ALL_TESTS_SET = new HashSet<String>();
045ef6
+    static {
045ef6
+        ALL_TESTS_SET.addAll(Arrays.asList(ALL_TESTS));
045ef6
+    }
045ef6
+    private static final String TEST_CLASSES = System.getProperty("test.classes", ".");
045ef6
+    private static final File RESULT_FILE = new File(TEST_CLASSES, "results.properties");
045ef6
+    private static final String LAST_TEST = ALL_TESTS[ALL_TESTS.length - 1];
045ef6
+    private ReferenceType targetClass;
045ef6
+    private ObjectReference thisObject;
045ef6
+    private int failedTests;
045ef6
+    private final String testMethod;
045ef6
+
045ef6
+    public OomDebugTest(String[] args) {
045ef6
+        super(args);
045ef6
+        if (args.length != 2) {
045ef6
+            throw new RuntimeException("Wrong number of command-line arguments specified.");
045ef6
+        }
045ef6
+        this.testMethod = args[1];
045ef6
+    }
045ef6
+
045ef6
+    @Override
045ef6
+    protected void runTests() throws Exception {
045ef6
+        try {
045ef6
+            addListener(new TargetAdapter() {
045ef6
+
045ef6
+                @Override
045ef6
+                public void exceptionThrown(ExceptionEvent event) {
045ef6
+                    String name = event.exception().referenceType().name();
045ef6
+                    System.err.println("DEBUG: Exception thrown in debuggee was: " + name);
045ef6
+                }
045ef6
+            });
045ef6
+            /*
045ef6
+             * Get to the top of entry()
045ef6
+             * to determine targetClass and mainThread
045ef6
+             */
045ef6
+            BreakpointEvent bpe = startTo("OomDebugTestTarget", "entry", "()V");
045ef6
+            targetClass = bpe.location().declaringType();
045ef6
+
045ef6
+            mainThread = bpe.thread();
045ef6
+
045ef6
+            StackFrame frame = mainThread.frame(0);
045ef6
+            thisObject = frame.thisObject();
045ef6
+            java.lang.reflect.Method m = findTestMethod();
045ef6
+            m.invoke(this);
045ef6
+        } catch (NoSuchMethodException e) {
045ef6
+            e.printStackTrace();
045ef6
+            failure();
045ef6
+        } catch (SecurityException e) {
045ef6
+            e.printStackTrace();
045ef6
+            failure();
045ef6
+        }
045ef6
+        /*
045ef6
+         * resume the target, listening for events
045ef6
+         */
045ef6
+        listenUntilVMDisconnect();
045ef6
+    }
045ef6
+
045ef6
+    private java.lang.reflect.Method findTestMethod()
045ef6
+            throws NoSuchMethodException, SecurityException {
045ef6
+        return OomDebugTest.class.getDeclaredMethod(testMethod);
045ef6
+    }
045ef6
+
045ef6
+    private void failure() {
045ef6
+        failedTests++;
045ef6
+    }
045ef6
+
045ef6
+    /*
045ef6
+     * Test case: Object reference as method parameter.
045ef6
+     */
045ef6
+    @SuppressWarnings("unused") // called via reflection
045ef6
+    private void test1() throws Exception {
045ef6
+        System.out.println("DEBUG: ------------> Running test1");
045ef6
+        try {
045ef6
+            Field field = targetClass.fieldByName("fooCls");
045ef6
+            ClassType clsType = (ClassType)field.type();
045ef6
+            Method constructor = getConstructorForClass(clsType);
045ef6
+            for (int i = 0; i < 15; i++) {
045ef6
+                @SuppressWarnings({ "rawtypes", "unchecked" })
045ef6
+                ObjectReference objRef = clsType.newInstance(mainThread,
045ef6
+                                                             constructor,
045ef6
+                                                             new ArrayList(0),
045ef6
+                                                             ObjectReference.INVOKE_NONVIRTUAL);
045ef6
+                if (objRef.isCollected()) {
045ef6
+                    System.out.println("DEBUG: Object got GC'ed before we can use it. NO-OP.");
045ef6
+                    continue;
045ef6
+                }
045ef6
+                invoke("testMethod", "(LOomDebugTestTarget$FooCls;)V", objRef);
045ef6
+            }
045ef6
+        } catch (InvocationException e) {
045ef6
+            handleFailure(e);
045ef6
+        }
045ef6
+    }
045ef6
+
045ef6
+    /*
045ef6
+     * Test case: Array reference as method parameter.
045ef6
+     */
045ef6
+    @SuppressWarnings("unused") // called via reflection
045ef6
+    private void test2() throws Exception {
045ef6
+        System.out.println("DEBUG: ------------> Running test2");
045ef6
+        try {
045ef6
+            Field field = targetClass.fieldByName("byteArray");
045ef6
+            ArrayType arrType = (ArrayType)field.type();
045ef6
+
045ef6
+            for (int i = 0; i < 15; i++) {
045ef6
+                ArrayReference byteArrayVal = arrType.newInstance(3000000);
045ef6
+                if (byteArrayVal.isCollected()) {
045ef6
+                    System.out.println("DEBUG: Object got GC'ed before we can use it. NO-OP.");
045ef6
+                    continue;
045ef6
+                }
045ef6
+                invoke("testPrimitive", "([B)V", byteArrayVal);
045ef6
+            }
045ef6
+        } catch (VMOutOfMemoryException e) {
045ef6
+            defaultHandleOOMFailure(e);
045ef6
+        }
045ef6
+    }
045ef6
+
045ef6
+    /*
045ef6
+     * Test case: Array reference as return value.
045ef6
+     */
045ef6
+    @SuppressWarnings("unused") // called via reflection
045ef6
+    private void test3() throws Exception {
045ef6
+        System.out.println("DEBUG: ------------> Running test3");
045ef6
+        try {
045ef6
+            for (int i = 0; i < 15; i++) {
045ef6
+                invoke("testPrimitiveArrRetval",
045ef6
+                       "()[B",
045ef6
+                       Collections.EMPTY_LIST,
045ef6
+                       vm().mirrorOfVoid());
045ef6
+            }
045ef6
+        } catch (InvocationException e) {
045ef6
+            handleFailure(e);
045ef6
+        }
045ef6
+    }
045ef6
+
045ef6
+    /*
045ef6
+     * Test case: Object reference as return value.
045ef6
+     */
045ef6
+    @SuppressWarnings("unused") // called via reflection
045ef6
+    private void test4() throws Exception {
045ef6
+        System.out.println("DEBUG: ------------> Running test4");
045ef6
+        try {
045ef6
+            for (int i = 0; i < 15; i++) {
045ef6
+                invoke("testFooClsRetval",
045ef6
+                       "()LOomDebugTestTarget$FooCls;",
045ef6
+                       Collections.EMPTY_LIST,
045ef6
+                       vm().mirrorOfVoid());
045ef6
+            }
045ef6
+        } catch (InvocationException e) {
045ef6
+            handleFailure(e);
045ef6
+        }
045ef6
+    }
045ef6
+
045ef6
+    /*
045ef6
+     * Test case: Constructor
045ef6
+     */
045ef6
+    @SuppressWarnings({ "unused", "unchecked", "rawtypes" }) // called via reflection
045ef6
+    private void test5() throws Exception {
045ef6
+        System.out.println("DEBUG: ------------> Running test5");
045ef6
+        try {
045ef6
+            ClassType type = (ClassType)thisObject.type();
045ef6
+            for (int i = 0; i < 15; i++) {
045ef6
+                type.newInstance(mainThread,
045ef6
+                                 findMethod(targetClass, "<init>", "()V"),
045ef6
+                                 new ArrayList(0),
045ef6
+                                 ObjectReference.INVOKE_NONVIRTUAL);
045ef6
+            }
045ef6
+        } catch (InvocationException e) {
045ef6
+            handleFailure(e);
045ef6
+        }
045ef6
+    }
045ef6
+
045ef6
+    private Method getConstructorForClass(ClassType clsType) {
045ef6
+        List<Method> methods = clsType.methodsByName("<init>");
045ef6
+        if (methods.size() != 1) {
045ef6
+            throw new RuntimeException("FAIL. Expected only one, the default, constructor");
045ef6
+        }
045ef6
+        return methods.get(0);
045ef6
+    }
045ef6
+
045ef6
+    private void handleFailure(InvocationException e) {
045ef6
+        // There is no good way to see the OOME diagnostic message in the target since the
045ef6
+        // TestScaffold might throw an exception while trying to print the stack trace. I.e
045ef6
+        // it might get a a VMDisconnectedException before the stack trace printing finishes.
045ef6
+        System.err.println("FAILURE: InvocationException thrown. Trying to determine cause...");
045ef6
+        defaultHandleOOMFailure(e);
045ef6
+    }
045ef6
+
045ef6
+    private void defaultHandleOOMFailure(Exception e) {
045ef6
+        e.printStackTrace();
045ef6
+        failure();
045ef6
+    }
045ef6
+
045ef6
+    @SuppressWarnings({ "rawtypes", "unchecked" })
045ef6
+    void invoke(String methodName, String methodSig, Value value)
045ef6
+            throws Exception {
045ef6
+        List args = new ArrayList(1);
045ef6
+        args.add(value);
045ef6
+        invoke(methodName, methodSig, args, value);
045ef6
+    }
045ef6
+
045ef6
+    void invoke(String methodName,
045ef6
+                String methodSig,
045ef6
+                @SuppressWarnings("rawtypes") List args,
045ef6
+                Value value) throws Exception {
045ef6
+        Method method = findMethod(targetClass, methodName, methodSig);
045ef6
+        if ( method == null) {
045ef6
+            failure("FAILED: Can't find method: "
045ef6
+                    + methodName  + " for class = " + targetClass);
045ef6
+            return;
045ef6
+        }
045ef6
+        invoke(method, args, value);
045ef6
+    }
045ef6
+
045ef6
+    @SuppressWarnings({ "rawtypes", "unchecked" })
045ef6
+    void invoke(Method method, List args, Value value) throws Exception {
045ef6
+        thisObject.invokeMethod(mainThread, method, args, 0);
045ef6
+        System.out.println("DEBUG: Done invoking method via debugger.");
045ef6
+    }
045ef6
+
045ef6
+    Value fieldValue(String fieldName) {
045ef6
+        Field field = targetClass.fieldByName(fieldName);
045ef6
+        return thisObject.getValue(field);
045ef6
+    }
045ef6
+
045ef6
+    // Determine the pass/fail status on some heuristic and don't fail the
045ef6
+    // test if < 3 of the total number of tests (currently 5) fail. This also
045ef6
+    // has the nice side effect that all tests are first attempted and only
045ef6
+    // all tests ran an overall pass/fail status is determined.
045ef6
+    private static void determineOverallTestStatus(OomDebugTest oomTest)
045ef6
+                                   throws IOException, FileNotFoundException {
045ef6
+        Properties resultProps = new Properties();
045ef6
+        if (!RESULT_FILE.exists()) {
045ef6
+            RESULT_FILE.createNewFile();
045ef6
+        }
045ef6
+        FileInputStream fin = null;
045ef6
+        try {
045ef6
+            fin = new FileInputStream(RESULT_FILE);
045ef6
+            resultProps.load(fin);
045ef6
+            resultProps.put(oomTest.testMethod,
045ef6
+                            Integer.toString(oomTest.failedTests));
045ef6
+        } finally {
045ef6
+            if (fin != null) {
045ef6
+                fin.close();
045ef6
+            }
045ef6
+        }
045ef6
+        System.out.println("DEBUG: Finished running test '"
045ef6
+                           + oomTest.testMethod + "'.");
045ef6
+        if (LAST_TEST.equals(oomTest.testMethod)) {
045ef6
+            System.out.println("DEBUG: Determining overall test status.");
045ef6
+            Set<String> actualTestsRun = new HashSet<String>();
045ef6
+            int totalTests = ALL_TESTS.length;
045ef6
+            int failedTests = 0;
045ef6
+            for (Object key: resultProps.keySet()) {
045ef6
+                actualTestsRun.add((String)key);
045ef6
+                Object propVal = resultProps.get(key);
045ef6
+                int value = Integer.parseInt((String)propVal);
045ef6
+                failedTests += value;
045ef6
+            }
045ef6
+            if (!ALL_TESTS_SET.equals(actualTestsRun)) {
045ef6
+                String errorMsg = "Test failed! Expected to run tests '"
045ef6
+                        + ALL_TESTS_SET + "', but only these were run '"
045ef6
+                        + actualTestsRun + "'";
045ef6
+                throw new RuntimeException(errorMsg);
045ef6
+            }
045ef6
+            if (failedTests >= 3) {
045ef6
+                String errorMsg = "Test failed. Expected < 3 sub-tests to fail "
045ef6
+                                  + "for a pass. Got " + failedTests
045ef6
+                                  + " failed tests out of " + totalTests + ".";
045ef6
+                throw new RuntimeException(errorMsg);
045ef6
+            }
045ef6
+            RESULT_FILE.delete();
045ef6
+            System.out.println("All " + totalTests + " tests passed.");
045ef6
+        } else {
045ef6
+            System.out.println("DEBUG: More tests to run. Continuing.");
045ef6
+            FileOutputStream fout = null;
045ef6
+            try {
045ef6
+                fout = new FileOutputStream(RESULT_FILE);
045ef6
+                resultProps.store(fout, "Storing results after test "
045ef6
+                                         + oomTest.testMethod);
045ef6
+            } finally {
045ef6
+                if (fout != null) {
045ef6
+                    fout.close();
045ef6
+                }
045ef6
+            }
045ef6
+        }
045ef6
+    }
045ef6
+
045ef6
+    public static void main(String[] args) throws Exception {
045ef6
+        System.setProperty("test.vm.opts", "-Xmx40m"); // Set debuggee VM option
045ef6
+        OomDebugTest oomTest = new OomDebugTest(args);
045ef6
+        try {
045ef6
+            oomTest.startTests();
045ef6
+        } catch (Throwable e) {
045ef6
+            System.out.println("DEBUG: Got exception for test run. " + e);
045ef6
+            e.printStackTrace();
045ef6
+            oomTest.failure();
045ef6
+        }
045ef6
+        determineOverallTestStatus(oomTest);
045ef6
+    }
045ef6
+
045ef6
+}
045ef6
diff --git a/test/com/sun/jdi/oom/OomDebugTestSetup.sh b/test/com/sun/jdi/oom/OomDebugTestSetup.sh
045ef6
new file mode 100644
045ef6
--- /dev/null
045ef6
+++ openjdk/jdk/test/com/sun/jdi/oom/OomDebugTestSetup.sh
045ef6
@@ -0,0 +1,46 @@
045ef6
+#!/bin/sh
045ef6
+#
045ef6
+# Copyright (c) 2016 Red Hat Inc.
045ef6
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
045ef6
+#
045ef6
+# This code is free software; you can redistribute it and/or modify it
045ef6
+# under the terms of the GNU General Public License version 2 only, as
045ef6
+# published by the Free Software Foundation.
045ef6
+#
045ef6
+# This code is distributed in the hope that it will be useful, but WITHOUT
045ef6
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
045ef6
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
045ef6
+# version 2 for more details (a copy is included in the LICENSE file that
045ef6
+# accompanied this code).
045ef6
+#
045ef6
+# You should have received a copy of the GNU General Public License version
045ef6
+# 2 along with this work; if not, write to the Free Software Foundation,
045ef6
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
045ef6
+#
045ef6
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
045ef6
+# or visit www.oracle.com if you need additional information or have any
045ef6
+# questions.
045ef6
+#
045ef6
+
045ef6
+
045ef6
+if [ "${TESTSRC}" = "" ]
045ef6
+then
045ef6
+  echo "TESTSRC not set.  Test cannot execute.  Failed."
045ef6
+  exit 1
045ef6
+fi
045ef6
+echo "TESTSRC=${TESTSRC}"
045ef6
+
045ef6
+if [ "${TESTJAVA}" = "" ]
045ef6
+then
045ef6
+  echo "TESTJAVA not set.  Test cannot execute.  Failed."
045ef6
+  exit 1
045ef6
+fi
045ef6
+echo "TESTJAVA=${TESTJAVA}"
045ef6
+
045ef6
+if [ "${TESTCLASSES}" = "" ]
045ef6
+then
045ef6
+  echo "TESTCLASSES not set.  Test cannot execute.  Failed."
045ef6
+  exit 1
045ef6
+fi
045ef6
+
045ef6
+cp ${TESTSRC}/@debuggeeVMOptions ${TESTCLASSES}/