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