• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.caliper.platform.jvm;
18 
19 import static com.google.common.base.Preconditions.checkState;
20 import static java.lang.Thread.currentThread;
21 
22 import com.google.caliper.platform.Platform;
23 import com.google.caliper.platform.VirtualMachineException;
24 import com.google.caliper.util.Util;
25 import com.google.common.annotations.VisibleForTesting;
26 import com.google.common.base.Preconditions;
27 import com.google.common.base.Predicate;
28 import com.google.common.collect.Collections2;
29 import com.google.common.collect.ImmutableMap;
30 import com.google.common.collect.ImmutableSet;
31 
32 import java.io.File;
33 import java.lang.management.ManagementFactory;
34 import java.util.Collection;
35 import java.util.Map;
36 
37 import javax.annotation.Nullable;
38 
39 /**
40  * An abstraction of a standard Java Virtual Machine platform.
41  */
42 public final class JvmPlatform extends Platform {
43 
44   /**
45    * Some default JVM args to keep worker VMs somewhat predictable.
46    */
47   @VisibleForTesting
48   public static final ImmutableSet<String> INSTRUMENT_JVM_ARGS = ImmutableSet.of(
49       // do compilation serially
50       "-Xbatch",
51       // make sure compilation doesn't run in parallel with itself
52       "-XX:CICompilerCount=1",
53       // ensure the parallel garbage collector
54       "-XX:+UseParallelGC",
55       // generate classes or don't, but do it immediately
56       "-Dsun.reflect.inflationThreshold=0");
57 
58   private static final ImmutableSet<String> WORKER_PROCESS_ARGS = ImmutableSet.of(
59       "-XX:+PrintFlagsFinal",
60       "-XX:+PrintCompilation",
61       "-XX:+PrintGC");
62 
63 
64   private static final Predicate<String> PROPERTIES_TO_RETAIN = new Predicate<String>() {
65     @Override public boolean apply(String input) {
66       return input.startsWith("java.vm")
67           || input.startsWith("java.runtime")
68           || input.equals("java.version")
69           || input.equals("java.vendor")
70           || input.equals("sun.reflect.noInflation")
71           || input.equals("sun.reflect.inflationThreshold");
72     }
73   };
74 
JvmPlatform()75   public JvmPlatform() {
76     super(Type.JVM);
77   }
78 
79   @Override
vmExecutable(File javaHome)80   public File vmExecutable(File javaHome) {
81     // TODO(gak): support other platforms. This currently supports finding the java executable on
82     // standard configurations of unix systems and windows.
83     File bin = new File(javaHome, "bin");
84     Preconditions.checkState(bin.exists() && bin.isDirectory(),
85         "Could not find %s under java home %s", bin, javaHome);
86     File jvm = new File(bin, "java");
87     if (!jvm.exists() || jvm.isDirectory()) {
88       jvm = new File(bin, "java.exe");
89       if (!jvm.exists() || jvm.isDirectory()) {
90         throw new IllegalStateException(
91             String.format("Cannot find java binary in %s, looked for java and java.exe", bin));
92       }
93     }
94 
95     return jvm;
96   }
97 
98   @Override
commonInstrumentVmArgs()99   public ImmutableSet<String> commonInstrumentVmArgs() {
100     return INSTRUMENT_JVM_ARGS;
101   }
102 
103   @Override
workerProcessArgs()104   public ImmutableSet<String> workerProcessArgs() {
105     return WORKER_PROCESS_ARGS;
106   }
107 
108   @Override
workerClassPath()109   public String workerClassPath() {
110     return getClassPath();
111   }
112 
getClassPath()113   private static String getClassPath() {
114     // Use the effective class path in case this is being invoked in an isolated class loader
115     String classpath =
116         EffectiveClassPath.getClassPathForClassLoader(currentThread().getContextClassLoader());
117     return classpath;
118   }
119 
120   @Override
inputArguments()121   public Collection<String> inputArguments() {
122     return Collections2.filter(ManagementFactory.getRuntimeMXBean().getInputArguments(),
123         new Predicate<String>() {
124           @Override
125           public boolean apply(String input) {
126             // Exclude the -agentlib:jdwp param which configures the socket debugging protocol.
127             // If this is set in the parent VM we do not want it to be inherited by the child
128             // VM.  If it is, the child will die immediately on startup because it will fail to
129             // bind to the debug port (because the parent VM is already bound to it).
130             return !input.startsWith("-agentlib:jdwp");
131           }
132         });
133   }
134 
135   @Override
136   public Predicate<String> vmPropertiesToRetain() {
137     return PROPERTIES_TO_RETAIN;
138   }
139 
140   @Override
141   public void checkVmProperties(Map<String, String> options) {
142     checkState(!options.isEmpty());
143   }
144 
145   @Override
146   public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName)
147           throws VirtualMachineException {
148     // Configuration can either be:
149     //   vm.<vmConfigName>.home = <homeDir>
150     // or
151     //   vm.baseDirectory = <baseDir>
152     //   homeDir = <baseDir>/<vmConfigName>
153     ImmutableMap<String, String> vmMap = Util.subgroupMap(vmGroupMap, vmConfigName);
154     return getJdkHomeDir(vmGroupMap.get("baseDirectory"), vmMap.get("home"), vmConfigName);
155   }
156 
157   // TODO(gak): check that the directory seems to be a jdk home (with a java binary and all of that)
158   // TODO(gak): make this work with different directory layouts.  I'm looking at you OS X...
159   public static File getJdkHomeDir(@Nullable String baseDirectoryPath,
160           @Nullable String homeDirPath, String vmConfigName)
161           throws VirtualMachineException {
162     if (homeDirPath == null) {
163       File baseDirectory = getBaseDirectory(baseDirectoryPath);
164       File homeDir = new File(baseDirectory, vmConfigName);
165       checkConfiguration(homeDir.isDirectory(), "%s is not a directory", homeDir);
166       return homeDir;
167     } else {
168       File potentialHomeDir = new File(homeDirPath);
169       if (potentialHomeDir.isAbsolute()) {
170         checkConfiguration(potentialHomeDir.isDirectory(), "%s is not a directory",
171                 potentialHomeDir);
172         return potentialHomeDir;
173       } else {
174         File baseDirectory = getBaseDirectory(baseDirectoryPath);
175         File homeDir = new File(baseDirectory, homeDirPath);
176         checkConfiguration(homeDir.isDirectory(), "%s is not a directory", potentialHomeDir);
177         return homeDir;
178       }
179     }
180   }
181 
182   private static File getBaseDirectory(@Nullable String baseDirectoryPath)
183           throws VirtualMachineException {
184     if (baseDirectoryPath == null) {
185       throw new VirtualMachineException(
186               "must set either a home directory or a base directory");
187     } else {
188       File baseDirectory = new File(baseDirectoryPath);
189       checkConfiguration(baseDirectory.isAbsolute(), "base directory cannot be a relative path");
190       checkConfiguration(baseDirectory.isDirectory(), "base directory must be a directory");
191       return baseDirectory;
192     }
193   }
194 
195   private static void checkConfiguration(boolean check, String message)
196           throws VirtualMachineException {
197     if (!check) {
198       throw new VirtualMachineException(message);
199     }
200   }
201 
202   private static void checkConfiguration(boolean check, String messageFormat, Object... args)
203           throws VirtualMachineException {
204     if (!check) {
205       throw new VirtualMachineException(String.format(messageFormat, args));
206     }
207   }
208 }
209