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 static jdk.jfr.internal.LogLevel.DEBUG;
  29 import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
  30 
  31 import java.io.ByteArrayOutputStream;
  32 import java.io.DataOutputStream;
  33 import java.io.IOException;
  34 import java.util.ArrayList;
  35 import java.util.Collections;
  36 import java.util.HashSet;
  37 import java.util.List;
  38 import java.util.Map;
  39 
  40 import jdk.jfr.AnnotationElement;
  41 import jdk.jfr.Event;
  42 import jdk.jfr.EventType;
  43 import jdk.jfr.Period;
  44 import jdk.jfr.StackTrace;
  45 import jdk.jfr.Threshold;
  46 import jdk.jfr.ValueDescriptor;
  47 import jdk.jfr.internal.RequestEngine.RequestHook;
  48 import jdk.jfr.internal.handlers.EventHandler;
  49 
  50 public final class MetadataRepository {
  51 
  52     private static final JVM jvm = JVM.getJVM();
  53     private static final MetadataRepository instace = new MetadataRepository();
  54 
  55     private final List<EventType> nativeEventTypes = new ArrayList<>(100);
  56     private final List<EventControl> nativeControls = new ArrayList<EventControl>(100);
  57     private final TypeLibrary typeLibrary = TypeLibrary.getInstance();
  58     private final SettingsManager settingsManager = new SettingsManager();
  59     private boolean staleMetadata = true;
  60     private boolean unregistered;
  61     private long lastUnloaded = -1;
  62 
  63     public MetadataRepository() {
  64         initializeJVMEventTypes();
  65     }
  66 
  67     private void initializeJVMEventTypes() {
  68         List<RequestHook> requestHooks = new ArrayList<>();
  69         for (Type type : typeLibrary.getTypes()) {
  70             if (type instanceof PlatformEventType) {
  71                 PlatformEventType pEventType = (PlatformEventType) type;
  72                 EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
  73                 pEventType.setHasDuration(eventType.getAnnotation(Threshold.class) != null);
  74                 pEventType.setHasStackTrace(eventType.getAnnotation(StackTrace.class) != null);
  75                 pEventType.setHasCutoff(eventType.getAnnotation(Cutoff.class) != null);
  76                 pEventType.setHasPeriod(eventType.getAnnotation(Period.class) != null);
  77                 // Must add hook before EventControl is created as it removes
  78                 // annotations, such as Period and Threshold.
  79                 if (pEventType.hasPeriod()) {
  80                     pEventType.setEventHook(true);
  81                     if (!(Type.EVENT_NAME_PREFIX + "ExecutionSample").equals(type.getName())) {
  82                         requestHooks.add(new RequestHook(pEventType));
  83                     }
  84                 }
  85                 nativeControls.add(new EventControl(pEventType));
  86                 nativeEventTypes.add(eventType);
  87             }
  88         }
  89         RequestEngine.addHooks(requestHooks);
  90     }
  91 
  92     public static MetadataRepository getInstance() {
  93         return instace;
  94     }
  95 
  96     public synchronized List<EventType> getRegisteredEventTypes() {
  97         List<EventHandler> handlers = getEventHandlers();
  98         List<EventType> eventTypes = new ArrayList<>(handlers.size() + nativeEventTypes.size());
  99         for (EventHandler h : handlers) {
 100             if (h.isRegistered()) {
 101                 eventTypes.add(h.getEventType());
 102             }
 103         }
 104         eventTypes.addAll(nativeEventTypes);
 105         return eventTypes;
 106     }
 107 
 108     public synchronized EventType getEventType(Class<? extends Event> eventClass) {
 109         EventHandler h = getHandler(eventClass);
 110         if (h != null && h.isRegistered()) {
 111             return h.getEventType();
 112         }
 113         throw new IllegalStateException("Event class " + eventClass.getName() + " is not registered");
 114     }
 115 
 116     public synchronized void unregister(Class<? extends Event> eventClass) {
 117         Utils.checkRegisterPermission();
 118         EventHandler handler = getHandler(eventClass);
 119         if (handler != null) {
 120             handler.setRegistered(false);
 121         }
 122         // never registered, ignore call
 123     }
 124     public synchronized EventType register(Class<? extends Event> eventClass) {
 125         return register(eventClass, Collections.emptyList(), Collections.emptyList());
 126     }
 127 
 128     public synchronized EventType register(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
 129         Utils.checkRegisterPermission();
 130         EventHandler handler = getHandler(eventClass);
 131         if (handler == null) {
 132             handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields);
 133         }
 134         handler.setRegistered(true);
 135         typeLibrary.addType(handler.getPlatformEventType());
 136         if (jvm.isRecording()) {
 137             storeDescriptorInJVM(); // needed for emergency dump
 138             settingsManager.setEventControl(handler.getEventControl());
 139             settingsManager.updateRetransform(Collections.singletonList((eventClass)));
 140         } else {
 141             setStaleMetadata();
 142         }
 143         return handler.getEventType();
 144     }
 145 
 146     private EventHandler getHandler(Class<? extends Event> eventClass) {
 147         Utils.ensureValidEventSubclass(eventClass);
 148         SecuritySupport.makeVisibleToJFR(eventClass);
 149         Utils.ensureInitialized(eventClass);
 150         return Utils.getHandler(eventClass);
 151     }
 152 
 153     private EventHandler makeHandler(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) throws InternalError {
 154         SecuritySupport.addHandlerExport(eventClass);
 155         PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields);
 156         EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
 157         EventControl ec = new EventControl(pEventType, eventClass);
 158         Class<? extends EventHandler> handlerClass = null;
 159         try {
 160             String eventHandlerName = EventHandlerCreator.makeEventHandlerName(eventType.getId());
 161             handlerClass = Class.forName(eventHandlerName, false, Event.class.getClassLoader()).asSubclass(EventHandler.class);
 162             // Created eagerly on class load, tag as instrumented
 163             pEventType.setInstrumented();
 164             Logger.log(JFR_SYSTEM, DEBUG, "Found existing event handler for " + eventType.getName());
 165        } catch (ClassNotFoundException cne) {
 166            EventHandlerCreator ehc = new EventHandlerCreator(eventType.getId(),  ec.getSettingInfos(), eventType, eventClass);
 167            handlerClass = ehc.makeEventHandlerClass();
 168            Logger.log(LogTag.JFR_SYSTEM, DEBUG, "Created event handler for " + eventType.getName());
 169        }
 170         EventHandler handler = EventHandlerCreator.instantiateEventHandler(handlerClass, true, eventType, ec);
 171         Utils.setHandler(eventClass, handler);
 172         return handler;
 173     }
 174 
 175 
 176     public synchronized void setSettings(List<Map<String, String>> list) {
 177         settingsManager.setSettings(list);
 178     }
 179 
 180     synchronized void disableEvents() {
 181         for (EventControl c : getEventControls()) {
 182             c.disable();
 183         }
 184     }
 185 
 186     public synchronized List<EventControl> getEventControls() {
 187         List<EventControl> controls = new ArrayList<>();
 188         controls.addAll(nativeControls);
 189         for (EventHandler eh : getEventHandlers()) {
 190             controls.add(eh.getEventControl());
 191         }
 192         return controls;
 193     }
 194 
 195     private void storeDescriptorInJVM() throws InternalError {
 196         jvm.storeMetadataDescriptor(getBinaryRepresentation());
 197         staleMetadata = false;
 198     }
 199 
 200     private static List<EventHandler> getEventHandlers() {
 201         List<Class<? extends Event>> allEventClasses = jvm.getAllEventClasses();
 202         List<EventHandler> eventHandlers = new ArrayList<>(allEventClasses.size());
 203         for (Class<? extends Event> clazz : allEventClasses) {
 204             EventHandler eh = Utils.getHandler(clazz);
 205             if (eh != null) {
 206                 eventHandlers.add(eh);
 207             }
 208         }
 209         return eventHandlers;
 210     }
 211 
 212     private byte[] getBinaryRepresentation() {
 213         ByteArrayOutputStream baos = new ByteArrayOutputStream(40000);
 214         DataOutputStream daos = new DataOutputStream(baos);
 215         try {
 216             List<Type> types = typeLibrary.getTypes();
 217             Collections.sort(types);
 218             MetadataDescriptor.write(types, daos);
 219             daos.flush();
 220             return baos.toByteArray();
 221         } catch (IOException e) {
 222             // should not happen
 223             throw new InternalError(e);
 224         }
 225     }
 226 
 227     synchronized boolean isEnabled(String eventName) {
 228         return settingsManager.isEnabled(eventName);
 229     }
 230 
 231     synchronized void setStaleMetadata() {
 232         staleMetadata = true;
 233     }
 234 
 235     // Lock around setOutput ensures that other threads dosn't
 236     // emit event after setOutput and unregister the event class, before a call
 237     // to storeDescriptorInJVM
 238     synchronized void setOutput(String filename) {
 239         jvm.setOutput(filename);
 240 
 241         unregisterUnloaded();
 242         if (unregistered) {
 243             staleMetadata = typeLibrary.clearUnregistered();
 244             unregistered = false;
 245         }
 246         if (staleMetadata) {
 247             storeDescriptorInJVM();
 248         }
 249     }
 250 
 251     private void unregisterUnloaded() {
 252         long unloaded = jvm.getUnloadedEventClassCount();
 253         if (this.lastUnloaded != unloaded) {
 254             this.lastUnloaded = unloaded;
 255             List<Class<? extends Event>> eventClasses = jvm.getAllEventClasses();
 256             HashSet<Long> knownIds = new HashSet<>(eventClasses.size());
 257             for (Class<? extends Event>  ec: eventClasses) {
 258                 knownIds.add(Type.getTypeId(ec));
 259             }
 260             for (Type type : typeLibrary.getTypes()) {
 261                 if (type instanceof PlatformEventType) {
 262                     if (!knownIds.contains(type.getId())) {
 263                         PlatformEventType pe = (PlatformEventType) type;
 264                         if (!pe.isJVM()) {
 265                             pe.setRegistered(false);
 266                         }
 267                     }
 268                 }
 269             }
 270         }
 271     }
 272 
 273     synchronized public void setUnregistered() {
 274        unregistered = true;
 275     }
 276 
 277 }