Package org.apache.harmony.tools.jdb

Source Code of org.apache.harmony.tools.jdb.Main

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package org.apache.harmony.tools.jdb;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.Map.Entry;

import org.eclipse.jdi.Bootstrap;
import org.eclipse.jdi.internal.VirtualMachineManagerImpl;
import org.eclipse.jdi.internal.connect.SocketAttachingConnectorImpl;

import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.VirtualMachineManager;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventIterator;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.MethodExitEvent;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.event.ThreadStartEvent;
import com.sun.jdi.event.VMDisconnectEvent;
import com.sun.jdi.event.VMStartEvent;
import com.sun.jdi.request.BreakpointRequest;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.MethodExitRequest;
import com.sun.jdi.request.StepRequest;
import com.sun.jdi.request.ThreadStartRequest;

/**
* The java implementation of Harmony JDB command line tool.
*/
public class Main {

    /**
     * The JDB command enumeration.
     */
    static enum Command {
       
        // complete
        CONNECTORS("connectors", "connectors", "") {
            @Override
            @SuppressWarnings("unchecked")
            final String run(String[] args) {
                List<Connector> connectors =
                    Main.jdb.virturalMachineManager.allConnectors();
                StringBuilder sb = new StringBuilder(
                        "Available connectors are:\n");
                for (Connector c : connectors) {
                    sb.append(String.format(
                            "\n  Connector: %s  Transport: %s\n",
                            c.name(), c.transport().name()));
                    sb.append(String.format(
                            "    description: %s\n",
                            c.description()));
                    Map<String, Connector.Argument> argumentMap =
                        c.defaultArguments();
                    Set<Entry<String, Connector.Argument>> argumentSet =
                        argumentMap.entrySet();
                    for (Entry<String, Connector.Argument> e : argumentSet) {
                        Connector.Argument arg = e.getValue();
                        if (arg.mustSpecify()) {
                            sb.append(String.format(
                                    "\n    Required Argument: %s", arg.name()));
                        } else {
                            sb.append(String.format(
                                    "\n    Argument: %s", arg.name()));
                        }
                        String defVal = arg.value();
                        if (null == defVal) {
                            sb.append(" <no default>\n");
                        } else {
                            sb.append(String.format(
                                    " Default value: %s\n", defVal));
                        }
                        sb.append(String.format(
                                "    description: %s\n", arg.description()));
                    }
                }
                return sb.toString();
            }
        },
       
        // seems complete
        RUN("run", "run [class [args]]", "") {
            @Override
            final String run(String[] args) {
                if (jdb.status == Status.CONNECTED) {
                    jdb.debuggee.resume();
                    jdb.status = Status.STARTED;
                }
                return EMPTY_MSG;
            }
        },
       
