8304839: Move TestScaffold.main() to the separate class DebugeeWrapper

Reviewed-by: amenkov, cjplummer
This commit is contained in:
Leonid Mesnik 2023-09-26 18:35:13 +00:00
parent 36ac83904c
commit ee9776fa23
19 changed files with 158 additions and 121 deletions

View file

@ -50,12 +50,12 @@ class ClassesByName2Targ {
System.out.println("Howdy!"); System.out.println("Howdy!");
try { try {
Thread zero = TestScaffold.newThread (() -> { Thread zero = DebuggeeWrapper.newThread (() -> {
System.setProperty("java.awt.headless", "true"); System.setProperty("java.awt.headless", "true");
java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit(); java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit();
}, "ZERO"); }, "ZERO");
Thread one = TestScaffold.newThread (() -> { Thread one = DebuggeeWrapper.newThread (() -> {
try { try {
java.security.KeyPairGenerator keyGen = java.security.KeyPairGenerator keyGen =
java.security.KeyPairGenerator.getInstance("DSA", "SUN"); java.security.KeyPairGenerator.getInstance("DSA", "SUN");
@ -64,7 +64,7 @@ class ClassesByName2Targ {
} }
}, "ONE"); }, "ONE");
Thread two = TestScaffold.newThread (() -> { Thread two = DebuggeeWrapper.newThread (() -> {
try { try {
String s = String.format("%02x", 0xff); String s = String.format("%02x", 0xff);
} catch (Exception e) { } catch (Exception e) {

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ThreadFactory;
public class DebuggeeWrapper {
public static String PROPERTY_NAME = "main.wrapper";
private static final String OLD_MAIN_THREAD_NAME = "old-m-a-i-n";
private static ThreadFactory threadFactory = r -> new Thread(r);
private static final String wrapperName = System.getProperty(PROPERTY_NAME);
public static String getWrapperName() {
return wrapperName;
}
public static boolean isVirtual() {
return "Virtual".equals(wrapperName);
}
public static Thread newThread(Runnable task) {
return threadFactory.newThread(task);
}
public static Thread newThread(Runnable task, String name) {
Thread t = newThread(task);
t.setName(name);
return t;
}
public static void main(String[] args) throws Throwable {
String className = args[0];
String[] classArgs = new String[args.length - 1];
System.arraycopy(args, 1, classArgs, 0, args.length - 1);
Class c = Class.forName(className);
java.lang.reflect.Method mainMethod = c.getMethod("main", new Class[] { String[].class });
mainMethod.setAccessible(true);
if (isVirtual()) {
threadFactory = Thread.ofVirtual().factory();
MainThreadGroup tg = new MainThreadGroup();
Thread vthread = Thread.ofVirtual().unstarted(() -> {
try {
mainMethod.invoke(null, new Object[] { classArgs });
} catch (InvocationTargetException e) {
tg.uncaughtThrowable = e.getCause();
} catch (Throwable error) {
tg.uncaughtThrowable = error;
}
});
Thread.currentThread().setName(OLD_MAIN_THREAD_NAME);
vthread.setName("main");
vthread.start();
vthread.join();
if (tg.uncaughtThrowable != null) {
// Note we cant just rethrow tg.uncaughtThrowable because there are tests
// that track ExceptionEvents, and they will complain about the extra
// exception. So instead mimic what happens when the main thread exits
// with an exception.
System.out.println("Uncaught Exception: " + tg.uncaughtThrowable);
tg.uncaughtThrowable.printStackTrace(System.out);
System.exit(1);
}
} else if (getWrapperName().equals("Kernel")) {
MainThreadGroup tg = new MainThreadGroup();
Thread t = new Thread(tg, () -> {
try {
mainMethod.invoke(null, new Object[] { classArgs });
} catch (InvocationTargetException e) {
tg.uncaughtThrowable = e.getCause();
} catch (Throwable error) {
tg.uncaughtThrowable = error;
}
});
t.start();
t.join();
if (tg.uncaughtThrowable != null) {
throw new RuntimeException(tg.uncaughtThrowable);
}
} else {
mainMethod.invoke(null, new Object[] { classArgs });
}
}
static class MainThreadGroup extends ThreadGroup {
MainThreadGroup() {
super("MainThreadGroup");
}
public void uncaughtException(Thread t, Throwable e) {
if (e instanceof ThreadDeath) {
return;
}
e.printStackTrace(System.err);
uncaughtThrowable = e;
}
Throwable uncaughtThrowable = null;
}
}

View file

@ -74,8 +74,8 @@ class DeferredStepTestTarg {
jj1 obj1 = new jj1(); jj1 obj1 = new jj1();
jj2 obj2 = new jj2(); jj2 obj2 = new jj2();
Thread thread1 = TestScaffold.newThread(obj1, "jj1"); Thread thread1 = DebuggeeWrapper.newThread(obj1, "jj1");
Thread thread2 = TestScaffold.newThread(obj2, "jj2"); Thread thread2 = DebuggeeWrapper.newThread(obj2, "jj2");
thread1.start(); thread1.start();
thread2.start(); thread2.start();
// Threads might be deamon threads, so wait here for them to complete. // Threads might be deamon threads, so wait here for them to complete.

View file

@ -843,7 +843,7 @@ abstract class EATestCaseBaseTarget extends EATestCaseBaseShared implements Runn
public static void staticSetUp() { public static void staticSetUp() {
inflatedLock = new XYVal(1, 1); inflatedLock = new XYVal(1, 1);
synchronized (inflatedLock) { synchronized (inflatedLock) {
inflatorThread = TestScaffold.newThread(() -> { inflatorThread = DebuggeeWrapper.newThread(() -> {
synchronized (inflatedLock) { synchronized (inflatedLock) {
inflatedLockIsPermanentlyInflated = true; inflatedLockIsPermanentlyInflated = true;
inflatedLock.notify(); // main thread inflatedLock.notify(); // main thread

View file

@ -169,7 +169,7 @@ public class ForceEarlyReturnTest extends TestScaffold {
protected void runTests() throws Exception { protected void runTests() throws Exception {
BreakpointEvent bpe = startTo("ForceEarlyReturnTestTarg", "loopOrSleep", "()V"); BreakpointEvent bpe = startTo("ForceEarlyReturnTestTarg", "loopOrSleep", "()V");
ThreadReference mainThread = bpe.thread(); ThreadReference mainThread = bpe.thread();
boolean is_vthread_mode = "Virtual".equals(System.getProperty("main.wrapper")); boolean is_vthread_mode = DebuggeeWrapper.isVirtual();
// Resume main thread until it is in Thread.sleep() or the infinite loop. // Resume main thread until it is in Thread.sleep() or the infinite loop.
mainThread.resume(); mainThread.resume();

View file

@ -47,7 +47,7 @@ class InterruptHangTarg {
public static void main(String[] args){ public static void main(String[] args){
int answer = 0; int answer = 0;
System.out.println("Howdy!"); System.out.println("Howdy!");
Thread interruptorThread = TestScaffold.newThread(new Interruptor(Thread.currentThread())); Thread interruptorThread = DebuggeeWrapper.newThread(new Interruptor(Thread.currentThread()));
synchronized(sync) { synchronized(sync) {
interruptorThread.start(); interruptorThread.start();

View file

@ -54,8 +54,8 @@ class InvokeHangTarg implements Runnable {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("Howdy!"); System.out.println("Howdy!");
Thread t1 = TestScaffold.newThread(new InvokeHangTarg(), name1); Thread t1 = DebuggeeWrapper.newThread(new InvokeHangTarg(), name1);
Thread t2 = TestScaffold.newThread(new InvokeHangTarg(), name2); Thread t2 = DebuggeeWrapper.newThread(new InvokeHangTarg(), name2);
t1.start(); t1.start();
t2.start(); t2.start();

View file

@ -39,7 +39,7 @@ class JdbLockTestTarg {
static String jj = "jj"; static String jj = "jj";
public static void main(String args[]) { public static void main(String args[]) {
synchronized(jj) { synchronized(jj) {
Thread xx = TestScaffold.newThread(new Sleeper()); Thread xx = DebuggeeWrapper.newThread(new Sleeper());
xx.start(); xx.start();
// Give the sleeper a chance to run and get to // Give the sleeper a chance to run and get to
// the synchronized statement. // the synchronized statement.

View file

@ -51,9 +51,9 @@ class JdbStopThreadidTestTarg {
MyTask myTask1 = test.new MyTask(); MyTask myTask1 = test.new MyTask();
MyTask myTask2 = test.new MyTask(); MyTask myTask2 = test.new MyTask();
MyTask myTask3 = test.new MyTask(); MyTask myTask3 = test.new MyTask();
Thread myThread1 = TestScaffold.newThread(myTask1, "MYTHREAD-1"); Thread myThread1 = DebuggeeWrapper.newThread(myTask1, "MYTHREAD-1");
Thread myThread2 = TestScaffold.newThread(myTask2, "MYTHREAD-2"); Thread myThread2 = DebuggeeWrapper.newThread(myTask2, "MYTHREAD-2");
Thread myThread3 = TestScaffold.newThread(myTask3, "MYTHREAD-3"); Thread myThread3 = DebuggeeWrapper.newThread(myTask3, "MYTHREAD-3");
synchronized (lockObj) { synchronized (lockObj) {
myThread1.start(); myThread1.start();

View file

@ -61,7 +61,7 @@ class MonitorEventTestTarg {
endingMonitor = new Object(); endingMonitor = new Object();
startingMonitor = new Object(); startingMonitor = new Object();
Thread t1 = TestScaffold.newThread(new MyTask()); Thread t1 = DebuggeeWrapper.newThread(new MyTask());
foo(); foo();
aboutEnterLock = false; aboutEnterLock = false;

View file

@ -143,7 +143,7 @@ class MultiBreakpointsTarg {
// //
//final String threadName = "DebuggeeThread: " + num; //final String threadName = "DebuggeeThread: " + num;
final String threadName = "" + num; final String threadName = "" + num;
Thread thrd = TestScaffold.newThread(() -> { Thread thrd = DebuggeeWrapper.newThread(() -> {
synchronized( isr ) { synchronized( isr ) {
boolean done = false; boolean done = false;
try { try {

View file

@ -186,7 +186,7 @@ public class PopAsynchronousTest extends TestScaffold {
/* /*
* start popping wildly away * start popping wildly away
*/ */
TestScaffold.newThread(new HarassThread()).start(); DebuggeeWrapper.newThread(new HarassThread()).start();
/* /*
* resume the target listening for events * resume the target listening for events

View file

@ -318,8 +318,7 @@ public class PopFramesTest extends TestScaffold {
* works. So you have an unmounted virtual thread with no native frames, which * works. So you have an unmounted virtual thread with no native frames, which
* results in OpaqueFrameException being thrown. * results in OpaqueFrameException being thrown.
*/ */
String mainWrapper = System.getProperty("main.wrapper"); if (DebuggeeWrapper.isVirtual()) {
if ("Virtual".equals(mainWrapper)) {
expected_exception = OpaqueFrameException.class; expected_exception = OpaqueFrameException.class;
} else { } else {
expected_exception = NativeMethodException.class; expected_exception = NativeMethodException.class;

View file

@ -43,8 +43,8 @@ class ResumeOneThreadTarg implements Runnable {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println(" Debuggee: Howdy!"); System.out.println(" Debuggee: Howdy!");
Thread t1 = TestScaffold.newThread(new ResumeOneThreadTarg(), name1); Thread t1 = DebuggeeWrapper.newThread(new ResumeOneThreadTarg(), name1);
Thread t2 = TestScaffold.newThread(new ResumeOneThreadTarg(), name2); Thread t2 = DebuggeeWrapper.newThread(new ResumeOneThreadTarg(), name2);
t1.start(); t1.start();
t2.start(); t2.start();
// We must block until these threads exit. Otherwise for virtual threads // We must block until these threads exit. Otherwise for virtual threads

View file

@ -174,7 +174,7 @@ public class SetLocalWhileThreadInNative extends TestScaffold {
} }
} }
} }
boolean isVirtualThread = "Virtual".equals(System.getProperty("main.wrapper")); boolean isVirtualThread = DebuggeeWrapper.isVirtual();
Asserts.assertTrue(caughtOFE == isVirtualThread); Asserts.assertTrue(caughtOFE == isVirtualThread);
Asserts.assertTrue(changedLocal == !isVirtualThread); Asserts.assertTrue(changedLocal == !isVirtualThread);

View file

@ -50,8 +50,8 @@ class SimulResumerTarg implements Runnable {
static int count = 10000; static int count = 10000;
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("Howdy!"); System.out.println("Howdy!");
Thread t1 = TestScaffold.newThread(new SimulResumerTarg(), name1); Thread t1 = DebuggeeWrapper.newThread(new SimulResumerTarg(), name1);
Thread t2 = TestScaffold.newThread(new SimulResumerTarg(), name2); Thread t2 = DebuggeeWrapper.newThread(new SimulResumerTarg(), name2);
t1.start(); t1.start();
t2.start(); t2.start();

View file

@ -24,10 +24,9 @@
import com.sun.jdi.*; import com.sun.jdi.*;
import com.sun.jdi.request.*; import com.sun.jdi.request.*;
import com.sun.jdi.event.*; import com.sun.jdi.event.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
import java.io.*; import java.io.*;
import java.util.concurrent.ThreadFactory;
/** /**
* Framework used by all JDI regression tests * Framework used by all JDI regression tests
@ -67,7 +66,6 @@ abstract public class TestScaffold extends TargetAdapter {
final String[] args; final String[] args;
protected boolean testFailed = false; protected boolean testFailed = false;
protected long startTime; protected long startTime;
public static final String OLD_MAIN_THREAD_NAME = "old-m-a-i-n";
static private class ArgInfo { static private class ArgInfo {
String targetVMArgs = ""; String targetVMArgs = "";
@ -513,8 +511,8 @@ abstract public class TestScaffold extends TargetAdapter {
// argInfo.targetAppCommandLine : Frames2Targ // argInfo.targetAppCommandLine : Frames2Targ
// argInfo.targetVMArgs : -Xss4M // argInfo.targetVMArgs : -Xss4M
// The result with wrapper enabled: // The result with wrapper enabled:
// argInfo.targetAppCommandLine : TestScaffold Virtual Frames2Targ // argInfo.targetAppCommandLine : DebuggeeWrapper Frames2Targ
// argInfo.targetVMArgs : -Xss4M // argInfo.targetVMArgs : -Xss4M -Dmain.wrapper=Virtual
boolean classNameParsed = false; boolean classNameParsed = false;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
String arg = args[i].trim(); String arg = args[i].trim();
@ -549,12 +547,12 @@ abstract public class TestScaffold extends TargetAdapter {
} }
} }
// Need to change args to run wrapper using command like 'TestScaffold Virtual <app-name>' // Need to change args to run wrapper using command like 'DebuggeeWrapper <app-name>'
String mainWrapper = System.getProperty("main.wrapper"); // and set property 'main.wrapper' so test could use DebuggeeWrapper.isVirtual() method
String mainWrapper = DebuggeeWrapper.getWrapperName();
if (mainWrapper != null && !argInfo.targetAppCommandLine.isEmpty()) { if (mainWrapper != null && !argInfo.targetAppCommandLine.isEmpty()) {
argInfo.targetVMArgs += "-Dmain.wrapper=" + mainWrapper; argInfo.targetVMArgs += "-D" + DebuggeeWrapper.PROPERTY_NAME + "=" + mainWrapper;
argInfo.targetAppCommandLine = TestScaffold.class.getName() + ' ' argInfo.targetAppCommandLine = DebuggeeWrapper.class.getName() + ' ' + argInfo.targetAppCommandLine;
+ mainWrapper + ' ' + argInfo.targetAppCommandLine;
} else if ("true".equals(System.getProperty("test.enable.preview"))) { } else if ("true".equals(System.getProperty("test.enable.preview"))) {
// the test specified @enablePreview. // the test specified @enablePreview.
argInfo.targetVMArgs += "--enable-preview "; argInfo.targetVMArgs += "--enable-preview ";
@ -1044,86 +1042,4 @@ abstract public class TestScaffold extends TargetAdapter {
vmDisconnected = true; vmDisconnected = true;
} }
private static ThreadFactory threadFactory = r -> new Thread(r);
public static void main(String[] args) throws Throwable {
String wrapper = args[0];
String className = args[1];
String[] classArgs = new String[args.length - 2];
System.arraycopy(args, 2, classArgs, 0, args.length - 2);
Class c = Class.forName(className);
java.lang.reflect.Method mainMethod = c.getMethod("main", new Class[] { String[].class });
mainMethod.setAccessible(true);
if (wrapper.equals("Virtual")) {
threadFactory = Thread.ofVirtual().factory();
MainThreadGroup tg = new MainThreadGroup();
// TODO fix to set virtual scheduler group when become available
Thread vthread = Thread.ofVirtual().unstarted(() -> {
try {
mainMethod.invoke(null, new Object[] { classArgs });
} catch (InvocationTargetException e) {
tg.uncaughtThrowable = e.getCause();
} catch (Throwable error) {
tg.uncaughtThrowable = error;
}
});
Thread.currentThread().setName(OLD_MAIN_THREAD_NAME);
vthread.setName("main");
vthread.start();
vthread.join();
if (tg.uncaughtThrowable != null) {
// Note we cant just rethrow tg.uncaughtThrowable because there are tests
// that track ExceptionEvents, and they will complain about the extra
// exception. So instead mimic what happens when the main thread exits
// with an exception.
System.out.println("Uncaught Exception: " + tg.uncaughtThrowable);
tg.uncaughtThrowable.printStackTrace(System.out);
System.exit(1);
}
} else if (wrapper.equals("Kernel")) {
MainThreadGroup tg = new MainThreadGroup();
Thread t = new Thread(tg, () -> {
try {
mainMethod.invoke(null, new Object[] { classArgs });
} catch (InvocationTargetException e) {
tg.uncaughtThrowable = e.getCause();
} catch (Throwable error) {
tg.uncaughtThrowable = error;
}
});
t.start();
t.join();
if (tg.uncaughtThrowable != null) {
throw new RuntimeException(tg.uncaughtThrowable);
}
} else {
mainMethod.invoke(null, new Object[] { classArgs });
}
}
static class MainThreadGroup extends ThreadGroup {
MainThreadGroup() {
super("MainThreadGroup");
}
public void uncaughtException(Thread t, Throwable e) {
if (e instanceof ThreadDeath) {
return;
}
e.printStackTrace(System.err);
uncaughtThrowable = e;
}
Throwable uncaughtThrowable = null;
}
public static Thread newThread(Runnable task) {
return threadFactory.newThread(task);
}
public static Thread newThread(Runnable task, String name) {
Thread t = newThread(task);
t.setName(name);
return t;
}
} }

View file

@ -58,19 +58,18 @@ class ThreadMemoryLeakTarg {
while (System.currentTimeMillis() - startTime < 100 * 1000) { while (System.currentTimeMillis() - startTime < 100 * 1000) {
iterations++; iterations++;
semaphore.acquire(); semaphore.acquire();
TestScaffold.newThread(() -> { DebuggeeWrapper.newThread(() -> {
adder.increment(); adder.increment();
long sum = adder.sum(); long sum = adder.sum();
if ((sum % 1000) == 0) { if ((sum % 1000) == 0) {
System.out.println("Progress: " + sum); System.out.println("Progress: " + sum);
} }
try { try {
String mainWrapper = System.getProperty("main.wrapper");
// Virtual thread creation tends to overwhelm the debugger, // Virtual thread creation tends to overwhelm the debugger,
// leading to high memory use for all the unprocessed events // leading to high memory use for all the unprocessed events
// that get queued up, so we need to slow it down a bit more // that get queued up, so we need to slow it down a bit more
// than we do for platform threads to avoid getting OOME. // than we do for platform threads to avoid getting OOME.
long timeToSleep = "Virtual".equals(mainWrapper) ? 100 : 50; long timeToSleep = DebuggeeWrapper.isVirtual() ? 100 : 50;
Thread.sleep(timeToSleep); Thread.sleep(timeToSleep);
} }
catch (InterruptedException e) { catch (InterruptedException e) {

View file

@ -50,8 +50,8 @@ class TwoThreadsTarg implements Runnable {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("Howdy!"); System.out.println("Howdy!");
Thread t1 = TestScaffold.newThread(new TwoThreadsTarg(), name1); Thread t1 = DebuggeeWrapper.newThread(new TwoThreadsTarg(), name1);
Thread t2 = TestScaffold.newThread(new TwoThreadsTarg(), name2); Thread t2 = DebuggeeWrapper.newThread(new TwoThreadsTarg(), name2);
t1.start(); t1.start();
t2.start(); t2.start();