| /* |
| * Copyright (C) 2015 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.caliper.platform.jvm; |
| |
| import static com.google.common.base.Preconditions.checkState; |
| import static java.lang.Thread.currentThread; |
| |
| import com.google.caliper.platform.Platform; |
| import com.google.caliper.platform.VirtualMachineException; |
| import com.google.caliper.util.Util; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.Collections2; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| |
| import java.io.File; |
| import java.lang.management.ManagementFactory; |
| import java.util.Collection; |
| import java.util.Map; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * An abstraction of a standard Java Virtual Machine platform. |
| */ |
| public final class JvmPlatform extends Platform { |
| |
| /** |
| * Some default JVM args to keep worker VMs somewhat predictable. |
| */ |
| @VisibleForTesting |
| public static final ImmutableSet<String> INSTRUMENT_JVM_ARGS = ImmutableSet.of( |
| // do compilation serially |
| "-Xbatch", |
| // make sure compilation doesn't run in parallel with itself |
| "-XX:CICompilerCount=1", |
| // ensure the parallel garbage collector |
| "-XX:+UseParallelGC", |
| // generate classes or don't, but do it immediately |
| "-Dsun.reflect.inflationThreshold=0"); |
| |
| private static final ImmutableSet<String> WORKER_PROCESS_ARGS = ImmutableSet.of( |
| "-XX:+PrintFlagsFinal", |
| "-XX:+PrintCompilation", |
| "-XX:+PrintGC"); |
| |
| |
| private static final Predicate<String> PROPERTIES_TO_RETAIN = new Predicate<String>() { |
| @Override public boolean apply(String input) { |
| return input.startsWith("java.vm") |
| || input.startsWith("java.runtime") |
| || input.equals("java.version") |
| || input.equals("java.vendor") |
| || input.equals("sun.reflect.noInflation") |
| || input.equals("sun.reflect.inflationThreshold"); |
| } |
| }; |
| |
| public JvmPlatform() { |
| super(Type.JVM); |
| } |
| |
| @Override |
| public File vmExecutable(File javaHome) { |
| // TODO(gak): support other platforms. This currently supports finding the java executable on |
| // standard configurations of unix systems and windows. |
| File bin = new File(javaHome, "bin"); |
| Preconditions.checkState(bin.exists() && bin.isDirectory(), |
| "Could not find %s under java home %s", bin, javaHome); |
| File jvm = new File(bin, "java"); |
| if (!jvm.exists() || jvm.isDirectory()) { |
| jvm = new File(bin, "java.exe"); |
| if (!jvm.exists() || jvm.isDirectory()) { |
| throw new IllegalStateException( |
| String.format("Cannot find java binary in %s, looked for java and java.exe", bin)); |
| } |
| } |
| |
| return jvm; |
| } |
| |
| @Override |
| public ImmutableSet<String> commonInstrumentVmArgs() { |
| return INSTRUMENT_JVM_ARGS; |
| } |
| |
| @Override |
| public ImmutableSet<String> workerProcessArgs() { |
| return WORKER_PROCESS_ARGS; |
| } |
| |
| @Override |
| public String workerClassPath() { |
| return getClassPath(); |
| } |
| |
| private static String getClassPath() { |
| // Use the effective class path in case this is being invoked in an isolated class loader |
| String classpath = |
| EffectiveClassPath.getClassPathForClassLoader(currentThread().getContextClassLoader()); |
| return classpath; |
| } |
| |
| @Override |
| public Collection<String> inputArguments() { |
| return Collections2.filter(ManagementFactory.getRuntimeMXBean().getInputArguments(), |
| new Predicate<String>() { |
| @Override |
| public boolean apply(String input) { |
| // Exclude the -agentlib:jdwp param which configures the socket debugging protocol. |
| // If this is set in the parent VM we do not want it to be inherited by the child |
| // VM. If it is, the child will die immediately on startup because it will fail to |
| // bind to the debug port (because the parent VM is already bound to it). |
| return !input.startsWith("-agentlib:jdwp"); |
| } |
| }); |
| } |
| |
| @Override |
| public Predicate<String> vmPropertiesToRetain() { |
| return PROPERTIES_TO_RETAIN; |
| } |
| |
| @Override |
| public void checkVmProperties(Map<String, String> options) { |
| checkState(!options.isEmpty()); |
| } |
| |
| @Override |
| public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName) |
| throws VirtualMachineException { |
| // Configuration can either be: |
| // vm.<vmConfigName>.home = <homeDir> |
| // or |
| // vm.baseDirectory = <baseDir> |
| // homeDir = <baseDir>/<vmConfigName> |
| ImmutableMap<String, String> vmMap = Util.subgroupMap(vmGroupMap, vmConfigName); |
| return getJdkHomeDir(vmGroupMap.get("baseDirectory"), vmMap.get("home"), vmConfigName); |
| } |
| |
| // TODO(gak): check that the directory seems to be a jdk home (with a java binary and all of that) |
| // TODO(gak): make this work with different directory layouts. I'm looking at you OS X... |
| public static File getJdkHomeDir(@Nullable String baseDirectoryPath, |
| @Nullable String homeDirPath, String vmConfigName) |
| throws VirtualMachineException { |
| if (homeDirPath == null) { |
| File baseDirectory = getBaseDirectory(baseDirectoryPath); |
| File homeDir = new File(baseDirectory, vmConfigName); |
| checkConfiguration(homeDir.isDirectory(), "%s is not a directory", homeDir); |
| return homeDir; |
| } else { |
| File potentialHomeDir = new File(homeDirPath); |
| if (potentialHomeDir.isAbsolute()) { |
| checkConfiguration(potentialHomeDir.isDirectory(), "%s is not a directory", |
| potentialHomeDir); |
| return potentialHomeDir; |
| } else { |
| File baseDirectory = getBaseDirectory(baseDirectoryPath); |
| File homeDir = new File(baseDirectory, homeDirPath); |
| checkConfiguration(homeDir.isDirectory(), "%s is not a directory", potentialHomeDir); |
| return homeDir; |
| } |
| } |
| } |
| |
| private static File getBaseDirectory(@Nullable String baseDirectoryPath) |
| throws VirtualMachineException { |
| if (baseDirectoryPath == null) { |
| throw new VirtualMachineException( |
| "must set either a home directory or a base directory"); |
| } else { |
| File baseDirectory = new File(baseDirectoryPath); |
| checkConfiguration(baseDirectory.isAbsolute(), "base directory cannot be a relative path"); |
| checkConfiguration(baseDirectory.isDirectory(), "base directory must be a directory"); |
| return baseDirectory; |
| } |
| } |
| |
| private static void checkConfiguration(boolean check, String message) |
| throws VirtualMachineException { |
| if (!check) { |
| throw new VirtualMachineException(message); |
| } |
| } |
| |
| private static void checkConfiguration(boolean check, String messageFormat, Object... args) |
| throws VirtualMachineException { |
| if (!check) { |
| throw new VirtualMachineException(String.format(messageFormat, args)); |
| } |
| } |
| } |