/*
 * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

package sun.jvm.hotspot;

import sun.jvm.hotspot.*;
import sun.jvm.hotspot.debugger.*;

import java.io.*;
import java.util.*;

public class CLHSDB {

    public CLHSDB(JVMDebugger d) {
        jvmDebugger = d;
    }

    public static void main(String[] args) {
        new CLHSDB(args).run();
    }

    public void run() {
        // If jvmDebugger is already set, we have been given a JVMDebugger.
        // Otherwise, if pidText != null we are supposed to attach to it.
        // Finally, if execPath != null, it is the path of a jdk/bin/java
        // and coreFilename is the pathname of a core file we are
        // supposed to attach to.

        agent = new HotSpotAgent();

        Runtime.getRuntime().addShutdownHook(new java.lang.Thread() {
                public void run() {
                    detachDebugger();
                }
            });

        if (jvmDebugger != null) {
            attachDebugger(jvmDebugger);
        } else if (pidText != null) {
            attachDebugger(pidText);
        } else if (execPath != null) {
            attachDebugger(execPath, coreFilename);
        }


        CommandProcessor.DebuggerInterface di = new CommandProcessor.DebuggerInterface() {
                public HotSpotAgent getAgent() {
                    return agent;
                }
                public boolean isAttached() {
                    return attached;
                }
                public void attach(String pid) {
                    attachDebugger(pid);
                }
                public void attach(String java, String core) {
                    attachDebugger(java, core);
                }
                public void detach() {
                    detachDebugger();
                }
                public void reattach() {
                    if (attached) {
                        detachDebugger();
                    }
                    if (pidText != null) {
                        attach(pidText);
                    } else {
                        attach(execPath, coreFilename);
                    }
                }
            };


        BufferedReader in =
            new BufferedReader(new InputStreamReader(System.in));
        CommandProcessor cp = new CommandProcessor(di, in, System.out, System.err);
        cp.run(true);

    }

    //--------------------------------------------------------------------------------
    // Internals only below this point
    //
    private HotSpotAgent agent;
    private JVMDebugger jvmDebugger;
    private boolean      attached;
    // These had to be made data members because they are referenced in inner classes.
    private String pidText;
    private int pid;
    private String execPath;
    private String coreFilename;

    private void doUsage() {
        System.out.println("Usage:  java CLHSDB [[pid] | [path-to-java-executable [path-to-corefile]] | help ]");
        System.out.println("           pid:                     attach to the process whose id is 'pid'");
        System.out.println("           path-to-java-executable: Debug a core file produced by this program");
        System.out.println("           path-to-corefile:        Debug this corefile.  The default is 'core'");
        System.out.println("        If no arguments are specified, you can select what to do from the GUI.\n");
        HotSpotAgent.showUsage();
    }

    private CLHSDB(String[] args) {
        switch (args.length) {
        case (0):
            break;

        case (1):
            if (args[0].equals("help") || args[0].equals("-help")) {
                doUsage();
                return;
            }
            // If all numbers, it is a PID to attach to
            // Else, it is a pathname to a .../bin/java for a core file.
            try {
                int unused = Integer.parseInt(args[0]);
                // If we get here, we have a PID and not a core file name
                pidText = args[0];
            } catch (NumberFormatException e) {
                execPath = args[0];
                coreFilename = "core";
            }
            break;

        case (2):
            execPath = args[0];
            coreFilename = args[1];
            break;

        default:
            System.out.println("HSDB Error: Too many options specified");
            doUsage();
            return;
        }
    }

    private void attachDebugger(JVMDebugger d) {
        agent.attach(d);
        attached = true;
     }

    
NOTE we are in a different thread here than either the main thread or the Swing/AWT event handler thread, so we must be very careful when creating or removing widgets
/** NOTE we are in a different thread here than either the main thread or the Swing/AWT event handler thread, so we must be very careful when creating or removing widgets */
private void attachDebugger(String pidText) { try { this.pidText = pidText; pid = Integer.parseInt(pidText); } catch (NumberFormatException e) { System.err.print("Unable to parse process ID \"" + pidText + "\".\nPlease enter a number."); } try { System.err.println("Attaching to process " + pid + ", please wait..."); // FIXME: display exec'd debugger's output messages during this // lengthy call agent.attach(pid); attached = true; } catch (DebuggerException e) { final String errMsg = formatMessage(e.getMessage(), 80); System.err.println("Unable to connect to process ID " + pid + ":\n\n" + errMsg); agent.detach(); e.printStackTrace(); return; } }
NOTE we are in a different thread here than either the main thread or the Swing/AWT event handler thread, so we must be very careful when creating or removing widgets
/** NOTE we are in a different thread here than either the main thread or the Swing/AWT event handler thread, so we must be very careful when creating or removing widgets */
private void attachDebugger(final String executablePath, final String corePath) { // Try to open this core file try { System.out.println("Opening core file, please wait..."); // FIXME: display exec'd debugger's output messages during this // lengthy call agent.attach(executablePath, corePath); attached = true; } catch (DebuggerException e) { final String errMsg = formatMessage(e.getMessage(), 80); System.err.println("Unable to open core file\n" + corePath + ":\n\n" + errMsg); agent.detach(); e.printStackTrace(); return; } }
NOTE we are in a different thread here than either the main thread or the Swing/AWT event handler thread, so we must be very careful when creating or removing widgets
/** NOTE we are in a different thread here than either the main thread or the Swing/AWT event handler thread, so we must be very careful when creating or removing widgets */
private void connect(final String remoteMachineName) { // Try to open this core file try { System.err.println("Connecting to debug server, please wait..."); agent.attach(remoteMachineName); attached = true; } catch (DebuggerException e) { final String errMsg = formatMessage(e.getMessage(), 80); System.err.println("Unable to connect to machine \"" + remoteMachineName + "\":\n\n" + errMsg); agent.detach(); e.printStackTrace(); return; } } private void detachDebugger() { if (!attached) { return; } agent.detach(); attached = false; } private void detach() { detachDebugger(); }
Punctuates the given string with \n's where necessary to not exceed the given number of characters per line. Strips extraneous whitespace.
/** Punctuates the given string with \n's where necessary to not exceed the given number of characters per line. Strips extraneous whitespace. */
private String formatMessage(String message, int charsPerLine) { StringBuffer buf = new StringBuffer(message.length()); StringTokenizer tokenizer = new StringTokenizer(message); int curLineLength = 0; while (tokenizer.hasMoreTokens()) { String tok = tokenizer.nextToken(); if (curLineLength + tok.length() > charsPerLine) { buf.append('\n'); curLineLength = 0; } else { if (curLineLength != 0) { buf.append(' '); ++curLineLength; } } buf.append(tok); curLineLength += tok.length(); } return buf.toString(); } }