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 }