1 /* 2 * Copyright (c) 2016, 2019, 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.lang.annotation.Annotation; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.InvocationTargetException; 31 import java.lang.reflect.Method; 32 import java.lang.reflect.Modifier; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 import java.util.Set; 40 41 import jdk.jfr.AnnotationElement; 42 import jdk.jfr.Enabled; 43 import jdk.jfr.Event; 44 import jdk.jfr.Name; 45 import jdk.jfr.Period; 46 import jdk.jfr.SettingControl; 47 import jdk.jfr.SettingDefinition; 48 import jdk.jfr.StackTrace; 49 import jdk.jfr.Threshold; 50 import jdk.jfr.events.ActiveSettingEvent; 51 import jdk.jfr.internal.EventInstrumentation.SettingInfo; 52 import jdk.jfr.internal.settings.CutoffSetting; 53 import jdk.jfr.internal.settings.EnabledSetting; 54 import jdk.jfr.internal.settings.PeriodSetting; 55 import jdk.jfr.internal.settings.StackTraceSetting; 56 import jdk.jfr.internal.settings.ThresholdSetting; 57 58 // This class can't have a hard reference from PlatformEventType, since it 59 // holds SettingControl instances that need to be released 60 // when a class is unloaded (to avoid memory leaks). 61 public final class EventControl { 62 63 static final String FIELD_SETTING_PREFIX = "setting"; 64 private static final Type TYPE_ENABLED = TypeLibrary.createType(EnabledSetting.class); 65 private static final Type TYPE_THRESHOLD = TypeLibrary.createType(ThresholdSetting.class); 66 private static final Type TYPE_STACK_TRACE = TypeLibrary.createType(StackTraceSetting.class); 67 private static final Type TYPE_PERIOD = TypeLibrary.createType(PeriodSetting.class); 68 private static final Type TYPE_CUTOFF = TypeLibrary.createType(CutoffSetting.class); 69 70 private final List<SettingInfo> settingInfos = new ArrayList<>(); 71 private final Map<String, Control> eventControls = new HashMap<>(5); 72 private final PlatformEventType type; 73 private final String idName; 74 75 EventControl(PlatformEventType eventType) { 76 eventControls.put(Enabled.NAME, defineEnabled(eventType)); 77 if (eventType.hasDuration()) { 78 eventControls.put(Threshold.NAME, defineThreshold(eventType)); 79 } 80 if (eventType.hasStackTrace()) { 81 eventControls.put(StackTrace.NAME, defineStackTrace(eventType)); 82 } 83 if (eventType.hasPeriod()) { 84 eventControls.put(Period.NAME, definePeriod(eventType)); 85 } 86 if (eventType.hasCutoff()) { 87 eventControls.put(Cutoff.NAME, defineCutoff(eventType)); 88 } 89 90 ArrayList<AnnotationElement> aes = new ArrayList<>(eventType.getAnnotationElements()); 91 remove(eventType, aes, Threshold.class); 92 remove(eventType, aes, Period.class); 93 remove(eventType, aes, Enabled.class); 94 remove(eventType, aes, StackTrace.class); 95 remove(eventType, aes, Cutoff.class); 96 aes.trimToSize(); 97 eventType.setAnnotations(aes); 98 this.type = eventType; 99 this.idName = String.valueOf(eventType.getId()); 100 } 101 102 static void remove(PlatformEventType type, List<AnnotationElement> aes, Class<? extends java.lang.annotation.Annotation> clazz) { 103 long id = Type.getTypeId(clazz); 104 for (AnnotationElement a : type.getAnnotationElements()) { 105 if (a.getTypeId() == id && a.getTypeName().equals(clazz.getName())) { 106 aes.remove(a); 107 } 108 } 109 } 110 111 EventControl(PlatformEventType es, Class<? extends Event> eventClass) { 112 this(es); 113 defineSettings(eventClass); 114 } 115 116 @SuppressWarnings("unchecked") 117 private void defineSettings(Class<?> eventClass) { 118 // Iterate up the class hierarchy and let 119 // subclasses shadow base classes. 120 boolean allowPrivateMethod = true; 121 while (eventClass != null) { 122 for (Method m : eventClass.getDeclaredMethods()) { 123 boolean isPrivate = Modifier.isPrivate(m.getModifiers()); 124 if (m.getReturnType() == Boolean.TYPE && m.getParameterCount() == 1 && (!isPrivate || allowPrivateMethod)) { 125 SettingDefinition se = m.getDeclaredAnnotation(SettingDefinition.class); 126 if (se != null) { 127 Class<?> settingClass = m.getParameters()[0].getType(); 128 if (!Modifier.isAbstract(settingClass.getModifiers()) && SettingControl.class.isAssignableFrom(settingClass)) { 129 String name = m.getName(); 130 Name n = m.getAnnotation(Name.class); 131 if (n != null) { 132 name = n.value(); 133 } 134 if (!eventControls.containsKey(name)) { 135 defineSetting((Class<? extends SettingControl>) settingClass, m, type, name); 136 } 137 } 138 } 139 } 140 } 141 eventClass = eventClass.getSuperclass(); 142 allowPrivateMethod = false; 143 } 144 } 145 146 private void defineSetting(Class<? extends SettingControl> settingsClass, Method method, PlatformEventType eventType, String settingName) { 147 try { 148 int index = settingInfos.size(); 149 SettingInfo si = new SettingInfo(FIELD_SETTING_PREFIX + index, index); 150 si.settingControl = instantiateSettingControl(settingsClass); 151 Control c = si.settingControl; 152 c.setDefault(); 153 String defaultValue = c.getValueSafe(); 154 if (defaultValue != null) { 155 Type settingType = TypeLibrary.createType(settingsClass); 156 ArrayList<AnnotationElement> aes = new ArrayList<>(); 157 for (Annotation a : method.getDeclaredAnnotations()) { 158 AnnotationElement ae = TypeLibrary.createAnnotation(a); 159 if (ae != null) { 160 aes.add(ae); 161 } 162 } 163 aes.trimToSize(); 164 eventControls.put(settingName, si.settingControl); 165 eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, settingName, defaultValue, aes)); 166 settingInfos.add(si); 167 } 168 } catch (InstantiationException e) { 169 // Programming error by user, fail fast 170 throw new InstantiationError("Could not instantiate setting " + settingsClass.getName() + " for event " + eventType.getLogName() + ". " + e.getMessage()); 171 } catch (IllegalAccessException e) { 172 // Programming error by user, fail fast 173 throw new IllegalAccessError("Could not access setting " + settingsClass.getName() + " for event " + eventType.getLogName() + ". " + e.getMessage()); 174 } 175 } 176 177 private SettingControl instantiateSettingControl(Class<? extends SettingControl> settingControlClass) throws IllegalAccessException, InstantiationException { 178 final Constructor<?> cc; 179 try { 180 cc = settingControlClass.getDeclaredConstructors()[0]; 181 } catch (Exception e) { 182 throw (Error) new InternalError("Could not get constructor for " + settingControlClass.getName()).initCause(e); 183 } 184 SecuritySupport.setAccessible(cc); 185 try { 186 return (SettingControl) cc.newInstance(); 187 } catch (IllegalArgumentException | InvocationTargetException e) { 188 throw (Error) new InternalError("Could not instantiate setting for class " + settingControlClass.getName()); 189 } 190 } 191 192 private static Control defineEnabled(PlatformEventType type) { 193 Enabled enabled = type.getAnnotation(Enabled.class); 194 // Java events are enabled by default, 195 // JVM events are not, maybe they should be? Would lower learning curve 196 // there too. 197 String def = type.isJVM() ? "false" : "true"; 198 if (enabled != null) { 199 def = Boolean.toString(enabled.value()); 200 } 201 type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_ENABLED, Enabled.NAME, def, Collections.emptyList())); 202 return new EnabledSetting(type, def); 203 } 204 205 private static Control defineThreshold(PlatformEventType type) { 206 Threshold threshold = type.getAnnotation(Threshold.class); 207 String def = "0 ns"; 208 if (threshold != null) { 209 def = threshold.value(); 210 } 211 type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THRESHOLD, Threshold.NAME, def, Collections.emptyList())); 212 return new ThresholdSetting(type, def); 213 } 214 215 private static Control defineStackTrace(PlatformEventType type) { 216 StackTrace stackTrace = type.getAnnotation(StackTrace.class); 217 String def = "true"; 218 if (stackTrace != null) { 219 def = Boolean.toString(stackTrace.value()); 220 } 221 type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_STACK_TRACE, StackTrace.NAME, def, Collections.emptyList())); 222 return new StackTraceSetting(type, def); 223 } 224 225 private static Control defineCutoff(PlatformEventType type) { 226 Cutoff cutoff = type.getAnnotation(Cutoff.class); 227 String def = Cutoff.INIFITY; 228 if (cutoff != null) { 229 def = cutoff.value(); 230 } 231 type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_CUTOFF, Cutoff.NAME, def, Collections.emptyList())); 232 return new CutoffSetting(type, def); 233 } 234 235 236 private static Control definePeriod(PlatformEventType type) { 237 Period period = type.getAnnotation(Period.class); 238 String def = "everyChunk"; 239 if (period != null) { 240 def = period.value(); 241 } 242 type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_PERIOD, PeriodSetting.NAME, def, Collections.emptyList())); 243 return new PeriodSetting(type, def); 244 } 245 246 void disable() { 247 for (Control c : eventControls.values()) { 248 if (c instanceof EnabledSetting) { 249 c.setValueSafe("false"); 250 return; 251 } 252 } 253 } 254 255 void writeActiveSettingEvent() { 256 if (!type.isRegistered()) { 257 return; 258 } 259 for (Map.Entry<String, Control> entry : eventControls.entrySet()) { 260 Control c = entry.getValue(); 261 if (Utils.isSettingVisible(c, type.hasEventHook())) { 262 String value = c.getLastValue(); 263 if (value == null) { 264 value = c.getDefaultValue(); 265 } 266 ActiveSettingEvent ase = new ActiveSettingEvent(); 267 ase.id = type.getId(); 268 ase.name = entry.getKey(); 269 ase.value = value; 270 ase.commit(); 271 } 272 } 273 } 274 275 public Set<Entry<String, Control>> getEntries() { 276 return eventControls.entrySet(); 277 } 278 279 public PlatformEventType getEventType() { 280 return type; 281 } 282 283 public String getSettingsId() { 284 return idName; 285 } 286 287 public List<SettingInfo> getSettingInfos() { 288 return settingInfos; 289 } 290 }