1 /* 2 * Copyright (c) 2018, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.hotspot.management; 26 27 import org.graalvm.compiler.phases.common.jmx.HotSpotMBeanOperationProvider; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Comparator; 31 import java.util.List; 32 33 import javax.management.Attribute; 34 import javax.management.AttributeList; 35 import javax.management.AttributeNotFoundException; 36 import javax.management.DynamicMBean; 37 import javax.management.InvalidAttributeValueException; 38 import javax.management.MBeanAttributeInfo; 39 import javax.management.MBeanException; 40 import javax.management.MBeanInfo; 41 import javax.management.MBeanOperationInfo; 42 import javax.management.MBeanParameterInfo; 43 import javax.management.ObjectName; 44 import javax.management.ReflectionException; 45 46 import jdk.internal.vm.compiler.collections.EconomicMap; 47 import org.graalvm.compiler.core.common.SuppressFBWarnings; 48 import org.graalvm.compiler.debug.TTY; 49 import org.graalvm.compiler.hotspot.HotSpotGraalRuntime; 50 import org.graalvm.compiler.options.OptionDescriptor; 51 import org.graalvm.compiler.options.OptionDescriptors; 52 import org.graalvm.compiler.options.OptionsParser; 53 import org.graalvm.compiler.serviceprovider.GraalServices; 54 55 /** 56 * MBean used to access properties and operations of a {@link HotSpotGraalRuntime} instance. 57 */ 58 final class HotSpotGraalRuntimeMBean implements DynamicMBean { 59 60 /** 61 * The runtime instance to which this bean provides a management connection. 62 */ 63 private final HotSpotGraalRuntime runtime; 64 65 /** 66 * The object name under which the bean is registered. 67 */ 68 private final ObjectName objectName; 69 70 HotSpotGraalRuntimeMBean(ObjectName objectName, HotSpotGraalRuntime runtime) { 71 this.objectName = objectName; 72 this.runtime = runtime; 73 } 74 75 ObjectName getObjectName() { 76 return objectName; 77 } 78 79 HotSpotGraalRuntime getRuntime() { 80 return runtime; 81 } 82 83 private static final boolean DEBUG = Boolean.getBoolean(HotSpotGraalRuntimeMBean.class.getSimpleName() + ".debug"); 84 85 @Override 86 public Object getAttribute(String name) throws AttributeNotFoundException { 87 String[] result = runtime.getOptionValues(name); 88 String value = result[0]; 89 if (value == null) { 90 throw new AttributeNotFoundException(name); 91 } 92 if (DEBUG) { 93 System.out.printf("getAttribute: %s = %s (type: %s)%n", name, value, value == null ? "null" : value.getClass().getName()); 94 } 95 return result[0]; 96 } 97 98 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "reference equality on the receiver is what we want") 99 @Override 100 public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException { 101 String name = attribute.getName(); 102 Object value = attribute.getValue(); 103 String svalue = String.valueOf(value); 104 if (DEBUG) { 105 System.out.printf("setAttribute: %s = %s (type: %s)%n", name, svalue, value == null ? "null" : value.getClass().getName()); 106 } 107 String[] result = runtime.setOptionValues(new String[]{name}, new String[]{svalue}); 108 if (result[0] != name) { 109 if (result[0] == null) { 110 throw new AttributeNotFoundException(name); 111 } 112 throw new InvalidAttributeValueException(result[0]); 113 } 114 } 115 116 @Override 117 public AttributeList getAttributes(String[] names) { 118 String[] values = runtime.getOptionValues(names); 119 AttributeList list = new AttributeList(); 120 for (int i = 0; i < names.length; i++) { 121 String value = values[i]; 122 String name = names[i]; 123 if (value == null) { 124 TTY.printf("No such option named %s%n", name); 125 } else { 126 if (DEBUG) { 127 System.out.printf("getAttributes: %s = %s (type: %s)%n", name, value, value == null ? "null" : value.getClass().getName()); 128 } 129 list.add(new Attribute(name, value)); 130 } 131 } 132 return list; 133 } 134 135 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "reference equality on the receiver is what we want") 136 @Override 137 public AttributeList setAttributes(AttributeList attributes) { 138 String[] names = new String[attributes.size()]; 139 String[] values = new String[attributes.size()]; 140 141 int i = 0; 142 for (Attribute attr : attributes.asList()) { 143 String name = attr.getName(); 144 names[i] = name; 145 Object value = attr.getValue(); 146 String svalue = String.valueOf(value); 147 values[i] = svalue; 148 if (DEBUG) { 149 System.out.printf("setAttributes: %s = %s (type: %s)%n", name, svalue, value == null ? "null" : value.getClass().getName()); 150 } 151 i++; 152 } 153 String[] result = runtime.setOptionValues(names, values); 154 AttributeList setOk = new AttributeList(); 155 i = 0; 156 for (Attribute attr : attributes.asList()) { 157 if (names[i] == result[i]) { 158 setOk.add(attr); 159 } else if (result[i] == null) { 160 TTY.printf("Error setting %s to %s: unknown option%n", attr.getName(), attr.getValue()); 161 } else { 162 TTY.printf("Error setting %s to %s: %s%n", attr.getName(), attr.getValue(), result[i]); 163 } 164 i++; 165 } 166 return setOk; 167 } 168 169 @Override 170 public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { 171 try { 172 if (DEBUG) { 173 System.out.printf("invoke: %s%s%n", actionName, Arrays.asList(params)); 174 } 175 Object retvalue = null; 176 if ("dumpMethod".equals(actionName)) { 177 retvalue = runtime.invokeManagementAction(actionName, params); 178 } else { 179 boolean found = false; 180 for (HotSpotMBeanOperationProvider p : GraalServices.load(HotSpotMBeanOperationProvider.class)) { 181 List<MBeanOperationInfo> info = new ArrayList<>(); 182 p.registerOperations(MBeanOperationInfo.class, info); 183 for (MBeanOperationInfo op : info) { 184 if (actionName.equals(op.getName())) { 185 retvalue = p.invoke(actionName, params, signature); 186 found = true; 187 break; 188 } 189 } 190 } 191 if (!found) { 192 throw new MBeanException(new IllegalStateException("Cannot find operation " + actionName)); 193 } 194 } 195 if (DEBUG) { 196 System.out.printf("invoke: %s%s = %s%n", actionName, Arrays.asList(params), retvalue); 197 } 198 return retvalue; 199 } catch (MBeanException ex) { 200 throw ex; 201 } catch (Exception ex) { 202 throw new ReflectionException(ex); 203 } 204 } 205 206 @Override 207 public MBeanInfo getMBeanInfo() { 208 List<MBeanAttributeInfo> attrs = new ArrayList<>(); 209 for (OptionDescriptor option : getOptionDescriptors().getValues()) { 210 Class<?> optionValueType = option.getOptionValueType(); 211 if (Enum.class.isAssignableFrom(optionValueType)) { 212 // Enum values are passed through 213 // the management interface as Strings. 214 optionValueType = String.class; 215 } 216 attrs.add(new MBeanAttributeInfo(option.getName(), optionValueType.getName(), option.getHelp(), true, true, false)); 217 } 218 attrs.sort(new Comparator<MBeanAttributeInfo>() { 219 @Override 220 public int compare(MBeanAttributeInfo o1, MBeanAttributeInfo o2) { 221 return o1.getName().compareTo(o2.getName()); 222 } 223 }); 224 List<MBeanOperationInfo> opts = new ArrayList<>(); 225 opts.add(new MBeanOperationInfo("dumpMethod", "Enable IGV dumps for provided method", new MBeanParameterInfo[]{ 226 new MBeanParameterInfo("className", "java.lang.String", "Class to observe"), 227 new MBeanParameterInfo("methodName", "java.lang.String", "Method to observe"), 228 }, "void", MBeanOperationInfo.ACTION)); 229 opts.add(new MBeanOperationInfo("dumpMethod", "Enable IGV dumps for provided method", new MBeanParameterInfo[]{ 230 new MBeanParameterInfo("className", "java.lang.String", "Class to observe"), 231 new MBeanParameterInfo("methodName", "java.lang.String", "Method to observe"), 232 new MBeanParameterInfo("filter", "java.lang.String", "The parameter for Dump option"), 233 }, "void", MBeanOperationInfo.ACTION)); 234 opts.add(new MBeanOperationInfo("dumpMethod", "Enable IGV dumps for provided method", new MBeanParameterInfo[]{ 235 new MBeanParameterInfo("className", "java.lang.String", "Class to observe"), 236 new MBeanParameterInfo("methodName", "java.lang.String", "Method to observe"), 237 new MBeanParameterInfo("filter", "java.lang.String", "The parameter for Dump option"), 238 new MBeanParameterInfo("host", "java.lang.String", "The host where the IGV tool is running at"), 239 new MBeanParameterInfo("port", "int", "The port where the IGV tool is listening at"), 240 }, "void", MBeanOperationInfo.ACTION)); 241 242 for (HotSpotMBeanOperationProvider p : GraalServices.load(HotSpotMBeanOperationProvider.class)) { 243 p.registerOperations(MBeanOperationInfo.class, opts); 244 } 245 246 return new MBeanInfo( 247 HotSpotGraalRuntimeMBean.class.getName(), 248 "Graal", 249 attrs.toArray(new MBeanAttributeInfo[attrs.size()]), 250 null, 251 opts.toArray(new MBeanOperationInfo[opts.size()]), 252 null); 253 } 254 255 private static EconomicMap<String, OptionDescriptor> getOptionDescriptors() { 256 EconomicMap<String, OptionDescriptor> result = EconomicMap.create(); 257 for (OptionDescriptors set : OptionsParser.getOptionsLoader()) { 258 for (OptionDescriptor option : set) { 259 result.put(option.getName(), option); 260 } 261 } 262 return result; 263 } 264 }