1 /*
   2  * Copyright (c) 2005, 2011, 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 sun.tools.attach;
  27 
  28 import com.sun.tools.attach.VirtualMachine;
  29 import com.sun.tools.attach.AgentLoadException;
  30 import com.sun.tools.attach.AgentInitializationException;
  31 import com.sun.tools.attach.spi.AttachProvider;
  32 
  33 import java.io.InputStream;
  34 import java.io.IOException;
  35 import java.util.Properties;
  36 import java.util.Map;
  37 
  38 /*
  39  * The HotSpot implementation of com.sun.tools.attach.VirtualMachine.
  40  */
  41 
  42 public abstract class HotSpotVirtualMachine extends VirtualMachine {
  43 
  44     HotSpotVirtualMachine(AttachProvider provider, String id) {
  45         super(provider, id);
  46     }
  47 
  48     /*
  49      * Load agent library
  50      * If isAbsolute is true then the agent library is the absolute path
  51      * to the library and thus will not be expanded in the target VM.
  52      * if isAbsolute is false then the agent library is just a library
  53      * name and it will be expended in the target VM.
  54      */
  55     private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options)
  56         throws AgentLoadException, AgentInitializationException, IOException
  57     {
  58         InputStream in = execute("load",
  59                                  agentLibrary,
  60                                  isAbsolute ? "true" : "false",
  61                                  options);
  62         try {
  63             int result = readInt(in);
  64             if (result != 0) {
  65                 throw new AgentInitializationException("Agent_OnAttach failed", result);
  66             }
  67         } finally {
  68             in.close();
  69 
  70         }
  71     }
  72 
  73     /*
  74      * Load agent library - library name will be expanded in target VM
  75      */
  76     public void loadAgentLibrary(String agentLibrary, String options)
  77         throws AgentLoadException, AgentInitializationException, IOException
  78     {
  79         loadAgentLibrary(agentLibrary, false, options);
  80     }
  81 
  82     /*
  83      * Load agent - absolute path of library provided to target VM
  84      */
  85     public void loadAgentPath(String agentLibrary, String options)
  86         throws AgentLoadException, AgentInitializationException, IOException
  87     {
  88         loadAgentLibrary(agentLibrary, true, options);
  89     }
  90 
  91     /*
  92      * Load JPLIS agent which will load the agent JAR file and invoke
  93      * the agentmain method.
  94      */
  95     public void loadAgent(String agent, String options)
  96         throws AgentLoadException, AgentInitializationException, IOException
  97     {
  98         String args = agent;
  99         if (options != null) {
 100             args = args + "=" + options;
 101         }
 102         try {
 103             loadAgentLibrary("instrument", args);
 104         } catch (AgentLoadException x) {
 105             throw new InternalError("instrument library is missing in target VM", x);
 106         } catch (AgentInitializationException x) {
 107             /*
 108              * Translate interesting errors into the right exception and
 109              * message (FIXME: create a better interface to the instrument
 110              * implementation so this isn't necessary)
 111              */
 112             int rc = x.returnValue();
 113             switch (rc) {
 114                 case JNI_ENOMEM:
 115                     throw new AgentLoadException("Insuffient memory");
 116                 case ATTACH_ERROR_BADJAR:
 117                     throw new AgentLoadException("Agent JAR not found or no Agent-Class attribute");
 118                 case ATTACH_ERROR_NOTONCP:
 119                     throw new AgentLoadException("Unable to add JAR file to system class path");
 120                 case ATTACH_ERROR_STARTFAIL:
 121                     throw new AgentInitializationException("Agent JAR loaded but agent failed to initialize");
 122                 default :
 123                     throw new AgentLoadException("Failed to load agent - unknown reason: " + rc);
 124             }
 125         }
 126     }
 127 
 128     /*
 129      * The possible errors returned by JPLIS's agentmain
 130      */
 131     private static final int JNI_ENOMEM                 = -4;
 132     private static final int ATTACH_ERROR_BADJAR        = 100;
 133     private static final int ATTACH_ERROR_NOTONCP       = 101;
 134     private static final int ATTACH_ERROR_STARTFAIL     = 102;
 135 
 136 
 137     /*
 138      * Send "properties" command to target VM
 139      */
 140     public Properties getSystemProperties() throws IOException {
 141         InputStream in = null;
 142         Properties props = new Properties();
 143         try {
 144             in = executeCommand("properties");
 145             props.load(in);
 146         } finally {
 147             if (in != null) in.close();
 148         }
 149         return props;
 150     }
 151 
 152     public Properties getAgentProperties() throws IOException {
 153         InputStream in = null;
 154         Properties props = new Properties();
 155         try {
 156             in = executeCommand("agentProperties");
 157             props.load(in);
 158         } finally {
 159             if (in != null) in.close();
 160         }
 161         return props;
 162     }
 163 
 164     // --- HotSpot specific methods ---
 165 
 166     // same as SIGQUIT
 167     public void localDataDump() throws IOException {
 168         executeCommand("datadump").close();
 169     }
 170 
 171     // Remote ctrl-break. The output of the ctrl-break actions can
 172     // be read from the input stream.
 173     public InputStream remoteDataDump(Object ... args) throws IOException {
 174         return executeCommand("threaddump", args);
 175     }
 176 
 177     // Remote heap dump. The output (error message) can be read from the
 178     // returned input stream.
 179     public InputStream dumpHeap(Object ... args) throws IOException {
 180         return executeCommand("dumpheap", args);
 181     }
 182 
 183     // Heap histogram (heap inspection in HotSpot)
 184     public InputStream heapHisto(Object ... args) throws IOException {
 185         return executeCommand("inspectheap", args);
 186     }
 187 
 188     // set JVM command line flag
 189     public InputStream setFlag(String name, String value) throws IOException {
 190         return executeCommand("setflag", name, value);
 191     }
 192 
 193     // print command line flag
 194     public InputStream printFlag(String name) throws IOException {
 195         return executeCommand("printflag", name);
 196     }
 197 
 198     public InputStream executeJCmd(String command) throws IOException {
 199         return executeCommand("jcmd", command);
 200     }
 201 
 202     // -- Supporting methods
 203 
 204 
 205     /*
 206      * Execute the given command in the target VM - specific platform
 207      * implementation must implement this.
 208      */
 209     abstract InputStream execute(String cmd, Object ... args)
 210         throws AgentLoadException, IOException;
 211 
 212     /*
 213      * Convenience method for simple commands
 214      */
 215     private InputStream executeCommand(String cmd, Object ... args) throws IOException {
 216         try {
 217             return execute(cmd, args);
 218         } catch (AgentLoadException x) {
 219             throw new InternalError("Should not get here", x);
 220         }
 221     }
 222 
 223 
 224     /*
 225      * Utility method to read an 'int' from the input stream. Ideally
 226      * we should be using java.util.Scanner here but this implementation
 227      * guarantees not to read ahead.
 228      */
 229     int readInt(InputStream in) throws IOException {
 230         StringBuilder sb = new StringBuilder();
 231 
 232         // read to \n or EOF
 233         int n;
 234         byte buf[] = new byte[1];
 235         do {
 236             n = in.read(buf, 0, 1);
 237             if (n > 0) {
 238                 char c = (char)buf[0];
 239                 if (c == '\n') {
 240                     break;                  // EOL found
 241                 } else {
 242                     sb.append(c);
 243                 }
 244             }
 245         } while (n > 0);
 246 
 247         if (sb.length() == 0) {
 248             throw new IOException("Premature EOF");
 249         }
 250 
 251         int value;
 252         try {
 253             value = Integer.parseInt(sb.toString());
 254         } catch (NumberFormatException x) {
 255             throw new IOException("Non-numeric value found - int expected");
 256         }
 257         return value;
 258     }
 259 
 260     // -- attach timeout support
 261 
 262     private static long defaultAttachTimeout = 5000;
 263     private volatile long attachTimeout;
 264 
 265     /*
 266      * Return attach timeout based on the value of the sun.tools.attach.attachTimeout
 267      * property, or the default timeout if the property is not set to a positive
 268      * value.
 269      */
 270     long attachTimeout() {
 271         if (attachTimeout == 0) {
 272             synchronized(this) {
 273                 if (attachTimeout == 0) {
 274                     try {
 275                         String s =
 276                             System.getProperty("sun.tools.attach.attachTimeout");
 277                         attachTimeout = Long.parseLong(s);
 278                     } catch (SecurityException se) {
 279                     } catch (NumberFormatException ne) {
 280                     }
 281                     if (attachTimeout <= 0) {
 282                        attachTimeout = defaultAttachTimeout;
 283                     }
 284                 }
 285             }
 286         }
 287         return attachTimeout;
 288     }
 289 }