| /* |
| * Copyright 1998-2004 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. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun in the LICENSE file that accompanied this code. |
| * |
| * 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 com.sun.tools.example.debug.tty; |
| |
| import com.sun.jdi.*; |
| import com.sun.jdi.connect.Connector; |
| import com.sun.jdi.request.*; |
| import com.sun.tools.example.debug.expr.ExpressionParser; |
| import com.sun.tools.example.debug.expr.ParseException; |
| |
| import java.text.*; |
| import java.util.*; |
| import java.io.*; |
| |
| class Commands { |
| |
| abstract class AsyncExecution { |
| abstract void action(); |
| |
| AsyncExecution() { |
| execute(); |
| } |
| |
| void execute() { |
| /* |
| * Save current thread and stack frame. (BugId 4296031) |
| */ |
| final ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| final int stackFrame = threadInfo == null? 0 : threadInfo.getCurrentFrameIndex(); |
| Thread thread = new Thread("asynchronous jdb command") { |
| public void run() { |
| try { |
| action(); |
| } catch (UnsupportedOperationException uoe) { |
| //(BugId 4453329) |
| MessageOutput.println("Operation is not supported on the target VM"); |
| } catch (Exception e) { |
| MessageOutput.println("Internal exception during operation:", |
| e.getMessage()); |
| } finally { |
| /* |
| * This was an asynchronous command. Events may have been |
| * processed while it was running. Restore the thread and |
| * stack frame the user was looking at. (BugId 4296031) |
| */ |
| if (threadInfo != null) { |
| ThreadInfo.setCurrentThreadInfo(threadInfo); |
| try { |
| threadInfo.setCurrentFrameIndex(stackFrame); |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Current thread isnt suspended."); |
| } catch (ArrayIndexOutOfBoundsException e) { |
| MessageOutput.println("Requested stack frame is no longer active:", |
| new Object []{new Integer(stackFrame)}); |
| } |
| } |
| MessageOutput.printPrompt(); |
| } |
| } |
| }; |
| thread.start(); |
| } |
| } |
| |
| Commands() { |
| } |
| |
| private Value evaluate(String expr) { |
| Value result = null; |
| ExpressionParser.GetFrame frameGetter = null; |
| try { |
| final ThreadInfo threadInfo = ThreadInfo.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 (InvocationException ie) { |
| MessageOutput.println("Exception in expression:", |
| ie.exception().referenceType().name()); |
| } catch (Exception ex) { |
| String exMessage = ex.getMessage(); |
| if (exMessage == null) { |
| MessageOutput.printException(exMessage, ex); |
| } else { |
| String s; |
| try { |
| s = MessageOutput.format(exMessage); |
| } catch (MissingResourceException mex) { |
| s = ex.toString(); |
| } |
| MessageOutput.printDirectln(s);// Special case: use printDirectln() |
| } |
| } |
| return result; |
| } |
| |
| private String getStringValue() { |
| Value val = null; |
| String valStr = null; |
| try { |
| val = ExpressionParser.getMassagedValue(); |
| valStr = val.toString(); |
| } catch (ParseException e) { |
| String msg = e.getMessage(); |
| if (msg == null) { |
| MessageOutput.printException(msg, e); |
| } else { |
| String s; |
| try { |
| s = MessageOutput.format(msg); |
| } catch (MissingResourceException mex) { |
| s = e.toString(); |
| } |
| MessageOutput.printDirectln(s); |
| } |
| } |
| return valStr; |
| } |
| |
| private ThreadInfo doGetThread(String idToken) { |
| ThreadInfo threadInfo = ThreadInfo.getThreadInfo(idToken); |
| if (threadInfo == null) { |
| MessageOutput.println("is not a valid thread id", idToken); |
| } |
| return threadInfo; |
| } |
| |
| String typedName(Method method) { |
| StringBuffer buf = new StringBuffer(); |
| buf.append(method.name()); |
| buf.append("("); |
| |
| List<String> args = method.argumentTypeNames(); |
| int lastParam = args.size() - 1; |
| // output param types except for the last |
| for (int ii = 0; ii < lastParam; ii++) { |
| buf.append(args.get(ii)); |
| buf.append(", "); |
| } |
| if (lastParam >= 0) { |
| // output the last param |
| String lastStr = args.get(lastParam); |
| if (method.isVarArgs()) { |
| // lastParam is an array. Replace the [] with ... |
| buf.append(lastStr.substring(0, lastStr.length() - 2)); |
| buf.append("..."); |
| } else { |
| buf.append(lastStr); |
| } |
| } |
| buf.append(")"); |
| return buf.toString(); |
| } |
| |
| void commandConnectors(VirtualMachineManager vmm) { |
| Collection<Connector> ccs = vmm.allConnectors(); |
| if (ccs.isEmpty()) { |
| MessageOutput.println("Connectors available"); |
| } |
| for (Connector cc : ccs) { |
| String transportName = |
| cc.transport() == null ? "null" : cc.transport().name(); |
| MessageOutput.println(); |
| MessageOutput.println("Connector and Transport name", |
| new Object [] {cc.name(), transportName}); |
| MessageOutput.println("Connector description", cc.description()); |
| |
| for (Connector.Argument aa : cc.defaultArguments().values()) { |
| MessageOutput.println(); |
| |
| boolean requiredArgument = aa.mustSpecify(); |
| if (aa.value() == null || aa.value() == "") { |
| //no current value and no default. |
| MessageOutput.println(requiredArgument ? |
| "Connector required argument nodefault" : |
| "Connector argument nodefault", aa.name()); |
| } else { |
| MessageOutput.println(requiredArgument ? |
| "Connector required argument default" : |
| "Connector argument default", |
| new Object [] {aa.name(), aa.value()}); |
| } |
| MessageOutput.println("Connector description", aa.description()); |
| |
| } |
| } |
| |
| } |
| |
| void commandClasses() { |
| StringBuffer classList = new StringBuffer(); |
| for (ReferenceType refType : Env.vm().allClasses()) { |
| classList.append(refType.name()); |
| classList.append("\n"); |
| } |
| MessageOutput.print("** classes list **", classList.toString()); |
| } |
| |
| void commandClass(StringTokenizer t) { |
| List<ReferenceType> list = Env.vm().allClasses(); |
| |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No class specified."); |
| return; |
| } |
| |
| String idClass = t.nextToken(); |
| boolean showAll = false; |
| |
| if (t.hasMoreTokens()) { |
| if (t.nextToken().toLowerCase().equals("all")) { |
| showAll = true; |
| } else { |
| MessageOutput.println("Invalid option on class command"); |
| return; |
| } |
| } |
| ReferenceType type = Env.getReferenceTypeFromToken(idClass); |
| if (type == null) { |
| MessageOutput.println("is not a valid id or class name", idClass); |
| return; |
| } |
| if (type instanceof ClassType) { |
| ClassType clazz = (ClassType)type; |
| MessageOutput.println("Class:", clazz.name()); |
| |
| ClassType superclass = clazz.superclass(); |
| while (superclass != null) { |
| MessageOutput.println("extends:", superclass.name()); |
| superclass = showAll ? superclass.superclass() : null; |
| } |
| |
| List<InterfaceType> interfaces = |
| showAll ? clazz.allInterfaces() : clazz.interfaces(); |
| for (InterfaceType interfaze : interfaces) { |
| MessageOutput.println("implements:", interfaze.name()); |
| } |
| |
| for (ClassType sub : clazz.subclasses()) { |
| MessageOutput.println("subclass:", sub.name()); |
| } |
| for (ReferenceType nest : clazz.nestedTypes()) { |
| MessageOutput.println("nested:", nest.name()); |
| } |
| } else if (type instanceof InterfaceType) { |
| InterfaceType interfaze = (InterfaceType)type; |
| MessageOutput.println("Interface:", interfaze.name()); |
| for (InterfaceType superinterface : interfaze.superinterfaces()) { |
| MessageOutput.println("extends:", superinterface.name()); |
| } |
| for (InterfaceType sub : interfaze.subinterfaces()) { |
| MessageOutput.println("subinterface:", sub.name()); |
| } |
| for (ClassType implementor : interfaze.implementors()) { |
| MessageOutput.println("implementor:", implementor.name()); |
| } |
| for (ReferenceType nest : interfaze.nestedTypes()) { |
| MessageOutput.println("nested:", nest.name()); |
| } |
| } else { // array type |
| ArrayType array = (ArrayType)type; |
| MessageOutput.println("Array:", array.name()); |
| } |
| } |
| |
| void commandMethods(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No class specified."); |
| return; |
| } |
| |
| String idClass = t.nextToken(); |
| ReferenceType cls = Env.getReferenceTypeFromToken(idClass); |
| if (cls != null) { |
| StringBuffer methodsList = new StringBuffer(); |
| for (Method method : cls.allMethods()) { |
| methodsList.append(method.declaringType().name()); |
| methodsList.append(" "); |
| methodsList.append(typedName(method)); |
| methodsList.append('\n'); |
| } |
| MessageOutput.print("** methods list **", methodsList.toString()); |
| } else { |
| MessageOutput.println("is not a valid id or class name", idClass); |
| } |
| } |
| |
| void commandFields(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No class specified."); |
| return; |
| } |
| |
| String idClass = t.nextToken(); |
| ReferenceType cls = Env.getReferenceTypeFromToken(idClass); |
| if (cls != null) { |
| List<Field> fields = cls.allFields(); |
| List<Field> visible = cls.visibleFields(); |
| StringBuffer fieldsList = new StringBuffer(); |
| for (Field field : fields) { |
| String s; |
| if (!visible.contains(field)) { |
| s = MessageOutput.format("list field typename and name hidden", |
| new Object [] {field.typeName(), |
| field.name()}); |
| } else if (!field.declaringType().equals(cls)) { |
| s = MessageOutput.format("list field typename and name inherited", |
| new Object [] {field.typeName(), |
| field.name(), |
| field.declaringType().name()}); |
| } else { |
| s = MessageOutput.format("list field typename and name", |
| new Object [] {field.typeName(), |
| field.name()}); |
| } |
| fieldsList.append(s); |
| } |
| MessageOutput.print("** fields list **", fieldsList.toString()); |
| } else { |
| MessageOutput.println("is not a valid id or class name", idClass); |
| } |
| } |
| |
| private void printThreadGroup(ThreadGroupReference tg) { |
| ThreadIterator threadIter = new ThreadIterator(tg); |
| |
| MessageOutput.println("Thread Group:", tg.name()); |
| int maxIdLength = 0; |
| int maxNameLength = 0; |
| while (threadIter.hasNext()) { |
| ThreadReference thr = threadIter.next(); |
| maxIdLength = Math.max(maxIdLength, |
| Env.description(thr).length()); |
| maxNameLength = Math.max(maxNameLength, |
| thr.name().length()); |
| } |
| |
| threadIter = new ThreadIterator(tg); |
| while (threadIter.hasNext()) { |
| ThreadReference thr = threadIter.next(); |
| if (thr.threadGroup() == null) { |
| continue; |
| } |
| // Note any thread group changes |
| if (!thr.threadGroup().equals(tg)) { |
| tg = thr.threadGroup(); |
| MessageOutput.println("Thread Group:", tg.name()); |
| } |
| |
| /* |
| * Do a bit of filling with whitespace to get thread ID |
| * and thread names to line up in the listing, and also |
| * allow for proper localization. This also works for |
| * very long thread names, at the possible cost of lines |
| * being wrapped by the display device. |
| */ |
| StringBuffer idBuffer = new StringBuffer(Env.description(thr)); |
| for (int i = idBuffer.length(); i < maxIdLength; i++) { |
| idBuffer.append(" "); |
| } |
| StringBuffer nameBuffer = new StringBuffer(thr.name()); |
| for (int i = nameBuffer.length(); i < maxNameLength; i++) { |
| nameBuffer.append(" "); |
| } |
| |
| /* |
| * Select the output format to use based on thread status |
| * and breakpoint. |
| */ |
| String statusFormat; |
| switch (thr.status()) { |
| case ThreadReference.THREAD_STATUS_UNKNOWN: |
| if (thr.isAtBreakpoint()) { |
| statusFormat = "Thread description name unknownStatus BP"; |
| } else { |
| statusFormat = "Thread description name unknownStatus"; |
| } |
| break; |
| case ThreadReference.THREAD_STATUS_ZOMBIE: |
| if (thr.isAtBreakpoint()) { |
| statusFormat = "Thread description name zombieStatus BP"; |
| } else { |
| statusFormat = "Thread description name zombieStatus"; |
| } |
| break; |
| case ThreadReference.THREAD_STATUS_RUNNING: |
| if (thr.isAtBreakpoint()) { |
| statusFormat = "Thread description name runningStatus BP"; |
| } else { |
| statusFormat = "Thread description name runningStatus"; |
| } |
| break; |
| case ThreadReference.THREAD_STATUS_SLEEPING: |
| if (thr.isAtBreakpoint()) { |
| statusFormat = "Thread description name sleepingStatus BP"; |
| } else { |
| statusFormat = "Thread description name sleepingStatus"; |
| } |
| break; |
| case ThreadReference.THREAD_STATUS_MONITOR: |
| if (thr.isAtBreakpoint()) { |
| statusFormat = "Thread description name waitingStatus BP"; |
| } else { |
| statusFormat = "Thread description name waitingStatus"; |
| } |
| break; |
| case ThreadReference.THREAD_STATUS_WAIT: |
| if (thr.isAtBreakpoint()) { |
| statusFormat = "Thread description name condWaitstatus BP"; |
| } else { |
| statusFormat = "Thread description name condWaitstatus"; |
| } |
| break; |
| default: |
| throw new InternalError(MessageOutput.format("Invalid thread status.")); |
| } |
| MessageOutput.println(statusFormat, |
| new Object [] {idBuffer.toString(), |
| nameBuffer.toString()}); |
| } |
| } |
| |
| void commandThreads(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| printThreadGroup(ThreadInfo.group()); |
| return; |
| } |
| String name = t.nextToken(); |
| ThreadGroupReference tg = ThreadGroupIterator.find(name); |
| if (tg == null) { |
| MessageOutput.println("is not a valid threadgroup name", name); |
| } else { |
| printThreadGroup(tg); |
| } |
| } |
| |
| void commandThreadGroups() { |
| ThreadGroupIterator it = new ThreadGroupIterator(); |
| int cnt = 0; |
| while (it.hasNext()) { |
| ThreadGroupReference tg = it.nextThreadGroup(); |
| ++cnt; |
| MessageOutput.println("thread group number description name", |
| new Object [] { new Integer (cnt), |
| Env.description(tg), |
| tg.name()}); |
| } |
| } |
| |
| void commandThread(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Thread number not specified."); |
| return; |
| } |
| ThreadInfo threadInfo = doGetThread(t.nextToken()); |
| if (threadInfo != null) { |
| ThreadInfo.setCurrentThreadInfo(threadInfo); |
| } |
| } |
| |
| void commandThreadGroup(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Threadgroup name not specified."); |
| return; |
| } |
| String name = t.nextToken(); |
| ThreadGroupReference tg = ThreadGroupIterator.find(name); |
| if (tg == null) { |
| MessageOutput.println("is not a valid threadgroup name", name); |
| } else { |
| ThreadInfo.setThreadGroup(tg); |
| } |
| } |
| |
| void commandRun(StringTokenizer t) { |
| /* |
| * The 'run' command makes little sense in a |
| * that doesn't support restarts or multiple VMs. However, |
| * this is an attempt to emulate the behavior of the old |
| * JDB as much as possible. For new users and implementations |
| * it is much more straightforward to launch immedidately |
| * with the -launch option. |
| */ |
| VMConnection connection = Env.connection(); |
| if (!connection.isLaunch()) { |
| if (!t.hasMoreTokens()) { |
| commandCont(); |
| } else { |
| MessageOutput.println("run <args> command is valid only with launched VMs"); |
| } |
| return; |
| } |
| if (connection.isOpen()) { |
| MessageOutput.println("VM already running. use cont to continue after events."); |
| return; |
| } |
| |
| /* |
| * Set the main class and any arguments. Note that this will work |
| * only with the standard launcher, "com.sun.jdi.CommandLineLauncher" |
| */ |
| String args; |
| if (t.hasMoreTokens()) { |
| args = t.nextToken(""); |
| boolean argsSet = connection.setConnectorArg("main", args); |
| if (!argsSet) { |
| MessageOutput.println("Unable to set main class and arguments"); |
| return; |
| } |
| } else { |
| args = connection.connectorArg("main"); |
| if (args.length() == 0) { |
| MessageOutput.println("Main class and arguments must be specified"); |
| return; |
| } |
| } |
| MessageOutput.println("run", args); |
| |
| /* |
| * Launch the VM. |
| */ |
| connection.open(); |
| |
| } |
| |
| void commandLoad(StringTokenizer t) { |
| MessageOutput.println("The load command is no longer supported."); |
| } |
| |
| private List<ThreadReference> allThreads(ThreadGroupReference group) { |
| List<ThreadReference> list = new ArrayList<ThreadReference>(); |
| list.addAll(group.threads()); |
| for (ThreadGroupReference child : group.threadGroups()) { |
| list.addAll(allThreads(child)); |
| } |
| return list; |
| } |
| |
| void commandSuspend(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| Env.vm().suspend(); |
| MessageOutput.println("All threads suspended."); |
| } else { |
| while (t.hasMoreTokens()) { |
| ThreadInfo threadInfo = doGetThread(t.nextToken()); |
| if (threadInfo != null) { |
| threadInfo.getThread().suspend(); |
| } |
| } |
| } |
| } |
| |
| void commandResume(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| ThreadInfo.invalidateAll(); |
| Env.vm().resume(); |
| MessageOutput.println("All threads resumed."); |
| } else { |
| while (t.hasMoreTokens()) { |
| ThreadInfo threadInfo = doGetThread(t.nextToken()); |
| if (threadInfo != null) { |
| threadInfo.invalidate(); |
| threadInfo.getThread().resume(); |
| } |
| } |
| } |
| } |
| |
| void commandCont() { |
| if (ThreadInfo.getCurrentThreadInfo() == null) { |
| MessageOutput.println("Nothing suspended."); |
| return; |
| } |
| ThreadInfo.invalidateAll(); |
| Env.vm().resume(); |
| } |
| |
| void clearPreviousStep(ThreadReference thread) { |
| /* |
| * A previous step may not have completed on this thread; |
| * if so, it gets removed here. |
| */ |
| EventRequestManager mgr = Env.vm().eventRequestManager(); |
| for (StepRequest request : mgr.stepRequests()) { |
| if (request.thread().equals(thread)) { |
| mgr.deleteEventRequest(request); |
| break; |
| } |
| } |
| } |
| /* step |
| * |
| */ |
| void commandStep(StringTokenizer t) { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("Nothing suspended."); |
| return; |
| } |
| int depth; |
| if (t.hasMoreTokens() && |
| t.nextToken().toLowerCase().equals("up")) { |
| depth = StepRequest.STEP_OUT; |
| } else { |
| depth = StepRequest.STEP_INTO; |
| } |
| |
| clearPreviousStep(threadInfo.getThread()); |
| EventRequestManager reqMgr = Env.vm().eventRequestManager(); |
| StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(), |
| StepRequest.STEP_LINE, depth); |
| if (depth == StepRequest.STEP_INTO) { |
| Env.addExcludes(request); |
| } |
| // We want just the next step event and no others |
| request.addCountFilter(1); |
| request.enable(); |
| ThreadInfo.invalidateAll(); |
| Env.vm().resume(); |
| } |
| |
| /* stepi |
| * step instruction. |
| */ |
| void commandStepi() { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("Nothing suspended."); |
| return; |
| } |
| clearPreviousStep(threadInfo.getThread()); |
| EventRequestManager reqMgr = Env.vm().eventRequestManager(); |
| StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(), |
| StepRequest.STEP_MIN, |
| StepRequest.STEP_INTO); |
| Env.addExcludes(request); |
| // We want just the next step event and no others |
| request.addCountFilter(1); |
| request.enable(); |
| ThreadInfo.invalidateAll(); |
| Env.vm().resume(); |
| } |
| |
| void commandNext() { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("Nothing suspended."); |
| return; |
| } |
| clearPreviousStep(threadInfo.getThread()); |
| EventRequestManager reqMgr = Env.vm().eventRequestManager(); |
| StepRequest request = reqMgr.createStepRequest(threadInfo.getThread(), |
| StepRequest.STEP_LINE, |
| StepRequest.STEP_OVER); |
| Env.addExcludes(request); |
| // We want just the next step event and no others |
| request.addCountFilter(1); |
| request.enable(); |
| ThreadInfo.invalidateAll(); |
| Env.vm().resume(); |
| } |
| |
| void doKill(ThreadReference thread, StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No exception object specified."); |
| return; |
| } |
| String expr = t.nextToken(""); |
| Value val = evaluate(expr); |
| if ((val != null) && (val instanceof ObjectReference)) { |
| try { |
| thread.stop((ObjectReference)val); |
| MessageOutput.println("killed", thread.toString()); |
| } catch (InvalidTypeException e) { |
| MessageOutput.println("Invalid exception object"); |
| } |
| } else { |
| MessageOutput.println("Expression must evaluate to an object"); |
| } |
| } |
| |
| void doKillThread(final ThreadReference threadToKill, |
| final StringTokenizer tokenizer) { |
| new AsyncExecution() { |
| void action() { |
| doKill(threadToKill, tokenizer); |
| } |
| }; |
| } |
| |
| void commandKill(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Usage: kill <thread id> <throwable>"); |
| return; |
| } |
| ThreadInfo threadInfo = doGetThread(t.nextToken()); |
| if (threadInfo != null) { |
| MessageOutput.println("killing thread:", threadInfo.getThread().name()); |
| doKillThread(threadInfo.getThread(), t); |
| return; |
| } |
| } |
| |
| void listCaughtExceptions() { |
| boolean noExceptions = true; |
| |
| // Print a listing of the catch patterns currently in place |
| for (EventRequestSpec spec : Env.specList.eventRequestSpecs()) { |
| if (spec instanceof ExceptionSpec) { |
| if (noExceptions) { |
| noExceptions = false; |
| MessageOutput.println("Exceptions caught:"); |
| } |
| MessageOutput.println("tab", spec.toString()); |
| } |
| } |
| if (noExceptions) { |
| MessageOutput.println("No exceptions caught."); |
| } |
| } |
| |
| private EventRequestSpec parseExceptionSpec(StringTokenizer t) { |
| String notification = t.nextToken(); |
| boolean notifyCaught = false; |
| boolean notifyUncaught = false; |
| EventRequestSpec spec = null; |
| String classPattern = null; |
| |
| if (notification.equals("uncaught")) { |
| notifyCaught = false; |
| notifyUncaught = true; |
| } else if (notification.equals("caught")) { |
| notifyCaught = true; |
| notifyUncaught = false; |
| } else if (notification.equals("all")) { |
| notifyCaught = true; |
| notifyUncaught = true; |
| } else { |
| /* |
| * Handle the same as "all" for backward |
| * compatibility with existing .jdbrc files. |
| * |
| * Insert an "all" and take the current token as the |
| * intended classPattern |
| * |
| */ |
| notifyCaught = true; |
| notifyUncaught = true; |
| classPattern = notification; |
| } |
| if (classPattern == null && t.hasMoreTokens()) { |
| classPattern = t.nextToken(); |
| } |
| if ((classPattern != null) && (notifyCaught || notifyUncaught)) { |
| try { |
| spec = Env.specList.createExceptionCatch(classPattern, |
| notifyCaught, |
| notifyUncaught); |
| } catch (ClassNotFoundException exc) { |
| MessageOutput.println("is not a valid class name", classPattern); |
| } |
| } |
| return spec; |
| } |
| |
| void commandCatchException(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| listCaughtExceptions(); |
| } else { |
| EventRequestSpec spec = parseExceptionSpec(t); |
| if (spec != null) { |
| resolveNow(spec); |
| } else { |
| MessageOutput.println("Usage: catch exception"); |
| } |
| } |
| } |
| |
| void commandIgnoreException(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| listCaughtExceptions(); |
| } else { |
| EventRequestSpec spec = parseExceptionSpec(t); |
| if (Env.specList.delete(spec)) { |
| MessageOutput.println("Removed:", spec.toString()); |
| } else { |
| if (spec != null) { |
| MessageOutput.println("Not found:", spec.toString()); |
| } |
| MessageOutput.println("Usage: ignore exception"); |
| } |
| } |
| } |
| |
| void commandUp(StringTokenizer t) { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("Current thread not set."); |
| return; |
| } |
| |
| int nLevels = 1; |
| if (t.hasMoreTokens()) { |
| String idToken = t.nextToken(); |
| int i; |
| try { |
| NumberFormat nf = NumberFormat.getNumberInstance(); |
| nf.setParseIntegerOnly(true); |
| Number n = nf.parse(idToken); |
| i = n.intValue(); |
| } catch (java.text.ParseException jtpe) { |
| i = 0; |
| } |
| if (i <= 0) { |
| MessageOutput.println("Usage: up [n frames]"); |
| return; |
| } |
| nLevels = i; |
| } |
| |
| try { |
| threadInfo.up(nLevels); |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Current thread isnt suspended."); |
| } catch (ArrayIndexOutOfBoundsException e) { |
| MessageOutput.println("End of stack."); |
| } |
| } |
| |
| void commandDown(StringTokenizer t) { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("Current thread not set."); |
| return; |
| } |
| |
| int nLevels = 1; |
| if (t.hasMoreTokens()) { |
| String idToken = t.nextToken(); |
| int i; |
| try { |
| NumberFormat nf = NumberFormat.getNumberInstance(); |
| nf.setParseIntegerOnly(true); |
| Number n = nf.parse(idToken); |
| i = n.intValue(); |
| } catch (java.text.ParseException jtpe) { |
| i = 0; |
| } |
| if (i <= 0) { |
| MessageOutput.println("Usage: down [n frames]"); |
| return; |
| } |
| nLevels = i; |
| } |
| |
| try { |
| threadInfo.down(nLevels); |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Current thread isnt suspended."); |
| } catch (ArrayIndexOutOfBoundsException e) { |
| MessageOutput.println("End of stack."); |
| } |
| } |
| |
| private void dumpStack(ThreadInfo threadInfo, boolean showPC) { |
| List<StackFrame> stack = null; |
| try { |
| stack = threadInfo.getStack(); |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Current thread isnt suspended."); |
| return; |
| } |
| if (stack == null) { |
| MessageOutput.println("Thread is not running (no stack)."); |
| } else { |
| int nFrames = stack.size(); |
| for (int i = threadInfo.getCurrentFrameIndex(); i < nFrames; i++) { |
| StackFrame frame = stack.get(i); |
| dumpFrame (i, showPC, frame); |
| } |
| } |
| } |
| |
| private void dumpFrame (int frameNumber, boolean showPC, StackFrame frame) { |
| Location loc = frame.location(); |
| long pc = -1; |
| if (showPC) { |
| pc = loc.codeIndex(); |
| } |
| Method meth = loc.method(); |
| |
| long lineNumber = loc.lineNumber(); |
| String methodInfo = null; |
| if (meth.isNative()) { |
| methodInfo = MessageOutput.format("native method"); |
| } else if (lineNumber != -1) { |
| try { |
| methodInfo = loc.sourceName() + |
| MessageOutput.format("line number", |
| new Object [] {new Long(lineNumber)}); |
| } catch (AbsentInformationException e) { |
| methodInfo = MessageOutput.format("unknown"); |
| } |
| } |
| if (pc != -1) { |
| MessageOutput.println("stack frame dump with pc", |
| new Object [] {new Integer(frameNumber + 1), |
| meth.declaringType().name(), |
| meth.name(), |
| methodInfo, |
| new Long(pc)}); |
| } else { |
| MessageOutput.println("stack frame dump", |
| new Object [] {new Integer(frameNumber + 1), |
| meth.declaringType().name(), |
| meth.name(), |
| methodInfo}); |
| } |
| } |
| |
| void commandWhere(StringTokenizer t, boolean showPC) { |
| if (!t.hasMoreTokens()) { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("No thread specified."); |
| return; |
| } |
| dumpStack(threadInfo, showPC); |
| } else { |
| String token = t.nextToken(); |
| if (token.toLowerCase().equals("all")) { |
| for (ThreadInfo threadInfo : ThreadInfo.threads()) { |
| MessageOutput.println("Thread:", |
| threadInfo.getThread().name()); |
| dumpStack(threadInfo, showPC); |
| } |
| } else { |
| ThreadInfo threadInfo = doGetThread(token); |
| if (threadInfo != null) { |
| ThreadInfo.setCurrentThreadInfo(threadInfo); |
| dumpStack(threadInfo, showPC); |
| } |
| } |
| } |
| } |
| |
| void commandInterrupt(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("No thread specified."); |
| return; |
| } |
| threadInfo.getThread().interrupt(); |
| } else { |
| ThreadInfo threadInfo = doGetThread(t.nextToken()); |
| if (threadInfo != null) { |
| threadInfo.getThread().interrupt(); |
| } |
| } |
| } |
| |
| void commandMemory() { |
| MessageOutput.println("The memory command is no longer supported."); |
| } |
| |
| void commandGC() { |
| MessageOutput.println("The gc command is no longer necessary."); |
| } |
| |
| /* |
| * The next two methods are used by this class and by EventHandler |
| * to print consistent locations and error messages. |
| */ |
| static String locationString(Location loc) { |
| return MessageOutput.format("locationString", |
| new Object [] {loc.declaringType().name(), |
| loc.method().name(), |
| new Integer (loc.lineNumber()), |
| new Long (loc.codeIndex())}); |
| } |
| |
| void listBreakpoints() { |
| boolean noBreakpoints = true; |
| |
| // Print set breakpoints |
| for (EventRequestSpec spec : Env.specList.eventRequestSpecs()) { |
| if (spec instanceof BreakpointSpec) { |
| if (noBreakpoints) { |
| noBreakpoints = false; |
| MessageOutput.println("Breakpoints set:"); |
| } |
| MessageOutput.println("tab", spec.toString()); |
| } |
| } |
| if (noBreakpoints) { |
| MessageOutput.println("No breakpoints set."); |
| } |
| } |
| |
| |
| private void printBreakpointCommandUsage(String atForm, String inForm) { |
| MessageOutput.println("printbreakpointcommandusage", |
| new Object [] {atForm, inForm}); |
| } |
| |
| protected BreakpointSpec parseBreakpointSpec(StringTokenizer t, |
| String atForm, String inForm) { |
| BreakpointSpec breakpoint = null; |
| try { |
| String token = t.nextToken(":( \t\n\r"); |
| |
| // We can't use hasMoreTokens here because it will cause any leading |
| // paren to be lost. |
| String rest; |
| try { |
| rest = t.nextToken("").trim(); |
| } catch (NoSuchElementException e) { |
| rest = null; |
| } |
| |
| if ((rest != null) && rest.startsWith(":")) { |
| t = new StringTokenizer(rest.substring(1)); |
| String classId = token; |
| String lineToken = t.nextToken(); |
| |
| NumberFormat nf = NumberFormat.getNumberInstance(); |
| nf.setParseIntegerOnly(true); |
| Number n = nf.parse(lineToken); |
| int lineNumber = n.intValue(); |
| |
| if (t.hasMoreTokens()) { |
| printBreakpointCommandUsage(atForm, inForm); |
| return null; |
| } |
| try { |
| breakpoint = Env.specList.createBreakpoint(classId, |
| lineNumber); |
| } catch (ClassNotFoundException exc) { |
| MessageOutput.println("is not a valid class name", classId); |
| } |
| } else { |
| // Try stripping method from class.method token. |
| int idot = token.lastIndexOf("."); |
| if ( (idot <= 0) || /* No dot or dot in first char */ |
| (idot >= token.length() - 1) ) { /* dot in last char */ |
| printBreakpointCommandUsage(atForm, inForm); |
| return null; |
| } |
| String methodName = token.substring(idot + 1); |
| String classId = token.substring(0, idot); |
| List<String> argumentList = null; |
| if (rest != null) { |
| if (!rest.startsWith("(") || !rest.endsWith(")")) { |
| MessageOutput.println("Invalid method specification:", |
| methodName + rest); |
| printBreakpointCommandUsage(atForm, inForm); |
| return null; |
| } |
| // Trim the parens |
| rest = rest.substring(1, rest.length() - 1); |
| |
| argumentList = new ArrayList<String>(); |
| t = new StringTokenizer(rest, ","); |
| while (t.hasMoreTokens()) { |
| argumentList.add(t.nextToken()); |
| } |
| } |
| try { |
| breakpoint = Env.specList.createBreakpoint(classId, |
| methodName, |
| argumentList); |
| } catch (MalformedMemberNameException exc) { |
| MessageOutput.println("is not a valid method name", methodName); |
| } catch (ClassNotFoundException exc) { |
| MessageOutput.println("is not a valid class name", classId); |
| } |
| } |
| } catch (Exception e) { |
| printBreakpointCommandUsage(atForm, inForm); |
| return null; |
| } |
| return breakpoint; |
| } |
| |
| private void resolveNow(EventRequestSpec spec) { |
| boolean success = Env.specList.addEagerlyResolve(spec); |
| if (success && !spec.isResolved()) { |
| MessageOutput.println("Deferring.", spec.toString()); |
| } |
| } |
| |
| void commandStop(StringTokenizer t) { |
| Location bploc; |
| String atIn; |
| byte suspendPolicy = EventRequest.SUSPEND_ALL; |
| |
| if (t.hasMoreTokens()) { |
| atIn = t.nextToken(); |
| if (atIn.equals("go") && t.hasMoreTokens()) { |
| suspendPolicy = EventRequest.SUSPEND_NONE; |
| atIn = t.nextToken(); |
| } else if (atIn.equals("thread") && t.hasMoreTokens()) { |
| suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD; |
| atIn = t.nextToken(); |
| } |
| } else { |
| listBreakpoints(); |
| return; |
| } |
| |
| BreakpointSpec spec = parseBreakpointSpec(t, "stop at", "stop in"); |
| if (spec != null) { |
| // Enforcement of "at" vs. "in". The distinction is really |
| // unnecessary and we should consider not checking for this |
| // (and making "at" and "in" optional). |
| if (atIn.equals("at") && spec.isMethodBreakpoint()) { |
| MessageOutput.println("Use stop at to set a breakpoint at a line number"); |
| printBreakpointCommandUsage("stop at", "stop in"); |
| return; |
| } |
| spec.suspendPolicy = suspendPolicy; |
| resolveNow(spec); |
| } |
| } |
| |
| void commandClear(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| listBreakpoints(); |
| return; |
| } |
| |
| BreakpointSpec spec = parseBreakpointSpec(t, "clear", "clear"); |
| if (spec != null) { |
| if (Env.specList.delete(spec)) { |
| MessageOutput.println("Removed:", spec.toString()); |
| } else { |
| MessageOutput.println("Not found:", spec.toString()); |
| } |
| } |
| } |
| |
| private List<WatchpointSpec> parseWatchpointSpec(StringTokenizer t) { |
| List<WatchpointSpec> list = new ArrayList<WatchpointSpec>(); |
| boolean access = false; |
| boolean modification = false; |
| int suspendPolicy = EventRequest.SUSPEND_ALL; |
| |
| String fieldName = t.nextToken(); |
| if (fieldName.equals("go")) { |
| suspendPolicy = EventRequest.SUSPEND_NONE; |
| fieldName = t.nextToken(); |
| } else if (fieldName.equals("thread")) { |
| suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD; |
| fieldName = t.nextToken(); |
| } |
| if (fieldName.equals("access")) { |
| access = true; |
| fieldName = t.nextToken(); |
| } else if (fieldName.equals("all")) { |
| access = true; |
| modification = true; |
| fieldName = t.nextToken(); |
| } else { |
| modification = true; |
| } |
| int dot = fieldName.lastIndexOf('.'); |
| if (dot < 0) { |
| MessageOutput.println("Class containing field must be specified."); |
| return list; |
| } |
| String className = fieldName.substring(0, dot); |
| fieldName = fieldName.substring(dot+1); |
| |
| try { |
| WatchpointSpec spec; |
| if (access) { |
| spec = Env.specList.createAccessWatchpoint(className, |
| fieldName); |
| spec.suspendPolicy = suspendPolicy; |
| list.add(spec); |
| } |
| if (modification) { |
| spec = Env.specList.createModificationWatchpoint(className, |
| fieldName); |
| spec.suspendPolicy = suspendPolicy; |
| list.add(spec); |
| } |
| } catch (MalformedMemberNameException exc) { |
| MessageOutput.println("is not a valid field name", fieldName); |
| } catch (ClassNotFoundException exc) { |
| MessageOutput.println("is not a valid class name", className); |
| } |
| return list; |
| } |
| |
| void commandWatch(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Field to watch not specified"); |
| return; |
| } |
| |
| for (WatchpointSpec spec : parseWatchpointSpec(t)) { |
| resolveNow(spec); |
| } |
| } |
| |
| void commandUnwatch(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Field to unwatch not specified"); |
| return; |
| } |
| |
| for (WatchpointSpec spec : parseWatchpointSpec(t)) { |
| if (Env.specList.delete(spec)) { |
| MessageOutput.println("Removed:", spec.toString()); |
| } else { |
| MessageOutput.println("Not found:", spec.toString()); |
| } |
| } |
| } |
| |
| void turnOnExitTrace(ThreadInfo threadInfo, int suspendPolicy) { |
| EventRequestManager erm = Env.vm().eventRequestManager(); |
| MethodExitRequest exit = erm.createMethodExitRequest(); |
| if (threadInfo != null) { |
| exit.addThreadFilter(threadInfo.getThread()); |
| } |
| Env.addExcludes(exit); |
| exit.setSuspendPolicy(suspendPolicy); |
| exit.enable(); |
| |
| } |
| |
| static String methodTraceCommand = null; |
| |
| void commandTrace(StringTokenizer t) { |
| String modif; |
| int suspendPolicy = EventRequest.SUSPEND_ALL; |
| ThreadInfo threadInfo = null; |
| String goStr = " "; |
| |
| /* |
| * trace [go] methods [thread] |
| * trace [go] method exit | exits [thread] |
| */ |
| if (t.hasMoreTokens()) { |
| modif = t.nextToken(); |
| if (modif.equals("go")) { |
| suspendPolicy = EventRequest.SUSPEND_NONE; |
| goStr = " go "; |
| if (t.hasMoreTokens()) { |
| modif = t.nextToken(); |
| } |
| } else if (modif.equals("thread")) { |
| // this is undocumented as it doesn't work right. |
| suspendPolicy = EventRequest.SUSPEND_EVENT_THREAD; |
| if (t.hasMoreTokens()) { |
| modif = t.nextToken(); |
| } |
| } |
| |
| if (modif.equals("method")) { |
| String traceCmd = null; |
| |
| if (t.hasMoreTokens()) { |
| String modif1 = t.nextToken(); |
| if (modif1.equals("exits") || modif1.equals("exit")) { |
| if (t.hasMoreTokens()) { |
| threadInfo = doGetThread(t.nextToken()); |
| } |
| if (modif1.equals("exit")) { |
| StackFrame frame; |
| try { |
| frame = ThreadInfo.getCurrentThreadInfo().getCurrentFrame(); |
| } catch (IncompatibleThreadStateException ee) { |
| MessageOutput.println("Current thread isnt suspended."); |
| return; |
| } |
| Env.setAtExitMethod(frame.location().method()); |
| traceCmd = MessageOutput.format("trace" + |
| goStr + "method exit " + |
| "in effect for", |
| Env.atExitMethod().toString()); |
| } else { |
| traceCmd = MessageOutput.format("trace" + |
| goStr + "method exits " + |
| "in effect"); |
| } |
| commandUntrace(new StringTokenizer("methods")); |
| turnOnExitTrace(threadInfo, suspendPolicy); |
| methodTraceCommand = traceCmd; |
| return; |
| } |
| } else { |
| MessageOutput.println("Can only trace"); |
| return; |
| } |
| } |
| if (modif.equals("methods")) { |
| // Turn on method entry trace |
| MethodEntryRequest entry; |
| EventRequestManager erm = Env.vm().eventRequestManager(); |
| if (t.hasMoreTokens()) { |
| threadInfo = doGetThread(t.nextToken()); |
| } |
| if (threadInfo != null) { |
| /* |
| * To keep things simple we want each 'trace' to cancel |
| * previous traces. However in this case, we don't do that |
| * to preserve backward compatibility with pre JDK 6.0. |
| * IE, you can currently do |
| * trace methods 0x21 |
| * trace methods 0x22 |
| * and you will get xxx traced just on those two threads |
| * But this feature is kind of broken because if you then do |
| * untrace 0x21 |
| * it turns off both traces instead of just the one. |
| * Another bogosity is that if you do |
| * trace methods |
| * trace methods |
| * and you will get two traces. |
| */ |
| |
| entry = erm.createMethodEntryRequest(); |
| entry.addThreadFilter(threadInfo.getThread()); |
| } else { |
| commandUntrace(new StringTokenizer("methods")); |
| entry = erm.createMethodEntryRequest(); |
| } |
| Env.addExcludes(entry); |
| entry.setSuspendPolicy(suspendPolicy); |
| entry.enable(); |
| turnOnExitTrace(threadInfo, suspendPolicy); |
| methodTraceCommand = MessageOutput.format("trace" + goStr + |
| "methods in effect"); |
| |
| return; |
| } |
| |
| MessageOutput.println("Can only trace"); |
| return; |
| } |
| |
| // trace all by itself. |
| if (methodTraceCommand != null) { |
| MessageOutput.printDirectln(methodTraceCommand); |
| } |
| |
| // More trace lines can be added here. |
| } |
| |
| void commandUntrace(StringTokenizer t) { |
| // untrace |
| // untrace methods |
| |
| String modif = null; |
| EventRequestManager erm = Env.vm().eventRequestManager(); |
| if (t.hasMoreTokens()) { |
| modif = t.nextToken(); |
| } |
| if (modif == null || modif.equals("methods")) { |
| erm.deleteEventRequests(erm.methodEntryRequests()); |
| erm.deleteEventRequests(erm.methodExitRequests()); |
| Env.setAtExitMethod(null); |
| methodTraceCommand = null; |
| } |
| } |
| |
| void commandList(StringTokenizer t) { |
| StackFrame frame = null; |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("No thread specified."); |
| return; |
| } |
| try { |
| frame = threadInfo.getCurrentFrame(); |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Current thread isnt suspended."); |
| return; |
| } |
| |
| if (frame == null) { |
| MessageOutput.println("No frames on the current call stack"); |
| return; |
| } |
| |
| Location loc = frame.location(); |
| if (loc.method().isNative()) { |
| MessageOutput.println("Current method is native"); |
| return; |
| } |
| |
| String sourceFileName = null; |
| try { |
| sourceFileName = loc.sourceName(); |
| |
| ReferenceType refType = loc.declaringType(); |
| int lineno = loc.lineNumber(); |
| |
| if (t.hasMoreTokens()) { |
| String id = t.nextToken(); |
| |
| // See if token is a line number. |
| try { |
| NumberFormat nf = NumberFormat.getNumberInstance(); |
| nf.setParseIntegerOnly(true); |
| Number n = nf.parse(id); |
| lineno = n.intValue(); |
| } catch (java.text.ParseException jtpe) { |
| // It isn't -- see if it's a method name. |
| List<Method> meths = refType.methodsByName(id); |
| if (meths == null || meths.size() == 0) { |
| MessageOutput.println("is not a valid line number or method name for", |
| new Object [] {id, refType.name()}); |
| return; |
| } else if (meths.size() > 1) { |
| MessageOutput.println("is an ambiguous method name in", |
| new Object [] {id, refType.name()}); |
| return; |
| } |
| loc = meths.get(0).location(); |
| lineno = loc.lineNumber(); |
| } |
| } |
| int startLine = Math.max(lineno - 4, 1); |
| int endLine = startLine + 9; |
| if (lineno < 0) { |
| MessageOutput.println("Line number information not available for"); |
| } else if (Env.sourceLine(loc, lineno) == null) { |
| MessageOutput.println("is an invalid line number for", |
| new Object [] {new Integer (lineno), |
| refType.name()}); |
| } else { |
| for (int i = startLine; i <= endLine; i++) { |
| String sourceLine = Env.sourceLine(loc, i); |
| if (sourceLine == null) { |
| break; |
| } |
| if (i == lineno) { |
| MessageOutput.println("source line number current line and line", |
| new Object [] {new Integer (i), |
| sourceLine}); |
| } else { |
| MessageOutput.println("source line number and line", |
| new Object [] {new Integer (i), |
| sourceLine}); |
| } |
| } |
| } |
| } catch (AbsentInformationException e) { |
| MessageOutput.println("No source information available for:", loc.toString()); |
| } catch(FileNotFoundException exc) { |
| MessageOutput.println("Source file not found:", sourceFileName); |
| } catch(IOException exc) { |
| MessageOutput.println("I/O exception occurred:", exc.toString()); |
| } |
| } |
| |
| void commandLines(StringTokenizer t) { // Undocumented command: useful for testing |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Specify class and method"); |
| } else { |
| String idClass = t.nextToken(); |
| String idMethod = t.hasMoreTokens() ? t.nextToken() : null; |
| try { |
| ReferenceType refType = Env.getReferenceTypeFromToken(idClass); |
| if (refType != null) { |
| List<Location> lines = null; |
| if (idMethod == null) { |
| lines = refType.allLineLocations(); |
| } else { |
| for (Method method : refType.allMethods()) { |
| if (method.name().equals(idMethod)) { |
| lines = method.allLineLocations(); |
| } |
| } |
| if (lines == null) { |
| MessageOutput.println("is not a valid method name", idMethod); |
| } |
| } |
| for (Location line : lines) { |
| MessageOutput.printDirectln(line.toString());// Special case: use printDirectln() |
| } |
| } else { |
| MessageOutput.println("is not a valid id or class name", idClass); |
| } |
| } catch (AbsentInformationException e) { |
| MessageOutput.println("Line number information not available for", idClass); |
| } |
| } |
| } |
| |
| void commandClasspath(StringTokenizer t) { |
| if (Env.vm() instanceof PathSearchingVirtualMachine) { |
| PathSearchingVirtualMachine vm = (PathSearchingVirtualMachine)Env.vm(); |
| MessageOutput.println("base directory:", vm.baseDirectory()); |
| MessageOutput.println("classpath:", vm.classPath().toString()); |
| MessageOutput.println("bootclasspath:", vm.bootClassPath().toString()); |
| } else { |
| MessageOutput.println("The VM does not use paths"); |
| } |
| } |
| |
| /* Get or set the source file path list. */ |
| void commandUse(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.printDirectln(Env.getSourcePath());// Special case: use printDirectln() |
| } else { |
| /* |
| * Take the remainder of the command line, minus |
| * leading or trailing whitespace. Embedded |
| * whitespace is fine. |
| */ |
| Env.setSourcePath(t.nextToken("").trim()); |
| } |
| } |
| |
| /* Print a stack variable */ |
| private void printVar(LocalVariable var, Value value) { |
| MessageOutput.println("expr is value", |
| new Object [] {var.name(), |
| value == null ? "null" : value.toString()}); |
| } |
| |
| /* Print all local variables in current stack frame. */ |
| void commandLocals() { |
| StackFrame frame; |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("No default thread specified:"); |
| return; |
| } |
| try { |
| frame = threadInfo.getCurrentFrame(); |
| if (frame == null) { |
| throw new AbsentInformationException(); |
| } |
| List<LocalVariable> vars = frame.visibleVariables(); |
| |
| if (vars.size() == 0) { |
| MessageOutput.println("No local variables"); |
| return; |
| } |
| Map<LocalVariable, Value> values = frame.getValues(vars); |
| |
| MessageOutput.println("Method arguments:"); |
| for (LocalVariable var : vars) { |
| if (var.isArgument()) { |
| Value val = values.get(var); |
| printVar(var, val); |
| } |
| } |
| MessageOutput.println("Local variables:"); |
| for (LocalVariable var : vars) { |
| if (!var.isArgument()) { |
| Value val = values.get(var); |
| printVar(var, val); |
| } |
| } |
| } catch (AbsentInformationException aie) { |
| MessageOutput.println("Local variable information not available."); |
| } catch (IncompatibleThreadStateException exc) { |
| MessageOutput.println("Current thread isnt suspended."); |
| } |
| } |
| |
| private void dump(ObjectReference obj, ReferenceType refType, |
| ReferenceType refTypeBase) { |
| for (Field field : refType.fields()) { |
| StringBuffer o = new StringBuffer(); |
| o.append(" "); |
| if (!refType.equals(refTypeBase)) { |
| o.append(refType.name()); |
| o.append("."); |
| } |
| o.append(field.name()); |
| o.append(MessageOutput.format("colon space")); |
| o.append(obj.getValue(field)); |
| MessageOutput.printDirectln(o.toString()); // Special case: use printDirectln() |
| } |
| if (refType instanceof ClassType) { |
| ClassType sup = ((ClassType)refType).superclass(); |
| if (sup != null) { |
| dump(obj, sup, refTypeBase); |
| } |
| } else if (refType instanceof InterfaceType) { |
| for (InterfaceType sup : ((InterfaceType)refType).superinterfaces()) { |
| dump(obj, sup, refTypeBase); |
| } |
| } else { |
| /* else refType is an instanceof ArrayType */ |
| if (obj instanceof ArrayReference) { |
| for (Iterator<Value> it = ((ArrayReference)obj).getValues().iterator(); |
| it.hasNext(); ) { |
| MessageOutput.printDirect(it.next().toString());// Special case: use printDirect() |
| if (it.hasNext()) { |
| MessageOutput.printDirect(", ");// Special case: use printDirect() |
| } |
| } |
| MessageOutput.println(); |
| } |
| } |
| } |
| |
| /* Print a specified reference. |
| */ |
| void doPrint(StringTokenizer t, boolean dumpObject) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No objects specified."); |
| return; |
| } |
| |
| while (t.hasMoreTokens()) { |
| String expr = t.nextToken(""); |
| Value val = evaluate(expr); |
| if (val == null) { |
| MessageOutput.println("expr is null", expr.toString()); |
| } else if (dumpObject && (val instanceof ObjectReference) && |
| !(val instanceof StringReference)) { |
| ObjectReference obj = (ObjectReference)val; |
| ReferenceType refType = obj.referenceType(); |
| MessageOutput.println("expr is value", |
| new Object [] {expr.toString(), |
| MessageOutput.format("grouping begin character")}); |
| dump(obj, refType, refType); |
| MessageOutput.println("grouping end character"); |
| } else { |
| String strVal = getStringValue(); |
| if (strVal != null) { |
| MessageOutput.println("expr is value", new Object [] {expr.toString(), |
| strVal}); |
| } |
| } |
| } |
| } |
| |
| void commandPrint(final StringTokenizer t, final boolean dumpObject) { |
| new AsyncExecution() { |
| void action() { |
| doPrint(t, dumpObject); |
| } |
| }; |
| } |
| |
| void commandSet(final StringTokenizer t) { |
| String all = t.nextToken(""); |
| |
| /* |
| * Bare bones error checking. |
| */ |
| if (all.indexOf('=') == -1) { |
| MessageOutput.println("Invalid assignment syntax"); |
| MessageOutput.printPrompt(); |
| return; |
| } |
| |
| /* |
| * The set command is really just syntactic sugar. Pass it on to the |
| * print command. |
| */ |
| commandPrint(new StringTokenizer(all), false); |
| } |
| |
| void doLock(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No object specified."); |
| return; |
| } |
| |
| String expr = t.nextToken(""); |
| Value val = evaluate(expr); |
| |
| try { |
| if ((val != null) && (val instanceof ObjectReference)) { |
| ObjectReference object = (ObjectReference)val; |
| String strVal = getStringValue(); |
| if (strVal != null) { |
| MessageOutput.println("Monitor information for expr", |
| new Object [] {expr.trim(), |
| strVal}); |
| } |
| ThreadReference owner = object.owningThread(); |
| if (owner == null) { |
| MessageOutput.println("Not owned"); |
| } else { |
| MessageOutput.println("Owned by:", |
| new Object [] {owner.name(), |
| new Integer (object.entryCount())}); |
| } |
| List<ThreadReference> waiters = object.waitingThreads(); |
| if (waiters.size() == 0) { |
| MessageOutput.println("No waiters"); |
| } else { |
| for (ThreadReference waiter : waiters) { |
| MessageOutput.println("Waiting thread:", waiter.name()); |
| } |
| } |
| } else { |
| MessageOutput.println("Expression must evaluate to an object"); |
| } |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Threads must be suspended"); |
| } |
| } |
| |
| void commandLock(final StringTokenizer t) { |
| new AsyncExecution() { |
| void action() { |
| doLock(t); |
| } |
| }; |
| } |
| |
| private void printThreadLockInfo(ThreadInfo threadInfo) { |
| ThreadReference thread = threadInfo.getThread(); |
| try { |
| MessageOutput.println("Monitor information for thread", thread.name()); |
| List<ObjectReference> owned = thread.ownedMonitors(); |
| if (owned.size() == 0) { |
| MessageOutput.println("No monitors owned"); |
| } else { |
| for (ObjectReference monitor : owned) { |
| MessageOutput.println("Owned monitor:", monitor.toString()); |
| } |
| } |
| ObjectReference waiting = thread.currentContendedMonitor(); |
| if (waiting == null) { |
| MessageOutput.println("Not waiting for a monitor"); |
| } else { |
| MessageOutput.println("Waiting for monitor:", waiting.toString()); |
| } |
| } catch (IncompatibleThreadStateException e) { |
| MessageOutput.println("Threads must be suspended"); |
| } |
| } |
| |
| void commandThreadlocks(final StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| ThreadInfo threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("Current thread not set."); |
| } else { |
| printThreadLockInfo(threadInfo); |
| } |
| return; |
| } |
| String token = t.nextToken(); |
| if (token.toLowerCase().equals("all")) { |
| for (ThreadInfo threadInfo : ThreadInfo.threads()) { |
| printThreadLockInfo(threadInfo); |
| } |
| } else { |
| ThreadInfo threadInfo = doGetThread(token); |
| if (threadInfo != null) { |
| ThreadInfo.setCurrentThreadInfo(threadInfo); |
| printThreadLockInfo(threadInfo); |
| } |
| } |
| } |
| |
| void doDisableGC(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No object specified."); |
| return; |
| } |
| |
| String expr = t.nextToken(""); |
| Value val = evaluate(expr); |
| if ((val != null) && (val instanceof ObjectReference)) { |
| ObjectReference object = (ObjectReference)val; |
| object.disableCollection(); |
| String strVal = getStringValue(); |
| if (strVal != null) { |
| MessageOutput.println("GC Disabled for", strVal); |
| } |
| } else { |
| MessageOutput.println("Expression must evaluate to an object"); |
| } |
| } |
| |
| void commandDisableGC(final StringTokenizer t) { |
| new AsyncExecution() { |
| void action() { |
| doDisableGC(t); |
| } |
| }; |
| } |
| |
| void doEnableGC(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No object specified."); |
| return; |
| } |
| |
| String expr = t.nextToken(""); |
| Value val = evaluate(expr); |
| if ((val != null) && (val instanceof ObjectReference)) { |
| ObjectReference object = (ObjectReference)val; |
| object.enableCollection(); |
| String strVal = getStringValue(); |
| if (strVal != null) { |
| MessageOutput.println("GC Enabled for", strVal); |
| } |
| } else { |
| MessageOutput.println("Expression must evaluate to an object"); |
| } |
| } |
| |
| void commandEnableGC(final StringTokenizer t) { |
| new AsyncExecution() { |
| void action() { |
| doEnableGC(t); |
| } |
| }; |
| } |
| |
| void doSave(StringTokenizer t) {// Undocumented command: useful for testing. |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No save index specified."); |
| return; |
| } |
| |
| String key = t.nextToken(); |
| |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No expression specified."); |
| return; |
| } |
| String expr = t.nextToken(""); |
| Value val = evaluate(expr); |
| if (val != null) { |
| Env.setSavedValue(key, val); |
| String strVal = getStringValue(); |
| if (strVal != null) { |
| MessageOutput.println("saved", strVal); |
| } |
| } else { |
| MessageOutput.println("Expression cannot be void"); |
| } |
| } |
| |
| void commandSave(final StringTokenizer t) { // Undocumented command: useful for testing. |
| if (!t.hasMoreTokens()) { |
| Set<String> keys = Env.getSaveKeys(); |
| if (keys.isEmpty()) { |
| MessageOutput.println("No saved values"); |
| return; |
| } |
| for (String key : keys) { |
| Value value = Env.getSavedValue(key); |
| if ((value instanceof ObjectReference) && |
| ((ObjectReference)value).isCollected()) { |
| MessageOutput.println("expr is value <collected>", |
| new Object [] {key, value.toString()}); |
| } else { |
| if (value == null){ |
| MessageOutput.println("expr is null", key); |
| } else { |
| MessageOutput.println("expr is value", |
| new Object [] {key, value.toString()}); |
| } |
| } |
| } |
| } else { |
| new AsyncExecution() { |
| void action() { |
| doSave(t); |
| } |
| }; |
| } |
| |
| } |
| |
| void commandBytecodes(final StringTokenizer t) { // Undocumented command: useful for testing. |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No class specified."); |
| return; |
| } |
| String className = t.nextToken(); |
| |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No method specified."); |
| return; |
| } |
| // Overloading is not handled here. |
| String methodName = t.nextToken(); |
| |
| List<ReferenceType> classes = Env.vm().classesByName(className); |
| // TO DO: handle multiple classes found |
| if (classes.size() == 0) { |
| if (className.indexOf('.') < 0) { |
| MessageOutput.println("not found (try the full name)", className); |
| } else { |
| MessageOutput.println("not found", className); |
| } |
| return; |
| } |
| |
| ReferenceType rt = classes.get(0); |
| if (!(rt instanceof ClassType)) { |
| MessageOutput.println("not a class", className); |
| return; |
| } |
| |
| byte[] bytecodes = null; |
| for (Method method : rt.methodsByName(methodName)) { |
| if (!method.isAbstract()) { |
| bytecodes = method.bytecodes(); |
| break; |
| } |
| } |
| |
| StringBuffer line = new StringBuffer(80); |
| line.append("0000: "); |
| for (int i = 0; i < bytecodes.length; i++) { |
| if ((i > 0) && (i % 16 == 0)) { |
| MessageOutput.printDirectln(line.toString());// Special case: use printDirectln() |
| line.setLength(0); |
| line.append(String.valueOf(i)); |
| line.append(": "); |
| int len = line.length(); |
| for (int j = 0; j < 6 - len; j++) { |
| line.insert(0, '0'); |
| } |
| } |
| int val = 0xff & bytecodes[i]; |
| String str = Integer.toHexString(val); |
| if (str.length() == 1) { |
| line.append('0'); |
| } |
| line.append(str); |
| line.append(' '); |
| } |
| if (line.length() > 6) { |
| MessageOutput.printDirectln(line.toString());// Special case: use printDirectln() |
| } |
| } |
| |
| void commandExclude(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.printDirectln(Env.excludesString());// Special case: use printDirectln() |
| } else { |
| String rest = t.nextToken(""); |
| if (rest.equals("none")) { |
| rest = ""; |
| } |
| Env.setExcludes(rest); |
| } |
| } |
| |
| void commandRedefine(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Specify classes to redefine"); |
| } else { |
| String className = t.nextToken(); |
| List<ReferenceType> classes = Env.vm().classesByName(className); |
| if (classes.size() == 0) { |
| MessageOutput.println("No class named", className); |
| return; |
| } |
| if (classes.size() > 1) { |
| MessageOutput.println("More than one class named", className); |
| return; |
| } |
| Env.setSourcePath(Env.getSourcePath()); |
| ReferenceType refType = classes.get(0); |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("Specify file name for class", className); |
| return; |
| } |
| String fileName = t.nextToken(); |
| File phyl = new File(fileName); |
| byte[] bytes = new byte[(int)phyl.length()]; |
| try { |
| InputStream in = new FileInputStream(phyl); |
| in.read(bytes); |
| in.close(); |
| } catch (Exception exc) { |
| MessageOutput.println("Error reading file", |
| new Object [] {fileName, exc.toString()}); |
| return; |
| } |
| Map<ReferenceType, byte[]> map |
| = new HashMap<ReferenceType, byte[]>(); |
| map.put(refType, bytes); |
| try { |
| Env.vm().redefineClasses(map); |
| } catch (Throwable exc) { |
| MessageOutput.println("Error redefining class to file", |
| new Object [] {className, |
| fileName, |
| exc}); |
| } |
| } |
| } |
| |
| void commandPopFrames(StringTokenizer t, boolean reenter) { |
| ThreadInfo threadInfo; |
| |
| if (t.hasMoreTokens()) { |
| String token = t.nextToken(); |
| threadInfo = doGetThread(token); |
| if (threadInfo == null) { |
| return; |
| } |
| } else { |
| threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| if (threadInfo == null) { |
| MessageOutput.println("No thread specified."); |
| return; |
| } |
| } |
| |
| try { |
| StackFrame frame = threadInfo.getCurrentFrame(); |
| threadInfo.getThread().popFrames(frame); |
| threadInfo = ThreadInfo.getCurrentThreadInfo(); |
| ThreadInfo.setCurrentThreadInfo(threadInfo); |
| if (reenter) { |
| commandStepi(); |
| } |
| } catch (Throwable exc) { |
| MessageOutput.println("Error popping frame", exc.toString()); |
| } |
| } |
| |
| void commandExtension(StringTokenizer t) { |
| if (!t.hasMoreTokens()) { |
| MessageOutput.println("No class specified."); |
| return; |
| } |
| |
| String idClass = t.nextToken(); |
| ReferenceType cls = Env.getReferenceTypeFromToken(idClass); |
| String extension = null; |
| if (cls != null) { |
| try { |
| extension = cls.sourceDebugExtension(); |
| MessageOutput.println("sourcedebugextension", extension); |
| } catch (AbsentInformationException e) { |
| MessageOutput.println("No sourcedebugextension specified"); |
| } |
| } else { |
| MessageOutput.println("is not a valid id or class name", idClass); |
| } |
| } |
| |
| void commandVersion(String debuggerName, |
| VirtualMachineManager vmm) { |
| MessageOutput.println("minus version", |
| new Object [] { debuggerName, |
| new Integer(vmm.majorInterfaceVersion()), |
| new Integer(vmm.minorInterfaceVersion()), |
| System.getProperty("java.version")}); |
| if (Env.connection() != null) { |
| try { |
| MessageOutput.printDirectln(Env.vm().description());// Special case: use printDirectln() |
| } catch (VMNotConnectedException e) { |
| MessageOutput.println("No VM connected"); |
| } |
| } |
| } |
| } |