1 /* 2 * Copyright (c) 2016, 2018, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.internal; 27 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.LinkedHashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Map.Entry; 37 import java.util.Set; 38 import java.util.StringJoiner; 39 40 import jdk.jfr.Event; 41 import jdk.jfr.internal.handlers.EventHandler; 42 43 final class SettingsManager { 44 45 private static class InternalSetting { 46 47 private final String identifier; 48 private Map<String, Set<String>> enabledMap = new LinkedHashMap<>(5); 49 private Map<String, Set<String>> allMap = new LinkedHashMap<>(5); 50 private boolean enabled; 51 52 /** 53 * Settings identifier, for example "com.oracle.jdk.HelloWorld" or "56" 54 * (id of event) 55 * 56 * @param settingsId 57 */ 58 public InternalSetting(String settingsId) { 59 this.identifier = settingsId; 60 } 61 62 public Set<String> getValues(String key) { 63 if (enabled) { 64 return enabledMap.get(key); 65 } else { 66 return allMap.get(key); 67 } 68 } 69 70 public void add(String attribute, String value) { 71 if ("enabled".equals(attribute) && "true".equals(value)) { 72 enabled = true; 73 allMap = null; // no need to keep these around 74 } 75 addToMap(enabledMap, attribute, value); 76 if (allMap != null) { 77 addToMap(allMap, attribute, value); 78 } 79 } 80 81 private void addToMap(Map<String, Set<String>> map, String attribute, String value) { 82 Set<String> values = map.get(attribute); 83 if (values == null) { 84 values = new HashSet<String>(5); 85 map.put(attribute, values); 86 } 87 values.add(value); 88 89 } 90 91 public String getSettingsId() { 92 return identifier; 93 } 94 95 public void add(InternalSetting enabled) { 96 for (Map.Entry<String, Set<String>> entry : enabled.enabledMap.entrySet()) { 97 for (String value : entry.getValue()) { 98 add(entry.getKey(), value); 99 } 100 } 101 } 102 103 public boolean isEnabled() { 104 return enabled; 105 } 106 107 @Override 108 public String toString() { 109 StringBuilder sb = new StringBuilder(); 110 sb.append(identifier); 111 sb.append(": "); 112 sb.append(enabledMap.toString()); 113 return sb.toString(); 114 } 115 116 public void finish() { 117 if (!enabled) { 118 // settings from disabled 119 // events should not impact results, but 120 // we can't clear enabledMap since enabled=false 121 // needs be there, so events that are enabled 122 // by default are turned off 123 Map<String, Set<String>> disabledMap = new HashMap<>(2); 124 Set<String> values = new HashSet<>(2); 125 values.add("false"); 126 disabledMap.put("enabled", values); 127 enabledMap = disabledMap; 128 } 129 } 130 } 131 132 private Map<String, InternalSetting> availableSettings = new LinkedHashMap<>(); 133 134 void setSettings(List<Map<String, String>> activeSettings) { 135 // store settings so they are available if a new event class is loaded 136 availableSettings = createSettingsMap(activeSettings); 137 List<EventControl> eventControls = MetadataRepository.getInstance().getEventControls(); 138 if (!JVM.getJVM().isRecording()) { 139 for (EventControl ec : eventControls) { 140 ec.disable(); 141 } 142 } else { 143 if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { 144 Collections.sort(eventControls, (x,y) -> x.getEventType().getName().compareTo(y.getEventType().getName())); 145 } 146 for (EventControl ec : eventControls) { 147 setEventControl(ec); 148 } 149 } 150 if (JVM.getJVM().getAllowedToDoEventRetransforms()) { 151 updateRetransform(JVM.getJVM().getAllEventClasses()); 152 } 153 } 154 155 public void updateRetransform(List<Class<? extends Event>> eventClasses) { 156 List<Class<?>> classes = new ArrayList<>(); 157 for(Class<? extends Event> eventClass: eventClasses) { 158 EventHandler eh = Utils.getHandler(eventClass); 159 if (eh != null ) { 160 PlatformEventType eventType = eh.getPlatformEventType(); 161 if (eventType.isMarkedForInstrumentation()) { 162 classes.add(eventClass); 163 eventType.markForInstrumentation(false); 164 // A bit premature to set it here, but hard to check 165 // after call to retransformClasses. 166 eventType.setInstrumented(); 167 } 168 } 169 } 170 if (!classes.isEmpty()) { 171 JVM.getJVM().retransformClasses(classes.toArray(new Class<?>[0])); 172 } 173 } 174 175 private Map<String, InternalSetting> createSettingsMap(List<Map<String,String>> activeSettings) { 176 Map<String, InternalSetting> map = new LinkedHashMap<>(activeSettings.size()); 177 for (Map<String, String> rec : activeSettings) { 178 for (InternalSetting internal : makeInternalSettings(rec)) { 179 InternalSetting is = map.get(internal.getSettingsId()); 180 if (is == null) { 181 map.put(internal.getSettingsId(), internal); 182 } else { 183 is.add(internal); 184 } 185 } 186 } 187 return map; 188 } 189 190 private Collection<InternalSetting> makeInternalSettings(Map<String, String> rec) { 191 Map<String, InternalSetting> internals = new LinkedHashMap<>(); 192 for (Map.Entry<String, String> entry : rec.entrySet()) { 193 String key = entry.getKey(); 194 String value = entry.getValue(); 195 int index = key.indexOf("#"); 196 if (index > 1 && index < key.length() - 2) { 197 String eventName = key.substring(0, index); 198 InternalSetting s = internals.get(eventName); 199 String settingName = key.substring(index + 1).trim(); 200 if (s == null) { 201 s = new InternalSetting(eventName); 202 internals.put(eventName, s); 203 } 204 s.add(settingName, value); 205 } 206 } 207 for (InternalSetting s : internals.values()) { 208 s.finish(); 209 } 210 211 return internals.values(); 212 } 213 214 void setEventControl(EventControl ec) { 215 InternalSetting is = getInternalSetting(ec); 216 Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {"); 217 for (Entry<String, Control> entry : ec.getEntries()) { 218 Set<String> values = null; 219 String settingName = entry.getKey(); 220 if (is != null) { 221 values = is.getValues(settingName); 222 } 223 Control control = entry.getValue(); 224 if (values != null) { 225 control.apply(values); 226 String after = control.getLastValue(); 227 if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { 228 if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) { 229 if (values.size() > 1) { 230 StringJoiner sj = new StringJoiner(", ", "{", "}"); 231 for (String s : values) { 232 sj.add("\"" + s + "\""); 233 } 234 String message = " " + settingName + "= " + sj.toString() + " => \"" + after + "\""; 235 Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); 236 } else { 237 String message = " " + settingName + "=\"" + control.getLastValue() + "\""; 238 Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); 239 } 240 } 241 } 242 } else { 243 control.setDefault(); 244 if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { 245 String message = " " + settingName + "=\"" + control.getLastValue() + "\""; 246 Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); 247 } 248 } 249 } 250 ec.writeActiveSettingEvent(); 251 Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}"); 252 } 253 254 private InternalSetting getInternalSetting(EventControl ec) { 255 String name = ec.getEventType().getName(); 256 InternalSetting nameBased = availableSettings.get(name); 257 InternalSetting idBased = availableSettings.get(ec.getSettingsId()); 258 259 if (nameBased == null && idBased == null) { 260 return null; 261 } 262 if (idBased == null) { 263 return nameBased; 264 } 265 if (nameBased == null) { 266 return idBased; 267 } 268 InternalSetting mixed = new InternalSetting(nameBased.getSettingsId()); 269 mixed.add(nameBased); 270 mixed.add(idBased); 271 return mixed; 272 } 273 274 @Override 275 public String toString() { 276 StringBuilder sb = new StringBuilder(); 277 for (InternalSetting enabled : availableSettings.values()) { 278 sb.append(enabled.toString()); 279 sb.append("\n"); 280 } 281 return sb.toString(); 282 } 283 284 boolean isEnabled(String eventName) { 285 InternalSetting is = availableSettings.get(eventName); 286 if (is == null) { 287 return false; 288 } 289 return is.isEnabled(); 290 } 291 }