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 }