1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 package org.openjdk.jmc.browser.attach; 34 35 import static org.openjdk.jmc.ui.common.jvm.Connectable.ATTACHABLE; 36 import static org.openjdk.jmc.ui.common.jvm.Connectable.MGMNT_AGENT_STARTED; 37 import static org.openjdk.jmc.ui.common.jvm.Connectable.NO; 38 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.net.URISyntaxException; 42 import java.util.ArrayList; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Properties; 47 import java.util.Set; 48 import java.util.WeakHashMap; 49 import java.util.concurrent.Callable; 50 import java.util.concurrent.ExecutorService; 51 import java.util.concurrent.Executors; 52 import java.util.concurrent.Future; 53 import java.util.concurrent.TimeUnit; 54 import java.util.logging.Level; 55 56 import javax.management.remote.JMXServiceURL; 57 58 import org.eclipse.jface.preference.IPreferenceStore; 59 import org.eclipse.jface.util.IPropertyChangeListener; 60 import org.eclipse.jface.util.PropertyChangeEvent; 61 import org.openjdk.jmc.attach.AttachToolkit; 62 import org.openjdk.jmc.browser.attach.internal.ExecuteTunnler; 63 import org.openjdk.jmc.browser.attach.preferences.PreferenceConstants; 64 import org.openjdk.jmc.common.version.JavaVMVersionToolkit; 65 import org.openjdk.jmc.rjmx.IConnectionDescriptor; 66 import org.openjdk.jmc.rjmx.IServerDescriptor; 67 import org.openjdk.jmc.ui.common.jvm.Connectable; 68 import org.openjdk.jmc.ui.common.jvm.JVMArch; 69 import org.openjdk.jmc.ui.common.jvm.JVMDescriptor; 70 import org.openjdk.jmc.ui.common.jvm.JVMType; 71 72 import com.sun.tools.attach.AgentLoadException; 73 import com.sun.tools.attach.AttachNotSupportedException; 74 import com.sun.tools.attach.VirtualMachine; 75 import com.sun.tools.attach.VirtualMachineDescriptor; 76 77 import sun.jvmstat.monitor.HostIdentifier; 78 import sun.jvmstat.monitor.MonitorException; 79 import sun.jvmstat.monitor.MonitoredHost; 80 import sun.jvmstat.monitor.MonitoredVm; 81 import sun.jvmstat.monitor.MonitoredVmUtil; 82 import sun.jvmstat.monitor.StringMonitor; 83 import sun.jvmstat.monitor.VmIdentifier; 84 import sun.tools.attach.HotSpotVirtualMachine; 85 86 /** 87 * The activator class controls the plug-in life cycle 88 */ 89 public class LocalJVMToolkit { 90 public static class DiscoveryEntry { 91 private final IServerDescriptor serverDescriptor; 92 private final IConnectionDescriptor connectionDescriptor; 93 94 public DiscoveryEntry(IServerDescriptor serverDescriptor, IConnectionDescriptor descriptor) { 95 this.serverDescriptor = serverDescriptor; 96 connectionDescriptor = descriptor; 97 } 98 99 public IConnectionDescriptor getConnectionDescriptor() { 100 return connectionDescriptor; 101 } 102 103 public IServerDescriptor getServerDescriptor() { 104 return serverDescriptor; 105 } 106 } 107 108 private static long SEQ_NUMBER = 0; 109 private static boolean isErrorMessageSent = false; 110 private static boolean m_unconnectableInited = false; 111 private static boolean m_showUnconnectable = false; 112 113 private static Map<Object, DiscoveryEntry> last = new WeakHashMap<>(); 114 115 static final String LOCAL_CONNECTOR_ADDRESS_PROP = "com.sun.management.jmxremote.localConnectorAddress"; //$NON-NLS-1$ 116 static final String JVM_ARGS_PROP = "sun.jvm.args"; //$NON-NLS-1$ 117 static final String JVM_FLAGS_PROP = "sun.jvm.flags"; //$NON-NLS-1$ 118 static final String JAVA_COMMAND_PROP = "sun.java.command"; //$NON-NLS-1$ 119 120 private static final int TIMEOUT_THRESHOLD = 5; 121 122 private LocalJVMToolkit() { 123 // Toolkit 124 } 125 126 /** 127 * @return returns the local JVM's that could be discovered. 128 */ 129 public static DiscoveryEntry[] getLocalConnections() { 130 HashMap<Object, DiscoveryEntry> map = new HashMap<>(); 131 populateAttachableVMs(map); 132 populateMonitoredVMs(map, showUnconnectableJvms()); 133 last = map; 134 ArrayList<DiscoveryEntry> list = new ArrayList<>(map.values()); 135 return list.toArray(new DiscoveryEntry[list.size()]); 136 } 137 138 private static final boolean showUnconnectableJvms() { 139 if (!m_unconnectableInited) { 140 IPreferenceStore store = BrowserAttachPlugin.getDefault().getPreferenceStore(); 141 if (store != null) { 142 m_showUnconnectable = store.getBoolean(PreferenceConstants.P_SHOW_UNCONNECTABLE); 143 store.addPropertyChangeListener(new IPropertyChangeListener() { 144 @Override 145 public void propertyChange(PropertyChangeEvent event) { 146 if (event.getProperty().equals(PreferenceConstants.P_SHOW_UNCONNECTABLE)) { 147 m_showUnconnectable = ((Boolean) event.getNewValue()).booleanValue(); 148 } 149 } 150 }); 151 m_unconnectableInited = true; 152 } 153 } 154 return m_showUnconnectable; 155 } 156 157 private static void populateMonitoredVMs(HashMap<Object, DiscoveryEntry> map, boolean includeUnconnectables) { 158 MonitoredHost host = getMonitoredHost(); 159 Set<?> vms; 160 try { 161 vms = host.activeVms(); 162 } catch (MonitorException mx) { 163 throw new InternalError(mx.getMessage()); 164 } 165 for (Object vmid : vms) { 166 if (vmid instanceof Integer) { 167 // Check if the map already contains a descriptor for this 168 if (map.containsKey(vmid)) { 169 continue; 170 } 171 // Check if we already have a descriptor *first*, to avoid unnecessary attach which may leak handles 172 DiscoveryEntry connDesc = last.get(vmid); 173 if (connDesc == null) { 174 connDesc = createMonitoredJvmDescriptor(host, (Integer) vmid); 175 } 176 177 if ((includeUnconnectables && connDesc != null) 178 || (connDesc != null && !connDesc.getServerDescriptor().getJvmInfo().isUnconnectable())) { 179 map.put(vmid, connDesc); 180 } 181 } 182 } 183 } 184 185 private static DiscoveryEntry createMonitoredJvmDescriptor(MonitoredHost host, Integer vmid) { 186 try { 187 // Enforce a timeout here to make sure we don't block forever if the JVM is busy/suspended. See JMC-5398 188 ExecutorService service = Executors.newSingleThreadExecutor(); 189 Future<DiscoveryEntry> future = service.submit(new Callable<DiscoveryEntry>() { 190 @Override 191 public DiscoveryEntry call() throws Exception { 192 DiscoveryEntry connDesc; 193 int pid = vmid.intValue(); 194 String name = vmid.toString(); // default to pid if name not available 195 Connectable connectable = NO; 196 JVMType type = JVMType.OTHER; 197 JVMArch jvmArch = JVMArch.OTHER; 198 boolean isDebug = false; 199 String address = null; 200 String version = null; 201 String jvmArgs = null; 202 try { 203 // This used to leak one \BaseNamedObjects\hsperfdata_* Section handle on Windows 204 MonitoredVm mvm = host.getMonitoredVm(new VmIdentifier(name)); 205 try { 206 // use the command line as the display name 207 name = MonitoredVmUtil.commandLine(mvm); 208 jvmArgs = MonitoredVmUtil.jvmArgs(mvm); 209 StringMonitor sm = (StringMonitor) mvm.findByName("java.property.java.vm.name"); //$NON-NLS-1$ 210 if (sm != null) { 211 type = getJVMType(sm.stringValue()); 212 } 213 214 sm = (StringMonitor) mvm.findByName("java.property.java.version"); //$NON-NLS-1$ 215 if (sm != null) { 216 version = sm.stringValue(); 217 } 218 219 if (version == null) { 220 // Use java.vm.version when java.version is not exposed as perfcounter (HotSpot 1.5 and JRockit) 221 sm = (StringMonitor) mvm.findByName("java.property.java.vm.version"); //$NON-NLS-1$ 222 if (sm != null) { 223 String vmVersion = sm.stringValue(); 224 if (type == JVMType.JROCKIT) { 225 version = JavaVMVersionToolkit.decodeJavaVersion(vmVersion); 226 } else { 227 version = JavaVMVersionToolkit.parseJavaVersion(vmVersion); 228 } 229 } 230 } 231 if (version == null) { 232 version = "0"; //$NON-NLS-1$ 233 } 234 235 if (sm != null) { 236 isDebug = isDebug(sm.stringValue()); 237 } 238 // NOTE: isAttachable seems to return true even if a real attach is not possible. 239 // attachable = MonitoredVmUtil.isAttachable(mvm); 240 241 jvmArch = getArch(vmid); 242 // Check if the in-memory agent has been started, in that case we can connect anyway 243 JMXServiceURL inMemURL = null; 244 try { 245 inMemURL = LocalJVMToolkit.getInMemoryURLFromPID(vmid); 246 } catch (IOException e) { 247 BrowserAttachPlugin.getPluginLogger().log(Level.WARNING, 248 "Got exception when trying to get in-memory url for jvm with PID " + vmid, e); //$NON-NLS-1$ 249 } 250 if (inMemURL != null) { 251 connectable = MGMNT_AGENT_STARTED; 252 } 253 254 // This used to leak one \BaseNamedObjects\hsperfdata_* Section handle on Windows 255 address = AttachToolkit.importFromPid(pid); 256 } finally { 257 // Although the current implementation of LocalMonitoredVm for Windows doesn't do much here, we should always call detach. 258 mvm.detach(); 259 } 260 } catch (Exception x) { 261 // ignore 262 } 263 connDesc = createDescriptor(name, jvmArgs, vmid, connectable, type, jvmArch, address, version, isDebug); 264 return connDesc; 265 } 266 }); 267 return future.get(TIMEOUT_THRESHOLD, TimeUnit.SECONDS); 268 } catch (Exception e) { 269 BrowserAttachPlugin.getPluginLogger().log(Level.WARNING, "Failed to create descriptor for jvm with PID " + vmid, e); //$NON-NLS-1$ 270 return null; 271 } 272 } 273 274 /* 275 * Try to attach to get info from the AttachNotSupportedException. 276 */ 277 private static JVMArch getArch(Integer vmid) throws IOException { 278 JVMArch jvmArch = JVMArch.OTHER; 279 List<VirtualMachineDescriptor> vms = VirtualMachine.list(); 280 if (vms != null) { 281 for (VirtualMachineDescriptor vmd : vms) { 282 if (vmid == Integer.parseInt(vmd.id())) { 283 try { 284 VirtualMachine vm = VirtualMachine.attach(vmd); 285 try { 286 jvmArch = JVMArch.getJVMArch(vm.getSystemProperties()); 287 } finally { 288 vm.detach(); 289 } 290 } catch (AttachNotSupportedException x) { 291 if (x.getMessage().contains("Unable to attach to 32-bit process")) { //$NON-NLS-1$ 292 jvmArch = JVMArch.BIT32; 293 } else if (x.getMessage().contains("Unable to attach to 64-bit process")) { //$NON-NLS-1$ 294 jvmArch = JVMArch.BIT64; 295 } 296 } 297 break; 298 } 299 } 300 } 301 return jvmArch; 302 } 303 304 private static JVMType getJVMType(String jvmName) { 305 if (JavaVMVersionToolkit.isJRockitJVMName(jvmName)) { 306 return JVMType.JROCKIT; 307 } else if (JavaVMVersionToolkit.isHotspotJVMName(jvmName)) { 308 return JVMType.HOTSPOT; 309 } 310 return JVMType.OTHER; 311 } 312 313 private static boolean isDebug(String stringValue) { 314 return stringValue.toUpperCase().contains("DEBUG"); //$NON-NLS-1$ 315 } 316 317 private static void populateAttachableVMs(Map<Object, DiscoveryEntry> map) { 318 // This used to leak \BaseNamedObjects\hsperfdata_* Section handles on Windows 319 List<VirtualMachineDescriptor> vms = VirtualMachine.list(); 320 if (vms == null) { 321 return; 322 } 323 324 for (VirtualMachineDescriptor vmd : vms) { 325 try { 326 Integer vmid = Integer.valueOf(vmd.id()); 327 if (!map.containsKey(vmid)) { 328 BrowserAttachPlugin.getPluginLogger().finest("Local attach resolving PID " + vmid); //$NON-NLS-1$ 329 // Check if we already have a descriptor *first* to avoid unnecessary attach which may leak handles 330 DiscoveryEntry connDesc = last.get(vmid); 331 if (connDesc == null) { 332 connDesc = createAttachableJvmDescriptor(vmd); 333 } 334 335 if (connDesc != null && !connDesc.getServerDescriptor().getJvmInfo().isUnconnectable()) { 336 map.put(vmid, connDesc); 337 } 338 } 339 } catch (NumberFormatException e) { 340 // do not support vmid different than pid 341 } 342 } 343 } 344 345 private static DiscoveryEntry createAttachableJvmDescriptor(VirtualMachineDescriptor vmd) { 346 try { 347 // Enforce a timeout here to ensure we don't block forever if the JVM is busy or suspended. See JMC-5398. 348 ExecutorService service = Executors.newSingleThreadExecutor(); 349 Future<DiscoveryEntry> future = service.submit(new Callable<DiscoveryEntry>() { 350 @Override 351 public DiscoveryEntry call() throws Exception { 352 DiscoveryEntry connDesc = null; 353 Connectable connectable; 354 boolean isDebug = false; 355 JVMType jvmType = JVMType.OTHER; 356 JVMArch jvmArch = JVMArch.OTHER; 357 String address = null; 358 String version = null; 359 String javaArgs = null; 360 String jvmArgs = null; 361 String jvmVersion = null; 362 VirtualMachine vm = null; 363 try { 364 // Attach creates one process handle on Windows. 365 // This leaks one thread handle due to Sun bug in j2se/src/windows/native/sun/tools/attach/WindowsVirtualMachine.c 366 vm = VirtualMachine.attach(vmd); 367 connectable = ATTACHABLE; 368 // This leaks one thread handle due to Sun bug in j2se/src/windows/native/sun/tools/attach/WindowsVirtualMachine.c 369 Properties props = null; 370 try { 371 props = vm.getSystemProperties(); 372 } catch (IOException e) { 373 BrowserAttachPlugin.getPluginLogger().log(Level.FINER, 374 "Got the following exception message when getting system properties from vm with PID " //$NON-NLS-1$ 375 + vmd + ": " + e.getMessage()); //$NON-NLS-1$ 376 } 377 if (props != null) { 378 String vmName = props.getProperty("java.vm.name"); //$NON-NLS-1$ 379 jvmType = getJVMType(vmName); 380 version = props.getProperty("java.version"); //$NON-NLS-1$ 381 jvmVersion = props.getProperty("java.vm.version"); //$NON-NLS-1$ 382 isDebug = isDebug(jvmVersion); 383 jvmArch = JVMArch.getJVMArch(props); 384 } 385 Properties agentProps = vm.getAgentProperties(); 386 address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP); 387 javaArgs = resolveCommandLine(vm, vmd.displayName(), props, agentProps); 388 jvmArgs = (String) agentProps.get("sun.jvm.args"); //$NON-NLS-1$ 389 } catch (AttachNotSupportedException x) { 390 // Not attachable 391 connectable = NO; 392 } finally { 393 // Always detach. Releases one process handle on Windows. 394 vm.detach(); 395 } 396 if (connectable.isAttachable()) { 397 connDesc = createDescriptor(javaArgs, jvmArgs, Integer.parseInt(vmd.id()), connectable, jvmType, jvmArch, 398 address, version, isDebug); 399 } 400 BrowserAttachPlugin.getPluginLogger().info("Done resolving PID " + vmd); //$NON-NLS-1$ 401 return connDesc; 402 } 403 }); 404 return future.get(TIMEOUT_THRESHOLD, TimeUnit.SECONDS); 405 } catch (Throwable t) { 406 // Serious problem for this JVM, let's skip this one. 407 if (!isErrorMessageSent) { 408 BrowserAttachPlugin.getPluginLogger().log(Level.FINER, 409 "Scanning using attach/getAgentProperties failed on " //$NON-NLS-1$ 410 + vmd 411 + ". This message will only be printed once, so errors for subsequent PIDs will not be logged...", //$NON-NLS-1$ 412 t); 413 isErrorMessageSent = true; 414 } 415 return null; 416 } 417 } 418 419 private static MonitoredHost getMonitoredHost() { 420 try { 421 return MonitoredHost.getMonitoredHost(new HostIdentifier((String) null)); 422 } catch (MonitorException e) { 423 throw new InternalError(e.getMessage()); 424 } catch (URISyntaxException e) { 425 throw new InternalError(e.getMessage()); 426 } 427 } 428 429 // Workaround to resolve command line when Eclipse is launched with -vm ... jvm.dll 430 private static String resolveCommandLine( 431 VirtualMachine vm, String displayName, Properties vmProps, Properties agentProps) { 432 if (isValidDisplayName(displayName)) { 433 return displayName; 434 } 435 if (vmProps != null) { 436 String eclipseVmargs = vmProps.getProperty("eclipse.vmargs"); //$NON-NLS-1$ 437 if (eclipseVmargs != null) { 438 String[] parts = eclipseVmargs.split("java.class.path="); //$NON-NLS-1$ 439 return parts.length == 2 ? parts[1] : eclipseVmargs; 440 } 441 } 442 if (agentProps != null) { 443 String jvmCmd = (String) agentProps.get(JAVA_COMMAND_PROP); 444 if (jvmCmd == null || jvmCmd.length() == 0) { 445 jvmCmd = (String) agentProps.get(JVM_ARGS_PROP); 446 } 447 if (jvmCmd == null || jvmCmd.length() == 0) { 448 jvmCmd = (String) agentProps.get(JVM_FLAGS_PROP); 449 } 450 if (jvmCmd != null && jvmCmd.length() > 0) { 451 return jvmCmd; 452 } 453 } 454 return displayName; 455 } 456 457 private static boolean isValidDisplayName(String displayName) { 458 return displayName != null && !displayName.equals("") && !displayName.equals("Unknown"); //$NON-NLS-1$ //$NON-NLS-2$ 459 } 460 461 private static DiscoveryEntry createDescriptor( 462 String javaCommand, String jvmArgs, int pid, Connectable connectable, JVMType type, JVMArch arch, 463 String address, String version, boolean isDebug) { 464 JVMDescriptor jvmInfo = new JVMDescriptor(version, type, arch, javaCommand, jvmArgs, pid, isDebug, connectable); 465 LocalConnectionDescriptor lcd = new LocalConnectionDescriptor(pid, address, connectable == ATTACHABLE); 466 String guid = "Local-[PID:" + pid + ", seq:" + (SEQ_NUMBER++) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 467 IServerDescriptor sd = IServerDescriptor.create(guid, null, jvmInfo); 468 return new DiscoveryEntry(sd, lcd); 469 } 470 471 /** 472 * @return descriptors for all discovered JVM's. 473 */ 474 public static synchronized DiscoveryEntry[] getAttachableJVMs() { 475 return getLocalConnections(); 476 } 477 478 /** 479 * Runs a jcmd in the specified HotSpot. 480 * 481 * @param pid 482 * @param command 483 * @return the result from running the jcmd. 484 * @throws AttachNotSupportedException 485 * @throws IOException 486 * @throws AgentLoadException 487 */ 488 public static String executeCommandForPid(String pid, String command) 489 throws AttachNotSupportedException, IOException, AgentLoadException { 490 return executeCommandForPid(pid, command, false); 491 } 492 493 /** 494 * Runs a jcmd in the specified HotSpot. 495 * 496 * @param pid 497 * @param command 498 * @param getCausingInformation 499 * @return the result from running the jcmd. 500 * @throws AttachNotSupportedException 501 * @throws IOException 502 * @throws AgentLoadException 503 */ 504 public static String executeCommandForPid(String pid, String command, boolean getCausingInformation) 505 throws AttachNotSupportedException, IOException, AgentLoadException { 506 VirtualMachine vm = VirtualMachine.attach(pid); 507 String result = executeCommandForPid(vm, pid, command, getCausingInformation); 508 vm.detach(); 509 return result; 510 } 511 512 /** 513 * Runs a jcmd in the specified HotSpot. 514 * 515 * @param vm 516 * @param pid 517 * @param command 518 * @return the result from running the jcmd. 519 * @throws AttachNotSupportedException 520 * @throws IOException 521 * @throws AgentLoadException 522 */ 523 public static String executeCommandForPid(VirtualMachine vm, String pid, String command) 524 throws AttachNotSupportedException, IOException, AgentLoadException { 525 return executeCommandForPid(vm, pid, command, false); 526 } 527 528 /** 529 * Runs a jcmd in the specified HotSpot. 530 * 531 * @param vm 532 * @param pid 533 * @param command 534 * @param throwCausingException 535 * If the target cause of an eventual exception should be returned as the result. 536 * @return the result from running the jcmd. 537 * @throws AttachNotSupportedException 538 * @throws IOException 539 * @throws AgentLoadException 540 */ 541 public static String executeCommandForPid( 542 VirtualMachine vm, String pid, String command, boolean throwCausingException) 543 throws AttachNotSupportedException, IOException, AgentLoadException { 544 HotSpotVirtualMachine hvm = (HotSpotVirtualMachine) vm; 545 InputStream in = ExecuteTunnler.execute(hvm, "jcmd", new Object[] {command}, throwCausingException); //$NON-NLS-1$ 546 byte b[] = new byte[256]; 547 int n; 548 StringBuffer buf = new StringBuffer(); 549 do { 550 n = in.read(b); 551 if (n > 0) { 552 String s = new String(b, 0, n, "UTF-8"); //$NON-NLS-1$ 553 buf.append(s); 554 } 555 } while (n > 0); 556 557 try { 558 in.close(); 559 } catch (IOException ex) { 560 /* Don't care */ 561 } 562 return buf.toString(); 563 } 564 565 /** 566 * @param pid 567 * the process ID of the process to communicate with. 568 * @return the JMXServiceURL for communicating with the in memory agent having the specified 569 * pid. 570 * @throws IOException 571 */ 572 public static JMXServiceURL getInMemoryURLFromPID(int pid) throws IOException { 573 JMXServiceURL inMemURL = null; 574 String address = AttachToolkit.importFromPid(pid); 575 if (address != null) { 576 inMemURL = new JMXServiceURL(address); 577 } 578 return inMemURL; 579 } 580 }