1 /*
   2  * Copyright (c) 2005, 2006, 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     // -- Supporting methods
 199 
 200 
 201     /*
 202      * Execute the given command in the target VM - specific platform
 203      * implementation must implement this.
 204      */
 205     abstract InputStream execute(String cmd, Object ... args)
 206         throws AgentLoadException, IOException;
 207 
 208     /*
 209      * Convenience method for simple commands
 210      */
 211     private InputStream executeCommand(String cmd, Object ... args) throws IOException {
 212         try {
 213             return execute(cmd, args);
 214         } catch (AgentLoadException x) {
 215             throw new InternalError("Should not get here", x);
 216         }
 217     }
 218 
 219 
 220     /*
 221      * Utility method to read an 'int' from the input stream. Ideally
 222      * we should be using java.util.Scanner here but this implementation
 223      * guarantees not to read ahead.
 224      */
 225     int readInt(InputStream in) throws IOException {
 226         StringBuilder sb = new StringBuilder();
 227 
 228         // read to \n or EOF
 229         int n;
 230         byte buf[] = new byte[1];
 231         do {
 232             n = in.read(buf, 0, 1);
 233             if (n > 0) {
 234                 char c = (char)buf[0];
 235                 if (c == '\n') {
 236                     break;                  // EOL found
 237                 } else {
 238                     sb.append(c);
 239                 }
 240             }
 241         } while (n > 0);
 242 
 243         if (sb.length() == 0) {
 244             throw new IOException("Premature EOF");
 245         }
 246 
 247         int value;
 248         try {
 249             value = Integer.parseInt(sb.toString());
 250         } catch (NumberFormatException x) {
 251             throw new IOException("Non-numeric value found - int expected");
 252         }
 253         return value;
 254     }
 255 
 256     // -- attach timeout support
 257 
 258     private static long defaultAttachTimeout = 5000;
 259     private volatile long attachTimeout;
 260 
 261     /*
 262      * Return attach timeout based on the value of the sun.tools.attach.attachTimeout
 263      * property, or the default timeout if the property is not set to a positive
 264      * value.
 265      */
 266     long attachTimeout() {
 267         if (attachTimeout == 0) {
 268             synchronized(this) {
 269                 if (attachTimeout == 0) {
 270                     try {
 271                         String s =
 272                             System.getProperty("sun.tools.attach.attachTimeout");
 273                         attachTimeout = Long.parseLong(s);
 274                     } catch (SecurityException se) {
 275                     } catch (NumberFormatException ne) {
 276                     }
 277                     if (attachTimeout <= 0) {
 278                        attachTimeout = defaultAttachTimeout;
 279                     }
 280                 }
 281             }
 282         }
 283         return attachTimeout;
 284     }
 285 }