1 /* 2 * Copyright (c) 2013, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.jmx; 27 28 import java.io.BufferedOutputStream; 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.lang.management.ManagementFactory; 33 import java.time.Instant; 34 import java.util.ArrayList; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 39 import com.sun.tools.attach.VirtualMachine; 40 import jdk.jfr.EventType; 41 import jdk.jfr.FlightRecorder; 42 import jdk.jfr.Recording; 43 import jdk.jfr.RecordingState; 44 import jdk.jfr.SettingDescriptor; 45 import jdk.jfr.consumer.RecordedEvent; 46 import jdk.jfr.consumer.RecordingFile; 47 import jdk.management.jfr.EventTypeInfo; 48 import jdk.management.jfr.FlightRecorderMXBean; 49 import jdk.management.jfr.RecordingInfo; 50 import jdk.management.jfr.SettingDescriptorInfo; 51 import jdk.test.lib.Asserts; 52 import jdk.test.lib.Utils; 53 import jdk.test.lib.jfr.CommonHelper; 54 import jdk.test.lib.jfr.Events; 55 56 import javax.management.JMX; 57 import javax.management.MBeanServerConnection; 58 import javax.management.ObjectName; 59 import javax.management.remote.JMXConnector; 60 import javax.management.remote.JMXConnectorFactory; 61 import javax.management.remote.JMXServiceURL; 62 63 public class JmxHelper { 64 private static final String LOCAL_CONNECTION_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress"; 65 66 public static RecordingInfo getJmxRecording(long recId) { 67 for (RecordingInfo r : getFlighteRecorderMXBean().getRecordings()) { 68 if (r.getId() == recId) { 69 return r; 70 } 71 } 72 Asserts.fail("No RecordingInfo with id " + recId); 73 return null; 74 } 75 76 public static Recording getJavaRecording(long recId) { 77 for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { 78 if (r.getId() == recId) { 79 return r; 80 } 81 } 82 Asserts.fail("No Recording with id " + recId); 83 return null; 84 } 85 86 public static void verifyState(long recId, RecordingState state, List<RecordingInfo> recordings) { 87 RecordingInfo r = verifyExists(recId, recordings); 88 verifyState(r, state); 89 } 90 91 public static void verifyState(RecordingInfo recording, RecordingState state) { 92 final String actual = recording.getState().toString(); 93 final String expected = state.toString(); 94 Asserts.assertEquals(actual, expected, "Wrong state"); 95 } 96 97 public static void verifyState(long recId, RecordingState state, FlightRecorderMXBean bean) throws Exception { 98 FlightRecorder jfr = FlightRecorder.getFlightRecorder(); 99 Recording recording = CommonHelper.verifyExists(recId, jfr.getRecordings()); 100 CommonHelper.verifyRecordingState(recording, state); 101 verifyState(recId, state, bean.getRecordings()); 102 } 103 104 public static void verifyNotExists(long recId, List<RecordingInfo> recordings) { 105 for (RecordingInfo r : recordings) { 106 if (recId == r.getId()) { 107 logRecordingInfos(recordings); 108 Asserts.fail("Recording should not exist, id=" + recId); 109 } 110 } 111 } 112 113 public static RecordingInfo verifyExists(long recId, List<RecordingInfo> recordings) { 114 for (RecordingInfo r : recordings) { 115 if (recId == r.getId()) { 116 return r; 117 } 118 } 119 logRecordingInfos(recordings); 120 Asserts.fail("Recording not found, id=" + recId); 121 return null; 122 } 123 124 125 public static void logRecordingInfos(List<RecordingInfo> recordings) { 126 System.out.println("RecordingInfos:"); 127 for (RecordingInfo r : recordings) { 128 System.out.println(asString(r)); 129 } 130 } 131 132 public static void logRecordings(List<Recording> recordings) { 133 System.out.println("Recordings:"); 134 for (Recording r : recordings) { 135 System.out.println(asString(r)); 136 } 137 } 138 139 static File dump(long streamId, FlightRecorderMXBean bean) throws IOException { 140 File f = Utils.createTempFile("stream_" + streamId + "_", ".jfr").toFile(); 141 try (FileOutputStream fos = new FileOutputStream(f); BufferedOutputStream bos = new BufferedOutputStream(fos)) { 142 while (true) { 143 byte[] data = bean.readStream(streamId); 144 if (data == null) { 145 bos.flush(); 146 return f; 147 } 148 bos.write(data); 149 } 150 } 151 } 152 153 public static List<RecordedEvent> parseStream(long streamId, FlightRecorderMXBean bean) throws Exception { 154 File dumpFile = dump(streamId, bean); 155 System.out.println("data.length=" + dumpFile.length()); 156 List<RecordedEvent> events = new ArrayList<>(); 157 for (RecordedEvent event : RecordingFile.readAllEvents(dumpFile.toPath())) { 158 System.out.println("EVENT:" + event); 159 events.add(event); 160 } 161 return events; 162 } 163 164 public static void verifyEquals(RecordingInfo ri, Recording r) { 165 String destination = r.getDestination() != null ? r.getDestination().toString() : null; 166 long maxAge = r.getMaxAge() != null ? r.getMaxAge().getSeconds() : 0; 167 long duration = r.getDuration() != null ? r.getDuration().getSeconds() : 0; 168 169 Asserts.assertEquals(destination, ri.getDestination(), "Wrong destination"); 170 Asserts.assertEquals(r.getDumpOnExit(), ri.getDumpOnExit(), "Wrong dumpOnExit"); 171 Asserts.assertEquals(duration, ri.getDuration(), "Wrong duration"); 172 Asserts.assertEquals(r.getId(), ri.getId(), "Wrong id"); 173 Asserts.assertEquals(maxAge, ri.getMaxAge(), "Wrong maxAge"); 174 Asserts.assertEquals(r.getMaxSize(), ri.getMaxSize(), "Wrong maxSize"); 175 Asserts.assertEquals(r.getName(), ri.getName(), "Wrong name"); 176 Asserts.assertEquals(r.getSize(), ri.getSize(), "Wrong size"); 177 Asserts.assertEquals(toEpochMillis(r.getStartTime()), ri.getStartTime(), "Wrong startTime"); 178 Asserts.assertEquals(r.getState().toString(), ri.getState(), "Wrong state"); 179 Asserts.assertEquals(toEpochMillis(r.getStopTime()), ri.getStopTime(), "Wrong stopTime"); 180 181 verifyMapEquals(r.getSettings(), ri.getSettings()); 182 } 183 184 public static String asString(RecordingInfo r) { 185 StringBuffer sb = new StringBuffer(); 186 sb.append(String.format("RecordingInfo:%n")); 187 sb.append(String.format("destination=%s%n", r.getDestination())); 188 sb.append(String.format("dumpOnExit=%b%n", r.getDumpOnExit())); 189 sb.append(String.format("duration=%d%n", r.getDuration())); 190 sb.append(String.format("id=%d%n", r.getId())); 191 sb.append(String.format("maxAge=%d%n", r.getMaxAge())); 192 sb.append(String.format("maxSize=%d%n", r.getMaxSize())); 193 sb.append(String.format("getName=%s%n", r.getName())); 194 sb.append(String.format("size=%d%n", r.getSize())); 195 sb.append(String.format("startTime=%d%n", r.getStartTime())); 196 sb.append(String.format("state=%s%n", r.getState())); 197 sb.append(String.format("stopTime=%d%n", r.getStopTime())); 198 return sb.toString(); 199 } 200 201 public static String asString(Recording r) { 202 StringBuffer sb = new StringBuffer(); 203 sb.append(String.format("Recording:%n")); 204 sb.append(String.format("destination=%s%n", r.getDestination())); 205 sb.append(String.format("dumpOnExit=%b%n", r.getDumpOnExit())); 206 sb.append(String.format("duration=%d%n", r.getDuration().getSeconds())); 207 sb.append(String.format("id=%d%n", r.getId())); 208 sb.append(String.format("maxAge=%d%n", r.getMaxAge().getSeconds())); 209 sb.append(String.format("maxSize=%d%n", r.getMaxSize())); 210 sb.append(String.format("getName=%s%n", r.getName())); 211 sb.append(String.format("size=%d%n", r.getSize())); 212 sb.append(String.format("startTime=%d%n", toEpochMillis(r.getStartTime()))); 213 sb.append(String.format("state=%s%n", r.getState())); 214 sb.append(String.format("stopTime=%d%n", toEpochMillis(r.getStopTime()))); 215 return sb.toString(); 216 } 217 218 public static void verifyMapEquals(Map<String, String> a, Map<String, String> b) { 219 try { 220 Asserts.assertEquals(a.size(), b.size(), "Wrong number of keys"); 221 for (String key : a.keySet()) { 222 Asserts.assertTrue(a.containsKey(key), "Missing key " + key); 223 Asserts.assertEquals(a.get(key), b.get(key), "Wrong values for key " + key); 224 //System.out.printf("equal: %s=%s%n", key, a.get(key)); 225 } 226 } catch (Exception e) { 227 System.out.println("Error: " + e.getMessage()); 228 logMap("a", a); 229 logMap("b", b); 230 throw e; 231 } 232 } 233 234 public static void logMap(String name, Map<String, String> map) { 235 for (String key : map.keySet()) { 236 System.out.printf("map %s: %s=%s%n", name, key, map.get(key)); 237 } 238 } 239 240 private static long toEpochMillis(Instant instant) { 241 return instant != null ? instant.toEpochMilli() : 0; 242 } 243 244 public static void verifyEventSettingsEqual(EventType javaType, EventTypeInfo jmxType) { 245 Map<String, SettingDescriptor> javaSettings = new HashMap<>(); 246 for (SettingDescriptor settingDescriptor : javaType.getSettingDescriptors()) { 247 javaSettings.put(settingDescriptor.getName(), settingDescriptor); 248 } 249 Asserts.assertFalse(javaSettings.isEmpty(), "No ValueDescriptor for EventType " + javaType.getName()); 250 251 for (SettingDescriptorInfo jmxSetting : jmxType.getSettingDescriptors()) { 252 final String name = jmxSetting.getName(); 253 System.out.printf("SettingDescriptorInfo: %s#%s=%s%n", jmxType.getName(), name, jmxSetting.getDefaultValue()); 254 SettingDescriptor javaSetting = javaSettings.remove(name); 255 Asserts.assertNotNull(javaSetting, "No Setting for name " + name); 256 Asserts.assertEquals(jmxSetting.getDefaultValue(), Events.getSetting(javaType, name).getDefaultValue(), "Wrong default value"); 257 Asserts.assertEquals(jmxSetting.getDescription(), javaSetting.getDescription(), "Wrong description"); 258 Asserts.assertEquals(jmxSetting.getLabel(), javaSetting.getLabel(), "Wrong label"); 259 Asserts.assertEquals(jmxSetting.getName(), javaSetting.getName(), "Wrong name"); 260 Asserts.assertEquals(jmxSetting.getTypeName(), javaSetting.getTypeName(), "Wrong type name"); 261 Asserts.assertEquals(jmxSetting.getContentType(), javaSetting.getContentType()); 262 } 263 264 // Verify that all Settings have been matched. 265 if (!javaSettings.isEmpty()) { 266 for (String name : javaSettings.keySet()) { 267 System.out.println("Missing setting" + name + " in EventTypeInfo for " + javaType.getName()); 268 } 269 System.out.println(); 270 System.out.println(javaType.getName() + " Java API"); 271 System.out.println("==============="); 272 for (SettingDescriptor v : javaType.getSettingDescriptors()) { 273 System.out.println(" - " + v.getName()); 274 } 275 System.out.println(); 276 System.out.println(jmxType.getName() + " JMX API"); 277 System.out.println("==============="); 278 for (SettingDescriptorInfo v : jmxType.getSettingDescriptors()) { 279 System.out.println(" - " + v.getName()); 280 } 281 282 Asserts.fail("Missing setting"); 283 } 284 } 285 286 287 public static FlightRecorderMXBean getFlighteRecorderMXBean() { 288 return ManagementFactory.getPlatformMXBean(FlightRecorderMXBean.class); 289 } 290 291 public static long getPID(){ 292 return ManagementFactory.getRuntimeMXBean().getPid(); 293 } 294 295 public static FlightRecorderMXBean getFlighteRecorderMXBean(long pid) throws Exception { 296 VirtualMachine targetVM = VirtualMachine.attach("" + pid); 297 String jmxServiceUrl = targetVM.getAgentProperties().getProperty(LOCAL_CONNECTION_ADDRESS); 298 JMXServiceURL jmxURL = new JMXServiceURL(jmxServiceUrl); 299 JMXConnector connector = JMXConnectorFactory.connect(jmxURL); 300 MBeanServerConnection connection = connector.getMBeanServerConnection(); 301 302 ObjectName objectName = new ObjectName("jdk.management.jfr:type=FlightRecorder"); 303 return JMX.newMXBeanProxy(connection, objectName, FlightRecorderMXBean.class); 304 } 305 }