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