package org.graalvm.compiler.hotspot.test;
import static java.util.Collections.singletonList;
import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Print;
import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAsFailure;
import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
import static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries;
import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes;
import static org.graalvm.compiler.hotspot.CompilationTask.CompilationTime;
import static org.graalvm.compiler.hotspot.CompilationTask.CompiledAndInstalledBytecodes;
import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS;
import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.InvalidateInstalledCode;
import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import jdk.internal.vm.compiler.collections.EconomicMap;
import jdk.internal.vm.compiler.collections.UnmodifiableMapCursor;
import org.graalvm.compiler.api.replacements.Snippet;
import org.graalvm.compiler.bytecode.Bytecodes;
import org.graalvm.compiler.core.CompilerThreadFactory;
import org.graalvm.compiler.core.phases.HighTier;
import org.graalvm.compiler.core.test.ReflectionOptionDescriptors;
import org.graalvm.compiler.debug.DebugOptions;
import org.graalvm.compiler.debug.GlobalMetrics;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.debug.MethodFilter;
import org.graalvm.compiler.debug.MetricKey;
import org.graalvm.compiler.debug.TTY;
import org.graalvm.compiler.hotspot.CompilationTask;
import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
import org.graalvm.compiler.hotspot.HotSpotGraalCompiler;
import org.graalvm.compiler.hotspot.HotSpotGraalRuntime;
import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
import org.graalvm.compiler.hotspot.test.CompileTheWorld.LibGraalParams.StackTraceBuffer;
import org.graalvm.compiler.options.OptionDescriptors;
import org.graalvm.compiler.options.OptionKey;
import org.graalvm.compiler.options.OptionValues;
import org.graalvm.compiler.options.OptionsParser;
import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.compiler.api.test.ModuleSupport;
import jdk.internal.vm.compiler.libgraal.LibGraal;
import jdk.internal.vm.compiler.libgraal.LibGraalScope;
import org.graalvm.util.OptionsEncoder;
import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
import jdk.vm.ci.hotspot.HotSpotInstalledCode;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.runtime.JVMCI;
import jdk.vm.ci.runtime.JVMCICompiler;
import sun.misc.Unsafe;
public final class CompileTheWorld {
static {
ModuleSupport.exportAndOpenAllPackagesToUnnamed("jdk.internal.vm.compiler");
}
public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path";
public static final String JRT_CLASS_PATH_ENTRY = "<jrt>";
public static EconomicMap<OptionKey<?>, Object> parseOptions(String options) {
EconomicMap<OptionKey<?>, Object> values = OptionValues.newOptionMap();
if (options != null) {
EconomicMap<String, String> optionSettings = EconomicMap.create();
for (String optionSetting : options.split("\\s+|#")) {
OptionsParser.parseOptionSettingTo(optionSetting, optionSettings);
}
ServiceLoader<OptionDescriptors> loader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
OptionsParser.parseOptions(optionSettings, values, loader);
}
if (!values.containsKey(HighTier.Options.Inline)) {
values.put(HighTier.Options.Inline, false);
}
return values;
}
private final HotSpotJVMCIRuntime jvmciRuntime;
private final HotSpotGraalCompiler compiler;
private final String inputClassPath;
private final int startAt;
private final int stopAt;
private final int maxClasses;
private final MethodFilter[] methodFilters;
private final MethodFilter[] excludeMethodFilters;
private int classFileCounter = 0;
private AtomicLong compiledMethodsCounter = new AtomicLong();
private AtomicLong compileTime = new AtomicLong();
private AtomicLong memoryUsed = new AtomicLong();
private boolean verbose;
private boolean running;
private ThreadPoolExecutor threadPool;
private final OptionValues harnessOptions;
private final OptionValues compilerOptions;
static class LibGraalParams implements AutoCloseable {
static {
LibGraal.registerNativeMethods(HotSpotJVMCIRuntime.runtime(), CompileTheWorld.class);
}
static class OptionsBuffer {
private long address;
final int size;
final int hash;
OptionsBuffer(OptionValues options) {
Map<String, Object> map = new HashMap<>();
UnmodifiableMapCursor<OptionKey<?>, Object> cursor = options.getMap().getEntries();
while (cursor.advance()) {
final OptionKey<?> key = cursor.getKey();
Object value = cursor.getValue();
map.put(key.getName(), value);
}
byte[] encoded = OptionsEncoder.encode(map);
size = encoded.length;
hash = Arrays.hashCode(encoded);
address = UNSAFE.allocateMemory(encoded.length);
UNSAFE.copyMemory(encoded, ARRAY_BYTE_BASE_OFFSET, null, address, size);
}
long getAddress() {
if (address == 0) {
throw new IllegalStateException();
}
return address;
}
void free() {
if (address != 0) {
UNSAFE.freeMemory(address);
address = 0;
}
}
}
static class StackTraceBuffer {
final int size;
private long address;
StackTraceBuffer(int size) {
this.size = size;
address = UNSAFE.allocateMemory(size);
}
void free() {
if (address != 0L) {
UNSAFE.freeMemory(address);
address = 0L;
}
}
long getAddress() {
if (address == 0) {
throw new IllegalStateException();
}
return address;
}
}
final OptionsBuffer options;
private final List<StackTraceBuffer> stackTraceBuffers = new ArrayList<>();
StackTraceBuffer getStackTraceBuffer() {
return stackTraceBuffer.get();
}
private final ThreadLocal<StackTraceBuffer> stackTraceBuffer = new ThreadLocal<StackTraceBuffer>() {
@Override
protected StackTraceBuffer initialValue() {
StackTraceBuffer buffer = new StackTraceBuffer(10_000);
synchronized (stackTraceBuffers) {
stackTraceBuffers.add(buffer);
}
return buffer;
}
};
LibGraalParams(OptionValues options) {
this.options = new OptionsBuffer(options);
}
@Override
public void close() {
options.free();
synchronized (stackTraceBuffers) {
for (StackTraceBuffer buffer : stackTraceBuffers) {
buffer.free();
}
stackTraceBuffers.clear();
}
}
}
public CompileTheWorld(HotSpotJVMCIRuntime jvmciRuntime,
HotSpotGraalCompiler compiler,
String files,
int startAt,
int stopAt,
int maxClasses,
String methodFilters,
String excludeMethodFilters,
boolean verbose,
OptionValues harnessOptions,
OptionValues compilerOptions) {
this.jvmciRuntime = jvmciRuntime;
this.compiler = compiler;
this.inputClassPath = files;
this.startAt = Math.max(startAt, 1);
this.stopAt = Math.max(stopAt, 1);
this.maxClasses = Math.max(maxClasses, 1);
this.methodFilters = methodFilters == null || methodFilters.isEmpty() ? null : MethodFilter.parse(methodFilters);
this.excludeMethodFilters = excludeMethodFilters == null || excludeMethodFilters.isEmpty() ? null : MethodFilter.parse(excludeMethodFilters);
this.verbose = verbose;
this.harnessOptions = harnessOptions;
EconomicMap<OptionKey<?>, Object> compilerOptionsMap = EconomicMap.create(compilerOptions.getMap());
CompilationBailoutAsFailure.putIfAbsent(compilerOptionsMap, true);
CompilationFailureAction.putIfAbsent(compilerOptionsMap, Print);
DebugOptions.MetricsThreadFilter.putIfAbsent(compilerOptionsMap, "^CompileTheWorld");
this.compilerOptions = new OptionValues(compilerOptionsMap);
}
public CompileTheWorld(HotSpotJVMCIRuntime jvmciRuntime,
HotSpotGraalCompiler compiler,
OptionValues harnessOptions,
OptionValues compilerOptions) {
this(jvmciRuntime, compiler, Options.Classpath.getValue(harnessOptions),
Options.StartAt.getValue(harnessOptions),
Options.StopAt.getValue(harnessOptions),
Options.MaxClasses.getValue(harnessOptions),
Options.MethodFilter.getValue(harnessOptions),
Options.ExcludeMethodFilter.getValue(harnessOptions),
Options.Verbose.hasBeenSet(harnessOptions) ? Options.Verbose.getValue(harnessOptions) : !Options.MultiThreaded.getValue(harnessOptions),
harnessOptions,
new OptionValues(compilerOptions, parseOptions(Options.Config.getValue(harnessOptions))));
}
@SuppressWarnings("try")
public void compile() throws Throwable {
try (LibGraalParams libgraal = LibGraal.isAvailable() ? new LibGraalParams(compilerOptions) : null) {
if (SUN_BOOT_CLASS_PATH.equals(inputClassPath)) {
String bcpEntry = null;
if (JavaVersionUtil.JAVA_SPEC <= 8) {
final String[] entries = System.getProperty(SUN_BOOT_CLASS_PATH).split(File.pathSeparator);
for (int i = 0; i < entries.length && bcpEntry == null; i++) {
String entry = entries[i];
File entryFile = new File(entry);
if (entryFile.getName().endsWith("rt.jar") && entryFile.isFile()) {
bcpEntry = entry;
}
}
if (bcpEntry == null) {
throw new GraalError("Could not find rt.jar on boot class path %s", System.getProperty(SUN_BOOT_CLASS_PATH));
}
} else {
bcpEntry = JRT_CLASS_PATH_ENTRY;
}
compile(bcpEntry, libgraal);
} else {
compile(inputClassPath, libgraal);
}
}
}
public void println() {
println("");
}
public void println(String format, Object... args) {
println(String.format(format, args));
}
public void println(String s) {
println(verbose, s);
}
public static void println(boolean cond, String s) {
if (cond) {
TTY.println(s);
}
}
public void printStackTrace(Throwable t) {
if (verbose) {
t.printStackTrace(TTY.out);
}
}
@SuppressWarnings("unused")
private static void dummy() {
}
abstract static class ClassPathEntry implements Closeable {
final String name;
ClassPathEntry(String name) {
this.name = name;
}
public abstract ClassLoader createClassLoader() throws IOException;
public abstract List<String> getClassNames() throws IOException;
@Override
public String toString() {
return name;
}
@Override
public void close() throws IOException {
}
}
static class DirClassPathEntry extends ClassPathEntry {
private final File dir;
DirClassPathEntry(String name) {
super(name);
dir = new File(name);
assert dir.isDirectory();
}
@Override
public ClassLoader createClassLoader() throws IOException {
URL url = dir.toURI().toURL();
return new URLClassLoader(new URL[]{url});
}
@Override
public List<String> getClassNames() throws IOException {
List<String> classNames = new ArrayList<>();
String root = dir.getPath();
SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (attrs.isRegularFile()) {
File path = file.toFile();
if (path.getName().endsWith(".class")) {
String pathString = path.getPath();
assert pathString.startsWith(root);
String classFile = pathString.substring(root.length() + 1);
String className = classFile.replace(File.separatorChar, '.');
classNames.add(className.replace('/', '.').substring(0, className.length() - ".class".length()));
}
}
return super.visitFile(file, attrs);
}
};
Files.walkFileTree(dir.toPath(), visitor);
return classNames;
}
}
static class JarClassPathEntry extends ClassPathEntry {
private final JarFile jarFile;
JarClassPathEntry(String name) throws IOException {
super(name);
jarFile = new JarFile(name);
}
@Override
public ClassLoader createClassLoader() throws IOException {
URL url = new URL("jar", "", "file:" + name + "!/");
return new URLClassLoader(new URL[]{url});
}
static Pattern MultiReleaseJarVersionedClassRE = Pattern.compile("META-INF/versions/[1-9][0-9]*/(.+)");
@Override
public List<String> getClassNames() throws IOException {
Enumeration<JarEntry> e = jarFile.entries();
List<String> classNames = new ArrayList<>(jarFile.size());
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if (je.isDirectory() || !je.getName().endsWith(".class")) {
continue;
}
String className = je.getName().substring(0, je.getName().length() - ".class".length());
if (className.equals("module-info")) {
continue;
}
if (className.startsWith("META-INF/versions/")) {
Matcher m = MultiReleaseJarVersionedClassRE.matcher(className);
if (m.matches()) {
className = m.group(1);
} else {
continue;
}
}
classNames.add(className.replace('/', '.'));
}
return classNames;
}
@Override
public void close() throws IOException {
jarFile.close();
}
}
static class JRTClassPathEntry extends ClassPathEntry {
private final String limitModules;
JRTClassPathEntry(String name, String limitModules) {
super(name);
this.limitModules = limitModules;
}
@Override
public ClassLoader createClassLoader() throws IOException {
URL url = URI.create("jrt:/").toURL();
return new URLClassLoader(new URL[]{url});
}
@Override
public List<String> getClassNames() throws IOException {
Set<String> negative = new HashSet<>();
Set<String> positive = new HashSet<>();
if (limitModules != null && !limitModules.isEmpty()) {
for (String s : limitModules.split(",")) {
if (s.startsWith("~")) {
negative.add(s.substring(1));
} else {
positive.add(s);
}
}
}
List<String> classNames = new ArrayList<>();
FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap());
Path top = fs.getPath("/modules/");
Files.find(top, Integer.MAX_VALUE,
(path, attrs) -> attrs.isRegularFile()).forEach(p -> {
int nameCount = p.getNameCount();
if (nameCount > 2) {
String base = p.getName(nameCount - 1).toString();
if (base.endsWith(".class") && !base.equals("module-info.class")) {
String module = p.getName(1).toString();
if (positive.isEmpty() || positive.contains(module)) {
if (negative.isEmpty() || !negative.contains(module)) {
String className = p.subpath(2, nameCount).toString().replace('/', '.');
className = className.replace('/', '.').substring(0, className.length() - ".class".length());
classNames.add(className);
}
}
}
}
});
return classNames;
}
}
private boolean isClassIncluded(String className) {
if (methodFilters != null && !MethodFilter.matchesClassName(methodFilters, className)) {
return false;
}
if (excludeMethodFilters != null && MethodFilter.matchesClassName(excludeMethodFilters, className)) {
return false;
}
return true;
}
private ClassPathEntry openClassPathEntry(String entry) throws IOException {
if (entry.endsWith(".zip") || entry.endsWith(".jar")) {
return new JarClassPathEntry(entry);
} else if (entry.equals(JRT_CLASS_PATH_ENTRY)) {
return new JRTClassPathEntry(entry, Options.LimitModules.getValue(harnessOptions));
} else {
if (!new File(entry).isDirectory()) {
return null;
}
return new DirClassPathEntry(entry);
}
}
@SuppressWarnings("try")
private void compile(String classPath, LibGraalParams libgraal) throws IOException {
final String[] entries = classPath.split(File.pathSeparator);
long start = System.nanoTime();
Map<Thread, StackTraceElement[]> initialThreads = Thread.getAllStackTraces();
if (libgraal == null) {
try {
HotSpotResolvedJavaMethod dummyMethod = (HotSpotResolvedJavaMethod) JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess().lookupJavaMethod(
CompileTheWorld.class.getDeclaredMethod("dummy"));
int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
boolean useProfilingInfo = false;
boolean installAsDefault = false;
CompilationTask task = new CompilationTask(jvmciRuntime, compiler, new HotSpotCompilationRequest(dummyMethod, entryBCI, 0L), useProfilingInfo, installAsDefault);
task.runCompilation(compilerOptions);
} catch (NoSuchMethodException | SecurityException e1) {
printStackTrace(e1);
}
}
int threadCount = 1;
if (Options.MultiThreaded.getValue(harnessOptions)) {
threadCount = Options.Threads.getValue(harnessOptions);
if (threadCount == 0) {
threadCount = Runtime.getRuntime().availableProcessors();
}
} else {
running = true;
}
threadPool = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CompilerThreadFactory("CompileTheWorld"));
int compileStartAt = startAt;
int compileStopAt = stopAt;
int compileStep = 1;
if (maxClasses != Integer.MAX_VALUE) {
int totalClassFileCount = 0;
for (String entry : entries) {
try (ClassPathEntry cpe = openClassPathEntry(entry)) {
if (cpe != null) {
totalClassFileCount += cpe.getClassNames().size();
}
}
}
int lastClassFile = totalClassFileCount - 1;
compileStartAt = Math.min(startAt, lastClassFile);
compileStopAt = Math.min(stopAt, lastClassFile);
int range = compileStopAt - compileStartAt + 1;
if (maxClasses < range) {
compileStep = range / maxClasses;
}
}
for (int i = 0; i < entries.length; i++) {
final String entry = entries[i];
try (ClassPathEntry cpe = openClassPathEntry(entry)) {
if (cpe == null) {
println("CompileTheWorld : Skipped classes in " + entry);
println();
continue;
}
if (methodFilters == null || methodFilters.length == 0) {
println("CompileTheWorld : Compiling all classes in " + entry);
} else {
String include = Arrays.asList(methodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
println("CompileTheWorld : Compiling all methods in " + entry + " matching one of the following filters: " + include);
}
if (excludeMethodFilters != null && excludeMethodFilters.length > 0) {
String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude);
}
println();
ClassLoader loader = cpe.createClassLoader();
for (String className : cpe.getClassNames()) {
if (classFileCounter >= compileStopAt) {
break;
}
classFileCounter++;
if (compileStep > 1 && ((classFileCounter - compileStartAt) % compileStep) != 0) {
continue;
}
if (className.startsWith("jdk.management.") ||
className.startsWith("jdk.internal.cmm.*") ||
className.startsWith("sun.tools.jconsole.")) {
continue;
}
if (!isClassIncluded(className)) {
continue;
}
try {
Class<?> javaClass = Class.forName(className, true, loader);
MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
try {
HotSpotResolvedObjectType objectType = (HotSpotResolvedObjectType) metaAccess.lookupJavaType(javaClass);
ConstantPool constantPool = objectType.getConstantPool();
for (int cpi = 1; cpi < constantPool.length(); cpi++) {
constantPool.loadReferencedType(cpi, Bytecodes.LDC);
}
} catch (Throwable t) {
if (isClassIncluded(className)) {
println("Preloading failed for (%d) %s: %s", classFileCounter, className, t);
}
continue;
}
if (classFileCounter >= compileStartAt) {
long start0 = System.nanoTime();
for (Constructor<?> constructor : javaClass.getDeclaredConstructors()) {
HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(constructor);
if (canBeCompiled(javaMethod, constructor.getModifiers())) {
compileMethod(javaMethod, libgraal);
}
}
for (Method method : javaClass.getDeclaredMethods()) {
HotSpotResolvedJavaMethod javaMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method);
if (canBeCompiled(javaMethod, method.getModifiers())) {
compileMethod(javaMethod, libgraal);
}
}
HotSpotResolvedJavaMethod clinit = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaType(javaClass).getClassInitializer();
if (clinit != null && canBeCompiled(clinit, clinit.getModifiers())) {
compileMethod(clinit, libgraal);
}
println("CompileTheWorld (%d) : %s (%d us)", classFileCounter, className, (System.nanoTime() - start0) / 1000);
}
} catch (Throwable t) {
if (isClassIncluded(className)) {
println("CompileTheWorld (%d) : Skipping %s %s", classFileCounter, className, t.toString());
printStackTrace(t);
}
}
}
}
}
if (!running) {
startThreads();
}
int wakeups = 0;
long lastCompletedTaskCount = 0;
for (long completedTaskCount = threadPool.getCompletedTaskCount(); completedTaskCount != threadPool.getTaskCount(); completedTaskCount = threadPool.getCompletedTaskCount()) {
if (wakeups % 15 == 0) {
TTY.printf("CompileTheWorld : Waiting for %d compiles, just completed %d compiles%n", threadPool.getTaskCount() - completedTaskCount, completedTaskCount - lastCompletedTaskCount);
lastCompletedTaskCount = completedTaskCount;
}
try {
threadPool.awaitTermination(1, TimeUnit.SECONDS);
wakeups++;
} catch (InterruptedException e) {
}
}
threadPool.shutdown();
threadPool = null;
long elapsedTime = System.nanoTime() - start;
println();
int compiledClasses = classFileCounter > compileStartAt ? classFileCounter - compileStartAt : 0;
if (Options.MultiThreaded.getValue(harnessOptions)) {
TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms elapsed, %d ms compile time, %d bytes of memory used)", compiledClasses, compiledMethodsCounter.get(), elapsedTime,
compileTime.get() / 1000000, memoryUsed.get());
} else {
TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms, %d bytes of memory used)", compiledClasses, compiledMethodsCounter.get(), compileTime.get(), memoryUsed.get());
}
GlobalMetrics metricValues = ((HotSpotGraalRuntime) compiler.getGraalRuntime()).getMetricValues();
EconomicMap<MetricKey, Long> map = metricValues.asKeyValueMap();
Long compiledAndInstalledBytecodes = map.get(CompiledAndInstalledBytecodes);
Long compilationTime = map.get(CompilationTime);
if (compiledAndInstalledBytecodes != null && compilationTime != null) {
TTY.println("CompileTheWorld : Aggregate compile speed %d bytecodes per second (%d / %d)", (int) (compiledAndInstalledBytecodes / (compilationTime / 1000000000.0)),
compiledAndInstalledBytecodes, compilationTime);
}
metricValues.print(compilerOptions);
metricValues.clear();
Map<Thread, StackTraceElement[]> suspiciousThreads = new HashMap<>();
for (Map.Entry<Thread, StackTraceElement[]> e : Thread.getAllStackTraces().entrySet()) {
Thread thread = e.getKey();
if (thread != Thread.currentThread() && !initialThreads.containsKey(thread) && !thread.isDaemon() && thread.isAlive()) {
suspiciousThreads.put(thread, e.getValue());
}
}
if (!suspiciousThreads.isEmpty()) {
TTY.println("--- Non-daemon threads started during CTW ---");
for (Map.Entry<Thread, StackTraceElement[]> e : suspiciousThreads.entrySet()) {
Thread thread = e.getKey();
if (thread.isAlive()) {
TTY.println(thread.toString() + " " + thread.getState());
for (StackTraceElement ste : e.getValue()) {
TTY.println("\tat " + ste);
}
}
}
TTY.println("---------------------------------------------");
}
}
private synchronized void startThreads() {
running = true;
notifyAll();
}
private synchronized void waitToRun() {
while (!running) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
@SuppressWarnings("try")
private void compileMethod(HotSpotResolvedJavaMethod method, LibGraalParams libgraal) throws InterruptedException, ExecutionException {
if (methodFilters != null && !MethodFilter.matches(methodFilters, method)) {
return;
}
if (excludeMethodFilters != null && MethodFilter.matches(excludeMethodFilters, method)) {
return;
}
Future<?> task = threadPool.submit(new Runnable() {
@Override
public void run() {
waitToRun();
compileMethod(method, classFileCounter, libgraal);
}
});
if (threadPool.getCorePoolSize() == 1) {
task.get();
}
}
private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe();
static native long compileMethodInLibgraal(long isolateThread,
long methodHandle,
boolean useProfilingInfo,
boolean installAsDefault,
long optionsAddress,
int optionsSize,
int optionsHash,
long encodedThrowableBufferAddress,
int encodedThrowableBufferSize);
@SuppressWarnings("try")
private void compileMethod(HotSpotResolvedJavaMethod method, int counter, LibGraalParams libgraal) {
try {
long start = System.nanoTime();
long allocatedAtStart = getCurrentThreadAllocatedBytes();
boolean useProfilingInfo = false;
boolean installAsDefault = false;
HotSpotInstalledCode installedCode;
if (libgraal != null) {
HotSpotJVMCIRuntime runtime = HotSpotJVMCIRuntime.runtime();
try (LibGraalScope scope = new LibGraalScope(runtime)) {
long methodHandle = LibGraal.translate(runtime, method);
long isolateThread = LibGraalScope.getIsolateThread();
StackTraceBuffer stackTraceBuffer = libgraal.getStackTraceBuffer();
long stackTraceBufferAddress = stackTraceBuffer.getAddress();
long installedCodeHandle = compileMethodInLibgraal(isolateThread,
methodHandle,
useProfilingInfo,
installAsDefault,
libgraal.options.getAddress(),
libgraal.options.size,
libgraal.options.hash,
stackTraceBufferAddress,
stackTraceBuffer.size);
installedCode = LibGraal.unhand(runtime, HotSpotInstalledCode.class, installedCodeHandle);
if (installedCode == null) {
int length = UNSAFE.getInt(stackTraceBufferAddress);
byte[] data = new byte[length];
UNSAFE.copyMemory(null, stackTraceBufferAddress + Integer.BYTES, data, ARRAY_BYTE_BASE_OFFSET, length);
String stackTrace = new String(data).trim();
println(true, String.format("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r")));
println(true, stackTrace);
}
}
} else {
int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, 0L);
CompilationTask task = new CompilationTask(jvmciRuntime, compiler, request, useProfilingInfo, installAsDefault);
task.runCompilation(compilerOptions);
installedCode = task.getInstalledCode();
}
if (installedCode != null && InvalidateInstalledCode.getValue(compilerOptions)) {
installedCode.invalidate();
}
memoryUsed.getAndAdd(getCurrentThreadAllocatedBytes() - allocatedAtStart);
compileTime.getAndAdd(System.nanoTime() - start);
compiledMethodsCounter.incrementAndGet();
} catch (Throwable t) {
println("CompileTheWorld (%d) : Error compiling method: %s", counter, method.format("%H.%n(%p):%r"));
printStackTrace(t);
}
}
private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
return false;
}
GraalHotSpotVMConfig c = compiler.getGraalRuntime().getVMConfig();
if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) {
println(verbose || methodFilters != null,
String.format("CompileTheWorld (%d) : Skipping huge method %s (use -XX:-DontCompileHugeMethods or -XX:HugeMethodLimit=%d to include it)", classFileCounter,
javaMethod.format("%H.%n(%p):%r"),
javaMethod.getCodeSize()));
return false;
}
if (!javaMethod.canBeInlined()) {
return false;
}
for (Annotation annotation : javaMethod.getAnnotations()) {
if (annotation.annotationType().equals(Snippet.class)) {
return false;
}
}
return true;
}
static class Options {
public static final OptionKey<Boolean> Help = new OptionKey<>(false);