/*
 * 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 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.Set;
import java.util.StringJoiner;

import jdk.jfr.internal.EventControl.NamedControl;
import jdk.jfr.internal.handlers.EventHandler;

final class SettingsManager {

    private static class InternalSetting {

        private final String identifier;
        private Map<String, Set<String>> enabledMap = new LinkedHashMap<>(5);
        private Map<String, Set<String>> allMap = new LinkedHashMap<>(5);
        private boolean enabled;

        
Settings identifier, for example "com.example.HelloWorld" or "56" (id of event)
Params:
  • settingsId –
/** * Settings identifier, for example "com.example.HelloWorld" or "56" * (id of event) * * @param settingsId */
public InternalSetting(String settingsId) { this.identifier = settingsId; } public Set<String> 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<String, Set<String>> map, String attribute, String value) { Set<String> values = map.get(attribute); if (values == null) { values = new HashSet<String>(5); map.put(attribute, values); } values.add(value); } public String getSettingsId() { return identifier; } public void add(InternalSetting enabled) { for (Map.Entry<String, Set<String>> 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<String, Set<String>> disabledMap = new HashMap<>(2); Set<String> values = new HashSet<>(2); values.add("false"); disabledMap.put("enabled", values); enabledMap = disabledMap; } } } private Map<String, InternalSetting> availableSettings = new LinkedHashMap<>(); void setSettings(List<Map<String, String>> activeSettings) { // store settings so they are available if a new event class is loaded availableSettings = createSettingsMap(activeSettings); List<EventControl> eventControls = MetadataRepository.getInstance().getEventControls(); if (!JVM.getJVM().isRecording()) { for (EventControl ec : eventControls) { ec.disable(); } } else { if (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) { 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<Class<? extends jdk.internal.event.Event>> eventClasses) { List<Class<?>> classes = new ArrayList<>(); for(Class<? extends jdk.internal.event.Event> 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<String, InternalSetting> createSettingsMap(List<Map<String,String>> activeSettings) { Map<String, InternalSetting> map = new LinkedHashMap<>(activeSettings.size()); for (Map<String, String> 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<InternalSetting> makeInternalSettings(Map<String, String> rec) { Map<String, InternalSetting> internals = new LinkedHashMap<>(); for (Map.Entry<String, String> 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); eventName = Utils.upgradeLegacyJDKEvent(eventName); 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); boolean shouldLog = Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO); if (shouldLog) { Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {"); } for (NamedControl nc: ec.getNamedControls()) { Set<String> values = null; String settingName = nc.name; if (is != null) { values = is.getValues(settingName); } Control control = nc.control; if (values != null) { control.apply(values); String after = control.getLastValue(); if (shouldLog) { 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 (shouldLog) { String message = " " + settingName + "=\"" + control.getLastValue() + "\""; Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); } } } ec.writeActiveSettingEvent(); if (shouldLog) { 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(); } }