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);
 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 }