src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java
changeset 50113 caf115bb98ad
child 52334 a181612f0715
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     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 }