/* * Copyright 2010 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package org.visage.tools.debug.tty; import org.visage.jdi.VisageBootstrap; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.StackFrame; import com.sun.jdi.ThreadGroupReference; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; import com.sun.jdi.event.BreakpointEvent; import com.sun.jdi.event.ClassPrepareEvent; import com.sun.jdi.event.ClassUnloadEvent; import com.sun.jdi.event.Event; import com.sun.jdi.event.ExceptionEvent; import com.sun.jdi.event.MethodEntryEvent; import com.sun.jdi.event.MethodExitEvent; import com.sun.jdi.event.StepEvent; import com.sun.jdi.event.ThreadDeathEvent; import com.sun.jdi.event.ThreadStartEvent; import com.sun.jdi.event.VMDeathEvent; import com.sun.jdi.event.VMDisconnectEvent; import com.sun.jdi.event.VMStartEvent; import com.sun.jdi.event.WatchpointEvent; import org.visage.tools.debug.expr.ExpressionParser; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ReferenceType; import com.sun.jdi.connect.Connector; import com.sun.jdi.event.EventQueue; 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.ExceptionRequest; import com.sun.jdi.request.StepRequest; import com.sun.jdi.request.WatchpointRequest; import java.io.PrintStream; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.StringTokenizer; /** * This class is programmable equivalent of TTY.java (which is the main class of * the command line tool "visagedb"). * * @author sundar */ public class Debugger { private final Env env = new Env(); private final List<EventNotifier> listeners = Collections.synchronizedList(new LinkedList()); private final Commands evaluator = new Commands(env); private volatile EventHandler handler; // "args" is same as command line options supported by TTY.java except that // -listconnectors, -version and -help options are not supported. public Debugger(EventNotifier listener, String args) { if (listener != null) { this.listeners.add(listener); } init((args == null)? null : args.split(" ")); if (env.connection().isOpen() && env.vm().canBeModified()) { this.handler = new EventHandler(env, new EventNotifierImpl(), true); } } public Debugger(String args) { this(null, args); } public Debugger() { this(null); } /** * Returns the target VM. */ public VirtualMachine vm() { return env.vm(); } /** * Returns the event requests manager for the target VM. */ public EventRequestManager eventRequestManager() { return vm().eventRequestManager(); } /** * Returns the event queue of the target VM. */ public EventQueue eventQueue() { return vm().eventQueue(); } public void addListener(EventNotifier notifier) { listeners.add(notifier); } public void removeListener(EventNotifier notifier) { listeners.remove(notifier); } /* * Shutdown the target VM */ public void shutdown() { if (handler != null) { listeners.clear(); handler.shutdown(); handler = null; } env.shutdown(); } /** * Shutdown the target VM - but print message before that. */ public void shutdown(String message) { if (handler != null) { listeners.clear(); handler.shutdown(); handler = null; } env.shutdown(message); } // queries to target VM /** * Returns the first ReferenceType that matches the given name. */ public ReferenceType findReferenceType(String name) { List rts = vm().classesByName(name); Iterator iter = rts.iterator(); while (iter.hasNext()) { ReferenceType rt = (ReferenceType)iter.next(); if (rt.name().equals(name)) { return rt; } } return null; } /** * Find and return the method of given name and signature from the given * ReferenceType. Returns null when find fails. */ public Method findMethod(ReferenceType rt, String name, String signature) { List methods = rt.methods(); Iterator iter = methods.iterator(); while (iter.hasNext()) { Method method = (Method)iter.next(); if (method.name().equals(name) && method.signature().equals(signature)) { return method; } } return null; } /** * Returns location object representing the given line number within the * given ReferenceType's source. */ public Location findLocation(ReferenceType rt, int lineNumber) throws AbsentInformationException { List locs = rt.locationsOfLine(lineNumber); if (locs.size() == 0) { throw new IllegalArgumentException("Bad line number"); } else if (locs.size() > 1) { throw new IllegalArgumentException("Line number has multiple locations"); } return (Location)locs.get(0); } /* * Synchronous stepXXX, resumeTo methods and resumeToXXX methods. These methods * block the calling thread till the specific event occurs in the target VM. */ private StepEvent doStep(ThreadReference thread, int gran, int depth) { final StepRequest sr = eventRequestManager().createStepRequest(thread, gran, depth); sr.addClassExclusionFilter("java.*"); sr.addClassExclusionFilter("sun.*"); sr.addClassExclusionFilter("com.sun.*"); sr.addCountFilter(1); sr.enable(); StepEvent retEvent = (StepEvent)resumeToEvent(sr); eventRequestManager().deleteEventRequest(sr); return retEvent; } public StepEvent stepIntoInstruction(ThreadReference thread) { return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO); } public StepEvent stepIntoInstruction() { return stepIntoInstruction(getCurrentThread()); } public StepEvent stepIntoLine(ThreadReference thread) { return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO); } public StepEvent stepIntoLine() { return stepIntoLine(getCurrentThread()); } public StepEvent stepOverInstruction(ThreadReference thread) { return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER); } public StepEvent stepOverInstruction() { return stepOverInstruction(getCurrentThread()); } public StepEvent stepOverLine(ThreadReference thread) { return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER); } public StepEvent stepOverLine() { return stepOverLine(getCurrentThread()); } public StepEvent stepOut(ThreadReference thread) { return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT); } public StepEvent stepOut() { return stepOut(getCurrentThread()); } public BreakpointEvent resumeTo(Location loc) { final BreakpointRequest request = eventRequestManager().createBreakpointRequest(loc); request.addCountFilter(1); request.enable(); return (BreakpointEvent)resumeToEvent(request); } public BreakpointEvent resumeTo(String clsName, String methodName, String methodSignature) { ReferenceType rt = findReferenceType(clsName); if (rt == null) { rt = resumeToPrepareOf(clsName).referenceType(); } Method method = findMethod(rt, methodName, methodSignature); if (method == null) { throw new IllegalArgumentException("Bad method name/signature"); } return resumeTo(method.location()); } public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException { ReferenceType rt = findReferenceType(clsName); if (rt == null) { rt = resumeToPrepareOf(clsName).referenceType(); } return resumeTo(findLocation(rt, lineNumber)); } public ClassPrepareEvent resumeToPrepareOf(String className) { final ClassPrepareRequest request = eventRequestManager().createClassPrepareRequest(); request.addClassFilter(className); request.addCountFilter(1); request.enable(); return (ClassPrepareEvent)resumeToEvent(request); } public interface EventFilter { public boolean match(Event evt); } public Event resumeToEvent(final EventFilter filter) { class EventNotification { Event event; boolean disconnected = false; boolean eventReceived = false; } final EventNotification en = new EventNotification(); EventNotifierAdapter adapter = new EventNotifierAdapter() { @Override public void receivedEvent(Event event) { en.eventReceived = true; if (filter.match(event)) { synchronized (en) { en.event = event; en.notifyAll(); } removeThisListener(); } else if (event instanceof VMDisconnectEvent) { synchronized (en) { en.disconnected = true; en.notifyAll(); } removeThisListener(); } } @Override public void vmInterrupted() { synchronized (en) { if (en.eventReceived && en.event == null) { env.invalidateAllThreadInfo(); // VM suspended, but we don't have the event that is // expected -- resume the VM and keep waiting for it. vm().resume(); } } en.eventReceived = false; } }; addListener(adapter); try { synchronized (en) { vm().resume(); while (!en.disconnected && (en.event == null)) { en.wait(); } } } catch (InterruptedException e) { return null; } if (en.disconnected) { throw new RuntimeException("VM Disconnected before requested event occurred"); } EventRequest request = en.event.request(); if (request != null && request.suspendPolicy() == EventRequest.SUSPEND_ALL) { env.invalidateAllThreadInfo(); env.setCurrentThread(EventHandler.eventThread(en.event)); } return en.event; } public Event resumeToEvent(final EventRequest request) { return resumeToEvent(new EventFilter() { public boolean match(Event evt) { return request.equals(evt.request()); } }); } public BreakpointEvent resumeToBreakpoint() { return (BreakpointEvent) resumeToEvent(new EventFilter() { public boolean match(Event evt) { return (evt instanceof BreakpointEvent); } }); } public StepEvent resumeToStep() { return (StepEvent) resumeToEvent(new EventFilter() { public boolean match(Event evt) { return (evt instanceof StepEvent); } }); } public WatchpointEvent resumeToWatchpoint() { return (WatchpointEvent) resumeToEvent(new EventFilter() { public boolean match(Event evt) { return (evt instanceof WatchpointEvent); } }); } public ExceptionEvent resumeToException() { return (ExceptionEvent) resumeToEvent(new EventFilter() { public boolean match(Event evt) { return (evt instanceof ExceptionEvent); } }); } /** * Resumes the target VM and waits till VMDeath or VMDisconnect event occurs. */ public VMDeathEvent resumeToVMDeath() { return (VMDeathEvent) resumeToEvent(new EventFilter() { public boolean match(Event evt) { return (evt instanceof VMDeathEvent); } }); } public Event resumeToAnyEvent() { return resumeToEvent(new EventFilter() { public boolean match(Event evt) { return true; } }); } /** * Evaluate "jdb"-style expressions. The expression syntax is same as what * you'd use with jdb's "print" or "set" command. The expression is evaluated * in current thread's current frame context. You can access locals from that * frame and also evaluate object fields/static fields etc. from there. For * example, if "seq" is a local variable of type Visage integer sequence, * * Debugger dbg = ... * dbg.evaluate("seq[0]"); * * and that will return JDI IntegerValue type object in this case. */ public Value evaluate(String expr) { Value result = null; ExpressionParser.GetFrame frameGetter = null; try { final ThreadInfo threadInfo = env.getCurrentThreadInfo(); if (threadInfo != null && threadInfo.getCurrentFrame() != null) { frameGetter = new ExpressionParser.GetFrame() { public StackFrame get() throws IncompatibleThreadStateException { return threadInfo.getCurrentFrame(); } }; } result = ExpressionParser.evaluate(expr, env.vm(), frameGetter); } catch (RuntimeException rexp) { throw rexp; } catch (Exception exp) { throw new RuntimeException(exp); } return result; } // get/set source path - used to list source code. public void setSourcePath(String path) { env.setSourcePath(path); } public String getSourcePath() { return env.getSourcePath(); } // Set standard output, error streams for the debugger. public void setOutput(PrintStream out) { env.messageOutput().setOutput(out); } public void setError(PrintStream err) { env.messageOutput().setOutput(err); } public void resetOutputs() { env.messageOutput().resetOutputs(); } // set/get current thread and threadgroup. public void setCurrentThread(ThreadReference tref) { env.setCurrentThread(tref); } public ThreadReference getCurrentThread() { return env.getCurrentThreadInfo().getThread(); } public void setCurrentThreadGroup(ThreadGroupReference tgref) { env.setThreadGroup(tgref); } public ThreadGroupReference getCurrentThreadGroup() { return env.getCurrentThreadGroup(); } // return string id for the given ThreadReference object public String threadId(ThreadReference tref) { return "t@" + tref.uniqueID(); } // return string id for the given ThreadGroupReference object public String threadGroupId(ThreadGroupReference tgref) { return tgref.name(); } // The following methods support "jdb"-style "interactive" commands. // Many commands print messages to Debugger's standard output and/or // standard error streams. Commands that return boolean tell whether the // command was successful or not. /** * This is similar to "catch" command of "jdb". * Returns null for deferred events. */ public ExceptionRequest catchException(String command) { return evaluator.commandCatchException(new StringTokenizer(command)); } /** * Print all the classes loaded in the target VM. */ public void classes() { evaluator.commandClasses(); } /** * Prints classpath information of the target VM. */ public boolean classPath() { return evaluator.commandClasspath(new StringTokenizer("")); } /** * Clear all breakpoints. */ public boolean clear() { return clear(""); } /** * Clear all breakpoints specified by command String. */ public boolean clear(String command) { return evaluator.commandClear(new StringTokenizer(command)); } /** * Resumes the target VM after suspension and waits for next event. */ public Event cont() { env.invalidateAllThreadInfo(); return resumeToAnyEvent(); } /** * This is jdb-style print command. Accepts jdb-style expressions involving * current thread's current frame, allows instance/static variable access etc. */ public void dump(String command) { evaluator.doPrint(new StringTokenizer(command), true); } public void disableGC(String command) { evaluator.doDisableGC(new StringTokenizer(command)); } /** * Same as jdb's "down" command - moving across frames of current thread. */ public boolean down() { return down(""); } /** * Same as jdb's "down" command - moving across frames of current thread. */ public boolean down(String command) { return evaluator.commandDown(new StringTokenizer(command)); } public void enableGC(String command) { evaluator.doEnableGC(new StringTokenizer(command)); } /** * Same as jdb's "ignore" command - to ignore exceptions. */ public boolean ignoreException() { return ignoreException(""); } /** * Same as jdb's "ignore" command - to ignore exceptions. */ public boolean ignoreException(String command) { return evaluator.commandIgnoreException(new StringTokenizer(command)); } public boolean interrupt() { return interrupt(""); } public boolean interrupt(String command) { return evaluator.commandInterrupt(new StringTokenizer(command)); } public boolean kill(String command) { StringTokenizer st = new StringTokenizer(command); if (!st.hasMoreTokens()) { env.messageOutput().println("Usage: kill <thread id> <throwable>"); return false; } ThreadInfo threadInfo = evaluator.doGetThread(st.nextToken()); if (threadInfo != null) { env.messageOutput().println("killing thread:", threadInfo.getThread().name()); evaluator.doKill(threadInfo.getThread(), st); return true; } return false; } public boolean lines(String command) { return evaluator.commandLines(new StringTokenizer(command)); } public void list() { list(""); } public void list(String command) { evaluator.commandList(new StringTokenizer(command)); } /** * Prints all local variables of the current frame of the current thread. */ public boolean locals() { return evaluator.commandLocals(); } public void lock(String command) { evaluator.doLock(new StringTokenizer(command)); } // blocks the calling thread till "next" is complete in the target VM public boolean next() { StepRequest req = evaluator.commandNext(false); if (req != null) { resumeToEvent(req); return true; } else { return false; } } public boolean pop() { return pop(""); } public boolean pop(String command) { return evaluator.commandPopFrames(new StringTokenizer(command), false); } /** * This is jdb-style print command. Accepts jdb-style expressions involving * current thread's current frame, allows instance/static variable access etc. */ public void print(String command) { evaluator.doPrint(new StringTokenizer(command), false); } public boolean redefine(String command) { return evaluator.commandRedefine(new StringTokenizer(command)); } public boolean reenter() { return reenter(""); } public boolean reenter(String command) { return evaluator.commandPopFrames(new StringTokenizer(command), true); } public boolean run() { return run(""); } public boolean run(String command) { boolean result = evaluator.commandRun(new StringTokenizer(command)); if ((handler == null) && env.connection().isOpen()) { handler = new EventHandler(env, new EventNotifierImpl(), true); } return result; } public boolean set(String command) { StringTokenizer st = new StringTokenizer(command); String all = st.nextToken(""); /* * Bare bones error checking. */ if (all.indexOf('=') == -1) { env.messageOutput().println("Invalid assignment syntax"); env.printPrompt(); return false; } /* * The set command is really just syntactic sugar. Pass it on to the * print command. */ evaluator.doPrint(st, false); return true; } public void sourcePath() { sourcePath(""); } public void sourcePath(String command) { evaluator.commandUse(new StringTokenizer(command)); } // blocks the calling thread till "step" is complete in the target VM public boolean step() { return step(""); } // blocks the calling thread till "step" is complete in the target VM public boolean step(String command) { StepRequest req = evaluator.commandStep(new StringTokenizer(command), false); if (req != null) { resumeToEvent(req); return true; } else { return false; } } // blocks the calling thread till "stepi" is complete in the target VM public boolean stepi() { StepRequest req = evaluator.commandStepi(false); if (req != null) { resumeToEvent(req); return true; } else { return false; } } // Returns null for deferred breakpoints. public BreakpointRequest stop() { return stop(""); } // Returns null for deferred breakpoints. public BreakpointRequest stop(String command) { return evaluator.commandStop(new StringTokenizer(command)); } public boolean thread(String command) { return evaluator.commandThread(new StringTokenizer(command)); } public boolean threadGroup(String command) { return evaluator.commandThreadGroup(new StringTokenizer(command)); } public boolean threads() { return threads(""); } public boolean threads(String command) { return evaluator.commandThreads(new StringTokenizer(command)); } public void threadGroups() { evaluator.commandThreadGroups(); } public void trace() { trace(""); } public void trace(String command) { evaluator.commandTrace(new StringTokenizer(command)); } public void untrace() { untrace(""); } public void untrace(String command) { evaluator.commandUntrace(new StringTokenizer(command)); } public void unwatch(String command) { evaluator.commandUnwatch(new StringTokenizer(command)); } /** * Same as jdb's "up" command - moving across frames of current thread. */ public boolean up() { return up(""); } /** * Same as jdb's "up" command - moving across frames of current thread. */ public boolean up(String command) { return evaluator.commandUp(new StringTokenizer(command)); } public WatchpointRequest watch(String command) { return evaluator.commandWatch(new StringTokenizer(command)); } public boolean where() { return where(""); } public boolean where(String command) { return evaluator.commandWhere(new StringTokenizer(command), false); } public boolean wherei() { return wherei(""); } public boolean wherei(String command) { return evaluator.commandWhere(new StringTokenizer(command), true); } public void quit() { shutdown(); } // Internals only below this point private static void usageError(String messageKey) { throw new IllegalArgumentException(MessageOutput.format(messageKey)); } private static void usageError(String messageKey, String argument) { throw new IllegalArgumentException(MessageOutput.format(messageKey, argument)); } private static boolean supportsSharedMemory() { for (Connector connector : VisageBootstrap.virtualMachineManager().allConnectors()) { if (connector.transport() == null) { continue; } if ("dt_shmem".equals(connector.transport().name())) { return true; } } return false; } private static String addressToSocketArgs(String address) { int index = address.indexOf(':'); if (index != -1) { String hostString = address.substring(0, index); String portString = address.substring(index + 1); return "hostname=" + hostString + ",port=" + portString; } else { return "port=" + address; } } private static boolean hasWhitespace(String string) { int length = string.length(); for (int i = 0; i < length; i++) { if (Character.isWhitespace(string.charAt(i))) { return true; } } return false; } private static String addArgument(String string, String argument) { if (hasWhitespace(argument) || argument.indexOf(',') != -1) { // Quotes were stripped out for this argument, add 'em back. StringBuffer buffer = new StringBuffer(string); buffer.append('"'); for (int i = 0; i < argument.length(); i++) { char c = argument.charAt(i); if (c == '"') { buffer.append('\\'); } buffer.append(c); } buffer.append("\" "); return buffer.toString(); } else { return string + argument + ' '; } } private void init(String[] argv) { String cmdLine = ""; String javaArgs = ""; int traceFlags = VirtualMachine.TRACE_NONE; boolean launchImmediately = false; String connectSpec = null; // don't exit this VM on Env.shutdown() env.setExitDebuggerVM(false); if (argv != null) { for (int i = 0; i < argv.length; i++) { String token = argv[i]; if (token.equals("-dbgtrace")) { if ((i == argv.length - 1) || !Character.isDigit(argv[i + 1].charAt(0))) { traceFlags = VirtualMachine.TRACE_ALL; } else { String flagStr = ""; try { flagStr = argv[++i]; traceFlags = Integer.decode(flagStr).intValue(); } catch (NumberFormatException nfe) { usageError("dbgtrace flag value must be an integer:", flagStr); return; } } } else if (token.equals("-X")) { usageError("Use java minus X to see"); return; } else if ( // Standard VM options passed on token.equals("-v") || token.startsWith("-v:") || // -v[:...] token.startsWith("-verbose") || // -verbose[:...] token.startsWith("-D") || // -classpath handled below // NonStandard options passed on token.startsWith("-X") || // Old-style options (These should remain in place as long as // the standard VM accepts them) token.equals("-noasyncgc") || token.equals("-prof") || token.equals("-verify") || token.equals("-noverify") || token.equals("-verifyremote") || token.equals("-verbosegc") || token.startsWith("-ms") || token.startsWith("-mx") || token.startsWith("-ss") || token.startsWith("-oss")) { javaArgs = addArgument(javaArgs, token); } else if (token.equals("-tclassic")) { usageError("Classic VM no longer supported."); return; } else if (token.equals("-tclient")) { // -client must be the first one javaArgs = "-client " + javaArgs; } else if (token.equals("-tserver")) { // -server must be the first one javaArgs = "-server " + javaArgs; } else if (token.equals("-sourcepath")) { if (i == (argv.length - 1)) { usageError("No sourcepath specified."); return; } env.setSourcePath(argv[++i]); } else if (token.equals("-classpath")) { if (i == (argv.length - 1)) { usageError("No classpath specified."); return; } javaArgs = addArgument(javaArgs, token); javaArgs = addArgument(javaArgs, argv[++i]); } else if (token.equals("-attach")) { if (connectSpec != null) { usageError("cannot redefine existing connection", token); return; } if (i == (argv.length - 1)) { usageError("No attach address specified."); return; } String address = argv[++i]; /* * -attach is shorthand for Visage-JDI implementation's attaching * connectors. Use the shared memory attach if it's available; * otherwise, use sockets. Build a connect specification string * based on this decision. */ if (supportsSharedMemory()) { connectSpec = "org.visage.jdi.connect.VisageSharedMemoryAttachingConnector:name=" + address; } else { String suboptions = addressToSocketArgs(address); connectSpec = "org.visage.jdi.connect.VisageSocketAttachingConnector:" + suboptions; } } else if (token.equals("-listen") || token.equals("-listenany")) { if (connectSpec != null) { usageError("cannot redefine existing connection", token); return; } String address = null; if (token.equals("-listen")) { if (i == (argv.length - 1)) { usageError("No attach address specified."); return; } address = argv[++i]; } /* * -listen[any] is shorthand for one of the Visage-JDI implementation's * listening connectors. Use the shared memory listen if it's * available; otherwise, use sockets. Build a connect * specification string based on this decision. */ if (supportsSharedMemory()) { connectSpec = "org.visage.jdi.connect.VisageSharedMemoryListeningConnector:"; if (address != null) { connectSpec += ("name=" + address); } } else { connectSpec = "org.visage.jdi.connect.VisageSocketListeningConnector:"; if (address != null) { connectSpec += addressToSocketArgs(address); } } } else if (token.equals("-launch")) { launchImmediately = true; } else if (token.equals("-connect")) { /* * -connect allows the user to pick the connector * used in bringing up the target VM. This allows * use of connectors other than those in the reference * implementation. */ if (connectSpec != null) { usageError("cannot redefine existing connection", token); return; } if (i == (argv.length - 1)) { usageError("No connect specification."); return; } connectSpec = argv[++i]; } else if (token.startsWith("-")) { usageError("invalid option", token); return; } else { // Everything from here is part of the command line cmdLine = addArgument("", token); for (i++; i < argv.length; i++) { cmdLine = addArgument(cmdLine, argv[i]); } break; } } } /* * Unless otherwise specified, set the default connect spec. */ if (connectSpec == null) { connectSpec = "org.visage.jdi.connect.VisageLaunchingConnector:"; } else if (!connectSpec.endsWith(",") && !connectSpec.endsWith(":")) { connectSpec += ","; // (Bug ID 4285874) } cmdLine = cmdLine.trim(); javaArgs = javaArgs.trim(); if (cmdLine.length() > 0) { if (!connectSpec.startsWith("org.visage.jdi.connect.VisageLaunchingConnector:") && !connectSpec.startsWith("com.sun.jdi.CommandLineLaunch:")) { usageError("Cannot specify command line with connector:", connectSpec); return; } connectSpec += "main=" + cmdLine + ","; } if (javaArgs.length() > 0) { if (!connectSpec.startsWith("org.visage.jdi.connect.VisageLaunchingConnector:") && !connectSpec.startsWith("com.sun.jdi.CommandLineLaunch:")) { usageError("Cannot specify target vm arguments with connector:", connectSpec); return; } connectSpec += "options=" + javaArgs + ","; } try { if (!connectSpec.endsWith(",")) { connectSpec += ","; // (Bug ID 4285874) } env.init(connectSpec, launchImmediately, traceFlags); } catch (Exception e) { env.messageOutput().printException("Internal exception:", e); if (e instanceof RuntimeException) { throw (RuntimeException)e; } else { throw new RuntimeException(e); } } } // class implementing EventNotifier interface private class EventNotifierImpl implements EventNotifier { public boolean shouldRemoveListener() { return false; } public void breakpointEvent(BreakpointEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.breakpointEvent(evt); } } } public void classPrepareEvent(ClassPrepareEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.classPrepareEvent(evt); } } } public void classUnloadEvent(ClassUnloadEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.classUnloadEvent(evt); } } } public void exceptionEvent(ExceptionEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.exceptionEvent(evt); } } } public void fieldWatchEvent(WatchpointEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.fieldWatchEvent(evt); } } } public void methodEntryEvent(MethodEntryEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.methodEntryEvent(evt); } } } public boolean methodExitEvent(MethodExitEvent evt) { boolean result = false; synchronized (listeners) { for (EventNotifier en : listeners) { result |= en.methodExitEvent(evt); } } return result; } public void receivedEvent(Event evt) { synchronized (listeners) { ListIterator<EventNotifier> itr = listeners.listIterator(); if (itr.hasNext()) { EventNotifier en = itr.next(); if (en.shouldRemoveListener()) { itr.remove(); } else { en.receivedEvent(evt); if (en.shouldRemoveListener()) { itr.remove(); } } } } } public void stepEvent(StepEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.stepEvent(evt); } } } public void threadDeathEvent(ThreadDeathEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.threadDeathEvent(evt); } } } public void threadStartEvent(ThreadStartEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.threadStartEvent(evt); } } } public void vmDeathEvent(VMDeathEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.vmDeathEvent(evt); } } } public void vmDisconnectEvent(VMDisconnectEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.vmDisconnectEvent(evt); } } } public void vmInterrupted() { synchronized (listeners) { for (EventNotifier en : listeners) { en.vmInterrupted(); } } } public void vmStartEvent(VMStartEvent evt) { synchronized (listeners) { for (EventNotifier en : listeners) { en.vmStartEvent(evt); } } } } }