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 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 (!"com.oracle.jdk.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         Utils.ensureInitialized(eventClass);
 149         return Utils.getHandler(eventClass);
 150     }
 151 
 152     private EventHandler makeHandler(Class<? extends Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) throws InternalError {
 153         PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields);
 154         EventType eventType = PrivateAccess.getInstance().newEventType(pEventType);
 155         EventControl ec = new EventControl(pEventType, eventClass);
 156         Class<? extends EventHandler> handlerClass = null;
 157         try {
 158             String eventHandlerName = EventHandlerCreator.makeEventHandlerName(eventType.getId());
 159             handlerClass = Class.forName(eventHandlerName, false, Event.class.getClassLoader()).asSubclass(EventHandler.class);
 160             // Created eagerly on class load, tag as instrumented
 161             pEventType.setInstrumented();
 162             Logger.log(JFR_SYSTEM, DEBUG, "Found existing event handler for " + eventType.getName());
 163        } catch (ClassNotFoundException cne) {
 164            EventHandlerCreator ehc = new EventHandlerCreator(eventType.getId(),  ec.getSettingInfos(), eventType, eventClass);
 165            handlerClass = ehc.makeEventHandlerClass();
 166            Logger.log(LogTag.JFR_SYSTEM, DEBUG, "Created event handler for " + eventType.getName());
 167        }
 168         EventHandler handler = EventHandlerCreator.instantiateEventHandler(handlerClass, true, eventType, ec);
 169         Utils.setHandler(eventClass, handler);
 170         return handler;
 171     }
 172 
 173 
 174     public synchronized void setSettings(List<Map<String, String>> list) {
 175         settingsManager.setSettings(list);
 176     }
 177 
 178     synchronized void disableEvents() {
 179         for (EventControl c : getEventControls()) {
 180             c.disable();
 181         }
 182     }
 183 
 184     public synchronized List<EventControl> getEventControls() {
 185         List<EventControl> controls = new ArrayList<>();
 186         controls.addAll(nativeControls);
 187         for (EventHandler eh : getEventHandlers()) {
 188             controls.add(eh.getEventControl());
 189         }
 190         return controls;
 191     }
 192 
 193     private void storeDescriptorInJVM() throws InternalError {
 194         jvm.storeMetadataDescriptor(getBinaryRepresentation());
 195         staleMetadata = false;
 196     }
 197 
 198     private static List<EventHandler> getEventHandlers() {
 199         List<Class<? extends Event>> allEventClasses = jvm.getAllEventClasses();
 200         List<EventHandler> eventHandlers = new ArrayList<>(allEventClasses.size());
 201         for (Class<? extends Event> clazz : allEventClasses) {
 202             EventHandler eh = Utils.getHandler(clazz);
 203             if (eh != null) {
 204                 eventHandlers.add(eh);
 205             }
 206         }
 207         return eventHandlers;
 208     }
 209 
 210     private byte[] getBinaryRepresentation() {
 211         ByteArrayOutputStream baos = new ByteArrayOutputStream(40000);
 212         DataOutputStream daos = new DataOutputStream(baos);
 213         try {
 214             List<Type> types = typeLibrary.getTypes();
 215             Collections.sort(types);
 216             MetadataDescriptor.write(types, daos);
 217             daos.flush();
 218             return baos.toByteArray();
 219         } catch (IOException e) {
 220             // should not happen
 221             throw new InternalError(e);
 222         }
 223     }
 224 
 225     synchronized boolean isEnabled(String eventName) {
 226         return settingsManager.isEnabled(eventName);
 227     }
 228 
 229     synchronized void setStaleMetadata() {
 230         staleMetadata = true;
 231     }
 232 
 233     // Lock around setOutput ensures that other threads dosn't
 234     // emit event after setOutput and unregister the event class, before a call
 235     // to storeDescriptorInJVM
 236     synchronized void setOutput(String filename) {
 237         jvm.setOutput(filename);
 238 
 239         unregisterUnloaded();
 240         if (unregistered) {
 241             staleMetadata = typeLibrary.clearUnregistered();
 242             unregistered = false;
 243         }
 244         if (staleMetadata) {
 245             storeDescriptorInJVM();
 246         }
 247     }
 248 
 249     private void unregisterUnloaded() {
 250         long unloaded = jvm.getUnloadedEventClassCount();
 251         if (this.lastUnloaded != unloaded) {
 252             this.lastUnloaded = unloaded;
 253             List<Class<? extends Event>> eventClasses = jvm.getAllEventClasses();
 254             HashSet<Long> knownIds = new HashSet<>(eventClasses.size());
 255             for (Class<? extends Event>  ec: eventClasses) {
 256                 knownIds.add(Type.getTypeId(ec));
 257             }
 258             for (Type type : typeLibrary.getTypes()) {
 259                 if (type instanceof PlatformEventType) {
 260                     if (!knownIds.contains(type.getId())) {
 261                         PlatformEventType pe = (PlatformEventType) type;
 262                         if (!pe.isJVM()) {
 263                             pe.setRegistered(false);
 264                         }
 265                     }
 266                 }
 267             }
 268         }
 269     }
 270 
 271     synchronized public void setUnregistered() {
 272        unregistered = true;
 273     }
 274 
 275 }