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.
   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;
  26 
  27 import sun.jvm.hotspot.*;
  28 import sun.jvm.hotspot.debugger.*;
  29 
  30 import java.io.*;
  31 import java.util.*;
  32 
  33 public class CLHSDB {
  34 
  35     public CLHSDB(JVMDebugger d) {
  36         jvmDebugger = d;
  37     }
  38 
  39     public static void main(String[] args) {
  40         new CLHSDB(args).run();
  41     }
  42 
  43     public void run() {
  44         // If jvmDebugger is already set, we have been given a JVMDebugger.
  45         // Otherwise, if pidText != null we are supposed to attach to it.
  46         // Finally, if execPath != null, it is the path of a jdk/bin/java
  47         // and coreFilename is the pathname of a core file we are
  48         // supposed to attach to.
  49 
  50         agent = new HotSpotAgent();
  51 
  52         Runtime.getRuntime().addShutdownHook(new java.lang.Thread() {
  53                 public void run() {
  54                     detachDebugger();
  55                 }
  56             });
  57 
  58         if (jvmDebugger != null) {
  59             attachDebugger(jvmDebugger);
  60         } else if (pidText != null) {
  61             attachDebugger(pidText);
  62         } else if (execPath != null) {
  63             attachDebugger(execPath, coreFilename);
  64         }
  65 
  66 
  67         CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() {
  68                 public HotSpotAgent getAgent() {
  69                     return agent;
  70                 }
  71                 public boolean isAttached() {
  72                     return attached;
  73                 }
  74                 public void attach(String pid) {
  75                     attachDebugger(pid);
  76                 }
  77                 public void attach(String java, String core) {
  78                     attachDebugger(java, core);
  79                 }
  80                 public void detach() {
  81                     detachDebugger();
  82                 }
  83                 public void reattach() {
  84                     if (attached) {
  85                         detachDebugger();
  86                     }
  87                     if (pidText != null) {
  88                         attach(pidText);
  89                     } else {
  90                         attach(execPath, coreFilename);
  91                     }
  92                 }
  93             };
  94 
  95 
  96         BufferedReader in =
  97             new BufferedReader(new InputStreamReader(System.in));
  98         CommandProcessor cp = new CommandProcessor(di, in, System.out, System.err);
  99         cp.run(true);
 100 
 101     }
 102 
 103     //--------------------------------------------------------------------------------
 104     // Internals only below this point
 105     //
 106     private HotSpotAgent agent;
 107     private JVMDebugger jvmDebugger;
 108     private boolean      attached;
 109     // These had to be made data members because they are referenced in inner classes.
 110     private String pidText;
 111     private int pid;
 112     private String execPath;
 113     private String coreFilename;
 114 
 115     private void doUsage() {
 116         System.out.println("Usage:  java CLHSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
 117         System.out.println("           pid:                     attach to the process whose id is 'pid'");
 118         System.out.println("           path-to-java-executable: Debug a core file produced by this program");
 119         System.out.println("           path-to-corefile:        Debug this corefile.  The default is 'core'");
 120         System.out.println("        If no arguments are specified, you can select what to do from the GUI.\n");
 121         HotSpotAgent.showUsage();
 122     }
 123 
 124     private CLHSDB(String[] args) {
 125         switch (args.length) {
 126         case (0):
 127             break;
 128 
 129         case (1):
 130             if (args[0].equals("help") || args[0].equals("-help")) {
 131                 doUsage();
 132                 return;
 133             }
 134             // If all numbers, it is a PID to attach to
 135             // Else, it is a pathname to a .../bin/java for a core file.
 136             try {
 137                 int unused = Integer.parseInt(args[0]);
 138                 // If we get here, we have a PID and not a core file name
 139                 pidText = args[0];
 140             } catch (NumberFormatException e) {
 141                 execPath = args[0];
 142                 coreFilename = "core";
 143             }
 144             break;
 145 
 146         case (2):
 147             execPath = args[0];
 148             coreFilename = args[1];
 149             break;
 150 
 151         default:
 152             System.out.println("HSDB Error: Too many options specified");
 153             doUsage();
 154             return;
 155         }
 156     }
 157 
 158     private void attachDebugger(JVMDebugger d) {
 159         agent.attach(d);
 160         attached = true;
 161      }
 162 
 163     /** NOTE we are in a different thread here than either the main
 164         thread or the Swing/AWT event handler thread, so we must be very
 165         careful when creating or removing widgets */
 166     private void attachDebugger(String pidText) {
 167         try {
 168             this.pidText = pidText;
 169             pid = Integer.parseInt(pidText);
 170         }
 171         catch (NumberFormatException e) {
 172             System.err.print("Unable to parse process ID \"" + pidText + "\".\nPlease enter a number.");
 173         }
 174 
 175         try {
 176             System.err.println("Attaching to process " + pid + ", please wait...");
 177 
 178             // FIXME: display exec'd debugger's output messages during this
 179             // lengthy call
 180             agent.attach(pid);
 181             attached = true;
 182         }
 183         catch (DebuggerException e) {
 184             final String errMsg = formatMessage(e.getMessage(), 80);
 185             System.err.println("Unable to connect to process ID " + pid + ":\n\n" + errMsg);
 186             agent.detach();
 187             e.printStackTrace();
 188             return;
 189         }
 190     }
 191 
 192     /** NOTE we are in a different thread here than either the main
 193         thread or the Swing/AWT event handler thread, so we must be very
 194         careful when creating or removing widgets */
 195     private void attachDebugger(final String executablePath, final String corePath) {
 196         // Try to open this core file
 197         try {
 198             System.err.println("Opening core file, please wait...");
 199 
 200             // FIXME: display exec'd debugger's output messages during this
 201             // lengthy call
 202             agent.attach(executablePath, corePath);
 203             attached = true;
 204         }
 205         catch (DebuggerException e) {
 206             final String errMsg = formatMessage(e.getMessage(), 80);
 207             System.err.println("Unable to open core file\n" + corePath + ":\n\n" + errMsg);
 208             agent.detach();
 209             e.printStackTrace();
 210             return;
 211         }
 212     }
 213 
 214     /** NOTE we are in a different thread here than either the main
 215         thread or the Swing/AWT event handler thread, so we must be very
 216         careful when creating or removing widgets */
 217     private void connect(final String remoteMachineName) {
 218         // Try to open this core file
 219         try {
 220             System.err.println("Connecting to debug server, please wait...");
 221             agent.attach(remoteMachineName);
 222             attached = true;
 223         }
 224         catch (DebuggerException e) {
 225             final String errMsg = formatMessage(e.getMessage(), 80);
 226             System.err.println("Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg);
 227             agent.detach();
 228             e.printStackTrace();
 229             return;
 230         }
 231     }
 232 
 233     private void detachDebugger() {
 234         if (!attached) {
 235             return;
 236         }
 237         agent.detach();
 238         attached = false;
 239     }
 240 
 241     private void detach() {
 242         detachDebugger();
 243     }
 244 
 245     /** Punctuates the given string with \n's where necessary to not
 246         exceed the given number of characters per line. Strips
 247         extraneous whitespace. */
 248     private String formatMessage(String message, int charsPerLine) {
 249         StringBuffer buf = new StringBuffer(message.length());
 250         StringTokenizer tokenizer = new StringTokenizer(message);
 251         int curLineLength = 0;
 252         while (tokenizer.hasMoreTokens()) {
 253             String tok = tokenizer.nextToken();
 254             if (curLineLength + tok.length() > charsPerLine) {
 255                 buf.append('\n');
 256                 curLineLength = 0;
 257             } else {
 258                 if (curLineLength != 0) {
 259                     buf.append(' ');
 260                     ++curLineLength;
 261                 }
 262             }
 263             buf.append(tok);
 264             curLineLength += tok.length();
 265         }
 266         return buf.toString();
 267     }
 268 }