Blame 8153711-pr3313-rh1284948.patch

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