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.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;
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;
}
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) {
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) {
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);
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);
Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {");
for (Entry<String, Control> entry : ec.getEntries()) {
Set<String> 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 (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
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 (Logger.shouldLog(LogTag.JFR_SETTING, LogLevel.INFO)) {
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();
}
}