1 /* 2 * Copyright (C) 2012 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.config; 18 19 import static com.google.caliper.util.Util.subgroupMap; 20 import static com.google.common.base.Preconditions.checkArgument; 21 import static com.google.common.base.Preconditions.checkNotNull; 22 import static com.google.common.base.Preconditions.checkState; 23 24 import com.google.caliper.api.ResultProcessor; 25 import com.google.caliper.platform.Platform; 26 import com.google.caliper.platform.VirtualMachineException; 27 import com.google.caliper.util.Util; 28 import com.google.common.annotations.VisibleForTesting; 29 import com.google.common.base.MoreObjects; 30 import com.google.common.base.Strings; 31 import com.google.common.collect.BiMap; 32 import com.google.common.collect.HashBiMap; 33 import com.google.common.collect.ImmutableBiMap; 34 import com.google.common.collect.ImmutableList; 35 import com.google.common.collect.ImmutableMap; 36 import com.google.common.collect.ImmutableSet; 37 38 import java.io.File; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Map.Entry; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 45 import javax.annotation.Nullable; 46 47 /** 48 * Represents caliper configuration. By default, {@code ~/.caliper/config.properties} and 49 * {@code global-config.properties}. 50 * 51 * @author gak@google.com (Gregory Kick) 52 */ 53 public final class CaliperConfig { 54 @VisibleForTesting final ImmutableMap<String, String> properties; 55 private final ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig> 56 resultProcessorConfigs; 57 58 @VisibleForTesting CaliperConfig(ImmutableMap<String, String> properties)59 public CaliperConfig(ImmutableMap<String, String> properties) 60 throws InvalidConfigurationException { 61 this.properties = checkNotNull(properties); 62 this.resultProcessorConfigs = findResultProcessorConfigs(subgroupMap(properties, "results")); 63 } 64 65 private static final Pattern CLASS_PROPERTY_PATTERN = Pattern.compile("(\\w+)\\.class"); 66 mapGroupNamesToClasses( ImmutableMap<String, String> groupProperties, Class<T> type)67 private static <T> ImmutableBiMap<String, Class<? extends T>> mapGroupNamesToClasses( 68 ImmutableMap<String, String> groupProperties, Class<T> type) 69 throws InvalidConfigurationException { 70 BiMap<String, Class<? extends T>> namesToClasses = HashBiMap.create(); 71 for (Entry<String, String> entry : groupProperties.entrySet()) { 72 Matcher matcher = CLASS_PROPERTY_PATTERN.matcher(entry.getKey()); 73 if (matcher.matches() && !entry.getValue().isEmpty()) { 74 try { 75 Class<?> someClass = Util.loadClass(entry.getValue()); 76 checkState(type.isAssignableFrom(someClass)); 77 @SuppressWarnings("unchecked") 78 Class<? extends T> verifiedClass = (Class<? extends T>) someClass; 79 namesToClasses.put(matcher.group(1), verifiedClass); 80 } catch (ClassNotFoundException e) { 81 throw new InvalidConfigurationException("Cannot find result processor class: " 82 + entry.getValue()); 83 } 84 } 85 } 86 return ImmutableBiMap.copyOf(namesToClasses); 87 } 88 89 private static ImmutableMap<Class<? extends ResultProcessor>, ResultProcessorConfig> findResultProcessorConfigs(ImmutableMap<String, String> resultsProperties)90 findResultProcessorConfigs(ImmutableMap<String, String> resultsProperties) 91 throws InvalidConfigurationException { 92 ImmutableBiMap<String, Class<? extends ResultProcessor>> processorToClass = 93 mapGroupNamesToClasses(resultsProperties, ResultProcessor.class); 94 ImmutableMap.Builder<Class<? extends ResultProcessor>, ResultProcessorConfig> builder = 95 ImmutableMap.builder(); 96 for (Entry<String, Class<? extends ResultProcessor>> entry : processorToClass.entrySet()) { 97 builder.put(entry.getValue(), getResultProcessorConfig(resultsProperties, entry.getKey())); 98 } 99 return builder.build(); 100 } 101 properties()102 public ImmutableMap<String, String> properties() { 103 return properties; 104 } 105 106 /** 107 * Returns the configuration of the current host VM (including the flags used to create it). Any 108 * args specified using {@code vm.args} will also be applied 109 */ getDefaultVmConfig(Platform platform)110 public VmConfig getDefaultVmConfig(Platform platform) { 111 return new VmConfig.Builder(platform, platform.defaultVmHomeDir()) 112 .addAllOptions(platform.inputArguments()) 113 // still incorporate vm.args 114 .addAllOptions(getArgs(subgroupMap(properties, "vm"))) 115 .build(); 116 } 117 getVmConfig(Platform platform, String name)118 public VmConfig getVmConfig(Platform platform, String name) 119 throws InvalidConfigurationException { 120 checkNotNull(name); 121 ImmutableMap<String, String> vmGroupMap = subgroupMap(properties, "vm"); 122 ImmutableMap<String, String> vmMap = subgroupMap(vmGroupMap, name); 123 File homeDir; 124 try { 125 homeDir = platform.customVmHomeDir(vmGroupMap, name); 126 } catch (VirtualMachineException e) { 127 throw new InvalidConfigurationException(e); 128 } 129 return new VmConfig.Builder(platform, homeDir) 130 .addAllOptions(getArgs(vmGroupMap)) 131 .addAllOptions(getArgs(vmMap)) 132 .build(); 133 } 134 135 private static final Pattern INSTRUMENT_CLASS_PATTERN = Pattern.compile("([^\\.]+)\\.class"); 136 getConfiguredInstruments()137 public ImmutableSet<String> getConfiguredInstruments() { 138 ImmutableSet.Builder<String> resultBuilder = ImmutableSet.builder(); 139 for (String key : subgroupMap(properties, "instrument").keySet()) { 140 Matcher matcher = INSTRUMENT_CLASS_PATTERN.matcher(key); 141 if (matcher.matches()) { 142 resultBuilder.add(matcher.group(1)); 143 } 144 } 145 return resultBuilder.build(); 146 } 147 getInstrumentConfig(String name)148 public InstrumentConfig getInstrumentConfig(String name) { 149 checkNotNull(name); 150 ImmutableMap<String, String> instrumentGroupMap = subgroupMap(properties, "instrument"); 151 ImmutableMap<String, String> insrumentMap = subgroupMap(instrumentGroupMap, name); 152 @Nullable String className = insrumentMap.get("class"); 153 checkArgument(className != null, "no instrument configured named %s", name); 154 return new InstrumentConfig.Builder() 155 .className(className) 156 .addAllOptions(subgroupMap(insrumentMap, "options")) 157 .build(); 158 } 159 getConfiguredResultProcessors()160 public ImmutableSet<Class<? extends ResultProcessor>> getConfiguredResultProcessors() { 161 return resultProcessorConfigs.keySet(); 162 } 163 getResultProcessorConfig( Class<? extends ResultProcessor> resultProcessorClass)164 public ResultProcessorConfig getResultProcessorConfig( 165 Class<? extends ResultProcessor> resultProcessorClass) { 166 return resultProcessorConfigs.get(resultProcessorClass); 167 } 168 getResultProcessorConfig( ImmutableMap<String, String> resultsProperties, String name)169 private static ResultProcessorConfig getResultProcessorConfig( 170 ImmutableMap<String, String> resultsProperties, String name) { 171 ImmutableMap<String, String> resultsMap = subgroupMap(resultsProperties, name); 172 return new ResultProcessorConfig.Builder() 173 .className(resultsMap.get("class")) 174 .addAllOptions(subgroupMap(resultsMap, "options")) 175 .build(); 176 } 177 toString()178 @Override public String toString() { 179 return MoreObjects.toStringHelper(this) 180 .add("properties", properties) 181 .toString(); 182 } 183 getArgs(Map<String, String> properties)184 private static List<String> getArgs(Map<String, String> properties) { 185 String argsString = Strings.nullToEmpty(properties.get("args")); 186 ImmutableList.Builder<String> args = ImmutableList.builder(); 187 StringBuilder arg = new StringBuilder(); 188 for (int i = 0; i < argsString.length(); i++) { 189 char c = argsString.charAt(i); 190 switch (c) { 191 case '\\': 192 arg.append(argsString.charAt(++i)); 193 break; 194 case ' ': 195 if (arg.length() > 0) { 196 args.add(arg.toString()); 197 } 198 arg = new StringBuilder(); 199 break; 200 default: 201 arg.append(c); 202 break; 203 } 204 } 205 if (arg.length() > 0) { 206 args.add(arg.toString()); 207 } 208 return args.build(); 209 } 210 211 } 212