        THREADS("threads", "threads [threadgroup]", "") {
            @Override
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "threads");
                }
                StringBuilder sb = new StringBuilder("** thread list **\n");
                // TODO print the thread list
                return sb.toString();
            }
        },
       
        THREAD("thread", "thread <thread id>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "thread");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        SUSPEND("suspend", "suspend [thread id(s)]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "suspend");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        RESUME("resume", "resume [thread id(s)]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "resume");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        WHERE("where", "where [<thread id> | all]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "where");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        WHEREI("wherei", "wherei [<thread id> | all]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "wherei");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        UP("up", "up [n frames]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "up");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        DOWN("down", "down [n frames]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "down");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        KILL("kill", "kill <thread id> <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "kill");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        INTERRUPT("interrupt", "interrupt <thread id>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "interrupt");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        PRINT("print", "print <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "print");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        DUMP("dump", "dump <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "dump");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        EVAL("eval", "eval <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "eval");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        SET("set", "set <lvalue> = <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "set");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        LOCALS("locals", "locals", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "locals");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        CLASSES("classes", "classes", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "classes");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        CLASS("class", "class <class id>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "class");
                }
                return EMPTY_MSG;
            }
        },
       
        METHODS("methods", "methods <class id>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "methods");
                }
                return EMPTY_MSG;
            }
        },
       
        FIELDS("fields", "fields <class id>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "fields");
                }
                return EMPTY_MSG;
            }
        },
       
        THREADGROUPS("threadgroups", "threadgroups", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "threadgroups");
                }
                return EMPTY_MSG;
            }
        },
       
        THREADGROUP("threadgroup", "threadgroup <name>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "threadgroup");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        // TODO complete, not working well though
        STOP_IN("stop", "stop in <class id>.<method>[(argument_type,...)]",
                "") {
            @Override
            final boolean match(String[] args) {
                return super.match(args) && args[1].equals("in");
            }
            @Override
            @SuppressWarnings("unchecked")
            final String run(String[] args) {
                String[] params;
                try {
                    params = parseClassMethod(args[2]);
                } catch (Exception e) {
                    return usage;
                }
                List<ReferenceType> classes =
                    jdb.debuggee.classesByName(params[0]);
                // class not loaded yet
                if (classes.isEmpty()) {
                    if (!jdb.deferringMethodBreakpoint.containsKey(params[0])) {
                        jdb.deferringMethodBreakpoint.put(
                                params[0], new ArrayList<String>());
                    }
                    // method string: "method|argument_type,..."
                    jdb.deferringMethodBreakpoint.get(
                            params[0]).add(params[1] + "|" + params[2]);
                    return String.format(
                            "Deferring breakpoint %s\n" +
                            "it will be set after the class is loaded.",
                            args[2]);
                } else {
                    // Generate the target argument type string list
                    String[] argTypeNames = params[2].split(",");
                    // Get all methods of the class
                    ReferenceType clazz = classes.get(0);
                    List<Method> methodList = clazz.methodsByName(params[1]);
                    Method matchedMethod = null;
                    /*
                     * As the jdb command argument doesn't supply the result
                     * value type, it's impossible to generate a jni signature
                     * for the specified method. I just have to search...
                     */
                    for (Method m : methodList) {
                        List<String> types = m.argumentTypeNames();
                        if (types.size() != argTypeNames.length) {
                            continue;
                        } else {
                            boolean matched = true;
                            for (int i = 0; i < argTypeNames.length; i++) {
                                if (!types.get(i).equals(argTypeNames[i])) {
                                    matched = false;
                                    break;
                                }
                            }
                            if (matched) {
                                matchedMethod = m;
                                break;
                            }
                        }
                    }
                    if (null != matchedMethod) {
                        Location loc = matchedMethod.location();
                        if (null != loc) {
                            BreakpointRequest breakpointRequest =
                                jdb.eventRequestManager.
                                createBreakpointRequest(loc);
                            breakpointRequest.setSuspendPolicy(
                                    EventRequest.SUSPEND_ALL);
                            breakpointRequest.enable();
                            jdb.breakpointRegisterMap.put(
                                    loc.toString(), breakpointRequest);
                            return String.format("Breakpoint set: " + loc);
                        }
                    }
                }
                return EMPTY_MSG;
            }
        },
       
        // TODO complete, working well, need more check
        STOP_AT("stop", "stop at <class id>:<line>", "") {
            @Override
            final boolean match(String[] args) {
                return super.match(args) && args[1].equals("at");
            }
            @Override
            @SuppressWarnings("unchecked")
            final String run(String[] args) throws Exception {
                String[] params = args[2].split(":");
                List<ReferenceType> classes =
                    jdb.debuggee.classesByName(params[0]);
                if (classes.isEmpty()) {
                    if (!jdb.deferringLineBreakpoint.containsKey(params[0])) {
                        jdb.deferringLineBreakpoint.put(
                                params[0], new ArrayList<Integer>());
                    }
                    jdb.deferringLineBreakpoint.get(
                            params[0]).add(Integer.parseInt(params[1]));
                    return String.format(
                            "Deferring breakpoint %s\n" +
                            "it will be set after the class is loaded.",
                            args[2]);
                } else {
                    List<Location> locations = classes.get(0).
                            locationsOfLine(Integer.parseInt(params[1]));
                    if (!locations.isEmpty()) {
                        Location loc = locations.get(0);
                        BreakpointRequest breakpointRequest =
                            jdb.eventRequestManager.
                            createBreakpointRequest(loc);
                        breakpointRequest.setSuspendPolicy(
                                EventRequest.SUSPEND_ALL);
                        breakpointRequest.enable();
                        jdb.breakpointRegisterMap.put(
                                loc.toString(), breakpointRequest);
                        return String.format("Breakpoint set: " + loc);
                    } else {
                        return EMPTY_MSG;
                    }
                }
            }
        },
       
        // TODO complete, not working well though
        CLEAR_METHOD("clear", "clear <class id>.<method>[(argument_type,...)]", "") {
            final boolean match(String[] args) {
                return args.length >= 2
                && args[0].equals(prefix)
                && args[1].indexOf(':') == -1
                && args[1].indexOf('.') != -1;
            }
            @SuppressWarnings("unchecked")
            final String run(String[] args) {
                String[] params;
                boolean isFound = false;
                try {
                    params = parseClassMethod(args[1]);
                } catch (Exception e) {
                    return usage;
                }
                String methodStr = params[1] + "|" + params[2];
                List<String> methodStrList =
                    jdb.deferringMethodBreakpoint.get(params[0]);
                for (String str : methodStrList) {
                    if (str.equals(methodStr)) {
                        methodStrList.remove(str);
                        isFound = true;
                        break;
                    }
                }
                if (!isFound) {
                    List<ReferenceType> classes =
                        jdb.debuggee.classesByName(params[0]);
                    if (!classes.isEmpty()) {
                        // Generate the target argument type string list
                        String[] argTypeNames = params[2].split(",");
                        // Get all methods of the class
                        ReferenceType clazz = classes.get(0);
                        List<Method> methodList = clazz.methodsByName(params[1]);
                        Method matchedMethod = null;
                        /*
                         * As the jdb command argument doesn't supply the result
                         * value type, it's impossible to generate a jni signature
                         * for the specified method. I just have to search...
                         */
                        for (Method m : methodList) {
                            List<String> types = m.argumentTypeNames();
                            if (types.size() != argTypeNames.length) {
                                continue;
                            } else {
                                boolean matched = true;
                                for (int i = 0; i < argTypeNames.length; i++) {
                                    if (!types.get(i).equals(argTypeNames[i])) {
                                        matched = false;
                                        break;
                                    }
                                }
                                if (matched) {
                                    matchedMethod = m;
                                    break;
                                }
                            }
                        }
                        if (null != matchedMethod) {
                            Location loc = matchedMethod.location();
                            if (null != loc) {
                                String key = loc.toString();
                                jdb.breakpointRegisterMap.get(key).disable();
                                jdb.breakpointRegisterMap.remove(key);
                                isFound = true;
                            }
                        }
                    }
                }
                if (isFound) {
                    return String.format("Breakpoint cleared: " + args[1]);
                } else {
                    return String.format(
                            "Not found: breakpoint %s", args[1]);
                }
            }
        },
       
        // TODO complete, not working well though
        CLEAR_LINE("clear", "clear <class id>:<line>", "") {
            final boolean match(String[] args) {
                return args.length >= 2
                    && args[0].equals(prefix)
                    && args[1].indexOf(':') != -1;
            }
            @SuppressWarnings("unchecked")
            final String run(String[] args) throws Exception {
                String[] params = args[1].split(":");
                int line = Integer.parseInt(params[1]);
                boolean isFound = false;
                List<Integer> lineList =
                    jdb.deferringLineBreakpoint.get(params[0]);
                for (Integer i : lineList) {
                    if (i.intValue() == line) {
                        lineList.remove(i);
                        isFound = true;
                        break;
                    }
                }
                if (!isFound) {
                    List<ReferenceType> classes =
                        jdb.debuggee.classesByName(params[0]);
                    if (!classes.isEmpty()) {
                        List<Location> locations = classes.get(0).
                                locationsOfLine(line);
                        if (!locations.isEmpty()) {
                            Location loc = locations.get(0);
                            String key = loc.toString();
                            jdb.breakpointRegisterMap.get(key).disable();
                            jdb.breakpointRegisterMap.remove(key);
                            isFound = true;
                        }
                    }
                }
                if (isFound) {
                    return String.format("Breakpoint cleared: " + args[1]);
                } else {
                    return String.format(
                            "Not found: breakpoint %s", args[1]);
                }
            }
        },
       
        CLEAR("clear", "clear", "") {
            final boolean match(String[] args) {
                return args.length == 1 && args[0].equals(prefix);
            }
            @SuppressWarnings("unchecked")
            final String run(String[] args) {
                /*
                 * TODO have not include deferred breakpoints yet
                 */
                List<BreakpointRequest> list =
                    jdb.eventRequestManager.breakpointRequests();
                if (list.size() == 0) {
                    return "No breakpoints set.";
                } else {
                    StringBuilder sb = new StringBuilder();
                    for (BreakpointRequest request : list) {
                        sb.append(request.location() + "\n");
                    }
                    return sb.toString();
                }
            }
        },
       
        CATCH("catch", "catch [uncaught|caught|all]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "catch");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        IGNORE("ignore", "ignore [uncaught|caught|all]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "ignore");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        WATCH("watch", "watch [access|all] <class id>.<field name>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "watch");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        UNWATCH("unwatch", "unwatch [access|all] <class id>.<field name>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "unwatch");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        TRACE_METHODS("trace", "trace [go] methods [thread]", "") {
            final boolean match(String[] args) {
                if (args[0].equals(prefix) &&
                        (args.length == 1 || !args[1].equals("up"))) {
                    return true;
                } else {
                    return false;
                }
            }
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "trace");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        TRACE_METHOD("trace", "trace [go] method exit | exits [thread]", "") {
            final boolean match(String[] args) {
                return false;
            }
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "trace");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        UNTRACE("untrace", "untrace [methods]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "untrace");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        // TODO complete, not working well though
        STEP("step", "step", "") {
            final boolean match(String[] args) {
                if (args[0].equals(prefix) &&
                        (args.length == 1 || !args[1].equals("up"))) {
                    return true;
                } else {
                    return false;
                }
            }
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "step");
                }
                StepRequest request =
                    jdb.eventRequestManager.createStepRequest(jdb.thread,
                            StepRequest.STEP_LINE,
                            StepRequest.STEP_INTO);
                request.addCountFilter(1);// next step only
                request.enable();
                return "Step one line.";
            }
        },
       
        // TODO complete, not working well though
        STEP_UP("step", "step up", "") {
            final boolean match(String[] args) {
                if (args[0].equals(prefix) &&
                        args.length >=2 && args[1].equals("up")) {
                    return true;
                } else {
                    return false;
                }
            }
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "step up");
                }
                MethodExitRequest request =
                    jdb.eventRequestManager.createMethodExitRequest();
                request.addThreadFilter(jdb.thread);
                request.addCountFilter(1);
                request.enable();
                return "Step up to the caller.";
            }
        },
       
        // TODO complete, not working well though
        STEPI("stepi", "stepi", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "stepi");
                }
                StepRequest request =
                    jdb.eventRequestManager.createStepRequest(jdb.thread,
                            StepRequest.STEP_MIN,
                            StepRequest.STEP_INTO);
                request.addCountFilter(1);// next step only
                request.enable();
                return "Step one instruction.";
            }
        },
       
        // TODO complete, not working well though
        NEXT("next", "next", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "next");
                }
                StepRequest request =
                    jdb.eventRequestManager.createStepRequest(jdb.thread,
                            StepRequest.STEP_LINE,
                            StepRequest.STEP_OVER);
                request.addCountFilter(1);// next step only
                request.enable();
                return "Step one line (over calls)";
            }
        },
       
        // TODO, complete, working well, need more check
        CONT("cont", "cont", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "cont");
                }
                return EMPTY_MSG;
            }
        },
       
        LIST("list", "list [line number|method]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "list");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        USE("use|sourcepath", "use (or sourcepath) [source file path]", "") {
            final boolean match(String[] args) {
                if (args[0].equals("use") || args[0].equals("sourcepath")) {
                    return true;
                } else {
                    return false;
                }
            }
            final String run(String[] args) {
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        EXCLUDE("exclude", "exclude [<class pattern>, ... | \"none\"]", "") {
            final String run(String[] args) {
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        CLASSPATH("classpath", "classpath", "") {
            final String run(String[] args) {
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        MONITOR_CMD("monitor", "monitor <command>", "") {
            final boolean match(String[] args) {
                return false;
            }
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "monitor");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        MONITOR("monitor", "monitor", "") {
            final boolean match(String[] args) {
                return false;
            }
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "monitor");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        UNMONITOR("unmonitor", "unmonitor <monitor#>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "unmonitor");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        READ("read", "read <filename>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "read");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        LOCK("lock", "lock <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "lock");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        THREADLOCKS("threadlocks", "threadlocks [thread id]", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "threadlocks");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        POP("pop", "pop", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "pop");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        REENTER("reenter", "reenter", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "reenter");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        REDEFINE("redefine", "redefine <class id> <class file name>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "redefine");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        DISABLEGC("disablegc", "disablegc <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "disablegc");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        ENABLEGC("enablegc", "enablegc <expr>", "") {
            final String run(String[] args) {
                if (jdb.status != Status.STARTED) {
                    return String.format(NOT_VALID_UNTIL_STARTED, "enablegc");
                }
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        REPEAT("!!", "!!", "") {
            final String run(String[] args) {
                // TODO command logic
                return EMPTY_MSG;
            }
        },
       
        HELP("help|?", "help (or ?)", "") {
            final boolean match(String[] args) {
                if (args[0].equals("help") || args[0].equals("?")) {
                    return true;
                } else {
                    return false;
                }
            }
            final String run(String[] args) {
                StringBuilder sb = new StringBuilder("** command list **\n");
                // TODO build the info string
                return sb.toString();
            }
        },
       
        VERSION("version", "version", "") {
            final String run(String[] args) {
                return VERSION_STR;
            }
        },
       
        EXIT("exit|quit", "exit (or quit)", "") {
            final boolean match(String[] args) {
                if (args[0].equals("exit") || args[0].equals("quit")) {
                    return true;
                } else {
                    return false;
                }
            }
            final String run(String[] args) {
                return EMPTY_MSG;
            }
        },
       
        // Returned by the search method when no real command is matched
        NONCMD("", "" ,"") {
            final String run(String[] args) {
                return String.format("Unsupported command: '%s'.", args[0]);
            }
        };
       
        // Expected command prefix
        final String usage;
       
        // Command format and options
        final String comment;
       
        // Help information of the command
        final String prefix;
       
        // Iterate the command list and match the args against them
        static final Command search(final String[] args) {
            for (Command cmd : Command.values()) {
                if (cmd != NONCMD && cmd.match(args)) {
                    return cmd;
                }
            }
            return NONCMD;
        }
       
        // parse the "<class id>.<method>[(argument_type,...)]" string
        static final String[] parseClassMethod(String str) {
            String[] params = new String[3];
            int argumentStart = str.indexOf('(');
            if (argumentStart == -1) {
                // class id
                params[0] = str.substring(
                        0, str.lastIndexOf('.'));
                // method name
                params[1] = str.substring(
                        str.lastIndexOf('.') + 1);
                // empty argument type list
                params[2] = "";
            } else {
                String classMethod = str.substring(argumentStart);
                // class id
                params[0] = classMethod.substring(
                        0, classMethod.lastIndexOf('.'));
                // method name
                params[1] = classMethod.substring(
                        classMethod.lastIndexOf('.') + 1);
                // argument type list
                params[2] = str.substring(
                        argumentStart + 1, str.length() - 1);
            }
            return params;
        }
       
        /*
         * Generally, a command matching only needs to compare its prefix,
         * override it in enum instance if necessary.
         */
        boolean match(String[] args) {
            return args[0].equals(prefix);
        }
       
        /*
         * The default command running output is do nothing and return an empty
         * string, override it in enum instance if necessary.
         */
        String run(String[] args) throws Exception {
            return EMPTY_MSG;
        }
       
        Command(String prefix, String usage, String comment) {
            this.prefix = prefix;
            this.usage = usage;
            this.comment = comment;
        }
       
    }
   
    // TODO not decided how many statuses needed yet
    static enum Status {
        UNDEFINED, NO_VM_CONNECTED, CONNECTED, STARTED
    }
   
    // The default prompt
    private static final String PROMPT = "> ";
   
    /*
     * The empty message indicates there's no information coming out with
     * the command result
     */
    private static final String EMPTY_MSG = "";
   
    // The jdb version information
    private static final String VERSION_STR =
        "This is jdb version 1.6 <Apache Harmony 6>";
   
    /*
     * The string indicates that a command is not valid until the VM is started
     * with the 'run' command.
     */
    private static final String NOT_VALID_UNTIL_STARTED =
        "Command '%s' is not valid until " +
        "the VM is started with the 'run' command";
   
    // The singleton instance of jdb
    private static final Main jdb = new Main();
   
    private boolean isExit = false;
   
    // The debuggee status
    private Status status = Status.UNDEFINED;
   
    private VirtualMachineManager virturalMachineManager =
        Bootstrap.virtualMachineManager();
   
    private EventRequestManager eventRequestManager = null;
   
    private EventQueue eventQueue = null;
   
    private EventSet eventSet = null;
   
    private VirtualMachine debuggee = null;
   
    //private Process VMProcess = null;
    // Current thread
    private ThreadReference thread = null;
   
    //private String mainClass = null;
   
    private HashMap<String, List<Integer>> deferringLineBreakpoint =
                new HashMap<String, List<Integer>>();
   
    private HashMap<String, List<String>> deferringMethodBreakpoint =
                new HashMap<String, List<String>>();
   
    /*
     * A map that stores references to all breakpoints(not include the deferred
     * ones). The key of the map is a string indicate the location of the
     * breakpoint. The map is used by those commands that will clear some
     * breakpoint in certain location.
     */
    private HashMap<String, BreakpointRequest> breakpointRegisterMap =
                new HashMap<String, BreakpointRequest>();
   
    // TODO currentPrompt is prompt that indicate current context(fixed for now)
    private String currentPrompt = PROMPT;
   
    private Main() {}
   
    /*
     *  Prepare jdb according to the incoming arguments: decide working mode
     *  and corresponding parameter, then launch jdb and get ready to process
     *  commands.
     */
    @SuppressWarnings("unchecked")
    private void init(String[] args) throws Exception {
        /*
         * TODO As currently jdb just supports "jdb -attach [port]" command,
         * more work to do to parse the jdb launching command option
         */
        if (args.length >= 2 && args[0].equals("-attach")) {
            SocketAttachingConnectorImpl sac =
                new SocketAttachingConnectorImpl(
                        (VirtualMachineManagerImpl) Bootstrap.
                        virtualMachineManager());
            Map argMap = sac.defaultArguments();
            Connector.Argument value;
            value = (Connector.Argument) argMap.get("hostname");
            value.setValue("localhost");
            value = (Connector.Argument) argMap.get("port");
            value.setValue(args[1]);
            debuggee = sac.attach(argMap);
            //VMProcess = debuggee.process();
        } else {
            System.out.println("Wrong command option. " +
                    "The jdb currently only support jdb -attach [port].");
            System.exit(0);
        }
       
        eventRequestManager = debuggee.eventRequestManager();
       
        // Enable class prepare request for all classes
        ClassPrepareRequest classPrepareRequest
            = eventRequestManager.createClassPrepareRequest();
        classPrepareRequest.addClassFilter("*");
        classPrepareRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
        classPrepareRequest.enable();
       
        // Enable thread start request for all threads
        ThreadStartRequest threadStartRequest
            = eventRequestManager.createThreadStartRequest();
        threadStartRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
        threadStartRequest.enable();
    }
   
    // Start the loop
    private void start() throws Exception {
        eventQueue = debuggee.eventQueue();
        while (true) {
            if (isExit == true) {
                break;
            }
            eventSet = eventQueue.remove();
            EventIterator eventIterator = eventSet.eventIterator();
            while (eventIterator.hasNext()) {
                Event event = (Event) eventIterator.next();
                execute(event);
            }
            if (!isExit) {
                eventSet.resume();
            }
        }
    }
   
    @SuppressWarnings("unchecked")
    private void execute(Event event) throws Exception {
        /*
         * The variable indicates whether go to command looping after handling
         * the current event
         */
        boolean keepRunning = true;
        if (event instanceof VMStartEvent) {
            status = Status.CONNECTED;
            System.out.println("Initializing jdb ...");
            System.out.println(
                    "VM Started: No frames on the current call stack");
            // stop after the vm starts
            keepRunning = false;
        }
        if (event instanceof ThreadStartEvent) {
            thread = ((ThreadStartEvent) event).thread();
        }
        if (event instanceof MethodExitEvent) {
            // run command step up -> stop
            keepRunning = false;
        }
        if (event instanceof StepEvent) {
            // run commands step, stepi, or next -> stop
            keepRunning = false;
        }
        /*
         * after a class is loaded, check whether there are deferred
         * breakpoints(both line and method) related to the class; if yes,
         * set these breakpoints. The jdb won't stop running.
         */
        if (event instanceof ClassPrepareEvent) {
            // get the class name
            String className = ((ClassPrepareEvent)
                    event).referenceType().name();
            // set the possible deferred line breakpoints
            List<Integer> lineList = deferringLineBreakpoint.get(className);
            if (lineList != null) {
                ReferenceType classType = ((ClassPrepareEvent)
                        event).referenceType();
                for (Integer line : lineList) {
                    List<Location> locations = classType.locationsOfLine(line);
                    if (!locations.isEmpty()) {
                        Location loc = locations.get(0);
                        BreakpointRequest breakpointRequest =
                            jdb.eventRequestManager.
                            createBreakpointRequest(loc);
                        breakpointRequest.setSuspendPolicy(
                                EventRequest.SUSPEND_ALL);
                        breakpointRequest.enable();
                        jdb.breakpointRegisterMap.put(
                                loc.toString(), breakpointRequest);
                        System.out.println(
                                String.format("Breakpoint set: " + loc));
                    }
                }
            }
            // set the possible deferred method breakpoints
            List<String> methodStrList = deferringMethodBreakpoint.get(className);
            if (methodStrList != null) {
                ReferenceType classType = ((ClassPrepareEvent)
                        event).referenceType();
                for (String methodStr : methodStrList) {
                    String[] params = methodStr.split("|");
                    // Generate the target argument type string list
                    String[] argTypeNames;
                    if (params.length > 1) {
                        argTypeNames = params[1].split(",");
                    } else {
                        argTypeNames = new String[0];
                    }
                    // Get all methods of the class
                    List<Method> methodList = classType.methodsByName(params[0]);
                    Method matchedMethod = null;
                    /*
                     * As the jdb command argument doesn't supply the result
                     * value type, it's impossible to generate a jni signature
                     * for the specified method. I just have to search...
                     */
                    for (Method m : methodList) {
                        List<String> types = m.argumentTypeNames();
                        if (types.size() != argTypeNames.length) {
                            continue;
                        } else {
                            boolean matched = true;
                            for (int i = 0; i < argTypeNames.length; i++) {
                                if (!types.get(i).equals(argTypeNames[i])) {
                                    matched = false;
                                    break;
                                }
                            }
                            if (matched) {
                                matchedMethod = m;
                                break;
                            }
                        }
                    }
                    if (null != matchedMethod) {
                        Location loc = matchedMethod.location();
                        if (null != loc) {
                            BreakpointRequest breakpointRequest =
                                jdb.eventRequestManager.
                                createBreakpointRequest(loc);
                            breakpointRequest.setSuspendPolicy(
                                    EventRequest.SUSPEND_ALL);
                            breakpointRequest.enable();
                            jdb.breakpointRegisterMap.put(
                                    loc.toString(), breakpointRequest);
                            System.out.println(
                                    String.format("Breakpoint set: " + loc));
                        }
                    }
                }
            }
        }
        if (event instanceof BreakpointEvent) {
            // reach breakpoint and stop
            System.out.println("Reach breakpoint at " +
                    ((BreakpointEvent) event).location());
            keepRunning = false;
        }
        if (event instanceof VMDisconnectEvent) {
            // The jdb will end when disconnected from target vm
            System.out.println("Application ends.");
            isExit = true;
        }
       
        /*
         * If the event requires the jdb to stop running, the jdb turn to the user
         * interaction mode before resuming.
         */
        if (!keepRunning) {
            Scanner cmdScanner = new Scanner(System.in);
            while (true) {
                System.out.print(currentPrompt);
                String line = cmdScanner.nextLine();
                String[] args = line.trim().split(" ");
                if (!line.equals("")) {
                    Command cmd = Command.search(args);
                    if (cmd == Command.EXIT ) {
                        isExit = true;
                        break;
                    }
                    String result = cmd.run(args);
                    if (!result.equals(EMPTY_MSG)) {
                        System.out.println(result);
                    }
                    // If the command is one of those execution control
                    if (cmd == Command.STEP
                            || cmd == Command.STEP_UP
                            || cmd == Command.STEPI
                            || cmd == Command.RUN
                            || cmd == Command.CONT) {
                        break;
                    }
                }
            }
        }
    }
   
    public static void main(String[] args) throws Exception {
        jdb.init(args);
        jdb.start();
        if (jdb.status == Status.CONNECTED) {
            jdb.debuggee.dispose();
        }
    }

}
TOP

Related Classes of org.apache.harmony.tools.jdb.Main

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.