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