src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java Tue Apr 24 08:13:30 2018 -0700
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java Tue Apr 24 09:04:57 2018 -0700
@@ -22,53 +22,44 @@
*/
package org.graalvm.compiler.serviceprovider;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.Java8OrEarlier;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.addOpens;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.getModule;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.getPackages;
-import static org.graalvm.compiler.serviceprovider.JDK9Method.isOpenTo;
+import static java.lang.Thread.currentThread;
-import java.lang.reflect.Method;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Iterator;
+import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
-import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
import jdk.vm.ci.services.JVMCIPermission;
import jdk.vm.ci.services.Services;
/**
- * A mechanism for accessing service providers that abstracts over whether Graal is running on
- * JVMCI-8 or JVMCI-9. In JVMCI-8, a JVMCI specific mechanism is used to lookup services via the
- * hidden JVMCI class loader. In JVMCI-9, the standard {@link ServiceLoader} mechanism is used.
+ * Interface to functionality that abstracts over which JDK version Graal is running on.
*/
public final class GraalServices {
- private GraalServices() {
+ private static int getJavaSpecificationVersion() {
+ String value = System.getProperty("java.specification.version");
+ if (value.startsWith("1.")) {
+ value = value.substring(2);
+ }
+ return Integer.parseInt(value);
}
/**
- * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having
- * opened all its packages to the module defining {@link GraalServices}.
- *
- * @param other all JVMCI packages will be opened to the module defining this class
+ * The integer value corresponding to the value of the {@code java.specification.version} system
+ * property after any leading {@code "1."} has been stripped.
*/
- public static void openJVMCITo(Class<?> other) {
- Object jvmci = getModule(Services.class);
- Object otherModule = getModule(other);
- if (jvmci != otherModule) {
- Set<String> packages = getPackages(jvmci);
- for (String pkg : packages) {
- boolean opened = isOpenTo(jvmci, pkg, otherModule);
- if (!opened) {
- try {
- addOpens.invoke(jvmci, pkg, otherModule);
- } catch (Throwable throwable) {
- throw new InternalError(throwable);
- }
- }
- }
- }
+ public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion();
+
+ /**
+ * Determines if the Java runtime is version 8 or earlier.
+ */
+ public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8;
+
+ private GraalServices() {
}
/**
@@ -79,15 +70,12 @@
*/
public static <S> Iterable<S> load(Class<S> service) {
assert !service.getName().startsWith("jdk.vm.ci") : "JVMCI services must be loaded via " + Services.class.getName();
- if (Java8OrEarlier) {
- return load8(service);
- }
Iterable<S> iterable = ServiceLoader.load(service);
- return new Iterable<S>() {
+ return new Iterable<>() {
@Override
public Iterator<S> iterator() {
Iterator<S> iterator = iterable.iterator();
- return new Iterator<S>() {
+ return new Iterator<>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
@@ -111,19 +99,20 @@
}
/**
- * {@code Services.load(Class)} is only defined in JVMCI-8.
+ * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having
+ * opened all its packages to the module defining {@link GraalServices}.
+ *
+ * @param other all JVMCI packages will be opened to the module defining this class
*/
- private static volatile Method loadMethod;
-
- @SuppressWarnings("unchecked")
- private static <S> Iterable<S> load8(Class<S> service) throws InternalError {
- try {
- if (loadMethod == null) {
- loadMethod = Services.class.getMethod("load", Class.class);
+ static void openJVMCITo(Class<?> other) {
+ Module jvmciModule = JVMCI_MODULE;
+ Module otherModule = other.getModule();
+ if (jvmciModule != otherModule) {
+ for (String pkg : jvmciModule.getPackages()) {
+ if (!jvmciModule.isOpen(pkg, otherModule)) {
+ jvmciModule.addOpens(pkg, otherModule);
+ }
}
- return (Iterable<S>) loadMethod.invoke(null, service);
- } catch (Exception e) {
- throw new InternalError(e);
}
}
@@ -159,4 +148,185 @@
}
return singleProvider;
}
+
+ /**
+ * Gets the class file bytes for {@code c}.
+ */
+ public static InputStream getClassfileAsStream(Class<?> c) throws IOException {
+ String classfilePath = c.getName().replace('.', '/') + ".class";
+ return c.getModule().getResourceAsStream(classfilePath);
+ }
+
+ private static final Module JVMCI_MODULE = Services.class.getModule();
+
+ /**
+ * A JVMCI package dynamically exported to trusted modules.
+ */
+ private static final String JVMCI_RUNTIME_PACKAGE = "jdk.vm.ci.runtime";
+ static {
+ assert JVMCI_MODULE.getPackages().contains(JVMCI_RUNTIME_PACKAGE);
+ }
+
+ /**
+ * Determines if invoking {@link Object#toString()} on an instance of {@code c} will only run
+ * trusted code.
+ */
+ public static boolean isToStringTrusted(Class<?> c) {
+ Module module = c.getModule();
+ Module jvmciModule = JVMCI_MODULE;
+ assert jvmciModule.getPackages().contains("jdk.vm.ci.runtime");
+ if (module == jvmciModule || jvmciModule.isOpen(JVMCI_RUNTIME_PACKAGE, module)) {
+ // Can access non-statically-exported package in JVMCI
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Gets a unique identifier for this execution such as a process ID or a
+ * {@linkplain #getGlobalTimeStamp() fixed timestamp}.
+ */
+ public static String getExecutionID() {
+ return Long.toString(ProcessHandle.current().pid());
+ }
+
+ private static final AtomicLong globalTimeStamp = new AtomicLong();
+
+ /**
+ * Gets a time stamp for the current process. This method will always return the same value for
+ * the current VM execution.
+ */
+ public static long getGlobalTimeStamp() {
+ if (globalTimeStamp.get() == 0) {
+ globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
+ }
+ return globalTimeStamp.get();
+ }
+
+ /**
+ * Access to thread specific information made available via Java Management Extensions (JMX).
+ * Using this abstraction enables avoiding a dependency to the {@code java.management} and
+ * {@code jdk.management} modules on JDK 9 and later.
+ */
+ public abstract static class JMXService {
+ protected abstract long getThreadAllocatedBytes(long id);
+
+ protected abstract long getCurrentThreadCpuTime();
+
+ protected abstract boolean isThreadAllocatedMemorySupported();
+
+ protected abstract boolean isCurrentThreadCpuTimeSupported();
+
+ protected abstract List<String> getInputArguments();
+
+ // Placing this static field in JMXService (instead of GraalServices)
+ // allows for lazy initialization.
+ static final JMXService instance = loadSingle(JMXService.class, false);
+ }
+
+ /**
+ * Returns an approximation of the total amount of memory, in bytes, allocated in heap memory
+ * for the thread of the specified ID. The returned value is an approximation because some Java
+ * virtual machine implementations may use object allocation mechanisms that result in a delay
+ * between the time an object is allocated and the time its size is recorded.
+ * <p>
+ * If the thread of the specified ID is not alive or does not exist, this method returns
+ * {@code -1}. If thread memory allocation measurement is disabled, this method returns
+ * {@code -1}. A thread is alive if it has been started and has not yet died.
+ * <p>
+ * If thread memory allocation measurement is enabled after the thread has started, the Java
+ * virtual machine implementation may choose any time up to and including the time that the
+ * capability is enabled as the point where thread memory allocation measurement starts.
+ *
+ * @param id the thread ID of a thread
+ * @return an approximation of the total memory allocated, in bytes, in heap memory for a thread
+ * of the specified ID if the thread of the specified ID exists, the thread is alive,
+ * and thread memory allocation measurement is enabled; {@code -1} otherwise.
+ *
+ * @throws IllegalArgumentException if {@code id} {@code <=} {@code 0}.
+ * @throws UnsupportedOperationException if the Java virtual machine implementation does not
+ * {@linkplain #isThreadAllocatedMemorySupported() support} thread memory allocation
+ * measurement.
+ */
+ public static long getThreadAllocatedBytes(long id) {
+ JMXService jmx = JMXService.instance;
+ if (jmx == null) {
+ throw new UnsupportedOperationException();
+ }
+ return jmx.getThreadAllocatedBytes(id);
+ }
+
+ /**
+ * Convenience method for calling {@link #getThreadAllocatedBytes(long)} with the id of the
+ * current thread.
+ */
+ public static long getCurrentThreadAllocatedBytes() {
+ return getThreadAllocatedBytes(currentThread().getId());
+ }
+
+ /**
+ * Returns the total CPU time for the current thread in nanoseconds. The returned value is of
+ * nanoseconds precision but not necessarily nanoseconds accuracy. If the implementation
+ * distinguishes between user mode time and system mode time, the returned CPU time is the
+ * amount of time that the current thread has executed in user mode or system mode.
+ *
+ * @return the total CPU time for the current thread if CPU time measurement is enabled;
+ * {@code -1} otherwise.
+ *
+ * @throws UnsupportedOperationException if the Java virtual machine does not
+ * {@linkplain #isCurrentThreadCpuTimeSupported() support} CPU time measurement for
+ * the current thread
+ */
+ public static long getCurrentThreadCpuTime() {
+ JMXService jmx = JMXService.instance;
+ if (jmx == null) {
+ throw new UnsupportedOperationException();
+ }
+ return jmx.getCurrentThreadCpuTime();
+ }
+
+ /**
+ * Determines if the Java virtual machine implementation supports thread memory allocation
+ * measurement.
+ */
+ public static boolean isThreadAllocatedMemorySupported() {
+ JMXService jmx = JMXService.instance;
+ if (jmx == null) {
+ return false;
+ }
+ return jmx.isThreadAllocatedMemorySupported();
+ }
+
+ /**
+ * Determines if the Java virtual machine supports CPU time measurement for the current thread.
+ */
+ public static boolean isCurrentThreadCpuTimeSupported() {
+ JMXService jmx = JMXService.instance;
+ if (jmx == null) {
+ return false;
+ }
+ return jmx.isCurrentThreadCpuTimeSupported();
+ }
+
+ /**
+ * Gets the input arguments passed to the Java virtual machine which does not include the
+ * arguments to the {@code main} method. This method returns an empty list if there is no input
+ * argument to the Java virtual machine.
+ * <p>
+ * Some Java virtual machine implementations may take input arguments from multiple different
+ * sources: for examples, arguments passed from the application that launches the Java virtual
+ * machine such as the 'java' command, environment variables, configuration files, etc.
+ * <p>
+ * Typically, not all command-line options to the 'java' command are passed to the Java virtual
+ * machine. Thus, the returned input arguments may not include all command-line options.
+ *
+ * @return the input arguments to the JVM or {@code null} if they are unavailable
+ */
+ public static List<String> getInputArguments() {
+ JMXService jmx = JMXService.instance;
+ if (jmx == null) {
+ return null;
+ }
+ return jmx.getInputArguments();
+ }
}