8039499: Add all common classes used by tests on RTM support to testlibrary
Reviewed-by: kvn, iignatyev
Contributed-by: filipp.zhinkin@oracle.com
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/AbortProvoker.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import java.util.Objects;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+import com.oracle.java.testlibrary.Asserts;
+import com.oracle.java.testlibrary.Utils;
+import sun.misc.Unsafe;
+
+/**
+ * Base class for different transactional execution abortion
+ * provokers aimed to force abort due to specified reason.
+ */
+public abstract class AbortProvoker implements CompilableTest {
+ public static final long DEFAULT_ITERATIONS = 10000L;
+ /**
+ * Inflates monitor associated with object {@code monitor}.
+ * Inflation is forced by entering the same monitor from
+ * two different threads.
+ *
+ * @param monitor monitor to be inflated.
+ * @return inflated monitor.
+ * @throws Exception if something went wrong.
+ */
+ public static Object inflateMonitor(Object monitor) throws Exception {
+ Unsafe unsafe = Utils.getUnsafe();
+ CyclicBarrier barrier = new CyclicBarrier(2);
+
+ Runnable inflatingRunnable = () -> {
+ unsafe.monitorEnter(monitor);
+ try {
+ barrier.await();
+ barrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException(
+ "Synchronization issue occurred.", e);
+ } finally {
+ unsafe.monitorExit(monitor);
+ }
+ };
+
+ Thread t = new Thread(inflatingRunnable);
+ t.start();
+ // Wait until thread t enters the monitor.
+ barrier.await();
+ // At this point monitor will be owned by thread t,
+ // so our attempt to enter the same monitor will force
+ // monitor inflation.
+ Asserts.assertFalse(unsafe.tryMonitorEnter(monitor),
+ "Not supposed to enter the monitor first");
+ barrier.await();
+ t.join();
+ return monitor;
+ }
+
+
+ /**
+ * Get instance of specified AbortProvoker, inflate associated monitor
+ * if needed and then invoke forceAbort method in a loop.
+ *
+ * Usage:
+ * AbortProvoker <AbortType name> [<inflate monitor>
+ * [<iterations> [ <delay>]]]
+ *
+ * Default parameters are:
+ * <ul>
+ * <li>inflate monitor = <b>true</b></li>
+ * <li>iterations = {@code AbortProvoker.DEFAULT_ITERATIONS}</li>
+ * <li>delay = <b>0</b></li>
+ * </ul>
+ */
+ public static void main(String args[]) throws Throwable {
+ Asserts.assertGT(args.length, 0, "At least one argument is required.");
+
+ AbortType abortType = AbortType.lookup(Integer.valueOf(args[0]));
+ boolean monitorShouldBeInflated = true;
+ long iterations = AbortProvoker.DEFAULT_ITERATIONS;
+
+ if (args.length > 1) {
+ monitorShouldBeInflated = Boolean.valueOf(args[1]);
+
+ if (args.length > 2) {
+ iterations = Long.valueOf(args[2]);
+
+ if (args.length > 3) {
+ Thread.sleep(Integer.valueOf(args[3]));
+ }
+ }
+ }
+
+ AbortProvoker provoker = abortType.provoker();
+
+ if (monitorShouldBeInflated) {
+ provoker.inflateMonitor();
+ }
+
+ for (long i = 0; i < iterations; i++) {
+ provoker.forceAbort();
+ }
+ }
+
+ protected final Object monitor;
+
+ protected AbortProvoker() {
+ this(new Object());
+ }
+
+ protected AbortProvoker(Object monitor) {
+ this.monitor = Objects.requireNonNull(monitor);
+ }
+
+ /**
+ * Inflates monitor used by this AbortProvoker instance.
+ * @throws Exception
+ */
+ public void inflateMonitor() throws Exception {
+ AbortProvoker.inflateMonitor(monitor);
+ }
+
+ /**
+ * Forces transactional execution abortion.
+ */
+ public abstract void forceAbort();
+
+ /**
+ * Returns names of all methods that have to be compiled
+ * in order to successfully force transactional execution
+ * abortion.
+ *
+ * @return array with methods' names that have to be compiled.
+ */
+ @Override
+ public String[] getMethodsToCompileNames() {
+ return new String[] { getMethodWithLockName() };
+ }
+
+ /**
+ * Returns name of the method that will contain monitor whose locking
+ * will be elided using transactional execution.
+ *
+ * @return name of the method that will contain elided lock.
+ */
+ @Override
+ public String getMethodWithLockName() {
+ return this.getClass().getName() + "::forceAbort";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/AbortType.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import com.oracle.java.testlibrary.Asserts;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Type of transactional execution abort.
+ * For more details on different abort types please see
+ * shared/vm/runtime/rtmLocking.hpp
+ */
+public enum AbortType {
+ XABORT(0),
+ RETRIABLE(1),
+ MEM_CONFLICT(2),
+ BUF_OVERFLOW(3),
+ DEBUG_BREAKPOINT(4),
+ NESTED_ABORT(5);
+
+ private final int type;
+ private static final Map<Integer, AbortType> LOOKUP_MAP = new HashMap<>();
+
+ static {
+ for (AbortType abortType : AbortType.values()) {
+ Asserts.assertFalse(LOOKUP_MAP.containsKey(abortType.type),
+ "Abort type values should be unique.");
+ LOOKUP_MAP.put(abortType.type, abortType);
+ }
+ }
+
+ private AbortType(int type) {
+ this.type = type;
+ }
+
+ /**
+ * Returns AbortProvoker for aborts represented by this abort type.
+ *
+ * @return an AbortProvoker instance
+ */
+ public AbortProvoker provoker() {
+ return AbortType.createNewProvoker(this);
+ }
+
+ public static AbortType lookup(int type) {
+ Asserts.assertLT(type, AbortType.values().length,
+ "Unknown abort type.");
+ return LOOKUP_MAP.get(type);
+ }
+
+ /**
+ * Returns transaction execution abort provoker for specified abortion type.
+ *
+ * @param type a type of abort which will be forced by returned
+ * AbortProvoker instance.
+ * @return AbortProvoker instance that will force abort of specified type
+ * @throws RuntimeException if there is no provoker for specified type
+ */
+ private static AbortProvoker createNewProvoker(AbortType type) {
+ switch (type) {
+ case XABORT:
+ return new XAbortProvoker();
+ case MEM_CONFLICT:
+ return new MemoryConflictProvoker();
+ case BUF_OVERFLOW:
+ return new BufferOverflowProvoker();
+ case NESTED_ABORT:
+ return new NestedAbortProvoker();
+ default:
+ throw new RuntimeException("No provoker exists for type "
+ + type.name());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(type);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/BufferOverflowProvoker.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+/**
+ * In order to provoke transactional execution abort due to
+ * internal's buffer overflow BufferOverflowProvoker modifies
+ * 1MB of BYTES during single transaction.
+ */
+class BufferOverflowProvoker extends AbortProvoker {
+ /**
+ * To force buffer overflow abort we modify memory region with
+ * size more then L1d cache size.
+ */
+ private static final int MORE_THAN_L1D_SIZE = 1024 * 1024;
+ private static final byte[] DATA = new byte[MORE_THAN_L1D_SIZE];
+
+ @Override
+ public void forceAbort() {
+ synchronized(monitor) {
+ for (int i = 0; i < BufferOverflowProvoker.DATA.length; i++) {
+ BufferOverflowProvoker.DATA[i]++;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/BusyLock.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import com.oracle.java.testlibrary.Utils;
+import sun.misc.Unsafe;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+/**
+ * Test case for busy lock scenario.
+ * One thread enters the monitor and sleep for a while.
+ * Another thread is blocked on the same monitor.
+ */
+public class BusyLock implements CompilableTest, Runnable {
+ private static final int DEFAULT_TIMEOUT = 1000;
+ private final CyclicBarrier barrier;
+
+ // Following field have to be static in order to avoid escape analysis.
+ @SuppressWarnings("UnsuedDeclaration")
+ private static int field = 0;
+ private static final Unsafe UNSAFE = Utils.getUnsafe();
+ protected final Object monitor;
+ protected final int timeout;
+
+ public BusyLock() {
+ this(BusyLock.DEFAULT_TIMEOUT);
+ }
+
+ public BusyLock(int timeout) {
+ this.timeout = timeout;
+ this.monitor = new Object();
+ this.barrier = new CyclicBarrier(2);
+ }
+
+ @Override
+ public void run() {
+ try {
+ // wait until forceAbort leave monitor
+ barrier.await();
+ if (UNSAFE.tryMonitorEnter(monitor)) {
+ try {
+ barrier.await();
+ Thread.sleep(timeout);
+ } finally {
+ UNSAFE.monitorExit(monitor);
+ }
+ } else {
+ throw new RuntimeException("Monitor should be entered by " +
+ "::run() first.");
+ }
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException("Synchronization error happened.", e);
+ }
+ }
+
+ public void test() {
+ try {
+ barrier.await();
+ // wait until monitor is locked by a ::run method
+ barrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException("Synchronization error happened.", e);
+ }
+ synchronized(monitor) {
+ BusyLock.field++;
+ }
+ }
+
+ @Override
+ public String getMethodWithLockName() {
+ return this.getClass().getName() + "::test";
+ }
+
+ @Override
+ public String[] getMethodsToCompileNames() {
+ return new String[] { getMethodWithLockName() };
+ }
+
+ /**
+ * Usage:
+ * BusyLock [ <inflate monitor> [ <timeout> ] ]
+ *
+ * Default values are:
+ * <ul>
+ * <li>inflate monitor = {@code true}</li>
+ * <li>timeout = {@code BusyLock.DEFAULT_TIMEOUT}</li>
+ * </ul>
+ */
+ public static void main(String args[]) throws Exception {
+ int timeoutValue = BusyLock.DEFAULT_TIMEOUT;
+ boolean inflateMonitor = true;
+
+ if (args.length > 0 ) {
+ inflateMonitor = Boolean.valueOf(args[0]);
+
+ if (args.length > 1) {
+ timeoutValue = Integer.valueOf(args[1]);
+ }
+ }
+
+ BusyLock busyLock = new BusyLock(timeoutValue);
+
+ if (inflateMonitor) {
+ AbortProvoker.inflateMonitor(busyLock.monitor);
+ }
+
+ Thread t = new Thread(busyLock);
+ t.start();
+ busyLock.test();
+ t.join();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/CompilableTest.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+/**
+ * Interface for test scenarios that contain methods
+ * that should be compiled.
+ */
+public interface CompilableTest {
+ /**
+ * @return array with methods' names that should be compiled.
+ */
+ String[] getMethodsToCompileNames();
+
+ /**
+ * @return name of method with RTM-elided lock.
+ */
+ String getMethodWithLockName();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/MemoryConflictProvoker.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+
+/**
+ * To force transactional execution abort due to memory conflict
+ * one thread should access memory region from transactional region
+ * while another thread should modify the same memory region.
+ * Since this scenario is based on the race condition between threads
+ * you should not expect some particular amount of aborts.
+ */
+class MemoryConflictProvoker extends AbortProvoker {
+ // Following field have to be static in order to avoid escape analysis.
+ @SuppressWarnings("UnsuedDeclaration")
+ private static int field = 0;
+ private static final int INNER_ITERATIONS = 10000;
+ private final CyclicBarrier barrier;
+ /**
+ * This thread will access and modify memory region
+ * from outside of the transaction.
+ */
+ private final Runnable conflictingThread;
+
+ public MemoryConflictProvoker() {
+ this(new Object());
+ }
+
+ public MemoryConflictProvoker(Object monitor) {
+ super(monitor);
+ barrier = new CyclicBarrier(2);
+ conflictingThread = () -> {
+ try {
+ barrier.await();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ for (int i = 0; i < MemoryConflictProvoker.INNER_ITERATIONS; i++) {
+ MemoryConflictProvoker.field++;
+ }
+ };
+ }
+
+ /**
+ * Accesses and modifies memory region from within the transaction.
+ */
+ public void transactionalRegion() {
+ try {
+ barrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException(e);
+ }
+ for (int i = 0; i < MemoryConflictProvoker.INNER_ITERATIONS; i++) {
+ synchronized(monitor) {
+ MemoryConflictProvoker.field--;
+ }
+ }
+ }
+
+ @Override
+ public void forceAbort() {
+ try {
+ Thread t = new Thread(conflictingThread);
+ t.start();
+ transactionalRegion();
+ t.join();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getMethodWithLockName() {
+ return this.getClass().getName() + "::transactionalRegion";
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/NestedAbortProvoker.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import java.util.Arrays;
+
+/**
+ * In order to force nested transaction abort NestedAbortProvoker
+ * invoke BufferOverflowProvoker from transactional region.
+ */
+class NestedAbortProvoker extends AbortProvoker {
+ // Following field have to be static in order to avoid escape analysis.
+ @SuppressWarnings("UnsuedDeclaration")
+ private static int field = 0;
+ private final AbortProvoker nestedAbortProvoker;
+
+ public NestedAbortProvoker() {
+ this.nestedAbortProvoker = new XAbortProvoker(monitor);
+ }
+
+ @Override
+ public void forceAbort() {
+ synchronized(monitor) {
+ NestedAbortProvoker.field++;
+ nestedAbortProvoker.forceAbort();
+ NestedAbortProvoker.field--;
+ }
+ }
+
+ @Override
+ public String[] getMethodsToCompileNames() {
+ String nestedProvokerMethods[]
+ = nestedAbortProvoker.getMethodsToCompileNames();
+ String methods[] = Arrays.copyOf(nestedProvokerMethods,
+ nestedProvokerMethods.length + 1);
+ methods[methods.length - 1] = getMethodWithLockName();
+ return methods;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/RTMLockingStatistics.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import java.util.EnumMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * Wrapper for +UsePreciseRTMLockingStatistics output.
+ *
+ * Example of locking statistics:
+ *
+ * java/lang/ClassLoader.loadClass@7
+ * # rtm locks total (estimated): 0
+ * # rtm lock aborts : 13
+ * # rtm lock aborts 0: 12
+ * # rtm lock aborts 1: 0
+ * # rtm lock aborts 2: 0
+ * # rtm lock aborts 3: 0
+ * # rtm lock aborts 4: 0
+ * # rtm lock aborts 5: 0
+ */
+public class RTMLockingStatistics {
+ /**
+ * Pattern for aborts per abort type entries.
+ */
+ private static final Pattern ABORT_PATTERN;
+
+ /**
+ * Pattern for whole statistics.
+ */
+ private static final Pattern RTM_LOCKING_STATISTICS_PATTERN;
+
+ static {
+ String abortRe
+ = "# rtm lock aborts\\s+(?<type>[0-9]+):\\s(?<count>[0-9]+)";
+
+ ABORT_PATTERN = Pattern.compile(abortRe);
+ RTM_LOCKING_STATISTICS_PATTERN = Pattern.compile(
+ "(?<className>[^.\n]+)\\." +
+ "(?<methodName>[^@\n]+)@(?<bci>[0-9]+)\n" +
+ "# rtm locks total \\(estimated\\):\\s*" +
+ "(?<totalLocks>[0-9]+)\n" +
+ "# rtm lock aborts\\s+:\\s*(?<totalAborts>[0-9]+)\n" +
+ "(?<abortStats>(" + abortRe + "\n)+)");
+ }
+
+ private final long totalLocks;
+ private final long totalAborts;
+ private final String className;
+ private final String methodName;
+ private final int bci;
+ private final Map<AbortType, Long> aborts = new EnumMap<>(AbortType.class);
+
+ /**
+ * Constructs RTMLockingStatistics from matcher captured statistics entry.
+ * @param matcher Matcher captured statistics entry.
+ */
+ private RTMLockingStatistics(Matcher matcher) {
+ className = matcher.group("className");
+ methodName = matcher.group("methodName");
+ bci = Integer.valueOf(matcher.group("bci"));
+ totalLocks = Long.valueOf(matcher.group("totalLocks"));
+ totalAborts = Long.valueOf(matcher.group("totalAborts"));
+
+ Matcher abortMatcher = ABORT_PATTERN.matcher(matcher.
+ group("abortStats"));
+
+ while (abortMatcher.find()) {
+ int type = Integer.valueOf(abortMatcher.group("type"));
+ long count = Long.valueOf(abortMatcher.group("count"));
+ setAborts(AbortType.lookup(type), count);
+ }
+ }
+
+
+ /**
+ * Parses string and return all founded RTM locking statistics entries.
+ *
+ * @param str the string to be parsed.
+ * @return list with all founded RTM locking statistics entries or
+ * empty list if nothing was found.
+ */
+ public static List<RTMLockingStatistics> fromString(String str) {
+ List<RTMLockingStatistics> statistics = new LinkedList<>();
+ Matcher matcher = RTM_LOCKING_STATISTICS_PATTERN.matcher(str);
+
+ while (matcher.find()) {
+ RTMLockingStatistics lock = new RTMLockingStatistics(matcher);
+ statistics.add(lock);
+ }
+
+ return statistics;
+ }
+
+ /**
+ * Parses string and return all founded RTM locking statistics entries
+ * for locks in method {@code methodName}.
+ *
+ * @param methodName a name of the method for locks from which statistics
+ * should be gathered.
+ * @param str the string to be parsed.
+ * @return list with all founded RTM locking statistics entries or
+ * empty list if nothing was found.
+ */
+ public static List<RTMLockingStatistics> fromString(String methodName,
+ String str) {
+ String formattedMethodName = formatMethodName(methodName);
+
+ List<RTMLockingStatistics> statisticsForMethod = new LinkedList<>();
+ for (RTMLockingStatistics statistics : fromString(str)) {
+ if (statistics.getLockName().startsWith(formattedMethodName)) {
+ statisticsForMethod.add(statistics);
+ }
+ }
+ return statisticsForMethod;
+ }
+
+ /**
+ * Formats method's name so it will have the same format as
+ * in rtm locking statistics.
+ *
+ * <pre>
+ * Example:
+ * com/example/Klass::method => com/example/Klass.method
+ * com/example/Klass.method => com/example/Klass.method
+ * com.example.Klass::method => com/example/Klass.method
+ * com.example.Klass.method => com/example/Klass.method
+ * </pre>
+ *
+ * @param methodName method's name that should be formatted.
+ * @return formatted method's name.
+ */
+ private static String formatMethodName(String methodName) {
+ String m[];
+ if (methodName.contains("::")) {
+ m = methodName.split("::");
+ } else {
+ int splitAt = methodName.lastIndexOf('.');
+ m = new String[2];
+ m[0] = methodName.substring(0, splitAt);
+ m[1] = methodName.substring(splitAt + 1);
+ }
+ return String.format("%s.%s", m[0].replaceAll("\\.", "/"), m[1]);
+ }
+
+ /**
+ * Returns name of lock for which this statistics was collected.
+ * Lock name has following format:
+ * <class name>.<method name>@<bci>
+ *
+ * @return name of lock.
+ */
+ public String getLockName() {
+ return String.format("%s.%s@%d", className, methodName, bci);
+ }
+
+ /**
+ * Returns aborts count for specified abort type.
+ *
+ * @param type an abort type.
+ * @return count of aborts.
+ */
+ public long getAborts(AbortType type) {
+ return aborts.getOrDefault(type, 0L);
+ }
+
+ /**
+ * Sets aborts count for specified abort type.
+ *
+ * @param type an abort type.
+ * @param count count of aborts.
+ */
+ public void setAborts(AbortType type, long count) {
+ aborts.put(type, count);
+ }
+
+ public long getTotalLocks() {
+ return totalLocks;
+ }
+
+ public long getTotalAborts() {
+ return totalAborts;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getLockName()).append('\n');
+ builder.append(String.format("# rtm locks total (estimated): %d\n",
+ getTotalLocks()));
+ builder.append(String.format("# rtm lock aborts: %d\n",
+ getTotalLocks()));
+
+ for (AbortType type : AbortType.values()) {
+ builder.append(String.format("# rtm lock aborts %s %d\n",
+ type.toString(), getAborts(type)));
+ }
+ return builder.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/RTMTestBase.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import com.oracle.java.testlibrary.ProcessTools;
+import com.oracle.java.testlibrary.Utils;
+import com.oracle.java.testlibrary.cli.CommandLineOptionTest;
+
+/**
+ * Auxiliary methods used for RTM testing.
+ */
+public class RTMTestBase {
+ private static final String RTM_STATE_CHANGE_REASON = "rtm_state_change";
+ /**
+ * We don't parse compilation log as XML-document and use regular
+ * expressions instead, because in some cases it could be
+ * malformed.
+ */
+ private static final String FIRED_UNCOMMON_TRAP_PATTERN_TEMPLATE
+ = "<uncommon_trap thread='[0-9]+' reason='%s'";
+ private static final String INSTALLED_UNCOMMON_TRAP_PATTERN_TEMPLATE
+ = "<uncommon_trap bci='[0-9]+' reason='%s'";
+
+ /**
+ * Executes RTM test in a new JVM started with {@code options} cli options.
+ *
+ * @param test test case to execute.
+ * @param options additional options for VM
+ * @throws Exception when something went wrong.
+ */
+ public static OutputAnalyzer executeRTMTest(CompilableTest test,
+ String... options) throws Exception {
+ ProcessBuilder processBuilder
+ = ProcessTools.createJavaProcessBuilder(
+ RTMTestBase.prepareTestOptions(test, options));
+ OutputAnalyzer outputAnalyzer
+ = new OutputAnalyzer(processBuilder.start());
+ System.out.println(outputAnalyzer.getOutput());
+ return outputAnalyzer;
+ }
+
+ /**
+ * Executes test case and save compilation log to {@code logFileName}.
+ *
+ * @param logFileName a name of compilation log file
+ * @param test a test case to execute case to execute
+ * @param options additional options to VM
+ * @return OutputAnalyzer for started test case
+ * @throws Exception when something went wrong
+ */
+ public static OutputAnalyzer executeRTMTest(String logFileName,
+ CompilableTest test, String... options) throws Exception {
+ ProcessBuilder processBuilder
+ = ProcessTools.createJavaProcessBuilder(
+ RTMTestBase.prepareTestOptions(logFileName, test, options));
+ OutputAnalyzer outputAnalyzer
+ = new OutputAnalyzer(processBuilder.start());
+
+ System.out.println(outputAnalyzer.getOutput());
+
+ return outputAnalyzer;
+ }
+
+ /**
+ * Finds count of uncommon traps with reason {@code reason} installed
+ * during compilation.
+ *
+ * @param compilationLogFile a path to file with LogCompilation output.
+ * @param reason reason of installed uncommon traps.
+ * @return count of installed uncommon traps with reason {@code reason}.
+ * @throws IOException
+ */
+ public static int installedUncommonTraps(String compilationLogFile,
+ String reason)throws IOException {
+ String pattern = String.format(
+ RTMTestBase.INSTALLED_UNCOMMON_TRAP_PATTERN_TEMPLATE,
+ reason);
+ return RTMTestBase.findTraps(compilationLogFile, pattern);
+ }
+
+ /**
+ * Finds count of uncommon traps with reason <i>rtm_state_change</i>
+ * installed during compilation.
+ *
+ * @param compilationLogFile a path to file with LogCompilation output.
+ * @return count of installed uncommon traps with reason
+ * <i>rtm_state_change</i>.
+ * @throws IOException
+ */
+ public static int installedRTMStateChangeTraps(String compilationLogFile)
+ throws IOException {
+ return RTMTestBase.installedUncommonTraps(compilationLogFile,
+ RTMTestBase.RTM_STATE_CHANGE_REASON);
+ }
+
+ /**
+ * Finds count of fired uncommon traps with reason {@code reason}.
+ *
+ * @param compilationLogFile a path to file with LogCompilation output.
+ * @param reason a reason of fired uncommon traps.
+ * @return count of fired uncommon traps with reason {@code reason}.
+ * @throws IOException
+ */
+ public static int firedUncommonTraps(String compilationLogFile,
+ String reason) throws IOException {
+ String pattern = String.format(
+ RTMTestBase.FIRED_UNCOMMON_TRAP_PATTERN_TEMPLATE,
+ reason);
+ return RTMTestBase.findTraps(compilationLogFile, pattern);
+ }
+
+ /**
+ * Finds count of fired uncommon traps with reason <i>rtm_state_change</i>.
+ *
+ * @param compilationLogFile a path to file with LogCompilation output.
+ * @return count of fired uncommon traps with reason
+ * <i>rtm_state_change</i>.
+ * @throws IOException
+ */
+ public static int firedRTMStateChangeTraps(String compilationLogFile)
+ throws IOException {
+ return RTMTestBase.firedUncommonTraps(compilationLogFile,
+ RTMTestBase.RTM_STATE_CHANGE_REASON);
+ }
+
+ /**
+ * Finds count of uncommon traps that matches regular
+ * expression in {@code re}.
+ *
+ * @param compilationLogFile a path to file with LogCompilation output.
+ * @param re regular expression to match uncommon traps.
+ * @throws IOException
+ */
+ private static int findTraps(String compilationLogFile, String re)
+ throws IOException {
+ String compilationLog = RTMTestBase.fileAsString(compilationLogFile);
+ Pattern pattern = Pattern.compile(re);
+ Matcher matcher = pattern.matcher(compilationLog);
+ int traps = 0;
+ while (matcher.find()) {
+ traps++;
+ }
+ return traps;
+ }
+
+ /**
+ * Returns file's content as a string.
+ *
+ * @param path a path to file to operate on.
+ * @return string with content of file.
+ * @throws IOException
+ */
+ private static String fileAsString(String path) throws IOException {
+ byte[] fileAsBytes = Files.readAllBytes(Paths.get(path));
+ return new String(fileAsBytes);
+ }
+
+ /**
+ * Prepares VM options for test execution.
+ * This method get test java options, filter out all RTM-related options,
+ * adds CompileCommand=compileonly,method_name options for each method
+ * from {@code methodToCompile} and finally appends all {@code vmOpts}.
+ *
+ * @param test test case whose methods that should be compiled.
+ * If {@code null} then no additional <i>compileonly</i>
+ * commands will be added to VM options.
+ * @param vmOpts additional options to pass to VM.
+ * @return Array with VM options.
+ */
+ private static String[] prepareTestOptions(CompilableTest test,
+ String... vmOpts) {
+ return RTMTestBase.prepareFilteredTestOptions(test, null, vmOpts);
+ }
+
+ /**
+ * Prepares VM options for test execution.
+ * This method get test java options, filter out all RTM-related options
+ * and all options that matches regexps in {@code additionalFilters},
+ * adds CompileCommand=compileonly,method_name options for each method
+ * from {@code methodToCompile} and finally appends all {@code vmOpts}.
+ *
+ * @param test test case whose methods that should be compiled.
+ * If {@code null} then no additional <i>compileonly</i>
+ * commands will be added to VM options.
+ * @param additionalFilters array with regular expression that will be
+ * used to filter out test java options.
+ * If {@code null} then no additional filters
+ * will be used.
+ * @param vmOpts additional options to pass to VM.
+ * @return array with VM options.
+ */
+ private static String[] prepareFilteredTestOptions(CompilableTest test,
+ String[] additionalFilters, String... vmOpts) {
+ List<String> finalVMOpts = new LinkedList<>();
+ String[] filters;
+
+ if (additionalFilters != null) {
+ filters = Arrays.copyOf(additionalFilters,
+ additionalFilters.length + 1);
+ } else {
+ filters = new String[1];
+ }
+
+ filters[filters.length - 1] = "RTM";
+ String[] filteredVMOpts = Utils.getFilteredTestJavaOpts(filters);
+ Collections.addAll(finalVMOpts, filteredVMOpts);
+ Collections.addAll(finalVMOpts, "-Xcomp", "-server",
+ "-XX:-TieredCompilation", "-XX:+UseRTMLocking",
+ CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS,
+ CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS);
+
+ if (test != null) {
+ for (String method : test.getMethodsToCompileNames()) {
+ finalVMOpts.add("-XX:CompileCommand=compileonly," + method);
+ }
+ }
+ Collections.addAll(finalVMOpts, vmOpts);
+ return finalVMOpts.toArray(new String[finalVMOpts.size()]);
+ }
+
+ /**
+ * Adds additional options for VM required for successful execution of test.
+ *
+ * @param logFileName a name of compilation log file
+ * @param test a test case to execute
+ * @param options additional options to VM
+ * @return an array with VM options
+ */
+ private static String[] prepareTestOptions(String logFileName,
+ CompilableTest test, String... options) {
+ String[] preparedOptions = RTMTestBase.prepareFilteredTestOptions(
+ test,
+ new String[] {
+ "LogCompilation",
+ "LogFile"
+ });
+ List<String> updatedOptions = new LinkedList<>();
+ Collections.addAll(updatedOptions, preparedOptions);
+ Collections.addAll(updatedOptions,
+ "-XX:+LogCompilation",
+ "-XX:LogFile=" + logFileName);
+ Collections.addAll(updatedOptions, options);
+
+ return updatedOptions.toArray(new String[updatedOptions.size()]);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/XAbortProvoker.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm;
+
+import com.oracle.java.testlibrary.Utils;
+import sun.misc.Unsafe;
+
+/**
+ * Current RTM locking implementation force transaction abort
+ * before native method call by explicit xabort(0) call.
+ */
+class XAbortProvoker extends AbortProvoker {
+ // Following field have to be static in order to avoid escape analysis.
+ @SuppressWarnings("UnsuedDeclaration")
+ private static int field = 0;
+ private static final Unsafe UNSAFE = Utils.getUnsafe();
+
+ public XAbortProvoker() {
+ this(new Object());
+ }
+
+ public XAbortProvoker(Object monitor) {
+ super(monitor);
+ }
+
+ @Override
+ public void forceAbort() {
+ synchronized(monitor) {
+ XAbortProvoker.field = UNSAFE.addressSize();
+ }
+ }
+
+ @Override
+ public String[] getMethodsToCompileNames() {
+ return new String[] {
+ getMethodWithLockName(),
+ Unsafe.class.getName() + "::addressSize"
+ };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/predicate/SupportedCPU.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm.predicate;
+
+import sun.hotspot.cpuinfo.CPUInfo;
+
+import java.util.function.BooleanSupplier;
+
+public class SupportedCPU implements BooleanSupplier {
+ @Override
+ public boolean getAsBoolean() {
+ return CPUInfo.hasFeature("rtm");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/testlibrary/rtm/predicate/SupportedVM.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package rtm.predicate;
+
+import com.oracle.java.testlibrary.Platform;
+
+import java.util.function.BooleanSupplier;
+
+public class SupportedVM implements BooleanSupplier {
+ @Override
+ public boolean getAsBoolean() {
+ return Platform.isServer() && !Platform.isEmbedded();
+ }
+}
--- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/CPUSpecificCommandLineOptionTest.java Fri Apr 11 00:34:56 2014 +0400
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/CPUSpecificCommandLineOptionTest.java Fri Apr 11 00:35:11 2014 +0400
@@ -23,22 +23,16 @@
package com.oracle.java.testlibrary.cli;
-import sun.hotspot.cpuinfo.CPUInfo;
-import com.oracle.java.testlibrary.*;
+import com.oracle.java.testlibrary.cli.predicate.CPUSpecificPredicate;
/**
* Base class for command line options tests that
* requires specific CPU arch or specific CPU features.
*/
public abstract class CPUSpecificCommandLineOptionTest
- extends CommandLineOptionTest {
-
- private String cpuArchPattern;
- private String supportedCPUFeatures[];
- private String unsupportedCPUFeatures[];
-
+ extends CommandLineOptionTest {
/**
- * Create new CPU specific test instance that does not
+ * Creates new CPU specific test instance that does not
* require any CPU features.
*
* @param cpuArchPattern Regular expression that should
@@ -49,62 +43,23 @@
}
/**
- * Create new CPU specific test instance that does not
+ * Creates new CPU specific test instance that does not
* require from CPU support of {@code supportedCPUFeatures} features
* and no support of {@code unsupportedCPUFeatures}.
*
* @param cpuArchPattern Regular expression that should
* match os.arch.
* @param supportedCPUFeatures Array with names of features that
- * should be supported by CPU. If <b>null</b>,
+ * should be supported by CPU. If {@code null},
* then no features have to be supported.
* @param unsupportedCPUFeatures Array with names of features that
* should not be supported by CPU.
- * If <b>null</b>, then CPU may support any
+ * If {@code null}, then CPU may support any
* features.
*/
public CPUSpecificCommandLineOptionTest(String cpuArchPattern,
- String supportedCPUFeatures[],
- String unsupportedCPUFeatures[]) {
- this.cpuArchPattern = cpuArchPattern;
- this.supportedCPUFeatures = supportedCPUFeatures;
- this.unsupportedCPUFeatures = unsupportedCPUFeatures;
- }
-
- /**
- * Check that CPU on test box has appropriate architecture, support all
- * required features and does not support all features that should not be
- * supported.
- *
- * @return <b>true</b> if CPU on test box fulfill all requirements.
- */
- @Override
- public boolean checkPreconditions() {
- if (!Platform.getOsArch().matches(cpuArchPattern)) {
- System.out.println("CPU arch does not match " + cpuArchPattern);
- return false;
- }
-
- if (supportedCPUFeatures != null) {
- for (String feature : supportedCPUFeatures) {
- if (!CPUInfo.hasFeature(feature)) {
- System.out.println("CPU does not support " + feature +
- " feature");
- return false;
- }
- }
- }
-
- if (unsupportedCPUFeatures != null) {
- for (String feature : unsupportedCPUFeatures) {
- if (CPUInfo.hasFeature(feature)) {
- System.out.println("CPU support " + feature + " feature");
- return false;
- }
- }
- }
-
- return true;
+ String supportedCPUFeatures[], String unsupportedCPUFeatures[]) {
+ super(new CPUSpecificPredicate(cpuArchPattern, supportedCPUFeatures,
+ unsupportedCPUFeatures));
}
}
-
--- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/CommandLineOptionTest.java Fri Apr 11 00:34:56 2014 +0400
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/CommandLineOptionTest.java Fri Apr 11 00:35:11 2014 +0400
@@ -26,6 +26,7 @@
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.function.BooleanSupplier;
import com.oracle.java.testlibrary.*;
@@ -33,34 +34,71 @@
* Base class for command line option tests.
*/
public abstract class CommandLineOptionTest {
-
- public static final String UNRECOGNIZED_OPTION_ERROR_FORMAT =
- "Unrecognized VM option '[+-]?%s'";
-
- public static final String printFlagsFinalFormat = "%s\\s*:?=\\s*%s";
+ public static final String UNLOCK_DIAGNOSTIC_VM_OPTIONS
+ = "-XX:+UnlockDiagnosticVMOptions";
+ public static final String UNLOCK_EXPERIMENTAL_VM_OPTIONS
+ = "-XX:+UnlockExperimentalVMOptions";
+ protected static final String UNRECOGNIZED_OPTION_ERROR_FORMAT
+ = "Unrecognized VM option '[+-]?%s(=.*)?'";
+ protected static final String EXPERIMENTAL_OPTION_ERROR_FORMAT
+ = "VM option '%s' is experimental and must be enabled via "
+ + "-XX:\\+UnlockExperimentalVMOptions.";
+ protected static final String DIAGNOSTIC_OPTION_ERROR_FORMAT
+ = " VM option '%s' is diagnostic and must be enabled via "
+ + "-XX:\\+UnlockDiagnosticVMOptions.";
+ private static final String PRINT_FLAGS_FINAL_FORMAT = "%s\\s*:?=\\s*%s";
/**
- * Verify that JVM startup behaviour matches our expectations.
+ * Verifies that JVM startup behaviour matches our expectations.
*
- * @param option The option that should be passed to JVM
- * @param excpectedMessages Array of patterns that should occur
- * in JVM output. If <b>null</b> then
+ * @param option an option that should be passed to JVM
+ * @param expectedMessages an array of patterns that should occur
+ * in JVM output. If {@code null} then
* JVM output could be empty.
- * @param unexpectedMessages Array of patterns that should not
- * occur in JVM output. If <b>null</b> then
- * JVM output could be empty.
+ * @param unexpectedMessages an array of patterns that should not
+ * occur in JVM output. If {@code null} then
+ * JVM output could be empty.
* @param exitCode expected exit code.
* @throws Throwable if verification fails or some other issues occur.
*/
public static void verifyJVMStartup(String option,
- String expectedMessages[],
- String unexpectedMessages[],
- ExitCode exitCode)
- throws Throwable {
+ String expectedMessages[], String unexpectedMessages[],
+ ExitCode exitCode) throws Throwable {
+ CommandLineOptionTest.verifyJVMStartup(expectedMessages,
+ unexpectedMessages, exitCode, false, option);
+ }
- OutputAnalyzer outputAnalyzer =
- ProcessTools.executeTestJvm(option, "-version");
+ /**
+ * Verifies that JVM startup behaviour matches our expectations.
+ *
+ * @param expectedMessages an array of patterns that should occur
+ * in JVM output. If {@code null} then
+ * JVM output could be empty.
+ * @param unexpectedMessages an array of patterns that should not
+ * occur in JVM output. If {@code null} then
+ * JVM output could be empty.
+ * @param exitCode expected exit code.
+ * @param addTestVMOptions if {@code true} then test VM options will be
+ * passed to VM.
+ * @param options options that should be passed to VM in addition to mode
+ * flag.
+ * @throws Throwable if verification fails or some other issues occur.
+ */
+ public static void verifyJVMStartup(String expectedMessages[],
+ String unexpectedMessages[], ExitCode exitCode,
+ boolean addTestVMOptions, String... options) throws Throwable {
+ List<String> finalOptions = new ArrayList<>();
+ if (addTestVMOptions) {
+ Collections.addAll(finalOptions, Utils.getTestJavaOpts());
+ }
+ Collections.addAll(finalOptions, options);
+ finalOptions.add("-version");
+ ProcessBuilder processBuilder
+ = ProcessTools.createJavaProcessBuilder(finalOptions.toArray(
+ new String[finalOptions.size()]));
+ OutputAnalyzer outputAnalyzer
+ = new OutputAnalyzer(processBuilder.start());
outputAnalyzer.shouldHaveExitValue(exitCode.value);
if (expectedMessages != null) {
@@ -77,97 +115,216 @@
}
/**
- * Verify that value of specified JVM option is the same as
+ * Verifies that JVM startup behaviour matches our expectations when type
+ * of newly started VM is the same as the type of current.
+ *
+ * @param expectedMessages an array of patterns that should occur
+ * in JVM output. If {@code null} then
+ * JVM output could be empty.
+ * @param unexpectedMessages an array of patterns that should not
+ * occur in JVM output. If {@code null} then
+ * JVM output could be empty.
+ * @param exitCode expected exit code.
+ * @param options options that should be passed to VM in addition to mode
+ * flag.
+ * @throws Throwable if verification fails or some other issues occur.
+ */
+ public static void verifySameJVMStartup(String expectedMessages[],
+ String unexpectedMessages[], ExitCode exitCode, String... options)
+ throws Throwable {
+ List<String> finalOptions = new ArrayList<>();
+ finalOptions.add(CommandLineOptionTest.getVMTypeOption());
+ Collections.addAll(finalOptions, options);
+
+ CommandLineOptionTest.verifyJVMStartup(expectedMessages,
+ unexpectedMessages, exitCode, false,
+ finalOptions.toArray(new String[finalOptions.size()]));
+ }
+
+ /**
+ * Verifies that value of specified JVM option is the same as
* expected value.
* This method filter out option with {@code optionName}
* name from test java options.
*
- * @param optionName Name of tested option.
- * @param expectedValue Expected value of tested option.
- * @param additionalVMOpts Additonal options that should be
+ * @param optionName a name of tested option.
+ * @param expectedValue expected value of tested option.
+ * @param additionalVMOpts additional options that should be
* passed to JVM.
* @throws Throwable if verification fails or some other issues occur.
*/
public static void verifyOptionValue(String optionName,
- String expectedValue,
- String... additionalVMOpts)
- throws Throwable {
+ String expectedValue, String... additionalVMOpts) throws Throwable {
verifyOptionValue(optionName, expectedValue, true, additionalVMOpts);
}
/**
- * Verify that value of specified JVM option is the same as
+ * Verifies that value of specified JVM option is the same as
* expected value.
* This method filter out option with {@code optionName}
* name from test java options.
*
- * @param optionName Name of tested option.
- * @param expectedValue Expected value of tested option.
- * @param addTestVmOptions If <b>true</b>, then test VM options
+ * @param optionName a name of tested option.
+ * @param expectedValue expected value of tested option.
+ * @param addTestVmOptions if {@code true}, then test VM options
* will be used.
- * @param additionalVMOpts Additonal options that should be
+ * @param additionalVMOpts additional options that should be
* passed to JVM.
- * @throws Throwable if verification fails or some other issues occur.
+ * @throws Throwable if verification fails or some other issues
+ * occur.
*/
public static void verifyOptionValue(String optionName,
- String expectedValue,
- boolean addTestVmOptions,
- String... additionalVMOpts)
- throws Throwable {
-
- List<String> vmOpts = new ArrayList<String>();
+ String expectedValue, boolean addTestVmOptions,
+ String... additionalVMOpts) throws Throwable {
+ List<String> vmOpts = new ArrayList<>();
if (addTestVmOptions) {
Collections.addAll(vmOpts,
Utils.getFilteredTestJavaOpts(optionName));
}
Collections.addAll(vmOpts, additionalVMOpts);
- Collections.addAll(vmOpts, new String[] {
- "-XX:+PrintFlagsFinal",
- "-version"
- });
+ Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version");
- ProcessBuilder processBuilder =
- ProcessTools.
- createJavaProcessBuilder(vmOpts.
- toArray(new String[vmOpts.size()]));
+ ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder(
+ vmOpts.toArray(new String[vmOpts.size()]));
- OutputAnalyzer outputAnalyzer =
- new OutputAnalyzer(processBuilder.start());
+ OutputAnalyzer outputAnalyzer
+ = new OutputAnalyzer(processBuilder.start());
outputAnalyzer.shouldHaveExitValue(0);
- outputAnalyzer.shouldMatch(String.
- format(printFlagsFinalFormat,
- optionName,
- expectedValue));
+ outputAnalyzer.shouldMatch(String.format(
+ CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT,
+ optionName, expectedValue));
+ }
+
+ /**
+ * Verifies that value of specified JVM when type of newly started VM
+ * is the same as the type of current.
+ * This method filter out option with {@code optionName}
+ * name from test java options.
+ * Only mode flag will be passed to VM in addition to
+ * {@code additionalVMOpts}
+ *
+ * @param optionName name of tested option.
+ * @param expectedValue expected value of tested option.
+ * @param additionalVMOpts additional options that should be
+ * passed to JVM.
+ * @throws Throwable if verification fails or some other issues occur.
+ */
+ public static void verifyOptionValueForSameVM(String optionName,
+ String expectedValue, String... additionalVMOpts) throws Throwable {
+ List<String> finalOptions = new ArrayList<>();
+ finalOptions.add(CommandLineOptionTest.getVMTypeOption());
+ Collections.addAll(finalOptions, additionalVMOpts);
+
+ CommandLineOptionTest.verifyOptionValue(optionName, expectedValue,
+ false, finalOptions.toArray(new String[finalOptions.size()]));
+ }
+
+ /**
+ * Prepares boolean command line flag with name {@code name} according
+ * to it's {@code value}.
+ *
+ * @param name the name of option to be prepared
+ * @param value the value of option
+ * @return prepared command line flag
+ */
+ public static String prepareBooleanFlag(String name, boolean value) {
+ return String.format("-XX:%c%s", (value ? '+' : '-'), name);
+ }
+
+ /**
+ * Prepares numeric command line flag with name {@code name} by setting
+ * it's value to {@code value}.
+ *
+ * @param name the name of option to be prepared
+ * @param value the value of option
+ * @return prepared command line flag
+ */
+ public static String prepareNumericFlag(String name, Number value) {
+ return String.format("-XX:%s=%s", name, value.toString());
}
+ /**
+ * Returns message that should occur in VM output if option
+ * {@code optionName} if unrecognized.
+ *
+ * @param optionName the name of option for which message should be returned
+ * @return message saying that option {@code optionName} is unrecognized
+ */
+ public static String getUnrecognizedOptionErrorMessage(String optionName) {
+ return String.format(
+ CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT,
+ optionName);
+ }
+
+ /**
+ * Returns message that should occur in VM output if option
+ * {@code optionName} is experimental and
+ * -XX:+UnlockExperimentalVMOptions was not passed to VM.
+ *
+ * @param optionName the name of option for which message should be returned
+ * @return message saying that option {@code optionName} is experimental
+ */
+ public static String getExperimentalOptionErrorMessage(String optionName) {
+ return String.format(
+ CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT,
+ optionName);
+ }
/**
- * Run command line option test.
+ * Returns message that should occur in VM output if option
+ * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions
+ * was not passed to VM.
*
- * @throws Throwable if test failed.
+ * @param optionName the name of option for which message should be returned
+ * @return message saying that option {@code optionName} is diganostic
+ */
+ public static String getDiagnosticOptionErrorMessage(String optionName) {
+ return String.format(
+ CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT,
+ optionName);
+ }
+
+ /**
+ * @return option required to start a new VM with the same type as current.
+ * @throws RuntimeException when VM type is unknown.
+ */
+ private static String getVMTypeOption() {
+ if (Platform.isServer()) {
+ return "-server";
+ } else if (Platform.isClient()) {
+ return "-client";
+ } else if (Platform.isMinimal()) {
+ return "-minimal";
+ } else if (Platform.isGraal()) {
+ return "-graal";
+ }
+ throw new RuntimeException("Unknown VM mode.");
+ }
+
+ private final BooleanSupplier predicate;
+
+ /**
+ * Constructs new CommandLineOptionTest that will be executed only if
+ * predicate {@code predicate} return {@code true}.
+ * @param predicate a predicate responsible for test's preconditions check.
+ */
+ public CommandLineOptionTest(BooleanSupplier predicate) {
+ this.predicate = predicate;
+ }
+
+ /**
+ * Runs command line option test.
*/
public final void test() throws Throwable {
- if (checkPreconditions()) {
+ if (predicate.getAsBoolean()) {
runTestCases();
}
}
/**
- * Check that all preconditions for test execution are met.
- *
- * @return <b>true</b> if test could be executed.
+ * @throws Throwable if some issue happened during test cases execution.
*/
- public boolean checkPreconditions() {
- return true;
- }
-
- /**
- * Run test cases.
- *
- * @throws Throwable if test failed.
- */
- public abstract void runTestCases() throws Throwable;
+ protected abstract void runTestCases() throws Throwable;
}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/AndPredicate.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+package com.oracle.java.testlibrary.cli.predicate;
+
+import java.util.function.BooleanSupplier;
+
+public class AndPredicate implements BooleanSupplier {
+ private final BooleanSupplier a;
+ private final BooleanSupplier b;
+
+ public AndPredicate(BooleanSupplier a, BooleanSupplier b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ return a.getAsBoolean() && b.getAsBoolean();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/CPUSpecificPredicate.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+package com.oracle.java.testlibrary.cli.predicate;
+
+import com.oracle.java.testlibrary.Platform;
+import sun.hotspot.cpuinfo.CPUInfo;
+
+import java.util.function.BooleanSupplier;
+
+public class CPUSpecificPredicate implements BooleanSupplier {
+ private final String cpuArchPattern;
+ private final String supportedCPUFeatures[];
+ private final String unsupportedCPUFeatures[];
+
+ public CPUSpecificPredicate(String cpuArchPattern,
+ String supportedCPUFeatures[],
+ String unsupportedCPUFeatures[]) {
+ this.cpuArchPattern = cpuArchPattern;
+ this.supportedCPUFeatures = supportedCPUFeatures;
+ this.unsupportedCPUFeatures = unsupportedCPUFeatures;
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ if (!Platform.getOsArch().matches(cpuArchPattern)) {
+ System.out.println("CPU arch does not match " + cpuArchPattern);
+ return false;
+ }
+
+ if (supportedCPUFeatures != null) {
+ for (String feature : supportedCPUFeatures) {
+ if (!CPUInfo.hasFeature(feature)) {
+ System.out.println("CPU does not support " + feature
+ + " feature");
+ return false;
+ }
+ }
+ }
+
+ if (unsupportedCPUFeatures != null) {
+ for (String feature : unsupportedCPUFeatures) {
+ if (CPUInfo.hasFeature(feature)) {
+ System.out.println("CPU support " + feature + " feature");
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/NotPredicate.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package com.oracle.java.testlibrary.cli.predicate;
+
+import java.util.function.BooleanSupplier;
+
+public class NotPredicate implements BooleanSupplier {
+ private final BooleanSupplier s;
+
+ public NotPredicate(BooleanSupplier s) {
+ this.s = s;
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ return !s.getAsBoolean();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/cli/predicate/OrPredicate.java Fri Apr 11 00:35:11 2014 +0400
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+package com.oracle.java.testlibrary.cli.predicate;
+
+import java.util.function.BooleanSupplier;
+
+public class OrPredicate implements BooleanSupplier {
+ private final BooleanSupplier a;
+ private final BooleanSupplier b;
+
+ public OrPredicate(BooleanSupplier a, BooleanSupplier b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ @Override
+ public boolean getAsBoolean() {
+ return a.getAsBoolean() || b.getAsBoolean();
+ }
+}