1 /*
   2  * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.serviceprovider;
  26 
  27 import static java.lang.Thread.currentThread;
  28 
  29 import java.io.IOException;
  30 import java.io.InputStream;
  31 import java.util.Arrays;
  32 import java.util.Iterator;
  33 import java.util.List;
  34 import java.util.ServiceConfigurationError;
  35 import java.util.ServiceLoader;
  36 import java.util.concurrent.atomic.AtomicLong;
  37 
  38 import jdk.vm.ci.meta.SpeculationLog.SpeculationReason;
  39 import jdk.vm.ci.runtime.JVMCI;
  40 import jdk.vm.ci.services.JVMCIPermission;
  41 import jdk.vm.ci.services.Services;
  42 
  43 /**
  44  * JDK 9+ version of {@link GraalServices}.
  45  */
  46 public final class GraalServices {
  47 
  48     private GraalServices() {
  49     }
  50 
  51     /**
  52      * Gets an {@link Iterable} of the providers available for a given service.
  53      *
  54      * @throws SecurityException if on JDK8 and a security manager is present and it denies
  55      *             {@link JVMCIPermission}
  56      */
  57     public static <S> Iterable<S> load(Class<S> service) {
  58         Iterable<S> iterable = ServiceLoader.load(service);
  59         return new Iterable<>() {
  60             @Override
  61             public Iterator<S> iterator() {
  62                 Iterator<S> iterator = iterable.iterator();
  63                 return new Iterator<>() {
  64                     @Override
  65                     public boolean hasNext() {
  66                         return iterator.hasNext();
  67                     }
  68 
  69                     @Override
  70                     public S next() {
  71                         S provider = iterator.next();
  72                         // Allow Graal extensions to access JVMCI
  73                         openJVMCITo(provider.getClass());
  74                         return provider;
  75                     }
  76 
  77                     @Override
  78                     public void remove() {
  79                         iterator.remove();
  80                     }
  81                 };
  82             }
  83         };
  84     }
  85 
  86     /**
  87      * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having
  88      * opened all its packages to the module defining {@link GraalServices}.
  89      *
  90      * @param other all JVMCI packages will be opened to the module defining this class
  91      */
  92     static void openJVMCITo(Class<?> other) {
  93         Module jvmciModule = JVMCI_MODULE;
  94         Module otherModule = other.getModule();
  95         if (jvmciModule != otherModule) {
  96             for (String pkg : jvmciModule.getPackages()) {
  97                 if (!jvmciModule.isOpen(pkg, otherModule)) {
  98                     // JVMCI initialization opens all JVMCI packages
  99                     // to Graal which is a prerequisite for Graal to
 100                     // open JVMCI packages to other modules.
 101                     JVMCI.initialize();
 102 
 103                     jvmciModule.addOpens(pkg, otherModule);
 104                 }
 105             }
 106         }
 107     }
 108 
 109     /**
 110      * Gets the provider for a given service for which at most one provider must be available.
 111      *
 112      * @param service the service whose provider is being requested
 113      * @param required specifies if an {@link InternalError} should be thrown if no provider of
 114      *            {@code service} is available
 115      * @return the requested provider if available else {@code null}
 116      * @throws SecurityException if on JDK8 and a security manager is present and it denies
 117      *             {@link JVMCIPermission}
 118      */
 119     public static <S> S loadSingle(Class<S> service, boolean required) {
 120         assert !service.getName().startsWith("jdk.vm.ci") : "JVMCI services must be loaded via " + Services.class.getName();
 121         Iterable<S> providers = load(service);
 122         S singleProvider = null;
 123         try {
 124             for (Iterator<S> it = providers.iterator(); it.hasNext();) {
 125                 singleProvider = it.next();
 126                 if (it.hasNext()) {
 127                     S other = it.next();
 128                     throw new InternalError(String.format("Multiple %s providers found: %s, %s", service.getName(), singleProvider.getClass().getName(), other.getClass().getName()));
 129                 }
 130             }
 131         } catch (ServiceConfigurationError e) {
 132             // If the service is required we will bail out below.
 133         }
 134         if (singleProvider == null) {
 135             if (required) {
 136                 throw new InternalError(String.format("No provider for %s found", service.getName()));
 137             }
 138         }
 139         return singleProvider;
 140     }
 141 
 142     /**
 143      * Gets the class file bytes for {@code c}.
 144      */
 145     public static InputStream getClassfileAsStream(Class<?> c) throws IOException {
 146         String classfilePath = c.getName().replace('.', '/') + ".class";
 147         return c.getModule().getResourceAsStream(classfilePath);
 148     }
 149 
 150     private static final Module JVMCI_MODULE = Services.class.getModule();
 151 
 152     /**
 153      * A JVMCI package dynamically exported to trusted modules.
 154      */
 155     private static final String JVMCI_RUNTIME_PACKAGE = "jdk.vm.ci.runtime";
 156     static {
 157         assert JVMCI_MODULE.getPackages().contains(JVMCI_RUNTIME_PACKAGE);
 158     }
 159 
 160     /**
 161      * Determines if invoking {@link Object#toString()} on an instance of {@code c} will only run
 162      * trusted code.
 163      */
 164     public static boolean isToStringTrusted(Class<?> c) {
 165         Module module = c.getModule();
 166         Module jvmciModule = JVMCI_MODULE;
 167         assert jvmciModule.getPackages().contains("jdk.vm.ci.runtime");
 168         if (module == jvmciModule || jvmciModule.isOpen(JVMCI_RUNTIME_PACKAGE, module)) {
 169             // Can access non-statically-exported package in JVMCI
 170             return true;
 171         }
 172         return false;
 173     }
 174 
 175     /**
 176      * An implementation of {@link SpeculationReason} based on direct, unencoded values.
 177      */
 178     static final class DirectSpeculationReason implements SpeculationReason {
 179         final int groupId;
 180         final String groupName;
 181         final Object[] context;
 182 
 183         DirectSpeculationReason(int groupId, String groupName, Object[] context) {
 184             this.groupId = groupId;
 185             this.groupName = groupName;
 186             this.context = context;
 187         }
 188 
 189         @Override
 190         public boolean equals(Object obj) {
 191             if (obj instanceof DirectSpeculationReason) {
 192                 DirectSpeculationReason that = (DirectSpeculationReason) obj;
 193                 return this.groupId == that.groupId && Arrays.equals(this.context, that.context);
 194             }
 195             return false;
 196         }
 197 
 198         @Override
 199         public int hashCode() {
 200             return groupId + Arrays.hashCode(this.context);
 201         }
 202 
 203         @Override
 204         public String toString() {
 205             return String.format("%s@%d%s", groupName, groupId, Arrays.toString(context));
 206         }
 207     }
 208 
 209     static SpeculationReason createSpeculationReason(int groupId, String groupName, Object... context) {
 210         return new DirectSpeculationReason(groupId, groupName, context);
 211     }
 212 
 213     /**
 214      * Gets a unique identifier for this execution such as a process ID or a
 215      * {@linkplain #getGlobalTimeStamp() fixed timestamp}.
 216      */
 217     public static String getExecutionID() {
 218         return Long.toString(ProcessHandle.current().pid());
 219     }
 220 
 221     private static final AtomicLong globalTimeStamp = new AtomicLong();
 222 
 223     /**
 224      * Gets a time stamp for the current process. This method will always return the same value for
 225      * the current VM execution.
 226      */
 227     public static long getGlobalTimeStamp() {
 228         if (globalTimeStamp.get() == 0) {
 229             globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
 230         }
 231         return globalTimeStamp.get();
 232     }
 233 
 234     /**
 235      * Returns an approximation of the total amount of memory, in bytes, allocated in heap memory
 236      * for the thread of the specified ID. The returned value is an approximation because some Java
 237      * virtual machine implementations may use object allocation mechanisms that result in a delay
 238      * between the time an object is allocated and the time its size is recorded.
 239      * <p>
 240      * If the thread of the specified ID is not alive or does not exist, this method returns
 241      * {@code -1}. If thread memory allocation measurement is disabled, this method returns
 242      * {@code -1}. A thread is alive if it has been started and has not yet died.
 243      * <p>
 244      * If thread memory allocation measurement is enabled after the thread has started, the Java
 245      * virtual machine implementation may choose any time up to and including the time that the
 246      * capability is enabled as the point where thread memory allocation measurement starts.
 247      *
 248      * @param id the thread ID of a thread
 249      * @return an approximation of the total memory allocated, in bytes, in heap memory for a thread
 250      *         of the specified ID if the thread of the specified ID exists, the thread is alive,
 251      *         and thread memory allocation measurement is enabled; {@code -1} otherwise.
 252      *
 253      * @throws IllegalArgumentException if {@code id} {@code <=} {@code 0}.
 254      * @throws UnsupportedOperationException if the Java virtual machine implementation does not
 255      *             {@linkplain #isThreadAllocatedMemorySupported() support} thread memory allocation
 256      *             measurement.
 257      */
 258     public static long getThreadAllocatedBytes(long id) {
 259         JMXService jmx = JMXService.instance;
 260         if (jmx == null) {
 261             throw new UnsupportedOperationException();
 262         }
 263         return jmx.getThreadAllocatedBytes(id);
 264     }
 265 
 266     /**
 267      * Convenience method for calling {@link #getThreadAllocatedBytes(long)} with the id of the
 268      * current thread.
 269      */
 270     public static long getCurrentThreadAllocatedBytes() {
 271         return getThreadAllocatedBytes(currentThread().getId());
 272     }
 273 
 274     /**
 275      * Returns the total CPU time for the current thread in nanoseconds. The returned value is of
 276      * nanoseconds precision but not necessarily nanoseconds accuracy. If the implementation
 277      * distinguishes between user mode time and system mode time, the returned CPU time is the
 278      * amount of time that the current thread has executed in user mode or system mode.
 279      *
 280      * @return the total CPU time for the current thread if CPU time measurement is enabled;
 281      *         {@code -1} otherwise.
 282      *
 283      * @throws UnsupportedOperationException if the Java virtual machine does not
 284      *             {@linkplain #isCurrentThreadCpuTimeSupported() support} CPU time measurement for
 285      *             the current thread
 286      */
 287     public static long getCurrentThreadCpuTime() {
 288         JMXService jmx = JMXService.instance;
 289         if (jmx == null) {
 290             throw new UnsupportedOperationException();
 291         }
 292         return jmx.getCurrentThreadCpuTime();
 293     }
 294 
 295     /**
 296      * Determines if the Java virtual machine implementation supports thread memory allocation
 297      * measurement.
 298      */
 299     public static boolean isThreadAllocatedMemorySupported() {
 300         JMXService jmx = JMXService.instance;
 301         if (jmx == null) {
 302             return false;
 303         }
 304         return jmx.isThreadAllocatedMemorySupported();
 305     }
 306 
 307     /**
 308      * Determines if the Java virtual machine supports CPU time measurement for the current thread.
 309      */
 310     public static boolean isCurrentThreadCpuTimeSupported() {
 311         JMXService jmx = JMXService.instance;
 312         if (jmx == null) {
 313             return false;
 314         }
 315         return jmx.isCurrentThreadCpuTimeSupported();
 316     }
 317 
 318     /**
 319      * Gets the input arguments passed to the Java virtual machine which does not include the
 320      * arguments to the {@code main} method. This method returns an empty list if there is no input
 321      * argument to the Java virtual machine.
 322      * <p>
 323      * Some Java virtual machine implementations may take input arguments from multiple different
 324      * sources: for examples, arguments passed from the application that launches the Java virtual
 325      * machine such as the 'java' command, environment variables, configuration files, etc.
 326      * <p>
 327      * Typically, not all command-line options to the 'java' command are passed to the Java virtual
 328      * machine. Thus, the returned input arguments may not include all command-line options.
 329      *
 330      * @return the input arguments to the JVM or {@code null} if they are unavailable
 331      */
 332     public static List<String> getInputArguments() {
 333         JMXService jmx = JMXService.instance;
 334         if (jmx == null) {
 335             return null;
 336         }
 337         return jmx.getInputArguments();
 338     }
 339 }
 340