/* * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.jfr.internal; import static jdk.jfr.internal.LogLevel.DEBUG; import static jdk.jfr.internal.LogTag.JFR_SYSTEM; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import jdk.jfr.AnnotationElement; import jdk.jfr.Event; import jdk.jfr.EventType; import jdk.jfr.Period; import jdk.jfr.StackTrace; import jdk.jfr.Threshold; import jdk.jfr.ValueDescriptor; import jdk.jfr.internal.RequestEngine.RequestHook; import jdk.jfr.internal.handlers.EventHandler; public final class MetadataRepository { private static final JVM jvm = JVM.getJVM(); private static final MetadataRepository instace = new MetadataRepository(); private final List nativeEventTypes = new ArrayList<>(100); private final List nativeControls = new ArrayList(100); private final TypeLibrary typeLibrary = TypeLibrary.getInstance(); private final SettingsManager settingsManager = new SettingsManager(); private boolean staleMetadata = true; private boolean unregistered; private long lastUnloaded = -1; public MetadataRepository() { initializeJVMEventTypes(); } private void initializeJVMEventTypes() { List requestHooks = new ArrayList<>(); for (Type type : typeLibrary.getTypes()) { if (type instanceof PlatformEventType) { PlatformEventType pEventType = (PlatformEventType) type; EventType eventType = PrivateAccess.getInstance().newEventType(pEventType); pEventType.setHasDuration(eventType.getAnnotation(Threshold.class) != null); pEventType.setHasStackTrace(eventType.getAnnotation(StackTrace.class) != null); pEventType.setHasCutoff(eventType.getAnnotation(Cutoff.class) != null); pEventType.setHasPeriod(eventType.getAnnotation(Period.class) != null); // Must add hook before EventControl is created as it removes // annotations, such as Period and Threshold. if (pEventType.hasPeriod()) { pEventType.setEventHook(true); if (!"com.oracle.jdk.ExecutionSample".equals(type.getName())) { requestHooks.add(new RequestHook(pEventType)); } } nativeControls.add(new EventControl(pEventType)); nativeEventTypes.add(eventType); } } RequestEngine.addHooks(requestHooks); } public static MetadataRepository getInstance() { return instace; } public synchronized List getRegisteredEventTypes() { List handlers = getEventHandlers(); List eventTypes = new ArrayList<>(handlers.size() + nativeEventTypes.size()); for (EventHandler h : handlers) { if (h.isRegistered()) { eventTypes.add(h.getEventType()); } } eventTypes.addAll(nativeEventTypes); return eventTypes; } public synchronized EventType getEventType(Class eventClass) { EventHandler h = getHandler(eventClass); if (h != null && h.isRegistered()) { return h.getEventType(); } throw new IllegalStateException("Event class " + eventClass.getName() + " is not registered"); } public synchronized void unregister(Class eventClass) { Utils.checkRegisterPermission(); EventHandler handler = getHandler(eventClass); if (handler != null) { handler.setRegistered(false); } // never registered, ignore call } public synchronized EventType register(Class eventClass) { return register(eventClass, Collections.emptyList(), Collections.emptyList()); } public synchronized EventType register(Class eventClass, List dynamicAnnotations, List dynamicFields) { Utils.checkRegisterPermission(); EventHandler handler = getHandler(eventClass); if (handler == null) { handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields); } handler.setRegistered(true); typeLibrary.addType(handler.getPlatformEventType()); if (jvm.isRecording()) { storeDescriptorInJVM(); // needed for emergency dump settingsManager.setEventControl(handler.getEventControl()); settingsManager.updateRetransform(Collections.singletonList((eventClass))); } else { setStaleMetadata(); } return handler.getEventType(); } private EventHandler getHandler(Class eventClass) { Utils.ensureValidEventSubclass(eventClass); Utils.ensureInitialized(eventClass); return Utils.getHandler(eventClass); } private EventHandler makeHandler(Class eventClass, List dynamicAnnotations, List dynamicFields) throws InternalError { PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields); EventType eventType = PrivateAccess.getInstance().newEventType(pEventType); EventControl ec = new EventControl(pEventType, eventClass); Class handlerClass = null; try { String eventHandlerName = EventHandlerCreator.makeEventHandlerName(eventType.getId()); handlerClass = Class.forName(eventHandlerName, false, Event.class.getClassLoader()).asSubclass(EventHandler.class); // Created eagerly on class load, tag as instrumented pEventType.setInstrumented(); Logger.log(JFR_SYSTEM, DEBUG, "Found existing event handler for " + eventType.getName()); } catch (ClassNotFoundException cne) { EventHandlerCreator ehc = new EventHandlerCreator(eventType.getId(), ec.getSettingInfos(), eventType, eventClass); handlerClass = ehc.makeEventHandlerClass(); Logger.log(LogTag.JFR_SYSTEM, DEBUG, "Created event handler for " + eventType.getName()); } EventHandler handler = EventHandlerCreator.instantiateEventHandler(handlerClass, true, eventType, ec); Utils.setHandler(eventClass, handler); return handler; } public synchronized void setSettings(List> list) { settingsManager.setSettings(list); } synchronized void disableEvents() { for (EventControl c : getEventControls()) { c.disable(); } } public synchronized List getEventControls() { List controls = new ArrayList<>(); controls.addAll(nativeControls); for (EventHandler eh : getEventHandlers()) { controls.add(eh.getEventControl()); } return controls; } private void storeDescriptorInJVM() throws InternalError { jvm.storeMetadataDescriptor(getBinaryRepresentation()); staleMetadata = false; } private static List getEventHandlers() { List> allEventClasses = jvm.getAllEventClasses(); List eventHandlers = new ArrayList<>(allEventClasses.size()); for (Class clazz : allEventClasses) { EventHandler eh = Utils.getHandler(clazz); if (eh != null) { eventHandlers.add(eh); } } return eventHandlers; } private byte[] getBinaryRepresentation() { ByteArrayOutputStream baos = new ByteArrayOutputStream(40000); DataOutputStream daos = new DataOutputStream(baos); try { List types = typeLibrary.getTypes(); Collections.sort(types); MetadataDescriptor.write(types, daos); daos.flush(); return baos.toByteArray(); } catch (IOException e) { // should not happen throw new InternalError(e); } } synchronized boolean isEnabled(String eventName) { return settingsManager.isEnabled(eventName); } synchronized void setStaleMetadata() { staleMetadata = true; } // Lock around setOutput ensures that other threads dosn't // emit event after setOutput and unregister the event class, before a call // to storeDescriptorInJVM synchronized void setOutput(String filename) { jvm.setOutput(filename); unregisterUnloaded(); if (unregistered) { staleMetadata = typeLibrary.clearUnregistered(); unregistered = false; } if (staleMetadata) { storeDescriptorInJVM(); } } private void unregisterUnloaded() { long unloaded = jvm.getUnloadedEventClassCount(); if (this.lastUnloaded != unloaded) { this.lastUnloaded = unloaded; List> eventClasses = jvm.getAllEventClasses(); HashSet knownIds = new HashSet<>(eventClasses.size()); for (Class ec: eventClasses) { knownIds.add(Type.getTypeId(ec)); } for (Type type : typeLibrary.getTypes()) { if (type instanceof PlatformEventType) { if (!knownIds.contains(type.getId())) { PlatformEventType pe = (PlatformEventType) type; if (!pe.isJVM()) { pe.setRegistered(false); } } } } } } synchronized public void setUnregistered() { unregistered = true; } }