1 /*
   2  * Copyright (c) 2002, 2013, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.tools;
  26 
  27 import java.io.PrintStream;
  28 
  29 import sun.jvm.hotspot.HotSpotAgent;
  30 import sun.jvm.hotspot.SALauncher;
  31 import sun.jvm.hotspot.debugger.DebuggerException;
  32 import sun.jvm.hotspot.debugger.JVMDebugger;
  33 import sun.jvm.hotspot.runtime.VM;
  34 
  35 // generic command line or GUI tool.
  36 // override run & code main as shown below.
  37 
  38 public abstract class Tool implements Runnable {
  39    private HotSpotAgent agent;
  40    private JVMDebugger jvmDebugger;
  41    private int debugeeType;
  42 
  43    // debugeeType is one of constants below
  44    protected static final int DEBUGEE_PID    = 0;
  45    protected static final int DEBUGEE_CORE   = 1;
  46    protected static final int DEBUGEE_REMOTE = 2;
  47 
  48    public Tool() {
  49    }
  50 
  51    public Tool(JVMDebugger d) {
  52       jvmDebugger = d;
  53    }
  54 
  55    public String getName() {
  56       return getClass().getName();
  57    }
  58 
  59    protected boolean needsJavaPrefix() {
  60       return true;
  61    }
  62 
  63    protected void setAgent(HotSpotAgent a) {
  64       agent = a;
  65    }
  66 
  67    protected void setDebugeeType(int dt) {
  68       debugeeType = dt;
  69    }
  70 
  71    protected HotSpotAgent getAgent() {
  72       return agent;
  73    }
  74 
  75    protected int getDebugeeType() {
  76       return debugeeType;
  77    }
  78 
  79    protected void printUsage() {
  80       String name = null;
  81       if (needsJavaPrefix()) {
  82          name = "java " + getName();
  83       } else {
  84          name = getName();
  85       }
  86       System.out.println("Usage: " + name + " [option] <pid>");
  87       System.out.println("\t\t(to connect to a live java process)");
  88       System.out.println("   or " + name + " [option] <executable> <core>");
  89       System.out.println("\t\t(to connect to a core file)");
  90       System.out.println("   or " + name + " [option] [server_id@]<remote server IP or hostname>");
  91       System.out.println("\t\t(to connect to a remote debug server)");
  92       System.out.println();
  93       System.out.println("where option must be one of:");
  94       printFlagsUsage();
  95    }
  96 
  97    protected void printFlagsUsage() {
  98        System.out.println("    -h | -help\tto print this help message");
  99    }
 100 
 101    protected void usage() {
 102       StackWalker walker = StackWalker.getInstance(
 103                                      StackWalker.Option.RETAIN_CLASS_REFERENCE);
 104       Boolean throughSALauncher = walker.walk(s ->
 105                                s.map(StackWalker.StackFrame::getDeclaringClass)
 106                                 .anyMatch(c -> c.equals(SALauncher.class)));
 107       if (throughSALauncher.booleanValue()) {
 108           String toolName = this.getClass().getSimpleName().toLowerCase();
 109           SALauncher.toolHelp(toolName);
 110       } else {
 111           printUsage();
 112       }
 113    }
 114 
 115    /*
 116       Derived class main should be of the following form:
 117 
 118       public static void main(String[] args) {
 119          <derived class> obj = new <derived class>;
 120          obj.execute(args);
 121       }
 122 
 123    */
 124 
 125    protected void execute(String[] args) {
 126        int returnStatus = 1;
 127 
 128        try {
 129            returnStatus = start(args);
 130        } finally {
 131            stop();
 132        }
 133 
 134        // Exit with 0 or 1
 135        System.exit(returnStatus);
 136    }
 137 
 138    public void stop() {
 139       if (agent != null) {
 140          agent.detach();
 141       }
 142    }
 143 
 144    private int start(String[] args) {
 145 
 146       if ((args.length < 1) || (args.length > 2)) {
 147          usage();
 148          return 1;
 149       }
 150 
 151       // Attempt to handle -h or -help or some invalid flag
 152       if (args[0].startsWith("-h")) {
 153           usage();
 154           return 0;
 155       } else if (args[0].startsWith("-")) {
 156           usage();
 157           return 1;
 158       }
 159 
 160       PrintStream err = System.err;
 161       PrintStream out = System.out;
 162 
 163       int pid = 0;
 164       String coreFileName   = null;
 165       String executableName = null;
 166       String remoteServer   = null;
 167 
 168       switch (args.length) {
 169         case 1:
 170            try {
 171               pid = Integer.parseInt(args[0]);
 172               debugeeType = DEBUGEE_PID;
 173            } catch (NumberFormatException e) {
 174               // try remote server
 175               remoteServer = args[0];
 176               debugeeType  = DEBUGEE_REMOTE;
 177            }
 178            break;
 179 
 180         case 2:
 181            executableName = args[0];
 182            coreFileName   = args[1];
 183            debugeeType    = DEBUGEE_CORE;
 184            break;
 185 
 186         default:
 187            usage();
 188            return 1;
 189       }
 190 
 191       agent = new HotSpotAgent();
 192       try {
 193         switch (debugeeType) {
 194           case DEBUGEE_PID:
 195              out.println("Attaching to process ID " + pid + ", please wait...");
 196              agent.attach(pid);
 197              break;
 198 
 199           case DEBUGEE_CORE:
 200              out.println("Attaching to core " + coreFileName +
 201                          " from executable " + executableName + ", please wait...");
 202              agent.attach(executableName, coreFileName);
 203              break;
 204 
 205           case DEBUGEE_REMOTE:
 206              out.println("Attaching to remote server " + remoteServer + ", please wait...");
 207              agent.attach(remoteServer);
 208              break;
 209         }
 210       }
 211       catch (DebuggerException e) {
 212         switch (debugeeType) {
 213           case DEBUGEE_PID:
 214              err.print("Error attaching to process: ");
 215              break;
 216 
 217           case DEBUGEE_CORE:
 218              err.print("Error attaching to core file: ");
 219              break;
 220 
 221           case DEBUGEE_REMOTE:
 222              err.print("Error attaching to remote server: ");
 223              break;
 224         }
 225         if (e.getMessage() != null) {
 226           err.println(e.getMessage());
 227           e.printStackTrace();
 228         }
 229         err.println();
 230         return 1;
 231       }
 232 
 233       out.println("Debugger attached successfully.");
 234       startInternal();
 235       return 0;
 236    }
 237 
 238    // When using an existing JVMDebugger.
 239    public void start() {
 240 
 241       if (jvmDebugger == null) {
 242          throw new RuntimeException("Tool.start() called with no JVMDebugger set.");
 243       }
 244       agent = new HotSpotAgent();
 245       agent.attach(jvmDebugger);
 246       startInternal();
 247    }
 248 
 249    // Remains of the start mechanism, common to both start methods.
 250    private void startInternal() {
 251 
 252       PrintStream out = System.out;
 253       VM vm = VM.getVM();
 254       if (vm.isCore()) {
 255         out.println("Core build detected.");
 256       } else if (vm.isClientCompiler()) {
 257         out.println("Client compiler detected.");
 258       } else if (vm.isServerCompiler()) {
 259         out.println("Server compiler detected.");
 260       } else {
 261         throw new RuntimeException("Fatal error: "
 262             + "should have been able to detect core/C1/C2 build");
 263       }
 264 
 265       String version = vm.getVMRelease();
 266       if (version != null) {
 267         out.print("JVM version is ");
 268         out.println(version);
 269       }
 270 
 271       run();
 272    }
 273 }