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     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             e.printStackTrace();
 174             return;
 175         }
 176     }
 177 
 178     /** NOTE we are in a different thread here than either the main
 179         thread or the Swing/AWT event handler thread, so we must be very
 180         careful when creating or removing widgets */
 181     private void attachDebugger(final String executablePath, final String corePath) {
 182         // Try to open this core file
 183         try {
 184             System.err.println("Opening core file, please wait...");
 185 
 186             // FIXME: display exec'd debugger's output messages during this
 187             // lengthy call
 188             agent.attach(executablePath, corePath);
 189             attached = true;
 190         }
 191         catch (DebuggerException e) {
 192             final String errMsg = formatMessage(e.getMessage(), 80);
 193             System.err.println("Unable to open core file\n" + corePath + ":\n\n" + errMsg);
 194             agent.detach();
 195             e.printStackTrace();
 196             return;
 197         }
 198     }
 199 
 200     /** NOTE we are in a different thread here than either the main
 201         thread or the Swing/AWT event handler thread, so we must be very
 202         careful when creating or removing widgets */
 203     private void connect(final String remoteMachineName) {
 204         // Try to open this core file
 205         try {
 206             System.err.println("Connecting to debug server, please wait...");
 207             agent.attach(remoteMachineName);
 208             attached = true;
 209         }
 210         catch (DebuggerException e) {
 211             final String errMsg = formatMessage(e.getMessage(), 80);
 212             System.err.println("Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg);
 213             agent.detach();
 214             e.printStackTrace();
 215             return;
 216         }
 217     }
 218 
 219     private void detachDebugger() {
 220         if (!attached) {
 221             return;
 222         }
 223         agent.detach();
 224         attached = false;
 225     }
 226 
 227     private void detach() {
 228         detachDebugger();
 229     }
 230 
 231     /** Punctuates the given string with \n's where necessary to not
 232         exceed the given number of characters per line. Strips
 233         extraneous whitespace. */
 234     private String formatMessage(String message, int charsPerLine) {
 235         StringBuffer buf = new StringBuffer(message.length());
 236         StringTokenizer tokenizer = new StringTokenizer(message);
 237         int curLineLength = 0;
 238         while (tokenizer.hasMoreTokens()) {
 239             String tok = tokenizer.nextToken();
 240             if (curLineLength + tok.length() > charsPerLine) {
 241                 buf.append('\n');
 242                 curLineLength = 0;
 243             } else {
 244                 if (curLineLength != 0) {
 245                     buf.append(' ');
 246                     ++curLineLength;
 247                 }
 248             }
 249             buf.append(tok);
 250             curLineLength += tok.length();
 251         }
 252         return buf.toString();
 253     }
 254 }