Blame SOURCES/8153711-pr3313-rh1284948.patch

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