1 /* 2 * Copyright (c) 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 = initDebug(); 84 85 private static boolean initDebug() { 86 try { 87 return Boolean.getBoolean(HotSpotGraalRuntimeMBean.class.getSimpleName() + ".debug"); 88 } catch (SecurityException e) { 89 // Swallow the exception 90 return false; 91 } 92 } 93 94 @Override 95 public Object getAttribute(String name) throws AttributeNotFoundException { 96 String[] result = runtime.getOptionValues(name); 97 String value = result[0]; 98 if (value == null) { 99 throw new AttributeNotFoundException(name); 100 } 101 if (DEBUG) { 102 System.out.printf("getAttribute: %s = %s (type: %s)%n", name, value, value == null ? "null" : value.getClass().getName()); 103 } 104 return result[0]; 105 } 106 107 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "reference equality on the receiver is what we want") 108 @Override 109 public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException { 110 String name = attribute.getName(); 111 Object value = attribute.getValue(); 112 String svalue = String.valueOf(value); 113 if (DEBUG) { 114 System.out.printf("setAttribute: %s = %s (type: %s)%n", name, svalue, value == null ? "null" : value.getClass().getName()); 115 } 116 String[] result = runtime.setOptionValues(new String[]{name}, new String[]{svalue}); 117 if (result[0] != name) { 118 if (result[0] == null) { 119 throw new AttributeNotFoundException(name); 120 } 121 throw new InvalidAttributeValueException(result[0]); 122 } 123 } 124 125 @Override 126 public AttributeList getAttributes(String[] names) { 127 String[] values = runtime.getOptionValues(names); 128 AttributeList list = new AttributeList(); 129 for (int i = 0; i < names.length; i++) { 130 String value = values[i]; 131 String name = names[i]; 132 if (value == null) { 133 TTY.printf("No such option named %s%n", name); 134 } else { 135 if (DEBUG) { 136 System.out.printf("getAttributes: %s = %s (type: %s)%n", name, value, value == null ? "null" : value.getClass().getName()); 137 } 138 list.add(new Attribute(name, value)); 139 } 140 } 141 return list; 142 } 143 144 @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "reference equality on the receiver is what we want") 145 @Override 146 public AttributeList setAttributes(AttributeList attributes) { 147 String[] names = new String[attributes.size()]; 148 String[] values = new String[attributes.size()]; 149 150 int i = 0; 151 for (Attribute attr : attributes.asList()) { 152 String name = attr.getName(); 153 names[i] = name; 154 Object value = attr.getValue(); 155 String svalue = String.valueOf(value); 156 values[i] = svalue; 157 if (DEBUG) { 158 System.out.printf("setAttributes: %s = %s (type: %s)%n", name, svalue, value == null ? "null" : value.getClass().getName()); 159 } 160 i++; 161 } 162 String[] result = runtime.setOptionValues(names, values); 163 AttributeList setOk = new AttributeList(); 164 i = 0; 165 for (Attribute attr : attributes.asList()) { 166 if (names[i] == result[i]) { 167 setOk.add(attr); 168 } else if (result[i] == null) { 169 TTY.printf("Error setting %s to %s: unknown option%n", attr.getName(), attr.getValue()); 170 } else { 171 TTY.printf("Error setting %s to %s: %s%n", attr.getName(), attr.getValue(), result[i]); 172 } 173 i++; 174 } 175 return setOk; 176 } 177 178 @Override 179 public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException { 180 try { 181 if (DEBUG) { 182 System.out.printf("invoke: %s%s%n", actionName, Arrays.asList(params)); 183 } 184 Object retvalue = null; 185 if ("dumpMethod".equals(actionName)) { 186 retvalue = runtime.invokeManagementAction(actionName, params); 187 } else { 188 boolean found = false; 189 for (HotSpotMBeanOperationProvider p : GraalServices.load(HotSpotMBeanOperationProvider.class)) { 190 List<MBeanOperationInfo> info = new ArrayList<>(); 191 p.registerOperations(MBeanOperationInfo.class, info); 192 for (MBeanOperationInfo op : info) { 193 if (actionName.equals(op.getName())) { 194 retvalue = p.invoke(actionName, params, signature); 195 found = true; 196 break; 197 } 198 } 199 } 200 if (!found) { 201 throw new MBeanException(new IllegalStateException("Cannot find operation " + actionName)); 202 } 203 } 204 if (DEBUG) { 205 System.out.printf("invoke: %s%s = %s%n", actionName, Arrays.asList(params), retvalue); 206 } 207 return retvalue; 208 } catch (MBeanException ex) { 209 throw ex; 210 } catch (Exception ex) { 211 throw new ReflectionException(ex); 212 } 213 } 214 215 @Override 216 public MBeanInfo getMBeanInfo() { 217 List<MBeanAttributeInfo> attrs = new ArrayList<>(); 218 for (OptionDescriptor option : getOptionDescriptors().getValues()) { 219 Class<?> optionValueType = option.getOptionValueType(); 220 if (Enum.class.isAssignableFrom(optionValueType)) { 221 // Enum values are passed through 222 // the management interface as Strings. 223 optionValueType = String.class; 224 } 225 attrs.add(new MBeanAttributeInfo(option.getName(), optionValueType.getName(), option.getHelp(), true, true, false)); 226 } 227 attrs.sort(new Comparator<MBeanAttributeInfo>() { 228 @Override 229 public int compare(MBeanAttributeInfo o1, MBeanAttributeInfo o2) { 230 return o1.getName().compareTo(o2.getName()); 231 } 232 }); 233 List<MBeanOperationInfo> opts = new ArrayList<>(); 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 }, "void", MBeanOperationInfo.ACTION)); 238 opts.add(new MBeanOperationInfo("dumpMethod", "Enable IGV dumps for provided method", new MBeanParameterInfo[]{ 239 new MBeanParameterInfo("className", "java.lang.String", "Class to observe"), 240 new MBeanParameterInfo("methodName", "java.lang.String", "Method to observe"), 241 new MBeanParameterInfo("filter", "java.lang.String", "The parameter for Dump option"), 242 }, "void", MBeanOperationInfo.ACTION)); 243 opts.add(new MBeanOperationInfo("dumpMethod", "Enable IGV dumps for provided method", new MBeanParameterInfo[]{ 244 new MBeanParameterInfo("className", "java.lang.String", "Class to observe"), 245 new MBeanParameterInfo("methodName", "java.lang.String", "Method to observe"), 246 new MBeanParameterInfo("filter", "java.lang.String", "The parameter for Dump option"), 247 new MBeanParameterInfo("host", "java.lang.String", "The host where the IGV tool is running at"), 248 new MBeanParameterInfo("port", "int", "The port where the IGV tool is listening at"), 249 }, "void", MBeanOperationInfo.ACTION)); 250 251 for (HotSpotMBeanOperationProvider p : GraalServices.load(HotSpotMBeanOperationProvider.class)) { 252 p.registerOperations(MBeanOperationInfo.class, opts); 253 } 254 255 return new MBeanInfo( 256 HotSpotGraalRuntimeMBean.class.getName(), 257 "Graal", 258 attrs.toArray(new MBeanAttributeInfo[attrs.size()]), 259 null, 260 opts.toArray(new MBeanOperationInfo[opts.size()]), 261 null); 262 } 263 264 private static EconomicMap<String, OptionDescriptor> getOptionDescriptors() { 265 EconomicMap<String, OptionDescriptor> result = EconomicMap.create(); 266 for (OptionDescriptors set : OptionsParser.getOptionsLoader()) { 267 for (OptionDescriptor option : set) { 268 result.put(option.getName(), option); 269 } 270 } 271 return result; 272 } 273 }