/* * Copyright (c) 2016, 2018, 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 java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.StringJoiner; import jdk.jfr.Event; import jdk.jfr.internal.handlers.EventHandler; final class SettingsManager { private static class InternalSetting { private final String identifier; private Map> enabledMap = new LinkedHashMap<>(5); private Map> allMap = new LinkedHashMap<>(5); private boolean enabled; /** * Settings identifier, for example "com.oracle.jdk.HelloWorld" or "56" * (id of event) * * @param settingsId */ public InternalSetting(String settingsId) { this.identifier = settingsId; } public Set getValues(String key) { if (enabled) { return enabledMap.get(key); } else { return allMap.get(key); } } public void add(String attribute, String value) { if ("enabled".equals(attribute) && "true".equals(value)) { enabled = true; allMap = null; // no need to keep these around } addToMap(enabledMap, attribute, value); if (allMap != null) { addToMap(allMap, attribute, value); } } private void addToMap(Map> map, String attribute, String value) { Set values = map.get(attribute); if (values == null) { values = new HashSet(5); map.put(attribute, values); } values.add(value); } public String getSettingsId() { return identifier; } public void add(InternalSetting enabled) { for (Map.Entry> entry : enabled.enabledMap.entrySet()) { for (String value : entry.getValue()) { add(entry.getKey(), value); } } } public boolean isEnabled() { return enabled; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(identifier); sb.append(": "); sb.append(enabledMap.toString()); return sb.toString(); } public void finish() { if (!enabled) { // settings from disabled // events should not impact results, but // we can't clear enabledMap since enabled=false // needs be there, so events that are enabled // by default are turned off Map> disabledMap = new HashMap<>(2); Set values = new HashSet<>(2); values.add("false"); disabledMap.put("enabled", values); enabledMap = disabledMap; } } } private Map availableSettings = new LinkedHashMap<>(); void setSettings(List> activeSettings) { // store settings so they are available if a new event class is loaded availableSettings = createSettingsMap(activeSettings); List eventControls = MetadataRepository.getInstance().getEventControls(); if (!JVM.getJVM().isRecording()) { for (EventControl ec : eventControls) { ec.disable(); } } else { if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { Collections.sort(eventControls, (x,y) -> x.getEventType().getName().compareTo(y.getEventType().getName())); } for (EventControl ec : eventControls) { setEventControl(ec); } } if (JVM.getJVM().getAllowedToDoEventRetransforms()) { updateRetransform(JVM.getJVM().getAllEventClasses()); } } public void updateRetransform(List> eventClasses) { List> classes = new ArrayList<>(); for(Class eventClass: eventClasses) { EventHandler eh = Utils.getHandler(eventClass); if (eh != null ) { PlatformEventType eventType = eh.getPlatformEventType(); if (eventType.isMarkedForInstrumentation()) { classes.add(eventClass); eventType.markForInstrumentation(false); // A bit premature to set it here, but hard to check // after call to retransformClasses. eventType.setInstrumented(); } } } if (!classes.isEmpty()) { JVM.getJVM().retransformClasses(classes.toArray(new Class[0])); } } private Map createSettingsMap(List> activeSettings) { Map map = new LinkedHashMap<>(activeSettings.size()); for (Map rec : activeSettings) { for (InternalSetting internal : makeInternalSettings(rec)) { InternalSetting is = map.get(internal.getSettingsId()); if (is == null) { map.put(internal.getSettingsId(), internal); } else { is.add(internal); } } } return map; } private Collection makeInternalSettings(Map rec) { Map internals = new LinkedHashMap<>(); for (Map.Entry entry : rec.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); int index = key.indexOf("#"); if (index > 1 && index < key.length() - 2) { String eventName = key.substring(0, index); InternalSetting s = internals.get(eventName); String settingName = key.substring(index + 1).trim(); if (s == null) { s = new InternalSetting(eventName); internals.put(eventName, s); } s.add(settingName, value); } } for (InternalSetting s : internals.values()) { s.finish(); } return internals.values(); } void setEventControl(EventControl ec) { InternalSetting is = getInternalSetting(ec); Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {"); for (Entry entry : ec.getEntries()) { Set values = null; String settingName = entry.getKey(); if (is != null) { values = is.getValues(settingName); } Control control = entry.getValue(); if (values != null) { control.apply(values); String after = control.getLastValue(); if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) { if (values.size() > 1) { StringJoiner sj = new StringJoiner(", ", "{", "}"); for (String s : values) { sj.add("\"" + s + "\""); } String message = " " + settingName + "= " + sj.toString() + " => \"" + after + "\""; Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); } else { String message = " " + settingName + "=\"" + control.getLastValue() + "\""; Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); } } } } else { control.setDefault(); if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { String message = " " + settingName + "=\"" + control.getLastValue() + "\""; Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); } } } ec.writeActiveSettingEvent(); Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}"); } private InternalSetting getInternalSetting(EventControl ec) { String name = ec.getEventType().getName(); InternalSetting nameBased = availableSettings.get(name); InternalSetting idBased = availableSettings.get(ec.getSettingsId()); if (nameBased == null && idBased == null) { return null; } if (idBased == null) { return nameBased; } if (nameBased == null) { return idBased; } InternalSetting mixed = new InternalSetting(nameBased.getSettingsId()); mixed.add(nameBased); mixed.add(idBased); return mixed; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (InternalSetting enabled : availableSettings.values()) { sb.append(enabled.toString()); sb.append("\n"); } return sb.toString(); } boolean isEnabled(String eventName) { InternalSetting is = availableSettings.get(eventName); if (is == null) { return false; } return is.isEnabled(); } }