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.ArrayList; 33 import java.util.HashMap; 34 import java.util.Iterator; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.ServiceConfigurationError; 38 import java.util.ServiceLoader; 39 import java.util.concurrent.atomic.AtomicLong; 40 import java.util.function.Supplier; 41 42 import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup.SpeculationContextObject; 43 44 import jdk.vm.ci.code.BytecodePosition; 45 import jdk.vm.ci.code.VirtualObject; 46 import jdk.vm.ci.meta.ResolvedJavaField; 47 import jdk.vm.ci.meta.ResolvedJavaMethod; 48 import jdk.vm.ci.meta.ResolvedJavaType; 49 import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; 50 import jdk.vm.ci.meta.SpeculationLog.SpeculationReasonEncoding; 51 import jdk.vm.ci.runtime.JVMCI; 52 import jdk.vm.ci.services.JVMCIPermission; 53 import jdk.vm.ci.services.Services; 54 55 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE; 56 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE; 57 58 /** 59 * JDK 13+ version of {@link GraalServices}. 60 */ 61 public final class GraalServices { 62 63 private static final Map<Class<?>, List<?>> servicesCache = IS_BUILDING_NATIVE_IMAGE ? new HashMap<>() : null; 64 65 private GraalServices() { 66 } 67 68 /** 69 * Gets an {@link Iterable} of the providers available for a given service. 70 * 71 * @throws SecurityException if on JDK8 and a security manager is present and it denies 72 * {@link JVMCIPermission} 73 */ 74 @SuppressWarnings("unchecked") 75 public static <S> Iterable<S> load(Class<S> service) { 76 if (IS_IN_NATIVE_IMAGE || IS_BUILDING_NATIVE_IMAGE) { 77 List<?> list = servicesCache.get(service); 78 if (list != null) { 79 return (Iterable<S>) list; 80 } 81 if (IS_IN_NATIVE_IMAGE) { 82 throw new InternalError(String.format("No %s providers found when building native image", service.getName())); 83 } 84 } 85 86 Iterable<S> providers = load0(service); 87 88 if (IS_BUILDING_NATIVE_IMAGE) { 89 synchronized (servicesCache) { 90 ArrayList<S> providersList = new ArrayList<>(); 91 for (S provider : providers) { 92 /* 93 * When building libgraal, we want providers that comes from the Graal community 94 * and enterprise modules but not those available on the native-image class 95 * path. 96 */ 97 Module module = provider.getClass().getModule(); 98 if (module.isNamed()) { 99 providersList.add(provider); 100 } 101 } 102 providers = providersList; 103 servicesCache.put(service, providersList); 104 return providers; 105 } 106 } 107 108 return providers; 109 } 110 111 protected static <S> Iterable<S> load0(Class<S> service) { 112 Iterable<S> iterable = ServiceLoader.load(service); 113 return new Iterable<>() { 114 @Override 115 public Iterator<S> iterator() { 116 Iterator<S> iterator = iterable.iterator(); 117 return new Iterator<>() { 118 @Override 119 public boolean hasNext() { 120 return iterator.hasNext(); 121 } 122 123 @Override 124 public S next() { 125 S provider = iterator.next(); 126 // Allow Graal extensions to access JVMCI 127 openJVMCITo(provider.getClass()); 128 return provider; 129 } 130 131 @Override 132 public void remove() { 133 iterator.remove(); 134 } 135 }; 136 } 137 }; 138 } 139 140 /** 141 * Opens all JVMCI packages to the module of a given class. This relies on JVMCI already having 142 * opened all its packages to the module defining {@link GraalServices}. 143 * 144 * @param other all JVMCI packages will be opened to the module defining this class 145 */ 146 static void openJVMCITo(Class<?> other) { 147 if (IS_IN_NATIVE_IMAGE) { 148 return; 149 } 150 151 Module jvmciModule = JVMCI_MODULE; 152 Module otherModule = other.getModule(); 153 if (jvmciModule != otherModule) { 154 for (String pkg : jvmciModule.getPackages()) { 155 if (!jvmciModule.isOpen(pkg, otherModule)) { 156 // JVMCI initialization opens all JVMCI packages 157 // to Graal which is a prerequisite for Graal to 158 // open JVMCI packages to other modules. 159 JVMCI.getRuntime(); 160 161 jvmciModule.addOpens(pkg, otherModule); 162 } 163 } 164 } 165 } 166 167 /** 168 * Gets the provider for a given service for which at most one provider must be available. 169 * 170 * @param service the service whose provider is being requested 171 * @param required specifies if an {@link InternalError} should be thrown if no provider of 172 * {@code service} is available 173 * @return the requested provider if available else {@code null} 174 * @throws SecurityException if on JDK8 and a security manager is present and it denies 175 * {@link JVMCIPermission} 176 */ 177 public static <S> S loadSingle(Class<S> service, boolean required) { 178 assert !service.getName().startsWith("jdk.vm.ci") : "JVMCI services must be loaded via " + Services.class.getName(); 179 Iterable<S> providers = load(service); 180 S singleProvider = null; 181 try { 182 for (Iterator<S> it = providers.iterator(); it.hasNext();) { 183 singleProvider = it.next(); 184 if (it.hasNext()) { 185 S other = it.next(); 186 throw new InternalError(String.format("Multiple %s providers found: %s, %s", service.getName(), singleProvider.getClass().getName(), other.getClass().getName())); 187 } 188 } 189 } catch (ServiceConfigurationError e) { 190 // If the service is required we will bail out below. 191 } 192 if (singleProvider == null) { 193 if (required) { 194 throw new InternalError(String.format("No provider for %s found", service.getName())); 195 } 196 } 197 return singleProvider; 198 } 199 200 /** 201 * Gets the class file bytes for {@code c}. 202 */ 203 public static InputStream getClassfileAsStream(Class<?> c) throws IOException { 204 String classfilePath = c.getName().replace('.', '/') + ".class"; 205 return c.getModule().getResourceAsStream(classfilePath); 206 } 207 208 private static final Module JVMCI_MODULE = Services.class.getModule(); 209 210 /** 211 * A JVMCI package dynamically exported to trusted modules. 212 */ 213 private static final String JVMCI_RUNTIME_PACKAGE = "jdk.vm.ci.runtime"; 214 static { 215 assert JVMCI_MODULE.getPackages().contains(JVMCI_RUNTIME_PACKAGE); 216 } 217 218 /** 219 * Determines if invoking {@link Object#toString()} on an instance of {@code c} will only run 220 * trusted code. 221 */ 222 public static boolean isToStringTrusted(Class<?> c) { 223 Module module = c.getModule(); 224 Module jvmciModule = JVMCI_MODULE; 225 assert jvmciModule.getPackages().contains("jdk.vm.ci.runtime"); 226 if (module == jvmciModule || jvmciModule.isOpen(JVMCI_RUNTIME_PACKAGE, module)) { 227 // Can access non-statically-exported package in JVMCI 228 return true; 229 } 230 return false; 231 } 232 233 /** 234 * An implementation of {@link SpeculationReason} based on direct, unencoded values. 235 */ 236 static final class DirectSpeculationReason implements SpeculationReason { 237 final int groupId; 238 final String groupName; 239 final Object[] context; 240 private SpeculationReasonEncoding encoding; 241 242 DirectSpeculationReason(int groupId, String groupName, Object[] context) { 243 this.groupId = groupId; 244 this.groupName = groupName; 245 this.context = context; 246 } 247 248 @Override 249 public boolean equals(Object obj) { 250 if (obj instanceof DirectSpeculationReason) { 251 DirectSpeculationReason that = (DirectSpeculationReason) obj; 252 return this.groupId == that.groupId && Arrays.equals(this.context, that.context); 253 } 254 return false; 255 } 256 257 @Override 258 public int hashCode() { 259 return groupId + Arrays.hashCode(this.context); 260 } 261 262 @Override 263 public String toString() { 264 return String.format("%s@%d%s", groupName, groupId, Arrays.toString(context)); 265 } 266 267 @Override 268 public SpeculationReasonEncoding encode(Supplier<SpeculationReasonEncoding> encodingSupplier) { 269 if (encoding == null) { 270 encoding = encodingSupplier.get(); 271 encoding.addInt(groupId); 272 for (Object o : context) { 273 if (o == null) { 274 encoding.addInt(0); 275 } else { 276 addNonNullObject(encoding, o); 277 } 278 } 279 } 280 return encoding; 281 } 282 283 static void addNonNullObject(SpeculationReasonEncoding encoding, Object o) { 284 Class<? extends Object> c = o.getClass(); 285 if (c == String.class) { 286 encoding.addString((String) o); 287 } else if (c == Byte.class) { 288 encoding.addByte((Byte) o); 289 } else if (c == Short.class) { 290 encoding.addShort((Short) o); 291 } else if (c == Character.class) { 292 encoding.addShort((Character) o); 293 } else if (c == Integer.class) { 294 encoding.addInt((Integer) o); 295 } else if (c == Long.class) { 296 encoding.addLong((Long) o); 297 } else if (c == Float.class) { 298 encoding.addInt(Float.floatToRawIntBits((Float) o)); 299 } else if (c == Double.class) { 300 encoding.addLong(Double.doubleToRawLongBits((Double) o)); 301 } else if (o instanceof Enum) { 302 encoding.addInt(((Enum<?>) o).ordinal()); 303 } else if (o instanceof ResolvedJavaMethod) { 304 encoding.addMethod((ResolvedJavaMethod) o); 305 } else if (o instanceof ResolvedJavaType) { 306 encoding.addType((ResolvedJavaType) o); 307 } else if (o instanceof ResolvedJavaField) { 308 encoding.addField((ResolvedJavaField) o); 309 } else if (o instanceof SpeculationContextObject) { 310 SpeculationContextObject sco = (SpeculationContextObject) o; 311 // These are compiler objects which all have the same class 312 // loader so the class name uniquely identifies the class. 313 encoding.addString(o.getClass().getName()); 314 sco.accept(new EncodingAdapter(encoding)); 315 } else if (o.getClass() == BytecodePosition.class) { 316 BytecodePosition p = (BytecodePosition) o; 317 while (p != null) { 318 encoding.addInt(p.getBCI()); 319 encoding.addMethod(p.getMethod()); 320 p = p.getCaller(); 321 } 322 } else { 323 throw new IllegalArgumentException("Unsupported type for encoding: " + c.getName()); 324 } 325 } 326 } 327 328 static class EncodingAdapter implements SpeculationContextObject.Visitor { 329 private final SpeculationReasonEncoding encoding; 330 331 EncodingAdapter(SpeculationReasonEncoding encoding) { 332 this.encoding = encoding; 333 } 334 335 @Override 336 public void visitBoolean(boolean v) { 337 encoding.addByte(v ? 1 : 0); 338 } 339 340 @Override 341 public void visitByte(byte v) { 342 encoding.addByte(v); 343 } 344 345 @Override 346 public void visitChar(char v) { 347 encoding.addShort(v); 348 } 349 350 @Override 351 public void visitShort(short v) { 352 encoding.addInt(v); 353 } 354 355 @Override 356 public void visitInt(int v) { 357 encoding.addInt(v); 358 } 359 360 @Override 361 public void visitLong(long v) { 362 encoding.addLong(v); 363 } 364 365 @Override 366 public void visitFloat(float v) { 367 encoding.addInt(Float.floatToRawIntBits(v)); 368 } 369 370 @Override 371 public void visitDouble(double v) { 372 encoding.addLong(Double.doubleToRawLongBits(v)); 373 } 374 375 @Override 376 public void visitObject(Object v) { 377 if (v == null) { 378 encoding.addInt(0); 379 } else { 380 DirectSpeculationReason.addNonNullObject(encoding, v); 381 } 382 } 383 } 384 385 static SpeculationReason createSpeculationReason(int groupId, String groupName, Object... context) { 386 return new DirectSpeculationReason(groupId, groupName, context); 387 } 388 389 /** 390 * Gets a unique identifier for this execution such as a process ID or a 391 * {@linkplain #getGlobalTimeStamp() fixed timestamp}. 392 */ 393 public static String getExecutionID() { 394 return Long.toString(ProcessHandle.current().pid()); 395 } 396 397 private static final AtomicLong globalTimeStamp = new AtomicLong(); 398 399 /** 400 * Gets a time stamp for the current process. This method will always return the same value for 401 * the current VM execution. 402 */ 403 public static long getGlobalTimeStamp() { 404 if (globalTimeStamp.get() == 0) { 405 globalTimeStamp.compareAndSet(0, System.currentTimeMillis()); 406 } 407 return globalTimeStamp.get(); 408 } 409 410 /** 411 * Returns an approximation of the total amount of memory, in bytes, allocated in heap memory 412 * for the thread of the specified ID. The returned value is an approximation because some Java 413 * virtual machine implementations may use object allocation mechanisms that result in a delay 414 * between the time an object is allocated and the time its size is recorded. 415 * <p> 416 * If the thread of the specified ID is not alive or does not exist, this method returns 417 * {@code -1}. If thread memory allocation measurement is disabled, this method returns 418 * {@code -1}. A thread is alive if it has been started and has not yet died. 419 * <p> 420 * If thread memory allocation measurement is enabled after the thread has started, the Java 421 * virtual machine implementation may choose any time up to and including the time that the 422 * capability is enabled as the point where thread memory allocation measurement starts. 423 * 424 * @param id the thread ID of a thread 425 * @return an approximation of the total memory allocated, in bytes, in heap memory for a thread 426 * of the specified ID if the thread of the specified ID exists, the thread is alive, 427 * and thread memory allocation measurement is enabled; {@code -1} otherwise. 428 * 429 * @throws IllegalArgumentException if {@code id} {@code <=} {@code 0}. 430 * @throws UnsupportedOperationException if the Java virtual machine implementation does not 431 * {@linkplain #isThreadAllocatedMemorySupported() support} thread memory allocation 432 * measurement. 433 */ 434 public static long getThreadAllocatedBytes(long id) { 435 JMXService jmx = JMXService.instance; 436 if (jmx == null) { 437 throw new UnsupportedOperationException(); 438 } 439 return jmx.getThreadAllocatedBytes(id); 440 } 441 442 /** 443 * Convenience method for calling {@link #getThreadAllocatedBytes(long)} with the id of the 444 * current thread. 445 */ 446 public static long getCurrentThreadAllocatedBytes() { 447 return getThreadAllocatedBytes(currentThread().getId()); 448 } 449 450 /** 451 * Returns the total CPU time for the current thread in nanoseconds. The returned value is of 452 * nanoseconds precision but not necessarily nanoseconds accuracy. If the implementation 453 * distinguishes between user mode time and system mode time, the returned CPU time is the 454 * amount of time that the current thread has executed in user mode or system mode. 455 * 456 * @return the total CPU time for the current thread if CPU time measurement is enabled; 457 * {@code -1} otherwise. 458 * 459 * @throws UnsupportedOperationException if the Java virtual machine does not 460 * {@linkplain #isCurrentThreadCpuTimeSupported() support} CPU time measurement for 461 * the current thread 462 */ 463 public static long getCurrentThreadCpuTime() { 464 JMXService jmx = JMXService.instance; 465 if (jmx == null) { 466 throw new UnsupportedOperationException(); 467 } 468 return jmx.getCurrentThreadCpuTime(); 469 } 470 471 /** 472 * Determines if the Java virtual machine implementation supports thread memory allocation 473 * measurement. 474 */ 475 public static boolean isThreadAllocatedMemorySupported() { 476 JMXService jmx = JMXService.instance; 477 if (jmx == null) { 478 return false; 479 } 480 return jmx.isThreadAllocatedMemorySupported(); 481 } 482 483 /** 484 * Determines if the Java virtual machine supports CPU time measurement for the current thread. 485 */ 486 public static boolean isCurrentThreadCpuTimeSupported() { 487 JMXService jmx = JMXService.instance; 488 if (jmx == null) { 489 return false; 490 } 491 return jmx.isCurrentThreadCpuTimeSupported(); 492 } 493 494 /** 495 * Gets the input arguments passed to the Java virtual machine which does not include the 496 * arguments to the {@code main} method. This method returns an empty list if there is no input 497 * argument to the Java virtual machine. 498 * <p> 499 * Some Java virtual machine implementations may take input arguments from multiple different 500 * sources: for examples, arguments passed from the application that launches the Java virtual 501 * machine such as the 'java' command, environment variables, configuration files, etc. 502 * <p> 503 * Typically, not all command-line options to the 'java' command are passed to the Java virtual 504 * machine. Thus, the returned input arguments may not include all command-line options. 505 * 506 * @return the input arguments to the JVM or {@code null} if they are unavailable 507 */ 508 public static List<String> getInputArguments() { 509 JMXService jmx = JMXService.instance; 510 if (jmx == null) { 511 return null; 512 } 513 return jmx.getInputArguments(); 514 } 515 516 /** 517 * Returns the fused multiply add of the three arguments; that is, returns the exact product of 518 * the first two arguments summed with the third argument and then rounded once to the nearest 519 * {@code float}. 520 */ 521 public static float fma(float a, float b, float c) { 522 return Math.fma(a, b, c); 523 } 524 525 /** 526 * Returns the fused multiply add of the three arguments; that is, returns the exact product of 527 * the first two arguments summed with the third argument and then rounded once to the nearest 528 * {@code double}. 529 */ 530 public static double fma(double a, double b, double c) { 531 return Math.fma(a, b, c); 532 } 533 534 public static VirtualObject createVirtualObject(ResolvedJavaType type, int id, boolean isAutoBox) { 535 return VirtualObject.get(type, id, isAutoBox); 536 } 537 }