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